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

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