[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

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