Annotation of sys/dev/pci/piixpm.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: piixpm.c,v 1.27 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 PIIX and compatible Power Management 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/piixreg.h>
! 37:
! 38: #include <dev/i2c/i2cvar.h>
! 39:
! 40: #ifdef PIIXPM_DEBUG
! 41: #define DPRINTF(x) printf x
! 42: #else
! 43: #define DPRINTF(x)
! 44: #endif
! 45:
! 46: #define PIIXPM_DELAY 200
! 47: #define PIIXPM_TIMEOUT 1
! 48:
! 49: struct piixpm_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 piixpm_match(struct device *, void *, void *);
! 69: void piixpm_attach(struct device *, struct device *, void *);
! 70:
! 71: int piixpm_i2c_acquire_bus(void *, int);
! 72: void piixpm_i2c_release_bus(void *, int);
! 73: int piixpm_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
! 74: void *, size_t, int);
! 75:
! 76: int piixpm_intr(void *);
! 77:
! 78: struct cfattach piixpm_ca = {
! 79: sizeof(struct piixpm_softc),
! 80: piixpm_match,
! 81: piixpm_attach
! 82: };
! 83:
! 84: struct cfdriver piixpm_cd = {
! 85: NULL, "piixpm", DV_DULL
! 86: };
! 87:
! 88: const struct pci_matchid piixpm_ids[] = {
! 89: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82371AB_PM },
! 90: { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82440MX_PM },
! 91: { PCI_VENDOR_RCC, PCI_PRODUCT_RCC_OSB4 },
! 92: { PCI_VENDOR_RCC, PCI_PRODUCT_RCC_CSB5 },
! 93: { PCI_VENDOR_RCC, PCI_PRODUCT_RCC_CSB6 },
! 94: { PCI_VENDOR_RCC, PCI_PRODUCT_RCC_HT_1000 },
! 95: { PCI_VENDOR_SMSC, PCI_PRODUCT_SMSC_VICTORY66_PM },
! 96: { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_SB200_SMB },
! 97: { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_IXP_SMB_300 },
! 98: { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_IXP_SMB_400 },
! 99: { PCI_VENDOR_ATI, PCI_PRODUCT_ATI_IXP_SMB_600 }
! 100: };
! 101:
! 102: int
! 103: piixpm_match(struct device *parent, void *match, void *aux)
! 104: {
! 105: return (pci_matchbyid(aux, piixpm_ids,
! 106: sizeof(piixpm_ids) / sizeof(piixpm_ids[0])));
! 107: }
! 108:
! 109: void
! 110: piixpm_attach(struct device *parent, struct device *self, void *aux)
! 111: {
! 112: struct piixpm_softc *sc = (struct piixpm_softc *)self;
! 113: struct pci_attach_args *pa = aux;
! 114: struct i2cbus_attach_args iba;
! 115: pcireg_t base, conf;
! 116: pci_intr_handle_t ih;
! 117: const char *intrstr = NULL;
! 118:
! 119: /* Read configuration */
! 120: conf = pci_conf_read(pa->pa_pc, pa->pa_tag, PIIX_SMB_HOSTC);
! 121: DPRINTF((": conf 0x%08x", conf));
! 122:
! 123: if ((conf & PIIX_SMB_HOSTC_HSTEN) == 0) {
! 124: printf(": SMBus disabled\n");
! 125: return;
! 126: }
! 127:
! 128: /* Map I/O space */
! 129: sc->sc_iot = pa->pa_iot;
! 130: base = pci_conf_read(pa->pa_pc, pa->pa_tag, PIIX_SMB_BASE) & 0xffff;
! 131: if (PCI_MAPREG_IO_ADDR(base) == 0 ||
! 132: bus_space_map(sc->sc_iot, PCI_MAPREG_IO_ADDR(base),
! 133: PIIX_SMB_SIZE, 0, &sc->sc_ioh)) {
! 134: printf(": can't map I/O space\n");
! 135: return;
! 136: }
! 137:
! 138: sc->sc_poll = 1;
! 139: if ((conf & PIIX_SMB_HOSTC_INTMASK) == PIIX_SMB_HOSTC_SMI) {
! 140: /* No PCI IRQ */
! 141: printf(": SMI");
! 142: } else {
! 143: if ((conf & PIIX_SMB_HOSTC_INTMASK) == PIIX_SMB_HOSTC_IRQ) {
! 144: /* Install interrupt handler */
! 145: if (pci_intr_map(pa, &ih) == 0) {
! 146: intrstr = pci_intr_string(pa->pa_pc, ih);
! 147: sc->sc_ih = pci_intr_establish(pa->pa_pc,
! 148: ih, IPL_BIO, piixpm_intr, sc,
! 149: sc->sc_dev.dv_xname);
! 150: if (sc->sc_ih != NULL) {
! 151: printf(": %s", intrstr);
! 152: sc->sc_poll = 0;
! 153: }
! 154: }
! 155: }
! 156: if (sc->sc_poll)
! 157: printf(": polling");
! 158: }
! 159:
! 160: printf("\n");
! 161:
! 162: /* Attach I2C bus */
! 163: rw_init(&sc->sc_i2c_lock, "iiclk");
! 164: sc->sc_i2c_tag.ic_cookie = sc;
! 165: sc->sc_i2c_tag.ic_acquire_bus = piixpm_i2c_acquire_bus;
! 166: sc->sc_i2c_tag.ic_release_bus = piixpm_i2c_release_bus;
! 167: sc->sc_i2c_tag.ic_exec = piixpm_i2c_exec;
! 168:
! 169: bzero(&iba, sizeof(iba));
! 170: iba.iba_name = "iic";
! 171: iba.iba_tag = &sc->sc_i2c_tag;
! 172: config_found(self, &iba, iicbus_print);
! 173:
! 174: return;
! 175: }
! 176:
! 177: int
! 178: piixpm_i2c_acquire_bus(void *cookie, int flags)
! 179: {
! 180: struct piixpm_softc *sc = cookie;
! 181:
! 182: if (cold || sc->sc_poll || (flags & I2C_F_POLL))
! 183: return (0);
! 184:
! 185: return (rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR));
! 186: }
! 187:
! 188: void
! 189: piixpm_i2c_release_bus(void *cookie, int flags)
! 190: {
! 191: struct piixpm_softc *sc = cookie;
! 192:
! 193: if (cold || sc->sc_poll || (flags & I2C_F_POLL))
! 194: return;
! 195:
! 196: rw_exit(&sc->sc_i2c_lock);
! 197: }
! 198:
! 199: int
! 200: piixpm_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
! 201: const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
! 202: {
! 203: struct piixpm_softc *sc = cookie;
! 204: u_int8_t *b;
! 205: u_int8_t ctl, st;
! 206: int retries;
! 207:
! 208: DPRINTF(("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, "
! 209: "flags 0x%02x\n", sc->sc_dev.dv_xname, op, addr, cmdlen,
! 210: len, flags));
! 211:
! 212: /* Wait for bus to be idle */
! 213: for (retries = 100; retries > 0; retries--) {
! 214: st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS);
! 215: if (!(st & PIIX_SMB_HS_BUSY))
! 216: break;
! 217: DELAY(PIIXPM_DELAY);
! 218: }
! 219: DPRINTF(("%s: exec: st 0x%b\n", sc->sc_dev.dv_xname, st,
! 220: PIIX_SMB_HS_BITS));
! 221: if (st & PIIX_SMB_HS_BUSY)
! 222: return (1);
! 223:
! 224: if (cold || sc->sc_poll)
! 225: flags |= I2C_F_POLL;
! 226:
! 227: if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
! 228: return (1);
! 229:
! 230: /* Setup transfer */
! 231: sc->sc_i2c_xfer.op = op;
! 232: sc->sc_i2c_xfer.buf = buf;
! 233: sc->sc_i2c_xfer.len = len;
! 234: sc->sc_i2c_xfer.flags = flags;
! 235: sc->sc_i2c_xfer.error = 0;
! 236:
! 237: /* Set slave address and transfer direction */
! 238: bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_TXSLVA,
! 239: PIIX_SMB_TXSLVA_ADDR(addr) |
! 240: (I2C_OP_READ_P(op) ? PIIX_SMB_TXSLVA_READ : 0));
! 241:
! 242: b = (void *)cmdbuf;
! 243: if (cmdlen > 0)
! 244: /* Set command byte */
! 245: bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HCMD, b[0]);
! 246:
! 247: if (I2C_OP_WRITE_P(op)) {
! 248: /* Write data */
! 249: b = buf;
! 250: if (len > 0)
! 251: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
! 252: PIIX_SMB_HD0, b[0]);
! 253: if (len > 1)
! 254: bus_space_write_1(sc->sc_iot, sc->sc_ioh,
! 255: PIIX_SMB_HD1, b[1]);
! 256: }
! 257:
! 258: /* Set SMBus command */
! 259: if (len == 0)
! 260: ctl = PIIX_SMB_HC_CMD_BYTE;
! 261: else if (len == 1)
! 262: ctl = PIIX_SMB_HC_CMD_BDATA;
! 263: else if (len == 2)
! 264: ctl = PIIX_SMB_HC_CMD_WDATA;
! 265:
! 266: if ((flags & I2C_F_POLL) == 0)
! 267: ctl |= PIIX_SMB_HC_INTREN;
! 268:
! 269: /* Start transaction */
! 270: ctl |= PIIX_SMB_HC_START;
! 271: bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HC, ctl);
! 272:
! 273: if (flags & I2C_F_POLL) {
! 274: /* Poll for completion */
! 275: DELAY(PIIXPM_DELAY);
! 276: for (retries = 1000; retries > 0; retries--) {
! 277: st = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
! 278: PIIX_SMB_HS);
! 279: if ((st & PIIX_SMB_HS_BUSY) == 0)
! 280: break;
! 281: DELAY(PIIXPM_DELAY);
! 282: }
! 283: if (st & PIIX_SMB_HS_BUSY)
! 284: goto timeout;
! 285: piixpm_intr(sc);
! 286: } else {
! 287: /* Wait for interrupt */
! 288: if (tsleep(sc, PRIBIO, "iicexec", PIIXPM_TIMEOUT * hz))
! 289: goto timeout;
! 290: }
! 291:
! 292: if (sc->sc_i2c_xfer.error)
! 293: return (1);
! 294:
! 295: return (0);
! 296:
! 297: timeout:
! 298: /*
! 299: * Transfer timeout. Kill the transaction and clear status bits.
! 300: */
! 301: printf("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, "
! 302: "flags 0x%02x: timeout, status 0x%b\n",
! 303: sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags,
! 304: st, PIIX_SMB_HS_BITS);
! 305: bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HC,
! 306: PIIX_SMB_HC_KILL);
! 307: DELAY(PIIXPM_DELAY);
! 308: st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS);
! 309: if ((st & PIIX_SMB_HS_FAILED) == 0)
! 310: printf("%s: abort failed, status 0x%b\n",
! 311: sc->sc_dev.dv_xname, st, PIIX_SMB_HS_BITS);
! 312: bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS, st);
! 313: return (1);
! 314: }
! 315:
! 316: int
! 317: piixpm_intr(void *arg)
! 318: {
! 319: struct piixpm_softc *sc = arg;
! 320: u_int8_t st;
! 321: u_int8_t *b;
! 322: size_t len;
! 323:
! 324: /* Read status */
! 325: st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS);
! 326: if ((st & PIIX_SMB_HS_BUSY) != 0 || (st & (PIIX_SMB_HS_INTR |
! 327: PIIX_SMB_HS_DEVERR | PIIX_SMB_HS_BUSERR |
! 328: PIIX_SMB_HS_FAILED)) == 0)
! 329: /* Interrupt was not for us */
! 330: return (0);
! 331:
! 332: DPRINTF(("%s: intr st 0x%b\n", sc->sc_dev.dv_xname, st,
! 333: PIIX_SMB_HS_BITS));
! 334:
! 335: /* Clear status bits */
! 336: bus_space_write_1(sc->sc_iot, sc->sc_ioh, PIIX_SMB_HS, st);
! 337:
! 338: /* Check for errors */
! 339: if (st & (PIIX_SMB_HS_DEVERR | PIIX_SMB_HS_BUSERR |
! 340: PIIX_SMB_HS_FAILED)) {
! 341: sc->sc_i2c_xfer.error = 1;
! 342: goto done;
! 343: }
! 344:
! 345: if (st & PIIX_SMB_HS_INTR) {
! 346: if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
! 347: goto done;
! 348:
! 349: /* Read data */
! 350: b = sc->sc_i2c_xfer.buf;
! 351: len = sc->sc_i2c_xfer.len;
! 352: if (len > 0)
! 353: b[0] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
! 354: PIIX_SMB_HD0);
! 355: if (len > 1)
! 356: b[1] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
! 357: PIIX_SMB_HD1);
! 358: }
! 359:
! 360: done:
! 361: if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
! 362: wakeup(sc);
! 363: return (1);
! 364: }
CVSweb