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

Annotation of sys/dev/pci/amdiic.c, Revision 1.1

1.1     ! nbrk        1: /*     $OpenBSD: amdiic.c,v 1.6 2007/05/03 09:36:26 dlg Exp $  */
        !             2:
        !             3: /*
        !             4:  * Copyright (c) 2005 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:  * AMD-8111 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: #ifdef AMDIIC_DEBUG
        !            39: #define DPRINTF(x) printf x
        !            40: #else
        !            41: #define DPRINTF(x)
        !            42: #endif
        !            43:
        !            44: #define AMDIIC_DELAY   100
        !            45: #define AMDIIC_TIMEOUT 1
        !            46:
        !            47: /* PCI configuration registers */
        !            48: #define AMD8111_SMB_BASE       0x10    /* SMBus base address */
        !            49: #define AMD8111_SMB_MISC       0x48    /* miscellaneous control */
        !            50: #define AMD8111_SMB_MISC_SU    (1 << 0)        /* 16x clock speed-up */
        !            51: #define AMD8111_SMB_MISC_INTEN (1 << 1)        /* PCI IRQ enabled */
        !            52: #define AMD8111_SMB_MISC_SCIEN (1 << 2)        /* SCI enabled */
        !            53:
        !            54: /* SMBus I/O registers */
        !            55: #define AMD8111_SMB_SC_DATA    0x00    /* data port */
        !            56: #define AMD8111_SMB_SC_ST      0x04    /* status */
        !            57: #define AMD8111_SMB_SC_ST_OBF  (1 << 0)        /* output buffer full */
        !            58: #define AMD8111_SMB_SC_ST_IBF  (1 << 1)        /* input buffer full */
        !            59: #define AMD8111_SMB_SC_ST_CMD  (1 << 3)        /* command byte */
        !            60: #define AMD8111_SMB_SC_ST_BITS "\020\001OBF\002IBF\004CMD"
        !            61: #define AMD8111_SMB_SC_CMD     0x04    /* command port */
        !            62: #define AMD8111_SMB_SC_CMD_RD  0x80            /* read */
        !            63: #define AMD8111_SMB_SC_CMD_WR  0x81            /* write */
        !            64: #define AMD8111_SMB_SC_IC      0x08    /* interrupt control */
        !            65:
        !            66: /* Host controller interface registers */
        !            67: #define AMD8111_SMB_PROTO      0x00    /* protocol */
        !            68: #define AMD8111_SMB_PROTO_READ 0x01            /* read direction */
        !            69: #define AMD8111_SMB_PROTO_QUICK        0x02            /* QUICK command */
        !            70: #define AMD8111_SMB_PROTO_BYTE 0x04            /* BYTE command */
        !            71: #define AMD8111_SMB_PROTO_BDATA        0x06            /* BYTE DATA command */
        !            72: #define AMD8111_SMB_PROTO_WDATA        0x08            /* WORD DATA command */
        !            73: #define AMD8111_SMB_STAT       0x01    /* status */
        !            74: #define AMD8111_SMB_STAT_MASK  0x1f
        !            75: #define AMD8111_SMB_STAT_DONE  (1 << 7)        /* command completion */
        !            76: #define AMD8111_SMB_ADDR       0x02    /* address */
        !            77: #define AMD8111_SMB_ADDR_SHIFT 1
        !            78: #define AMD8111_SMB_CMD                0x03    /* SMBus command */
        !            79: #define AMD8111_SMB_DATA(x)    (0x04 + (x)) /* SMBus data */
        !            80:
        !            81: struct amdiic_softc {
        !            82:        struct device           sc_dev;
        !            83:
        !            84:        bus_space_tag_t         sc_iot;
        !            85:        bus_space_handle_t      sc_ioh;
        !            86:        void *                  sc_ih;
        !            87:        int                     sc_poll;
        !            88:
        !            89:        struct i2c_controller   sc_i2c_tag;
        !            90:        struct rwlock           sc_i2c_lock;
        !            91:        struct {
        !            92:                i2c_op_t     op;
        !            93:                void *       buf;
        !            94:                size_t       len;
        !            95:                int          flags;
        !            96:                volatile int error;
        !            97:        }                       sc_i2c_xfer;
        !            98: };
        !            99:
        !           100: int    amdiic_match(struct device *, void *, void *);
        !           101: void   amdiic_attach(struct device *, struct device *, void *);
        !           102:
        !           103: int    amdiic_read(struct amdiic_softc *, u_int8_t);
        !           104: int    amdiic_write(struct amdiic_softc *, u_int8_t, u_int8_t);
        !           105: int    amdiic_wait(struct amdiic_softc *, int);
        !           106:
        !           107: int    amdiic_i2c_acquire_bus(void *, int);
        !           108: void   amdiic_i2c_release_bus(void *, int);
        !           109: int    amdiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
        !           110:            void *, size_t, int);
        !           111:
        !           112: int    amdiic_intr(void *);
        !           113:
        !           114: struct cfattach amdiic_ca = {
        !           115:        sizeof(struct amdiic_softc),
        !           116:        amdiic_match,
        !           117:        amdiic_attach
        !           118: };
        !           119:
        !           120: struct cfdriver amdiic_cd = {
        !           121:        NULL, "amdiic", DV_DULL
        !           122: };
        !           123:
        !           124: const struct pci_matchid amdiic_ids[] = {
        !           125:        { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_8111_SMB }
        !           126: };
        !           127:
        !           128: int
        !           129: amdiic_match(struct device *parent, void *match, void *aux)
        !           130: {
        !           131:        return (pci_matchbyid(aux, amdiic_ids,
        !           132:            sizeof(amdiic_ids) / sizeof(amdiic_ids[0])));
        !           133: }
        !           134:
        !           135: void
        !           136: amdiic_attach(struct device *parent, struct device *self, void *aux)
        !           137: {
        !           138:        struct amdiic_softc *sc = (struct amdiic_softc *)self;
        !           139:        struct pci_attach_args *pa = aux;
        !           140:        struct i2cbus_attach_args iba;
        !           141:        pcireg_t conf;
        !           142:        bus_size_t iosize;
        !           143:        pci_intr_handle_t ih;
        !           144:        const char *intrstr = NULL;
        !           145:
        !           146:        /* Map I/O space */
        !           147:        if (pci_mapreg_map(pa, AMD8111_SMB_BASE, PCI_MAPREG_TYPE_IO, 0,
        !           148:            &sc->sc_iot, &sc->sc_ioh, NULL, &iosize, 0)) {
        !           149:                printf(": can't map I/O space\n");
        !           150:                return;
        !           151:        }
        !           152:
        !           153:        /* Read configuration */
        !           154:        conf = pci_conf_read(pa->pa_pc, pa->pa_tag, AMD8111_SMB_MISC);
        !           155:        DPRINTF((": conf 0x%08x", conf));
        !           156:
        !           157:        sc->sc_poll = 1;
        !           158:        if (conf & AMD8111_SMB_MISC_SCIEN) {
        !           159:                /* No PCI IRQ */
        !           160:                printf(": SCI");
        !           161:        } else if (conf & AMD8111_SMB_MISC_INTEN) {
        !           162:                /* Install interrupt handler */
        !           163:                if (pci_intr_map(pa, &ih) == 0) {
        !           164:                        intrstr = pci_intr_string(pa->pa_pc, ih);
        !           165:                        sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO,
        !           166:                            amdiic_intr, sc, sc->sc_dev.dv_xname);
        !           167:                        if (sc->sc_ih != NULL) {
        !           168:                                printf(": %s", intrstr);
        !           169:                                sc->sc_poll = 0;
        !           170:                        }
        !           171:                }
        !           172:                if (sc->sc_poll)
        !           173:                        printf(": polling");
        !           174:        }
        !           175:
        !           176:        printf("\n");
        !           177:
        !           178:        /* Attach I2C bus */
        !           179:        rw_init(&sc->sc_i2c_lock, "iiclk");
        !           180:        sc->sc_i2c_tag.ic_cookie = sc;
        !           181:        sc->sc_i2c_tag.ic_acquire_bus = amdiic_i2c_acquire_bus;
        !           182:        sc->sc_i2c_tag.ic_release_bus = amdiic_i2c_release_bus;
        !           183:        sc->sc_i2c_tag.ic_exec = amdiic_i2c_exec;
        !           184:
        !           185:        bzero(&iba, sizeof(iba));
        !           186:        iba.iba_name = "iic";
        !           187:        iba.iba_tag = &sc->sc_i2c_tag;
        !           188:        config_found(self, &iba, iicbus_print);
        !           189:
        !           190:        return;
        !           191: }
        !           192:
        !           193: int
        !           194: amdiic_read(struct amdiic_softc *sc, u_int8_t reg)
        !           195: {
        !           196:        if (amdiic_wait(sc, 0))
        !           197:                return (-1);
        !           198:        bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_CMD,
        !           199:            AMD8111_SMB_SC_CMD_RD);
        !           200:        if (amdiic_wait(sc, 0))
        !           201:                return (-1);
        !           202:        bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_DATA, reg);
        !           203:        if (amdiic_wait(sc, 1))
        !           204:                return (-1);
        !           205:
        !           206:        return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_DATA));
        !           207: }
        !           208:
        !           209: int
        !           210: amdiic_write(struct amdiic_softc *sc, u_int8_t reg, u_int8_t val)
        !           211: {
        !           212:        if (amdiic_wait(sc, 0))
        !           213:                return (-1);
        !           214:        bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_CMD,
        !           215:            AMD8111_SMB_SC_CMD_WR);
        !           216:        if (amdiic_wait(sc, 0))
        !           217:                return (-1);
        !           218:        bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_DATA, reg);
        !           219:        if (amdiic_wait(sc, 0))
        !           220:                return (-1);
        !           221:        bus_space_write_1(sc->sc_iot, sc->sc_ioh, AMD8111_SMB_SC_DATA, val);
        !           222:
        !           223:        return (0);
        !           224: }
        !           225:
        !           226: int
        !           227: amdiic_wait(struct amdiic_softc *sc, int output)
        !           228: {
        !           229:        int retries;
        !           230:        u_int8_t st;
        !           231:
        !           232:        for (retries = 100; retries > 0; retries--) {
        !           233:                st = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
        !           234:                    AMD8111_SMB_SC_ST);
        !           235:                if (output && (st & AMD8111_SMB_SC_ST_OBF))
        !           236:                        return (0);
        !           237:                if (!output && (st & AMD8111_SMB_SC_ST_IBF) == 0)
        !           238:                        return (0);
        !           239:                DELAY(1);
        !           240:        }
        !           241:        DPRINTF(("%s: %s wait timeout: st 0x%b\n", sc->sc_dev.dv_xname,
        !           242:            (output ? "output" : "input"), st));
        !           243:
        !           244:        return (1);
        !           245: }
        !           246:
        !           247: int
        !           248: amdiic_i2c_acquire_bus(void *cookie, int flags)
        !           249: {
        !           250:        struct amdiic_softc *sc = cookie;
        !           251:
        !           252:        if (cold || sc->sc_poll || (flags & I2C_F_POLL))
        !           253:                return (0);
        !           254:
        !           255:        return (rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR));
        !           256: }
        !           257:
        !           258: void
        !           259: amdiic_i2c_release_bus(void *cookie, int flags)
        !           260: {
        !           261:        struct amdiic_softc *sc = cookie;
        !           262:
        !           263:        if (cold || sc->sc_poll || (flags & I2C_F_POLL))
        !           264:                return;
        !           265:
        !           266:        rw_exit(&sc->sc_i2c_lock);
        !           267: }
        !           268:
        !           269: int
        !           270: amdiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
        !           271:     const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
        !           272: {
        !           273:        struct amdiic_softc *sc = cookie;
        !           274:        u_int8_t *b;
        !           275:        u_int8_t proto, st;
        !           276:        int retries;
        !           277:
        !           278:        DPRINTF(("%s: exec: op %d, addr 0x%02x, cmdlen %d, len %d, "
        !           279:            "flags 0x%02x\n", sc->sc_dev.dv_xname, op, addr,
        !           280:            cmdlen, len, flags));
        !           281:
        !           282:        if (cold || sc->sc_poll)
        !           283:                flags |= I2C_F_POLL;
        !           284:
        !           285:        if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
        !           286:                return (1);
        !           287:
        !           288:        /* Setup transfer */
        !           289:        sc->sc_i2c_xfer.op = op;
        !           290:        sc->sc_i2c_xfer.buf = buf;
        !           291:        sc->sc_i2c_xfer.len = len;
        !           292:        sc->sc_i2c_xfer.flags = flags;
        !           293:        sc->sc_i2c_xfer.error = 0;
        !           294:
        !           295:        /* Set slave address */
        !           296:        if (amdiic_write(sc, AMD8111_SMB_ADDR,
        !           297:            addr << AMD8111_SMB_ADDR_SHIFT) == -1)
        !           298:                return (1);
        !           299:
        !           300:        b = (void *)cmdbuf;
        !           301:        if (cmdlen > 0)
        !           302:                /* Set command byte */
        !           303:                if (amdiic_write(sc, AMD8111_SMB_CMD, b[0]) == -1)
        !           304:                        return (1);
        !           305:
        !           306:        if (I2C_OP_WRITE_P(op)) {
        !           307:                /* Write data */
        !           308:                b = buf;
        !           309:                if (len > 0)
        !           310:                        if (amdiic_write(sc, AMD8111_SMB_DATA(0), b[0]) == -1)
        !           311:                                return (1);
        !           312:                if (len > 1)
        !           313:                        if (amdiic_write(sc, AMD8111_SMB_DATA(1), b[1]) == -1)
        !           314:                                return (1);
        !           315:        }
        !           316:
        !           317:        /* Set SMBus command */
        !           318:        if (len == 0)
        !           319:                proto = AMD8111_SMB_PROTO_BYTE;
        !           320:        else if (len == 1)
        !           321:                proto = AMD8111_SMB_PROTO_BDATA;
        !           322:        else if (len == 2)
        !           323:                proto = AMD8111_SMB_PROTO_WDATA;
        !           324:
        !           325:        /* Set direction */
        !           326:        if (I2C_OP_READ_P(op))
        !           327:                proto |= AMD8111_SMB_PROTO_READ;
        !           328:
        !           329:        /* Start transaction */
        !           330:        amdiic_write(sc, AMD8111_SMB_PROTO, proto);
        !           331:
        !           332:        if (flags & I2C_F_POLL) {
        !           333:                /* Poll for completion */
        !           334:                DELAY(AMDIIC_DELAY);
        !           335:                for (retries = 1000; retries > 0; retries--) {
        !           336:                        st = amdiic_read(sc, AMD8111_SMB_STAT);
        !           337:                        if (st != 0)
        !           338:                                break;
        !           339:                        DELAY(AMDIIC_DELAY);
        !           340:                }
        !           341:                if (st == 0) {
        !           342:                        printf("%s: exec: op %d, addr 0x%02x, cmdlen %d, "
        !           343:                            "len %d, flags 0x%02x: timeout\n",
        !           344:                            sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags);
        !           345:                        return (1);
        !           346:                }
        !           347:                amdiic_intr(sc);
        !           348:        } else {
        !           349:                /* Wait for interrupt */
        !           350:                if (tsleep(sc, PRIBIO, "iicexec", AMDIIC_TIMEOUT * hz))
        !           351:                        return (1);
        !           352:        }
        !           353:
        !           354:        if (sc->sc_i2c_xfer.error)
        !           355:                return (1);
        !           356:
        !           357:        return (0);
        !           358: }
        !           359:
        !           360: int
        !           361: amdiic_intr(void *arg)
        !           362: {
        !           363:        struct amdiic_softc *sc = arg;
        !           364:        int st;
        !           365:        u_int8_t *b;
        !           366:        size_t len;
        !           367:
        !           368:        /* Read status */
        !           369:        if ((st = amdiic_read(sc, AMD8111_SMB_STAT)) == -1)
        !           370:                return (-1);
        !           371:        if (st == 0)
        !           372:                /* Interrupt was not for us */
        !           373:                return (0);
        !           374:
        !           375:        DPRINTF(("%s: intr: st 0x%02x\n", sc->sc_dev.dv_xname, st));
        !           376:
        !           377:        /* Check for errors */
        !           378:        if ((st & AMD8111_SMB_STAT_MASK) != 0) {
        !           379:                sc->sc_i2c_xfer.error = 1;
        !           380:                goto done;
        !           381:        }
        !           382:
        !           383:        if (st & AMD8111_SMB_STAT_DONE) {
        !           384:                if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
        !           385:                        goto done;
        !           386:
        !           387:                /* Read data */
        !           388:                b = sc->sc_i2c_xfer.buf;
        !           389:                len = sc->sc_i2c_xfer.len;
        !           390:                if (len > 0)
        !           391:                        b[0] = amdiic_read(sc, AMD8111_SMB_DATA(0));
        !           392:                if (len > 1)
        !           393:                        b[1] = amdiic_read(sc, AMD8111_SMB_DATA(1));
        !           394:        }
        !           395:
        !           396: done:
        !           397:        if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
        !           398:                wakeup(sc);
        !           399:        return (1);
        !           400: }

CVSweb