Annotation of sys/dev/pci/amdiic.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: amdiic.c,v 1.6 2007/05/03 09:36:26 dlg Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2005 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: * AMD-8111 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: #ifdef AMDIIC_DEBUG
! 39: #define DPRINTF(x) printf x
! 40: #else
! 41: #define DPRINTF(x)
! 42: #endif
! 43:
! 44: #define AMDIIC_DELAY 100
! 45: #define AMDIIC_TIMEOUT 1
! 46:
! 47: /* PCI configuration registers */
! 48: #define AMD8111_SMB_BASE 0x10 /* SMBus base address */
! 49: #define AMD8111_SMB_MISC 0x48 /* miscellaneous control */
! 50: #define AMD8111_SMB_MISC_SU (1 << 0) /* 16x clock speed-up */
! 51: #define AMD8111_SMB_MISC_INTEN (1 << 1) /* PCI IRQ enabled */
! 52: #define AMD8111_SMB_MISC_SCIEN (1 << 2) /* SCI enabled */
! 53:
! 54: /* SMBus I/O registers */
! 55: #define AMD8111_SMB_SC_DATA 0x00 /* data port */
! 56: #define AMD8111_SMB_SC_ST 0x04 /* status */
! 57: #define AMD8111_SMB_SC_ST_OBF (1 << 0) /* output buffer full */
! 58: #define AMD8111_SMB_SC_ST_IBF (1 << 1) /* input buffer full */
! 59: #define AMD8111_SMB_SC_ST_CMD (1 << 3) /* command byte */
! 60: #define AMD8111_SMB_SC_ST_BITS "\020\001OBF\002IBF\004CMD"
! 61: #define AMD8111_SMB_SC_CMD 0x04 /* command port */
! 62: #define AMD8111_SMB_SC_CMD_RD 0x80 /* read */
! 63: #define AMD8111_SMB_SC_CMD_WR 0x81 /* write */
! 64: #define AMD8111_SMB_SC_IC 0x08 /* interrupt control */
! 65:
! 66: /* Host controller interface registers */
! 67: #define AMD8111_SMB_PROTO 0x00 /* protocol */
! 68: #define AMD8111_SMB_PROTO_READ 0x01 /* read direction */
! 69: #define AMD8111_SMB_PROTO_QUICK 0x02 /* QUICK command */
! 70: #define AMD8111_SMB_PROTO_BYTE 0x04 /* BYTE command */
! 71: #define AMD8111_SMB_PROTO_BDATA 0x06 /* BYTE DATA command */
! 72: #define AMD8111_SMB_PROTO_WDATA 0x08 /* WORD DATA command */
! 73: #define AMD8111_SMB_STAT 0x01 /* status */
! 74: #define AMD8111_SMB_STAT_MASK 0x1f
! 75: #define AMD8111_SMB_STAT_DONE (1 << 7) /* command completion */
! 76: #define AMD8111_SMB_ADDR 0x02 /* address */
! 77: #define AMD8111_SMB_ADDR_SHIFT 1
! 78: #define AMD8111_SMB_CMD 0x03 /* SMBus command */
! 79: #define AMD8111_SMB_DATA(x) (0x04 + (x)) /* SMBus data */
! 80:
! 81: struct amdiic_softc {
! 82: struct device sc_dev;
! 83:
! 84: bus_space_tag_t sc_iot;
! 85: bus_space_handle_t sc_ioh;
! 86: void * sc_ih;
! 87: int sc_poll;
! 88:
! 89: struct i2c_controller sc_i2c_tag;
! 90: struct rwlock sc_i2c_lock;
! 91: struct {
! 92: i2c_op_t op;
! 93: void * buf;
! 94: size_t len;
! 95: int flags;
! 96: volatile int error;
! 97: } sc_i2c_xfer;
! 98: };
! 99:
! 100: int amdiic_match(struct device *, void *, void *);
! 101: void amdiic_attach(struct device *, struct device *, void *);
! 102:
! 103: int amdiic_read(struct amdiic_softc *, u_int8_t);
! 104: int amdiic_write(struct amdiic_softc *, u_int8_t, u_int8_t);
! 105: int amdiic_wait(struct amdiic_softc *, int);
! 106:
! 107: int amdiic_i2c_acquire_bus(void *, int);
! 108: void amdiic_i2c_release_bus(void *, int);
! 109: int amdiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
! 110: void *, size_t, int);
! 111:
! 112: int amdiic_intr(void *);
! 113:
! 114: struct cfattach amdiic_ca = {
! 115: sizeof(struct amdiic_softc),
! 116: amdiic_match,
! 117: amdiic_attach
! 118: };
! 119:
! 120: struct cfdriver amdiic_cd = {
! 121: NULL, "amdiic", DV_DULL
! 122: };
! 123:
! 124: const struct pci_matchid amdiic_ids[] = {
! 125: { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_8111_SMB }
! 126: };
! 127:
! 128: int
! 129: amdiic_match(struct device *parent, void *match, void *aux)
! 130: {
! 131: return (pci_matchbyid(aux, amdiic_ids,
! 132: sizeof(amdiic_ids) / sizeof(amdiic_ids[0])));
! 133: }
! 134:
! 135: void
! 136: amdiic_attach(struct device *parent, struct device *self, void *aux)
! 137: {
! 138: struct amdiic_softc *sc = (struct amdiic_softc *)self;
! 139: struct pci_attach_args *pa = aux;
! 140: struct i2cbus_attach_args iba;
! 141: pcireg_t conf;
! 142: bus_size_t iosize;
! 143: pci_intr_handle_t ih;
! 144: const char *intrstr = NULL;
! 145:
! 146: /* Map I/O space */
! 147: if (pci_mapreg_map(pa, AMD8111_SMB_BASE, PCI_MAPREG_TYPE_IO, 0,
! 148: &sc->sc_iot, &sc->sc_ioh, NULL, &iosize, 0)) {
! 149: printf(": can't map I/O space\n");
! 150: return;
! 151: }
! 152:
! 153: /* Read configuration */
! 154: conf = pci_conf_read(pa->pa_pc, pa->pa_tag, AMD8111_SMB_MISC);
! 155: DPRINTF((": conf 0x%08x", conf));
! 156:
! 157: sc->sc_poll = 1;
! 158: if (conf & AMD8111_SMB_MISC_SCIEN) {
! 159: /* No PCI IRQ */
! 160: printf(": SCI");
! 161: } else if (conf & AMD8111_SMB_MISC_INTEN) {
! 162: /* Install interrupt handler */
! 163: if (pci_intr_map(pa, &ih) == 0) {
! 164: intrstr = pci_intr_string(pa->pa_pc, ih);
! 165: sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO,
! 166: amdiic_intr, sc, sc->sc_dev.dv_xname);
! 167: if (sc->sc_ih != NULL) {
! 168: printf(": %s", intrstr);
! 169: sc->sc_poll = 0;
! 170: }
! 171: }
! 172: if (sc->sc_poll)
! 173: printf(": polling");
! 174: }
! 175:
! 176: printf("\n");
! 177:
! 178: /* Attach I2C bus */
! 179: rw_init(&sc->sc_i2c_lock, "iiclk");
! 180: sc->sc_i2c_tag.ic_cookie = sc;
! 181: sc->sc_i2c_tag.ic_acquire_bus = amdiic_i2c_acquire_bus;
! 182: sc->sc_i2c_tag.ic_release_bus = amdiic_i2c_release_bus;
! 183: sc->sc_i2c_tag.ic_exec = amdiic_i2c_exec;
! 184:
! 185: bzero(&iba, sizeof(iba));
! 186: iba.iba_name = "iic";
! 187: iba.iba_tag = &sc->sc_i2c_tag;
! 188: config_found(self, &iba, iicbus_print);
! 189:
! 190: return;
! 191: }
! 192:
! 193: int
! 194: amdiic_read(struct amdiic_softc *sc, u_int8_t reg)
! 195: {
! 196: if (amdiic_wait(sc, 0))
! 197: return (-1);
! 198: bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_CMD,
! 199: AMD8111_SMB_SC_CMD_RD);
! 200: if (amdiic_wait(sc, 0))
! 201: return (-1);
! 202: bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_DATA, reg);
! 203: if (amdiic_wait(sc, 1))
! 204: return (-1);
! 205:
! 206: return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_DATA));
! 207: }
! 208:
! 209: int
! 210: amdiic_write(struct amdiic_softc *sc, u_int8_t reg, u_int8_t val)
! 211: {
! 212: if (amdiic_wait(sc, 0))
! 213: return (-1);
! 214: bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_CMD,
! 215: AMD8111_SMB_SC_CMD_WR);
! 216: if (amdiic_wait(sc, 0))
! 217: return (-1);
! 218: bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_DATA, reg);
! 219: if (amdiic_wait(sc, 0))
! 220: return (-1);
! 221: bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_DATA, val);
! 222:
! 223: return (0);
! 224: }
! 225:
! 226: int
! 227: amdiic_wait(struct amdiic_softc *sc, int output)
! 228: {
! 229: int retries;
! 230: u_int8_t st;
! 231:
! 232: for (retries = 100; retries > 0; retries--) {
! 233: st = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
! 234: AMD8111_SMB_SC_ST);
! 235: if (output && (st & AMD8111_SMB_SC_ST_OBF))
! 236: return (0);
! 237: if (!output && (st & AMD8111_SMB_SC_ST_IBF) == 0)
! 238: return (0);
! 239: DELAY(1);
! 240: }
! 241: DPRINTF(("%s: %s wait timeout: st 0x%b\n", sc->sc_dev.dv_xname,
! 242: (output ? "output" : "input"), st));
! 243:
! 244: return (1);
! 245: }
! 246:
! 247: int
! 248: amdiic_i2c_acquire_bus(void *cookie, int flags)
! 249: {
! 250: struct amdiic_softc *sc = cookie;
! 251:
! 252: if (cold || sc->sc_poll || (flags & I2C_F_POLL))
! 253: return (0);
! 254:
! 255: return (rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR));
! 256: }
! 257:
! 258: void
! 259: amdiic_i2c_release_bus(void *cookie, int flags)
! 260: {
! 261: struct amdiic_softc *sc = cookie;
! 262:
! 263: if (cold || sc->sc_poll || (flags & I2C_F_POLL))
! 264: return;
! 265:
! 266: rw_exit(&sc->sc_i2c_lock);
! 267: }
! 268:
! 269: int
! 270: amdiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
! 271: const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
! 272: {
! 273: struct amdiic_softc *sc = cookie;
! 274: u_int8_t *b;
! 275: u_int8_t proto, st;
! 276: int retries;
! 277:
! 278: DPRINTF(("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, "
! 279: "flags 0x%02x\n", sc->sc_dev.dv_xname, op, addr,
! 280: cmdlen, len, flags));
! 281:
! 282: if (cold || sc->sc_poll)
! 283: flags |= I2C_F_POLL;
! 284:
! 285: if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
! 286: return (1);
! 287:
! 288: /* Setup transfer */
! 289: sc->sc_i2c_xfer.op = op;
! 290: sc->sc_i2c_xfer.buf = buf;
! 291: sc->sc_i2c_xfer.len = len;
! 292: sc->sc_i2c_xfer.flags = flags;
! 293: sc->sc_i2c_xfer.error = 0;
! 294:
! 295: /* Set slave address */
! 296: if (amdiic_write(sc, AMD8111_SMB_ADDR,
! 297: addr << AMD8111_SMB_ADDR_SHIFT) == -1)
! 298: return (1);
! 299:
! 300: b = (void *)cmdbuf;
! 301: if (cmdlen > 0)
! 302: /* Set command byte */
! 303: if (amdiic_write(sc, AMD8111_SMB_CMD, b[0]) == -1)
! 304: return (1);
! 305:
! 306: if (I2C_OP_WRITE_P(op)) {
! 307: /* Write data */
! 308: b = buf;
! 309: if (len > 0)
! 310: if (amdiic_write(sc, AMD8111_SMB_DATA(0), b[0]) == -1)
! 311: return (1);
! 312: if (len > 1)
! 313: if (amdiic_write(sc, AMD8111_SMB_DATA(1), b[1]) == -1)
! 314: return (1);
! 315: }
! 316:
! 317: /* Set SMBus command */
! 318: if (len == 0)
! 319: proto = AMD8111_SMB_PROTO_BYTE;
! 320: else if (len == 1)
! 321: proto = AMD8111_SMB_PROTO_BDATA;
! 322: else if (len == 2)
! 323: proto = AMD8111_SMB_PROTO_WDATA;
! 324:
! 325: /* Set direction */
! 326: if (I2C_OP_READ_P(op))
! 327: proto |= AMD8111_SMB_PROTO_READ;
! 328:
! 329: /* Start transaction */
! 330: amdiic_write(sc, AMD8111_SMB_PROTO, proto);
! 331:
! 332: if (flags & I2C_F_POLL) {
! 333: /* Poll for completion */
! 334: DELAY(AMDIIC_DELAY);
! 335: for (retries = 1000; retries > 0; retries--) {
! 336: st = amdiic_read(sc, AMD8111_SMB_STAT);
! 337: if (st != 0)
! 338: break;
! 339: DELAY(AMDIIC_DELAY);
! 340: }
! 341: if (st == 0) {
! 342: printf("%s: exec: op %d, addr 0x%02x, cmdlen %d, "
! 343: "len %d, flags 0x%02x: timeout\n",
! 344: sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags);
! 345: return (1);
! 346: }
! 347: amdiic_intr(sc);
! 348: } else {
! 349: /* Wait for interrupt */
! 350: if (tsleep(sc, PRIBIO, "iicexec", AMDIIC_TIMEOUT * hz))
! 351: return (1);
! 352: }
! 353:
! 354: if (sc->sc_i2c_xfer.error)
! 355: return (1);
! 356:
! 357: return (0);
! 358: }
! 359:
! 360: int
! 361: amdiic_intr(void *arg)
! 362: {
! 363: struct amdiic_softc *sc = arg;
! 364: int st;
! 365: u_int8_t *b;
! 366: size_t len;
! 367:
! 368: /* Read status */
! 369: if ((st = amdiic_read(sc, AMD8111_SMB_STAT)) == -1)
! 370: return (-1);
! 371: if (st == 0)
! 372: /* Interrupt was not for us */
! 373: return (0);
! 374:
! 375: DPRINTF(("%s: intr: st 0x%02x\n", sc->sc_dev.dv_xname, st));
! 376:
! 377: /* Check for errors */
! 378: if ((st & AMD8111_SMB_STAT_MASK) != 0) {
! 379: sc->sc_i2c_xfer.error = 1;
! 380: goto done;
! 381: }
! 382:
! 383: if (st & AMD8111_SMB_STAT_DONE) {
! 384: if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
! 385: goto done;
! 386:
! 387: /* Read data */
! 388: b = sc->sc_i2c_xfer.buf;
! 389: len = sc->sc_i2c_xfer.len;
! 390: if (len > 0)
! 391: b[0] = amdiic_read(sc, AMD8111_SMB_DATA(0));
! 392: if (len > 1)
! 393: b[1] = amdiic_read(sc, AMD8111_SMB_DATA(1));
! 394: }
! 395:
! 396: done:
! 397: if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
! 398: wakeup(sc);
! 399: return (1);
! 400: }
CVSweb