[BACK]Return to i2c.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / dev / i2c

Annotation of src/sys/dev/i2c/i2c.c, Revision 1.19.4.3

1.19.4.3! yamt        1: /*     $NetBSD: i2c.c,v 1.19.4.2 2009/05/04 08:12:39 yamt Exp $        */
1.1       thorpej     2:
                      3: /*
                      4:  * Copyright (c) 2003 Wasabi Systems, Inc.
                      5:  * All rights reserved.
                      6:  *
                      7:  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
                      8:  *
                      9:  * Redistribution and use in source and binary forms, with or without
                     10:  * modification, are permitted provided that the following conditions
                     11:  * are met:
                     12:  * 1. Redistributions of source code must retain the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer.
                     14:  * 2. Redistributions in binary form must reproduce the above copyright
                     15:  *    notice, this list of conditions and the following disclaimer in the
                     16:  *    documentation and/or other materials provided with the distribution.
                     17:  * 3. All advertising materials mentioning features or use of this software
                     18:  *    must display the following acknowledgement:
                     19:  *      This product includes software developed for the NetBSD Project by
                     20:  *      Wasabi Systems, Inc.
                     21:  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
                     22:  *    or promote products derived from this software without specific prior
                     23:  *    written permission.
                     24:  *
                     25:  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
                     26:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
                     27:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                     28:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
                     29:  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
                     30:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
                     31:  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
                     32:  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
                     33:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
                     34:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                     35:  * POSSIBILITY OF SUCH DAMAGE.
                     36:  */
                     37:
1.18      lukem      38: #include <sys/cdefs.h>
1.19.4.3! yamt       39: __KERNEL_RCSID(0, "$NetBSD: i2c.c,v 1.19.4.2 2009/05/04 08:12:39 yamt Exp $");
1.18      lukem      40:
1.1       thorpej    41: #include <sys/param.h>
                     42: #include <sys/systm.h>
                     43: #include <sys/device.h>
                     44: #include <sys/event.h>
                     45: #include <sys/conf.h>
1.11      jmcneill   46: #include <sys/malloc.h>
                     47: #include <sys/kthread.h>
                     48: #include <sys/proc.h>
                     49: #include <sys/kernel.h>
1.1       thorpej    50:
                     51: #include <dev/i2c/i2cvar.h>
                     52:
                     53: #include "locators.h"
1.19.4.2  yamt       54: #include <opt_i2cbus.h>
1.1       thorpej    55:
                     56: struct iic_softc {
                     57:        i2c_tag_t sc_tag;
1.6       jmcneill   58:        int sc_type;
1.1       thorpej    59: };
                     60:
1.11      jmcneill   61: static void    iic_smbus_intr_thread(void *);
1.19.4.3! yamt       62: static void    iic_fill_compat(struct i2c_attach_args*, const char*,
        !            63:                        size_t, char **);
1.11      jmcneill   64:
1.1       thorpej    65: int
1.10      christos   66: iicbus_print(void *aux, const char *pnp)
1.1       thorpej    67: {
                     68:
                     69:        if (pnp != NULL)
1.8       drochner   70:                aprint_normal("iic at %s", pnp);
1.1       thorpej    71:
                     72:        return (UNCONF);
                     73: }
                     74:
                     75: static int
1.19.4.3! yamt       76: iic_print_direct(void *aux, const char *pnp)
        !            77: {
        !            78:        struct i2c_attach_args *ia = aux;
        !            79:
        !            80:        if (pnp != NULL)
        !            81:                aprint_normal("%s at %s addr 0x%02x", ia->ia_name, pnp,
        !            82:                        ia->ia_addr);
        !            83:        else
        !            84:                aprint_normal(" addr 0x%02x", ia->ia_addr);
        !            85:
        !            86:        return UNCONF;
        !            87: }
        !            88:
        !            89: static int
1.10      christos   90: iic_print(void *aux, const char *pnp)
1.1       thorpej    91: {
                     92:        struct i2c_attach_args *ia = aux;
                     93:
1.6       jmcneill   94:        if (ia->ia_addr != (i2c_addr_t)-1)
                     95:                aprint_normal(" addr 0x%x", ia->ia_addr);
1.1       thorpej    96:
                     97:        return (UNCONF);
                     98: }
                     99:
                    100: static int
1.19.4.1  yamt      101: iic_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux)
1.1       thorpej   102: {
1.19.4.1  yamt      103:        struct iic_softc *sc = device_private(parent);
1.1       thorpej   104:        struct i2c_attach_args ia;
                    105:
                    106:        ia.ia_tag = sc->sc_tag;
                    107:        ia.ia_addr = cf->cf_loc[IICCF_ADDR];
                    108:        ia.ia_size = cf->cf_loc[IICCF_SIZE];
1.6       jmcneill  109:        ia.ia_type = sc->sc_type;
1.1       thorpej   110:
1.19.4.3! yamt      111:        ia.ia_name = NULL;
        !           112:        ia.ia_ncompat = 0;
        !           113:        ia.ia_compat = NULL;
        !           114:
1.1       thorpej   115:        if (config_match(parent, cf, &ia) > 0)
                    116:                config_attach(parent, cf, &ia, iic_print);
                    117:
                    118:        return (0);
                    119: }
                    120:
                    121: static int
1.19.4.1  yamt      122: iic_match(device_t parent, cfdata_t cf, void *aux)
1.1       thorpej   123: {
                    124:
1.8       drochner  125:        return (1);
1.1       thorpej   126: }
                    127:
                    128: static void
1.19.4.1  yamt      129: iic_attach(device_t parent, device_t self, void *aux)
1.1       thorpej   130: {
1.7       thorpej   131:        struct iic_softc *sc = device_private(self);
1.1       thorpej   132:        struct i2cbus_attach_args *iba = aux;
1.19.4.3! yamt      133:        prop_array_t child_devices;
        !           134:        char *buf;
1.14      ad        135:        i2c_tag_t ic;
                    136:        int rv;
1.1       thorpej   137:
                    138:        aprint_naive(": I2C bus\n");
                    139:        aprint_normal(": I2C bus\n");
                    140:
                    141:        sc->sc_tag = iba->iba_tag;
1.6       jmcneill  142:        sc->sc_type = iba->iba_type;
1.14      ad        143:        ic = sc->sc_tag;
1.19      cegger    144:        ic->ic_devname = device_xname(self);
1.11      jmcneill  145:
1.13      jmcneill  146:        LIST_INIT(&(sc->sc_tag->ic_list));
                    147:        LIST_INIT(&(sc->sc_tag->ic_proc_list));
1.14      ad        148:
                    149:        rv = kthread_create(PRI_NONE, 0, NULL, iic_smbus_intr_thread,
                    150:            ic, &ic->ic_intr_thread, "%s", ic->ic_devname);
                    151:        if (rv)
1.19.4.1  yamt      152:                aprint_error_dev(self, "unable to create intr thread\n");
1.1       thorpej   153:
1.19.4.2  yamt      154: #if I2C_SCAN
1.15      riz       155:        if (sc->sc_type == I2C_TYPE_SMBUS) {
1.19.4.2  yamt      156:                int err;
1.16      joerg     157:                int found = 0;
                    158:                i2c_addr_t addr;
1.19.4.2  yamt      159:                uint8_t val;
                    160:
                    161:                for (addr = 0x09; addr < 0x78; addr++) {
                    162:                        /*
                    163:                         * Skip certain i2c addresses:
                    164:                         *      0x00            General Call / START
                    165:                         *      0x01            CBUS Address
                    166:                         *      0x02            Different Bus format
                    167:                         *      0x03 - 0x07     Reserved
                    168:                         *      0x08            Host Address
                    169:                         *      0x0c            Alert Response Address
                    170:                         *      0x28            ACCESS.Bus host
                    171:                         *      0x37            ACCESS.Bus default address
                    172:                         *      0x48 - 0x4b     Prototypes
                    173:                         *      0x61            Device Default Address
                    174:                         *      0x78 - 0x7b     10-bit addresses
                    175:                         *      0x7c - 0x7f     Reserved
                    176:                         *
                    177:                         * Some of these are skipped by judicious selection
                    178:                         * of the range of the above for (;;) statement.
                    179:                         *
                    180:                         * if (addr <= 0x08 || addr >= 0x78)
                    181:                         *      continue;
                    182:                         */
                    183:                        if (addr == 0x0c || addr == 0x28 || addr == 0x37 ||
                    184:                            addr == 0x61 || (addr & 0x7c) == 0x48)
                    185:                                continue;
1.16      joerg     186:
1.15      riz       187:                        iic_acquire_bus(ic, 0);
1.19.4.2  yamt      188:                        /*
                    189:                         * Use SMBus quick_write command to detect most
                    190:                         * addresses;  should avoid hanging the bus on
                    191:                         * some write-only devices (like clocks that show
                    192:                         * up at address 0x69)
                    193:                         *
                    194:                         * XXX The quick_write() is allegedly known to
                    195:                         * XXX corrupt the Atmel AT24RF08 EEPROM found
                    196:                         * XXX on some IBM Thinkpads!
                    197:                         */
                    198:                        if ((addr & 0xf8) == 0x30 ||
                    199:                            (addr & 0xf0) == 0x50)
                    200:                                err = iic_smbus_receive_byte(ic, addr, &val, 0);
                    201:                        else
                    202:                                err = iic_smbus_quick_write(ic, addr, 0);
                    203:                        if (err == 0) {
1.15      riz       204:                                if (found == 0)
                    205:                                        aprint_normal("%s: devices at",
                    206:                                                        ic->ic_devname);
                    207:                                found++;
                    208:                                aprint_normal(" 0x%02x", addr);
                    209:                        }
                    210:                        iic_release_bus(ic, 0);
                    211:                }
                    212:                if (found == 0)
                    213:                        aprint_normal("%s: no devices found", ic->ic_devname);
                    214:                aprint_normal("\n");
                    215:        }
1.16      joerg     216: #endif
1.15      riz       217:
1.17      jmcneill  218:        if (!pmf_device_register(self, NULL, NULL))
                    219:                aprint_error_dev(self, "couldn't establish power handler\n");
                    220:
1.19.4.3! yamt      221:        child_devices = prop_dictionary_get(device_properties(parent),
        !           222:                "i2c-child-devices");
        !           223:        if (child_devices) {
        !           224:                unsigned int i, count;
        !           225:                prop_dictionary_t dev;
        !           226:                prop_data_t cdata;
        !           227:                uint32_t addr, size;
        !           228:                uint64_t cookie;
        !           229:                const char *name;
        !           230:                struct i2c_attach_args ia;
        !           231:                int loc[2];
        !           232:
        !           233:                memset(loc, 0, sizeof loc);
        !           234:                count = prop_array_count(child_devices);
        !           235:                for (i = 0; i < count; i++) {
        !           236:                        dev = prop_array_get(child_devices, i);
        !           237:                        if (!dev) continue;
        !           238:                        if (!prop_dictionary_get_cstring_nocopy(
        !           239:                            dev, "name", &name))
        !           240:                                continue;
        !           241:                        if (!prop_dictionary_get_uint32(dev, "addr", &addr))
        !           242:                                continue;
        !           243:                        if (!prop_dictionary_get_uint64(dev, "cookie", &cookie))
        !           244:                                cookie = 0;
        !           245:                        loc[0] = addr;
        !           246:                        if (prop_dictionary_get_uint32(dev, "size", &size))
        !           247:                                loc[1] = size;
        !           248:                        else
        !           249:                                loc[1] = -1;
        !           250:
        !           251:                        memset(&ia, 0, sizeof ia);
        !           252:                        ia.ia_addr = addr;
        !           253:                        ia.ia_type = sc->sc_type;
        !           254:                        ia.ia_tag = ic;
        !           255:                        ia.ia_name = name;
        !           256:                        ia.ia_cookie = cookie;
        !           257:
        !           258:                        buf = NULL;
        !           259:                        cdata = prop_dictionary_get(dev, "compatible");
        !           260:                        if (cdata)
        !           261:                                iic_fill_compat(&ia,
        !           262:                                    prop_data_data_nocopy(cdata),
        !           263:                                    prop_data_size(cdata), &buf);
        !           264:
        !           265:                        config_found_sm_loc(self, "iic", loc, &ia,
        !           266:                            iic_print_direct, NULL);
        !           267:
        !           268:                        if (ia.ia_compat)
        !           269:                                free(ia.ia_compat, M_TEMP);
        !           270:                        if (buf)
        !           271:                                free(buf, M_TEMP);
        !           272:                }
        !           273:        } else {
        !           274:                /*
        !           275:                 * Attach all i2c devices described in the kernel
        !           276:                 * configuration file.
        !           277:                 */
        !           278:                config_search_ia(iic_search, self, "iic", NULL);
        !           279:        }
1.1       thorpej   280: }
                    281:
1.11      jmcneill  282: static void
1.14      ad        283: iic_smbus_intr_thread(void *aux)
1.11      jmcneill  284: {
                    285:        i2c_tag_t ic;
                    286:        struct ic_intr_list *il;
                    287:        int rv;
                    288:
                    289:        ic = (i2c_tag_t)aux;
                    290:        ic->ic_running = 1;
                    291:        ic->ic_pending = 0;
                    292:
                    293:        while (ic->ic_running) {
                    294:                if (ic->ic_pending == 0)
                    295:                        rv = tsleep(ic, PZERO, "iicintr", hz);
                    296:                if (ic->ic_pending > 0) {
                    297:                        LIST_FOREACH(il, &(ic->ic_proc_list), il_next) {
                    298:                                (*il->il_intr)(il->il_intrarg);
                    299:                        }
                    300:                        ic->ic_pending--;
                    301:                }
                    302:        }
                    303:
                    304:        kthread_exit(0);
                    305: }
                    306:
                    307: void *
                    308: iic_smbus_intr_establish(i2c_tag_t ic, int (*intr)(void *), void *intrarg)
                    309: {
                    310:        struct ic_intr_list *il;
                    311:
                    312:        il = malloc(sizeof(struct ic_intr_list), M_DEVBUF, M_WAITOK);
                    313:        if (il == NULL)
                    314:                return NULL;
                    315:
                    316:        il->il_intr = intr;
                    317:        il->il_intrarg = intrarg;
                    318:
                    319:        LIST_INSERT_HEAD(&(ic->ic_list), il, il_next);
                    320:
                    321:        return il;
                    322: }
                    323:
                    324: void
                    325: iic_smbus_intr_disestablish(i2c_tag_t ic, void *hdl)
                    326: {
                    327:        struct ic_intr_list *il;
                    328:
                    329:        il = (struct ic_intr_list *)hdl;
                    330:
                    331:        LIST_REMOVE(il, il_next);
                    332:        free(il, M_DEVBUF);
                    333:
                    334:        return;
                    335: }
                    336:
                    337: void *
                    338: iic_smbus_intr_establish_proc(i2c_tag_t ic, int (*intr)(void *), void *intrarg)
                    339: {
                    340:        struct ic_intr_list *il;
                    341:
                    342:        il = malloc(sizeof(struct ic_intr_list), M_DEVBUF, M_WAITOK);
                    343:        if (il == NULL)
                    344:                return NULL;
                    345:
                    346:        il->il_intr = intr;
                    347:        il->il_intrarg = intrarg;
                    348:
                    349:        LIST_INSERT_HEAD(&(ic->ic_proc_list), il, il_next);
                    350:
                    351:        return il;
                    352: }
                    353:
                    354: void
                    355: iic_smbus_intr_disestablish_proc(i2c_tag_t ic, void *hdl)
                    356: {
                    357:        struct ic_intr_list *il;
                    358:
                    359:        il = (struct ic_intr_list *)hdl;
                    360:
                    361:        LIST_REMOVE(il, il_next);
                    362:        free(il, M_DEVBUF);
                    363:
                    364:        return;
                    365: }
                    366:
                    367: int
                    368: iic_smbus_intr(i2c_tag_t ic)
                    369: {
                    370:        struct ic_intr_list *il;
                    371:
                    372:        LIST_FOREACH(il, &(ic->ic_list), il_next) {
                    373:                (*il->il_intr)(il->il_intrarg);
                    374:        }
                    375:
                    376:        ic->ic_pending++;
                    377:        wakeup(ic);
                    378:
                    379:        return 1;
                    380: }
                    381:
1.19.4.3! yamt      382: static void
        !           383: iic_fill_compat(struct i2c_attach_args *ia, const char *compat, size_t len,
        !           384:        char **buffer)
        !           385: {
        !           386:        int count, i;
        !           387:        const char *c, *start, **ptr;
        !           388:
        !           389:        *buffer = NULL;
        !           390:        for (i = count = 0, c = compat; i < len; i++, c++)
        !           391:                if (*c == 0)
        !           392:                        count++;
        !           393:        count += 2;
        !           394:        ptr = malloc(sizeof(char*)*count, M_TEMP, M_WAITOK);
        !           395:        if (!ptr) return;
        !           396:
        !           397:        for (i = count = 0, start = c = compat; i < len; i++, c++) {
        !           398:                if (*c == 0) {
        !           399:                        ptr[count++] = start;
        !           400:                        start = c+1;
        !           401:                }
        !           402:        }
        !           403:        if (start < compat+len) {
        !           404:                /* last string not 0 terminated */
        !           405:                size_t l = c-start;
        !           406:                *buffer = malloc(l+1, M_TEMP, M_WAITOK);
        !           407:                memcpy(*buffer, start, l);
        !           408:                (*buffer)[l] = 0;
        !           409:                ptr[count++] = *buffer;
        !           410:        }
        !           411:        ptr[count] = NULL;
        !           412:
        !           413:        ia->ia_compat = ptr;
        !           414:        ia->ia_ncompat = count;
        !           415: }
        !           416:
        !           417: int
        !           418: iic_compat_match(struct i2c_attach_args *ia, const char ** compats)
        !           419: {
        !           420:        int i;
        !           421:
        !           422:        for (; compats && *compats; compats++) {
        !           423:                for (i = 0; i < ia->ia_ncompat; i++) {
        !           424:                        if (strcmp(*compats, ia->ia_compat[i]) == 0)
        !           425:                                return 1;
        !           426:                }
        !           427:        }
        !           428:        return 0;
        !           429: }
        !           430:
        !           431:
1.19.4.1  yamt      432: CFATTACH_DECL_NEW(iic, sizeof(struct iic_softc),
1.1       thorpej   433:     iic_match, iic_attach, NULL, NULL);

CVSweb <webmaster@jp.NetBSD.org>