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

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