Annotation of sys/dev/pci/viapm.c, Revision 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