[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     ! 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