[BACK]Return to amdiic.c CVS log [TXT][DIR] Up to [local] / sys / dev / pci

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