Annotation of sys/dev/pci/viapm.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: viapm.c,v 1.8 2007/05/03 09:36:26 dlg Exp $ */
2:
3: /*
4: * Copyright (c) 2005 Mark Kettenis <kettenis@openbsd.org>
5: *
6: * Permission to use, copy, modify, and distribute this software for any
7: * purpose with or without fee is hereby granted, provided that the above
8: * copyright notice and this permission notice appear in all copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17: */
18:
19: /*
20: * VIA VT8237 SMBus controller driver.
21: */
22:
23: #include <sys/param.h>
24: #include <sys/systm.h>
25: #include <sys/device.h>
26: #include <sys/kernel.h>
27: #include <sys/rwlock.h>
28: #include <sys/proc.h>
29:
30: #include <machine/bus.h>
31:
32: #include <dev/pci/pcidevs.h>
33: #include <dev/pci/pcireg.h>
34: #include <dev/pci/pcivar.h>
35:
36: #include <dev/i2c/i2cvar.h>
37:
38: /*
39: * VIA VT8237 ISA register definitions.
40: */
41:
42: /* PCI configuration registers */
43: #define VIAPM_SMB_BASE 0xd0 /* SMBus base address */
44: #define VIAPM_SMB_HOSTC 0xd2 /* host configuration */
45: #define VIAPM_SMB_HOSTC_HSTEN (1 << 0) /* enable host controller */
46: #define VIAPM_SMB_HOSTC_INTEN (1 << 1) /* enable SCI/SMI */
47: #define VIAPM_SMB_HOSTC_SCIEN (1 << 3) /* interrupt type (SCI/SMI) */
48:
49: /* SMBus I/O registers */
50: #define VIAPM_SMB_HS 0x00 /* host status */
51: #define VIAPM_SMB_HS_BUSY (1 << 0) /* running a command */
52: #define VIAPM_SMB_HS_INTR (1 << 1) /* command completed */
53: #define VIAPM_SMB_HS_DEVERR (1 << 2) /* command error */
54: #define VIAPM_SMB_HS_BUSERR (1 << 3) /* transaction collision */
55: #define VIAPM_SMB_HS_FAILED (1 << 4) /* failed bus transaction */
56: #define VIAPM_SMB_HS_INUSE (1 << 6) /* bus semaphore */
57: #define VIAPM_SMB_HS_BITS \
58: "\020\001BUSY\002INTR\003DEVERR\004BUSERR\005FAILED\007INUSE"
59: #define VIAPM_SMB_HC 0x02 /* host control */
60: #define VIAPM_SMB_HC_INTREN (1 << 0) /* enable interrupts */
61: #define VIAPM_SMB_HC_KILL (1 << 1) /* kill current transaction */
62: #define VIAPM_SMB_HC_CMD_QUICK (0 << 2) /* QUICK command */
63: #define VIAPM_SMB_HC_CMD_BYTE (1 << 2) /* BYTE command */
64: #define VIAPM_SMB_HC_CMD_BDATA (2 << 2) /* BYTE DATA command */
65: #define VIAPM_SMB_HC_CMD_WDATA (3 << 2) /* WORD DATA command */
66: #define VIAPM_SMB_HC_CMD_PCALL (4 << 2) /* PROCESS CALL command */
67: #define VIAPM_SMB_HC_CMD_BLOCK (5 << 2) /* BLOCK command */
68: #define VIAPM_SMB_HC_START (1 << 6) /* start transaction */
69: #define VIAPM_SMB_HCMD 0x03 /* host command */
70: #define VIAPM_SMB_TXSLVA 0x04 /* transmit slave address */
71: #define VIAPM_SMB_TXSLVA_READ (1 << 0) /* read direction */
72: #define VIAPM_SMB_TXSLVA_ADDR(x) (((x) & 0x7f) << 1) /* 7-bit address */
73: #define VIAPM_SMB_HD0 0x05 /* host data 0 */
74: #define VIAPM_SMB_HD1 0x06 /* host data 1 */
75: #define VIAPM_SMB_HBDB 0x07 /* host block data byte */
76:
77: #define VIAPM_SMB_SIZE 16
78:
79: #ifdef VIAPM_DEBUG
80: #define DPRINTF(x) printf x
81: #else
82: #define DPRINTF(x)
83: #endif
84:
85: #define VIAPM_DELAY 100
86: #define VIAPM_TIMEOUT 1
87:
88: struct viapm_softc {
89: struct device sc_dev;
90:
91: bus_space_tag_t sc_iot;
92: bus_space_handle_t sc_ioh;
93: void * sc_ih;
94: int sc_poll;
95:
96: struct i2c_controller sc_i2c_tag;
97: struct rwlock sc_i2c_lock;
98: struct {
99: i2c_op_t op;
100: void * buf;
101: size_t len;
102: int flags;
103: volatile int error;
104: } sc_i2c_xfer;
105: };
106:
107: int viapm_match(struct device *, void *, void *);
108: void viapm_attach(struct device *, struct device *, void *);
109:
110: int viapm_i2c_acquire_bus(void *, int);
111: void viapm_i2c_release_bus(void *, int);
112: int viapm_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
113: void *, size_t, int);
114:
115: int viapm_intr(void *);
116:
117: struct cfattach viapm_ca = {
118: sizeof(struct viapm_softc),
119: viapm_match,
120: viapm_attach
121: };
122:
123: struct cfdriver viapm_cd = {
124: NULL, "viapm", DV_DULL
125: };
126:
127: const struct pci_matchid viapm_ids[] = {
128: { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8233_ISA },
129: { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8233A_ISA },
130: { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8235_ISA },
131: { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8237_ISA },
132: { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8237A_ISA },
133: { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8251_ISA },
134: { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_CX700_ISA }
135: };
136:
137: int
138: viapm_match(struct device *parent, void *match, void *aux)
139: {
140: return (pci_matchbyid(aux, viapm_ids,
141: sizeof(viapm_ids) / sizeof(viapm_ids[0])));
142: }
143:
144: void
145: viapm_attach(struct device *parent, struct device *self, void *aux)
146: {
147: struct viapm_softc *sc = (struct viapm_softc *)self;
148: struct pci_attach_args *pa = aux;
149: struct i2cbus_attach_args iba;
150: pcireg_t conf, iobase;
151: #if 0
152: pci_intr_handle_t ih;
153: const char *intrstr = NULL;
154: #endif
155:
156: /* Map I/O space */
157: sc->sc_iot = pa->pa_iot;
158: iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, VIAPM_SMB_BASE);
159: if (iobase == 0 ||
160: bus_space_map(sc->sc_iot, iobase & 0xfffe,
161: VIAPM_SMB_SIZE, 0, &sc->sc_ioh)) {
162: printf(": can't map I/O space\n");
163: return;
164: }
165:
166: /* Read configuration */
167: conf = (iobase >> 16);
168: DPRINTF((": conf 0x%x", conf));
169:
170: if ((conf & VIAPM_SMB_HOSTC_HSTEN) == 0) {
171: printf(": SMBus host disabled\n");
172: goto fail;
173: }
174:
175: if (conf & VIAPM_SMB_HOSTC_INTEN) {
176: if (conf & VIAPM_SMB_HOSTC_SCIEN)
177: printf(": SCI");
178: else
179: printf(": SMI");
180: sc->sc_poll = 1;
181: } else {
182: #if 0
183: /* Install interrupt handler */
184: if (pci_intr_map(pa, &ih)) {
185: printf(": can't map interrupt\n");
186: goto fail;
187: }
188: intrstr = pci_intr_string(pa->pa_pc, ih);
189: sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO,
190: viapm_intr, sc, sc->sc_dev.dv_xname);
191: if (sc->sc_ih == NULL) {
192: printf(": can't establish interrupt");
193: if (intrstr != NULL)
194: printf(" at %s", intrstr);
195: printf("\n");
196: goto fail;
197: }
198: printf(": %s", intrstr);
199: #endif
200: sc->sc_poll = 1;
201: }
202:
203: printf("\n");
204:
205: /* Attach I2C bus */
206: rw_init(&sc->sc_i2c_lock, "iiclk");
207: sc->sc_i2c_tag.ic_cookie = sc;
208: sc->sc_i2c_tag.ic_acquire_bus = viapm_i2c_acquire_bus;
209: sc->sc_i2c_tag.ic_release_bus = viapm_i2c_release_bus;
210: sc->sc_i2c_tag.ic_exec = viapm_i2c_exec;
211:
212: bzero(&iba, sizeof iba);
213: iba.iba_name = "iic";
214: iba.iba_tag = &sc->sc_i2c_tag;
215: config_found(self, &iba, iicbus_print);
216:
217: return;
218:
219: fail:
220: bus_space_unmap(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_SIZE);
221: }
222:
223: int
224: viapm_i2c_acquire_bus(void *cookie, int flags)
225: {
226: struct viapm_softc *sc = cookie;
227:
228: if (cold || sc->sc_poll || (flags & I2C_F_POLL))
229: return (0);
230:
231: return (rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR));
232: }
233:
234: void
235: viapm_i2c_release_bus(void *cookie, int flags)
236: {
237: struct viapm_softc *sc = cookie;
238:
239: if (cold || sc->sc_poll || (flags & I2C_F_POLL))
240: return;
241:
242: rw_exit(&sc->sc_i2c_lock);
243: }
244:
245: int
246: viapm_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
247: const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
248: {
249: struct viapm_softc *sc = cookie;
250: u_int8_t *b;
251: u_int8_t ctl, st;
252: int retries;
253:
254: DPRINTF(("%s: exec op %d, addr 0x%x, cmdlen %d, len %d, "
255: "flags 0x%x, status 0x%b\n", sc->sc_dev.dv_xname, op, addr,
256: cmdlen, len, flags, bus_space_read_1(sc->sc_iot, sc->sc_ioh,
257: VIAPM_SMB_HS), VIAPM_SMB_HS_BITS));
258:
259: /* Check if there's a transfer already running */
260: st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HS);
261: DPRINTF(("%s: exec: st 0x%b\n", sc->sc_dev.dv_xname, st,
262: VIAPM_SMB_HS_BITS));
263: if (st & VIAPM_SMB_HS_BUSY)
264: return (1);
265:
266: if (cold || sc->sc_poll)
267: flags |= I2C_F_POLL;
268:
269: if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
270: return (1);
271:
272: /* Setup transfer */
273: sc->sc_i2c_xfer.op = op;
274: sc->sc_i2c_xfer.buf = buf;
275: sc->sc_i2c_xfer.len = len;
276: sc->sc_i2c_xfer.flags = flags;
277: sc->sc_i2c_xfer.error = 0;
278:
279: /* Set slave address and transfer direction */
280: bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_TXSLVA,
281: VIAPM_SMB_TXSLVA_ADDR(addr) |
282: (I2C_OP_READ_P(op) ? VIAPM_SMB_TXSLVA_READ : 0));
283:
284: b = (void *)cmdbuf;
285: if (cmdlen > 0)
286: /* Set command byte */
287: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
288: VIAPM_SMB_HCMD, b[0]);
289:
290: if (I2C_OP_WRITE_P(op)) {
291: /* Write data */
292: b = buf;
293: if (len > 0)
294: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
295: VIAPM_SMB_HD0, b[0]);
296: if (len > 1)
297: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
298: VIAPM_SMB_HD1, b[1]);
299: }
300:
301: /* Set SMBus command */
302: if (len == 0)
303: ctl = VIAPM_SMB_HC_CMD_BYTE;
304: else if (len == 1)
305: ctl = VIAPM_SMB_HC_CMD_BDATA;
306: else if (len == 2)
307: ctl = VIAPM_SMB_HC_CMD_WDATA;
308:
309: if ((flags & I2C_F_POLL) == 0)
310: ctl |= VIAPM_SMB_HC_INTREN;
311:
312: /* Start transaction */
313: ctl |= VIAPM_SMB_HC_START;
314: bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HC, ctl);
315:
316: if (flags & I2C_F_POLL) {
317: /* Poll for completion */
318: DELAY(VIAPM_DELAY);
319: for (retries = 1000; retries > 0; retries--) {
320: st = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
321: VIAPM_SMB_HS);
322: if ((st & VIAPM_SMB_HS_BUSY) == 0)
323: break;
324: DELAY(VIAPM_DELAY);
325: }
326: if (st & VIAPM_SMB_HS_BUSY)
327: goto timeout;
328: viapm_intr(sc);
329: } else {
330: /* Wait for interrupt */
331: if (tsleep(sc, PRIBIO, "iicexec", VIAPM_TIMEOUT * hz))
332: goto timeout;
333: }
334:
335: if (sc->sc_i2c_xfer.error)
336: return (1);
337:
338: return (0);
339:
340: timeout:
341: /*
342: * Transfer timeout. Kill the transaction and clear status bits.
343: */
344: printf("%s: timeout, status 0x%b\n", sc->sc_dev.dv_xname, st,
345: VIAPM_SMB_HS_BITS);
346: bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HC,
347: VIAPM_SMB_HC_KILL);
348: DELAY(VIAPM_DELAY);
349: st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HS);
350: if ((st & VIAPM_SMB_HS_FAILED) == 0)
351: printf("%s: transaction abort failed, status 0x%b\n",
352: sc->sc_dev.dv_xname, st, VIAPM_SMB_HS_BITS);
353: bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HS, st);
354: return (1);
355: }
356:
357: int
358: viapm_intr(void *arg)
359: {
360: struct viapm_softc *sc = arg;
361: u_int8_t st;
362: u_int8_t *b;
363: size_t len;
364:
365: /* Read status */
366: st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HS);
367: if ((st & VIAPM_SMB_HS_BUSY) != 0 || (st & (VIAPM_SMB_HS_INTR |
368: VIAPM_SMB_HS_DEVERR | VIAPM_SMB_HS_BUSERR |
369: VIAPM_SMB_HS_FAILED)) == 0)
370: /* Interrupt was not for us */
371: return (0);
372:
373: DPRINTF(("%s: intr st 0x%b\n", sc->sc_dev.dv_xname, st,
374: VIAPM_SMB_HS_BITS));
375:
376: /* Clear status bits */
377: bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HS, st);
378:
379: /* Check for errors */
380: if (st & (VIAPM_SMB_HS_DEVERR | VIAPM_SMB_HS_BUSERR |
381: VIAPM_SMB_HS_FAILED)) {
382: sc->sc_i2c_xfer.error = 1;
383: goto done;
384: }
385:
386: if (st & VIAPM_SMB_HS_INTR) {
387: if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
388: goto done;
389:
390: /* Read data */
391: b = sc->sc_i2c_xfer.buf;
392: len = sc->sc_i2c_xfer.len;
393: if (len > 0)
394: b[0] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
395: VIAPM_SMB_HD0);
396: if (len > 1)
397: b[1] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
398: VIAPM_SMB_HD1);
399: }
400:
401: done:
402: if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
403: wakeup(sc);
404: return (1);
405: }
CVSweb