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

Annotation of sys/dev/pci/viapm.c, Revision 1.1.1.1

1.1       nbrk        1: /*     $OpenBSD: viapm.c,v 1.8 2007/05/03 09:36:26 dlg Exp $   */
                      2:
                      3: /*
                      4:  * Copyright (c) 2005 Mark Kettenis <kettenis@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:  * VIA VT8237 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: /*
                     39:  * VIA VT8237 ISA register definitions.
                     40:  */
                     41:
                     42: /* PCI configuration registers */
                     43: #define VIAPM_SMB_BASE 0xd0            /* SMBus base address */
                     44: #define VIAPM_SMB_HOSTC        0xd2            /* host configuration */
                     45: #define VIAPM_SMB_HOSTC_HSTEN  (1 << 0)        /* enable host controller */
                     46: #define VIAPM_SMB_HOSTC_INTEN  (1 << 1)        /* enable SCI/SMI */
                     47: #define VIAPM_SMB_HOSTC_SCIEN  (1 << 3)        /* interrupt type (SCI/SMI) */
                     48:
                     49: /* SMBus I/O registers */
                     50: #define VIAPM_SMB_HS   0x00            /* host status */
                     51: #define VIAPM_SMB_HS_BUSY      (1 << 0)        /* running a command */
                     52: #define VIAPM_SMB_HS_INTR      (1 << 1)        /* command completed */
                     53: #define VIAPM_SMB_HS_DEVERR    (1 << 2)        /* command error */
                     54: #define VIAPM_SMB_HS_BUSERR    (1 << 3)        /* transaction collision */
                     55: #define VIAPM_SMB_HS_FAILED    (1 << 4)        /* failed bus transaction */
                     56: #define VIAPM_SMB_HS_INUSE     (1 << 6)        /* bus semaphore */
                     57: #define VIAPM_SMB_HS_BITS      \
                     58:   "\020\001BUSY\002INTR\003DEVERR\004BUSERR\005FAILED\007INUSE"
                     59: #define VIAPM_SMB_HC   0x02            /* host control */
                     60: #define VIAPM_SMB_HC_INTREN    (1 << 0)        /* enable interrupts */
                     61: #define VIAPM_SMB_HC_KILL      (1 << 1)        /* kill current transaction */
                     62: #define VIAPM_SMB_HC_CMD_QUICK (0 << 2)        /* QUICK command */
                     63: #define VIAPM_SMB_HC_CMD_BYTE  (1 << 2)        /* BYTE command */
                     64: #define VIAPM_SMB_HC_CMD_BDATA (2 << 2)        /* BYTE DATA command */
                     65: #define VIAPM_SMB_HC_CMD_WDATA (3 << 2)        /* WORD DATA command */
                     66: #define VIAPM_SMB_HC_CMD_PCALL (4 << 2)        /* PROCESS CALL command */
                     67: #define VIAPM_SMB_HC_CMD_BLOCK (5 << 2)        /* BLOCK command */
                     68: #define VIAPM_SMB_HC_START     (1 << 6)        /* start transaction */
                     69: #define VIAPM_SMB_HCMD 0x03            /* host command */
                     70: #define VIAPM_SMB_TXSLVA       0x04            /* transmit slave address */
                     71: #define VIAPM_SMB_TXSLVA_READ  (1 << 0)        /* read direction */
                     72: #define VIAPM_SMB_TXSLVA_ADDR(x) (((x) & 0x7f) << 1) /* 7-bit address */
                     73: #define VIAPM_SMB_HD0  0x05            /* host data 0 */
                     74: #define VIAPM_SMB_HD1  0x06            /* host data 1 */
                     75: #define VIAPM_SMB_HBDB 0x07            /* host block data byte */
                     76:
                     77: #define VIAPM_SMB_SIZE 16
                     78:
                     79: #ifdef VIAPM_DEBUG
                     80: #define DPRINTF(x) printf x
                     81: #else
                     82: #define DPRINTF(x)
                     83: #endif
                     84:
                     85: #define VIAPM_DELAY    100
                     86: #define VIAPM_TIMEOUT  1
                     87:
                     88: struct viapm_softc {
                     89:        struct device           sc_dev;
                     90:
                     91:        bus_space_tag_t         sc_iot;
                     92:        bus_space_handle_t      sc_ioh;
                     93:        void *                  sc_ih;
                     94:        int                     sc_poll;
                     95:
                     96:        struct i2c_controller   sc_i2c_tag;
                     97:        struct rwlock           sc_i2c_lock;
                     98:        struct {
                     99:                i2c_op_t     op;
                    100:                void *       buf;
                    101:                size_t       len;
                    102:                int          flags;
                    103:                volatile int error;
                    104:        }                       sc_i2c_xfer;
                    105: };
                    106:
                    107: int    viapm_match(struct device *, void *, void *);
                    108: void   viapm_attach(struct device *, struct device *, void *);
                    109:
                    110: int    viapm_i2c_acquire_bus(void *, int);
                    111: void   viapm_i2c_release_bus(void *, int);
                    112: int    viapm_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
                    113:            void *, size_t, int);
                    114:
                    115: int    viapm_intr(void *);
                    116:
                    117: struct cfattach viapm_ca = {
                    118:        sizeof(struct viapm_softc),
                    119:        viapm_match,
                    120:        viapm_attach
                    121: };
                    122:
                    123: struct cfdriver viapm_cd = {
                    124:        NULL, "viapm", DV_DULL
                    125: };
                    126:
                    127: const struct pci_matchid viapm_ids[] = {
                    128:        { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8233_ISA },
                    129:        { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8233A_ISA },
                    130:        { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8235_ISA },
                    131:        { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8237_ISA },
                    132:        { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8237A_ISA },
                    133:        { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_VT8251_ISA },
                    134:        { PCI_VENDOR_VIATECH, PCI_PRODUCT_VIATECH_CX700_ISA }
                    135: };
                    136:
                    137: int
                    138: viapm_match(struct device *parent, void *match, void *aux)
                    139: {
                    140:        return (pci_matchbyid(aux, viapm_ids,
                    141:            sizeof(viapm_ids) / sizeof(viapm_ids[0])));
                    142: }
                    143:
                    144: void
                    145: viapm_attach(struct device *parent, struct device *self, void *aux)
                    146: {
                    147:        struct viapm_softc *sc = (struct viapm_softc *)self;
                    148:        struct pci_attach_args *pa = aux;
                    149:        struct i2cbus_attach_args iba;
                    150:        pcireg_t conf, iobase;
                    151: #if 0
                    152:        pci_intr_handle_t ih;
                    153:        const char *intrstr = NULL;
                    154: #endif
                    155:
                    156:        /* Map I/O space */
                    157:        sc->sc_iot = pa->pa_iot;
                    158:        iobase = pci_conf_read(pa->pa_pc, pa->pa_tag, VIAPM_SMB_BASE);
                    159:        if (iobase == 0 ||
                    160:            bus_space_map(sc->sc_iot, iobase & 0xfffe,
                    161:            VIAPM_SMB_SIZE, 0, &sc->sc_ioh)) {
                    162:                printf(": can't map I/O space\n");
                    163:                return;
                    164:        }
                    165:
                    166:        /* Read configuration */
                    167:        conf = (iobase >> 16);
                    168:        DPRINTF((": conf 0x%x", conf));
                    169:
                    170:        if ((conf & VIAPM_SMB_HOSTC_HSTEN) == 0) {
                    171:                printf(": SMBus host disabled\n");
                    172:                goto fail;
                    173:        }
                    174:
                    175:        if (conf & VIAPM_SMB_HOSTC_INTEN) {
                    176:                if (conf & VIAPM_SMB_HOSTC_SCIEN)
                    177:                        printf(": SCI");
                    178:                else
                    179:                        printf(": SMI");
                    180:                sc->sc_poll = 1;
                    181:        } else {
                    182: #if 0
                    183:                /* Install interrupt handler */
                    184:                if (pci_intr_map(pa, &ih)) {
                    185:                        printf(": can't map interrupt\n");
                    186:                        goto fail;
                    187:                }
                    188:                intrstr = pci_intr_string(pa->pa_pc, ih);
                    189:                sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO,
                    190:                    viapm_intr, sc, sc->sc_dev.dv_xname);
                    191:                if (sc->sc_ih == NULL) {
                    192:                        printf(": can't establish interrupt");
                    193:                        if (intrstr != NULL)
                    194:                                printf(" at %s", intrstr);
                    195:                        printf("\n");
                    196:                        goto fail;
                    197:                }
                    198:                printf(": %s", intrstr);
                    199: #endif
                    200:                sc->sc_poll = 1;
                    201:        }
                    202:
                    203:        printf("\n");
                    204:
                    205:        /* Attach I2C bus */
                    206:        rw_init(&sc->sc_i2c_lock, "iiclk");
                    207:        sc->sc_i2c_tag.ic_cookie = sc;
                    208:        sc->sc_i2c_tag.ic_acquire_bus = viapm_i2c_acquire_bus;
                    209:        sc->sc_i2c_tag.ic_release_bus = viapm_i2c_release_bus;
                    210:        sc->sc_i2c_tag.ic_exec = viapm_i2c_exec;
                    211:
                    212:        bzero(&iba, sizeof iba);
                    213:        iba.iba_name = "iic";
                    214:        iba.iba_tag = &sc->sc_i2c_tag;
                    215:        config_found(self, &iba, iicbus_print);
                    216:
                    217:        return;
                    218:
                    219: fail:
                    220:        bus_space_unmap(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_SIZE);
                    221: }
                    222:
                    223: int
                    224: viapm_i2c_acquire_bus(void *cookie, int flags)
                    225: {
                    226:        struct viapm_softc *sc = cookie;
                    227:
                    228:        if (cold || sc->sc_poll || (flags & I2C_F_POLL))
                    229:                return (0);
                    230:
                    231:        return (rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR));
                    232: }
                    233:
                    234: void
                    235: viapm_i2c_release_bus(void *cookie, int flags)
                    236: {
                    237:        struct viapm_softc *sc = cookie;
                    238:
                    239:        if (cold || sc->sc_poll || (flags & I2C_F_POLL))
                    240:                return;
                    241:
                    242:        rw_exit(&sc->sc_i2c_lock);
                    243: }
                    244:
                    245: int
                    246: viapm_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
                    247:     const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
                    248: {
                    249:        struct viapm_softc *sc = cookie;
                    250:        u_int8_t *b;
                    251:        u_int8_t ctl, st;
                    252:        int retries;
                    253:
                    254:        DPRINTF(("%s: exec op %d, addr 0x%x, cmdlen %d, len %d, "
                    255:            "flags 0x%x, status 0x%b\n", sc->sc_dev.dv_xname, op, addr,
                    256:            cmdlen, len, flags, bus_space_read_1(sc->sc_iot, sc->sc_ioh,
                    257:            VIAPM_SMB_HS), VIAPM_SMB_HS_BITS));
                    258:
                    259:        /* Check if there's a transfer already running */
                    260:        st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HS);
                    261:        DPRINTF(("%s: exec: st 0x%b\n", sc->sc_dev.dv_xname, st,
                    262:            VIAPM_SMB_HS_BITS));
                    263:        if (st & VIAPM_SMB_HS_BUSY)
                    264:                return (1);
                    265:
                    266:        if (cold || sc->sc_poll)
                    267:                flags |= I2C_F_POLL;
                    268:
                    269:        if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
                    270:                return (1);
                    271:
                    272:        /* Setup transfer */
                    273:        sc->sc_i2c_xfer.op = op;
                    274:        sc->sc_i2c_xfer.buf = buf;
                    275:        sc->sc_i2c_xfer.len = len;
                    276:        sc->sc_i2c_xfer.flags = flags;
                    277:        sc->sc_i2c_xfer.error = 0;
                    278:
                    279:        /* Set slave address and transfer direction */
                    280:        bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_TXSLVA,
                    281:            VIAPM_SMB_TXSLVA_ADDR(addr) |
                    282:            (I2C_OP_READ_P(op) ? VIAPM_SMB_TXSLVA_READ : 0));
                    283:
                    284:        b = (void *)cmdbuf;
                    285:        if (cmdlen > 0)
                    286:                /* Set command byte */
                    287:                bus_space_write_1(sc->sc_iot, sc->sc_ioh,
                    288:                    VIAPM_SMB_HCMD, b[0]);
                    289:
                    290:        if (I2C_OP_WRITE_P(op)) {
                    291:                /* Write data */
                    292:                b = buf;
                    293:                if (len > 0)
                    294:                        bus_space_write_1(sc->sc_iot, sc->sc_ioh,
                    295:                            VIAPM_SMB_HD0, b[0]);
                    296:                if (len > 1)
                    297:                        bus_space_write_1(sc->sc_iot, sc->sc_ioh,
                    298:                            VIAPM_SMB_HD1, b[1]);
                    299:        }
                    300:
                    301:        /* Set SMBus command */
                    302:        if (len == 0)
                    303:                ctl = VIAPM_SMB_HC_CMD_BYTE;
                    304:        else if (len == 1)
                    305:                ctl = VIAPM_SMB_HC_CMD_BDATA;
                    306:        else if (len == 2)
                    307:                ctl = VIAPM_SMB_HC_CMD_WDATA;
                    308:
                    309:        if ((flags & I2C_F_POLL) == 0)
                    310:                ctl |= VIAPM_SMB_HC_INTREN;
                    311:
                    312:        /* Start transaction */
                    313:        ctl |= VIAPM_SMB_HC_START;
                    314:        bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HC, ctl);
                    315:
                    316:        if (flags & I2C_F_POLL) {
                    317:                /* Poll for completion */
                    318:                DELAY(VIAPM_DELAY);
                    319:                for (retries = 1000; retries > 0; retries--) {
                    320:                        st = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
                    321:                            VIAPM_SMB_HS);
                    322:                        if ((st & VIAPM_SMB_HS_BUSY) == 0)
                    323:                                break;
                    324:                        DELAY(VIAPM_DELAY);
                    325:                }
                    326:                if (st & VIAPM_SMB_HS_BUSY)
                    327:                        goto timeout;
                    328:                viapm_intr(sc);
                    329:        } else {
                    330:                /* Wait for interrupt */
                    331:                if (tsleep(sc, PRIBIO, "iicexec", VIAPM_TIMEOUT * hz))
                    332:                        goto timeout;
                    333:        }
                    334:
                    335:        if (sc->sc_i2c_xfer.error)
                    336:                return (1);
                    337:
                    338:        return (0);
                    339:
                    340: timeout:
                    341:        /*
                    342:         * Transfer timeout. Kill the transaction and clear status bits.
                    343:         */
                    344:        printf("%s: timeout, status 0x%b\n", sc->sc_dev.dv_xname, st,
                    345:            VIAPM_SMB_HS_BITS);
                    346:        bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HC,
                    347:            VIAPM_SMB_HC_KILL);
                    348:        DELAY(VIAPM_DELAY);
                    349:        st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HS);
                    350:        if ((st & VIAPM_SMB_HS_FAILED) == 0)
                    351:                printf("%s: transaction abort failed, status 0x%b\n",
                    352:                    sc->sc_dev.dv_xname, st, VIAPM_SMB_HS_BITS);
                    353:        bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HS, st);
                    354:        return (1);
                    355: }
                    356:
                    357: int
                    358: viapm_intr(void *arg)
                    359: {
                    360:        struct viapm_softc *sc = arg;
                    361:        u_int8_t st;
                    362:        u_int8_t *b;
                    363:        size_t len;
                    364:
                    365:        /* Read status */
                    366:        st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HS);
                    367:        if ((st & VIAPM_SMB_HS_BUSY) != 0 || (st & (VIAPM_SMB_HS_INTR |
                    368:            VIAPM_SMB_HS_DEVERR | VIAPM_SMB_HS_BUSERR |
                    369:            VIAPM_SMB_HS_FAILED)) == 0)
                    370:                /* Interrupt was not for us */
                    371:                return (0);
                    372:
                    373:        DPRINTF(("%s: intr st 0x%b\n", sc->sc_dev.dv_xname, st,
                    374:            VIAPM_SMB_HS_BITS));
                    375:
                    376:        /* Clear status bits */
                    377:        bus_space_write_1(sc->sc_iot, sc->sc_ioh, VIAPM_SMB_HS, st);
                    378:
                    379:        /* Check for errors */
                    380:        if (st & (VIAPM_SMB_HS_DEVERR | VIAPM_SMB_HS_BUSERR |
                    381:            VIAPM_SMB_HS_FAILED)) {
                    382:                sc->sc_i2c_xfer.error = 1;
                    383:                goto done;
                    384:        }
                    385:
                    386:        if (st & VIAPM_SMB_HS_INTR) {
                    387:                if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
                    388:                        goto done;
                    389:
                    390:                /* Read data */
                    391:                b = sc->sc_i2c_xfer.buf;
                    392:                len = sc->sc_i2c_xfer.len;
                    393:                if (len > 0)
                    394:                        b[0] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
                    395:                            VIAPM_SMB_HD0);
                    396:                if (len > 1)
                    397:                        b[1] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
                    398:                            VIAPM_SMB_HD1);
                    399:        }
                    400:
                    401: done:
                    402:        if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
                    403:                wakeup(sc);
                    404:        return (1);
                    405: }

CVSweb