Annotation of sys/dev/pci/ichiic.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: ichiic.c,v 1.18 2007/05/03 09:36:26 dlg Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2005, 2006 Alexander Yurchenko <grange@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: * Intel ICH 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/pci/ichreg.h>
! 37:
! 38: #include <dev/i2c/i2cvar.h>
! 39:
! 40: #ifdef ICHIIC_DEBUG
! 41: #define DPRINTF(x) printf x
! 42: #else
! 43: #define DPRINTF(x)
! 44: #endif
! 45:
! 46: #define ICHIIC_DELAY 100
! 47: #define ICHIIC_TIMEOUT 1
! 48:
! 49: struct ichiic_softc {
! 50: struct device sc_dev;
! 51:
! 52: bus_space_tag_t sc_iot;
! 53: bus_space_handle_t sc_ioh;
! 54: void * sc_ih;
! 55: int sc_poll;
! 56:
! 57: struct i2c_controller sc_i2c_tag;
! 58: struct rwlock sc_i2c_lock;
! 59: struct {
! 60: i2c_op_t op;
! 61: void * buf;
! 62: size_t len;
! 63: int flags;
! 64: volatile int error;
! 65: } sc_i2c_xfer;
! 66: };
! 67:
! 68: int ichiic_match(struct device *, void *, void *);
! 69: void ichiic_attach(struct device *, struct device *, void *);
! 70:
! 71: int ichiic_i2c_acquire_bus(void *, int);
! 72: void ichiic_i2c_release_bus(void *, int);
! 73: int ichiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
! 74: void *, size_t, int);
! 75:
! 76: int ichiic_intr(void *);
! 77:
! 78: struct cfattach ichiic_ca = {
! 79: sizeof(struct ichiic_softc),
! 80: ichiic_match,
! 81: ichiic_attach
! 82: };
! 83:
! 84: struct cfdriver ichiic_cd = {
! 85: NULL, "ichiic", DV_DULL
! 86: };
! 87:
! 88: const struct pci_matchid ichiic_ids[] = {
! 89: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6300ESB_SMB },
! 90: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_6321ESB_SMB },
! 91: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AA_SMB },
! 92: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801AB_SMB },
! 93: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801BA_SMB },
! 94: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801CA_SMB },
! 95: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801DB_SMB },
! 96: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801E_SMB },
! 97: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801EB_SMB },
! 98: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801FB_SMB },
! 99: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801GB_SMB },
! 100: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82801H_SMB }
! 101: };
! 102:
! 103: int
! 104: ichiic_match(struct device *parent, void *match, void *aux)
! 105: {
! 106: return (pci_matchbyid(aux, ichiic_ids,
! 107: sizeof(ichiic_ids) / sizeof(ichiic_ids[0])));
! 108: }
! 109:
! 110: void
! 111: ichiic_attach(struct device *parent, struct device *self, void *aux)
! 112: {
! 113: struct ichiic_softc *sc = (struct ichiic_softc *)self;
! 114: struct pci_attach_args *pa = aux;
! 115: struct i2cbus_attach_args iba;
! 116: pcireg_t conf;
! 117: bus_size_t iosize;
! 118: pci_intr_handle_t ih;
! 119: const char *intrstr = NULL;
! 120:
! 121: /* Read configuration */
! 122: conf = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_SMB_HOSTC);
! 123: DPRINTF((": conf 0x%08x", conf));
! 124:
! 125: if ((conf & ICH_SMB_HOSTC_HSTEN) == 0) {
! 126: printf(": SMBus disabled\n");
! 127: return;
! 128: }
! 129:
! 130: /* Map I/O space */
! 131: if (pci_mapreg_map(pa, ICH_SMB_BASE, PCI_MAPREG_TYPE_IO, 0,
! 132: &sc->sc_iot, &sc->sc_ioh, NULL, &iosize, 0)) {
! 133: printf(": can't map I/O space\n");
! 134: return;
! 135: }
! 136:
! 137: sc->sc_poll = 1;
! 138: if (conf & ICH_SMB_HOSTC_SMIEN) {
! 139: /* No PCI IRQ */
! 140: printf(": SMI");
! 141: } else {
! 142: /* Install interrupt handler */
! 143: if (pci_intr_map(pa, &ih) == 0) {
! 144: intrstr = pci_intr_string(pa->pa_pc, ih);
! 145: sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO,
! 146: ichiic_intr, sc, sc->sc_dev.dv_xname);
! 147: if (sc->sc_ih != NULL) {
! 148: printf(": %s", intrstr);
! 149: sc->sc_poll = 0;
! 150: }
! 151: }
! 152: if (sc->sc_poll)
! 153: printf(": polling");
! 154: }
! 155:
! 156: printf("\n");
! 157:
! 158: /* Attach I2C bus */
! 159: rw_init(&sc->sc_i2c_lock, "iiclk");
! 160: sc->sc_i2c_tag.ic_cookie = sc;
! 161: sc->sc_i2c_tag.ic_acquire_bus = ichiic_i2c_acquire_bus;
! 162: sc->sc_i2c_tag.ic_release_bus = ichiic_i2c_release_bus;
! 163: sc->sc_i2c_tag.ic_exec = ichiic_i2c_exec;
! 164:
! 165: bzero(&iba, sizeof(iba));
! 166: iba.iba_name = "iic";
! 167: iba.iba_tag = &sc->sc_i2c_tag;
! 168: config_found(self, &iba, iicbus_print);
! 169:
! 170: return;
! 171: }
! 172:
! 173: int
! 174: ichiic_i2c_acquire_bus(void *cookie, int flags)
! 175: {
! 176: struct ichiic_softc *sc = cookie;
! 177:
! 178: if (cold || sc->sc_poll || (flags & I2C_F_POLL))
! 179: return (0);
! 180:
! 181: return (rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR));
! 182: }
! 183:
! 184: void
! 185: ichiic_i2c_release_bus(void *cookie, int flags)
! 186: {
! 187: struct ichiic_softc *sc = cookie;
! 188:
! 189: if (cold || sc->sc_poll || (flags & I2C_F_POLL))
! 190: return;
! 191:
! 192: rw_exit(&sc->sc_i2c_lock);
! 193: }
! 194:
! 195: int
! 196: ichiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
! 197: const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
! 198: {
! 199: struct ichiic_softc *sc = cookie;
! 200: u_int8_t *b;
! 201: u_int8_t ctl, st;
! 202: int retries;
! 203:
! 204: DPRINTF(("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, "
! 205: "flags 0x%02x\n", sc->sc_dev.dv_xname, op, addr, cmdlen,
! 206: len, flags));
! 207:
! 208: /* Wait for bus to be idle */
! 209: for (retries = 100; retries > 0; retries--) {
! 210: st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS);
! 211: if (!(st & ICH_SMB_HS_BUSY))
! 212: break;
! 213: DELAY(ICHIIC_DELAY);
! 214: }
! 215: DPRINTF(("%s: exec: st 0x%b\n", sc->sc_dev.dv_xname, st,
! 216: ICH_SMB_HS_BITS));
! 217: if (st & ICH_SMB_HS_BUSY)
! 218: return (1);
! 219:
! 220: if (cold || sc->sc_poll)
! 221: flags |= I2C_F_POLL;
! 222:
! 223: if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
! 224: return (1);
! 225:
! 226: /* Setup transfer */
! 227: sc->sc_i2c_xfer.op = op;
! 228: sc->sc_i2c_xfer.buf = buf;
! 229: sc->sc_i2c_xfer.len = len;
! 230: sc->sc_i2c_xfer.flags = flags;
! 231: sc->sc_i2c_xfer.error = 0;
! 232:
! 233: /* Set slave address and transfer direction */
! 234: bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_TXSLVA,
! 235: ICH_SMB_TXSLVA_ADDR(addr) |
! 236: (I2C_OP_READ_P(op) ? ICH_SMB_TXSLVA_READ : 0));
! 237:
! 238: b = (void *)cmdbuf;
! 239: if (cmdlen > 0)
! 240: /* Set command byte */
! 241: bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HCMD, b[0]);
! 242:
! 243: if (I2C_OP_WRITE_P(op)) {
! 244: /* Write data */
! 245: b = buf;
! 246: if (len > 0)
! 247: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
! 248: ICH_SMB_HD0, b[0]);
! 249: if (len > 1)
! 250: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
! 251: ICH_SMB_HD1, b[1]);
! 252: }
! 253:
! 254: /* Set SMBus command */
! 255: if (len == 0)
! 256: ctl = ICH_SMB_HC_CMD_BYTE;
! 257: else if (len == 1)
! 258: ctl = ICH_SMB_HC_CMD_BDATA;
! 259: else if (len == 2)
! 260: ctl = ICH_SMB_HC_CMD_WDATA;
! 261:
! 262: if ((flags & I2C_F_POLL) == 0)
! 263: ctl |= ICH_SMB_HC_INTREN;
! 264:
! 265: /* Start transaction */
! 266: ctl |= ICH_SMB_HC_START;
! 267: bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HC, ctl);
! 268:
! 269: if (flags & I2C_F_POLL) {
! 270: /* Poll for completion */
! 271: DELAY(ICHIIC_DELAY);
! 272: for (retries = 1000; retries > 0; retries--) {
! 273: st = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
! 274: ICH_SMB_HS);
! 275: if ((st & ICH_SMB_HS_BUSY) == 0)
! 276: break;
! 277: DELAY(ICHIIC_DELAY);
! 278: }
! 279: if (st & ICH_SMB_HS_BUSY)
! 280: goto timeout;
! 281: ichiic_intr(sc);
! 282: } else {
! 283: /* Wait for interrupt */
! 284: if (tsleep(sc, PRIBIO, "iicexec", ICHIIC_TIMEOUT * hz))
! 285: goto timeout;
! 286: }
! 287:
! 288: if (sc->sc_i2c_xfer.error)
! 289: return (1);
! 290:
! 291: return (0);
! 292:
! 293: timeout:
! 294: /*
! 295: * Transfer timeout. Kill the transaction and clear status bits.
! 296: */
! 297: printf("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, "
! 298: "flags 0x%02x: timeout, status 0x%b\n",
! 299: sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags,
! 300: st, ICH_SMB_HS_BITS);
! 301: bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HC,
! 302: ICH_SMB_HC_KILL);
! 303: DELAY(ICHIIC_DELAY);
! 304: st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS);
! 305: if ((st & ICH_SMB_HS_FAILED) == 0)
! 306: printf("%s: abort failed, status 0x%b\n",
! 307: sc->sc_dev.dv_xname, st, ICH_SMB_HS_BITS);
! 308: bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS, st);
! 309: return (1);
! 310: }
! 311:
! 312: int
! 313: ichiic_intr(void *arg)
! 314: {
! 315: struct ichiic_softc *sc = arg;
! 316: u_int8_t st;
! 317: u_int8_t *b;
! 318: size_t len;
! 319:
! 320: /* Read status */
! 321: st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS);
! 322: if ((st & ICH_SMB_HS_BUSY) != 0 || (st & (ICH_SMB_HS_INTR |
! 323: ICH_SMB_HS_DEVERR | ICH_SMB_HS_BUSERR | ICH_SMB_HS_FAILED |
! 324: ICH_SMB_HS_SMBAL | ICH_SMB_HS_BDONE)) == 0)
! 325: /* Interrupt was not for us */
! 326: return (0);
! 327:
! 328: DPRINTF(("%s: intr st 0x%b\n", sc->sc_dev.dv_xname, st,
! 329: ICH_SMB_HS_BITS));
! 330:
! 331: /* Clear status bits */
! 332: bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS, st);
! 333:
! 334: /* Check for errors */
! 335: if (st & (ICH_SMB_HS_DEVERR | ICH_SMB_HS_BUSERR | ICH_SMB_HS_FAILED)) {
! 336: sc->sc_i2c_xfer.error = 1;
! 337: goto done;
! 338: }
! 339:
! 340: if (st & ICH_SMB_HS_INTR) {
! 341: if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
! 342: goto done;
! 343:
! 344: /* Read data */
! 345: b = sc->sc_i2c_xfer.buf;
! 346: len = sc->sc_i2c_xfer.len;
! 347: if (len > 0)
! 348: b[0] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
! 349: ICH_SMB_HD0);
! 350: if (len > 1)
! 351: b[1] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
! 352: ICH_SMB_HD1);
! 353: }
! 354:
! 355: done:
! 356: if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
! 357: wakeup(sc);
! 358: return (1);
! 359: }
CVSweb