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

Annotation of src/sys/dev/i2c/i2c_bitbang.c, Revision 1.9.14.2

1.9.14.2! wrstuden    1: /*     $NetBSD: i2c_bitbang.c,v 1.9.14.1 2008/06/23 04:31:01 wrstuden 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:
                     38: /*
                     39:  * Common module for bit-bang'ing an I2C bus.
                     40:  */
                     41:
1.9       lukem      42: #include <sys/cdefs.h>
1.9.14.2! wrstuden   43: __KERNEL_RCSID(0, "$NetBSD: i2c_bitbang.c,v 1.9.14.1 2008/06/23 04:31:01 wrstuden Exp $");
1.9       lukem      44:
1.1       thorpej    45: #include <sys/param.h>
                     46:
                     47: #include <dev/i2c/i2cvar.h>
                     48: #include <dev/i2c/i2c_bitbang.h>
                     49:
1.3       christos   50: #define        SETBITS(x)      ops->ibo_set_bits(v, (x))
1.1       thorpej    51: #define        DIR(x)          ops->ibo_set_dir(v, (x))
                     52: #define        READ            ops->ibo_read_bits(v)
                     53:
                     54: #define        SDA             ops->ibo_bits[I2C_BIT_SDA]      /* i2c signal */
                     55: #define        SCL             ops->ibo_bits[I2C_BIT_SCL]      /* i2c signal */
                     56: #define        OUTPUT          ops->ibo_bits[I2C_BIT_OUTPUT]   /* SDA is output */
                     57: #define        INPUT           ops->ibo_bits[I2C_BIT_INPUT]    /* SDA is input */
                     58:
1.7       macallan   59: #ifndef SCL_BAIL_COUNT
                     60: #define SCL_BAIL_COUNT 1000
                     61: #endif
                     62:
                     63: static inline int i2c_wait_for_scl(void *, i2c_bitbang_ops_t);
                     64:
                     65: static inline int
                     66: i2c_wait_for_scl(void *v, i2c_bitbang_ops_t ops)
                     67: {
                     68:        int bail = 0;
                     69:
                     70:        while (((READ & SCL) == 0) && (bail < SCL_BAIL_COUNT)) {
                     71:                delay(1);
                     72:                bail++;
                     73:        }
                     74:        if (bail == SCL_BAIL_COUNT) {
                     75:                i2c_bitbang_send_stop(v, 0, ops);
                     76:                return EIO;
                     77:        }
                     78:        return 0;
                     79: }
                     80:
1.1       thorpej    81: /*ARGSUSED*/
                     82: int
1.6       christos   83: i2c_bitbang_send_start(void *v, int flags, i2c_bitbang_ops_t ops)
1.1       thorpej    84: {
                     85:
1.9.14.1  wrstuden   86:        /* start condition: put SDL H->L edge during SCL=H */
                     87:
1.1       thorpej    88:        DIR(OUTPUT);
1.3       christos   89:        SETBITS(SDA | SCL);
1.9.14.1  wrstuden   90:        delay(5);               /* bus free time (4.7 us) */
                     91:        SETBITS(  0 | SCL);
1.7       macallan   92:        if (i2c_wait_for_scl(v, ops) != 0)
                     93:                return EIO;
1.9.14.1  wrstuden   94:        delay(4);               /* start hold time (4.0 us) */
1.8       kiyohara   95:
1.9.14.1  wrstuden   96:        /* leave SCL=L and SDL=L to avoid unexpected start/stop condition */
                     97:        SETBITS(  0 |   0);
1.1       thorpej    98:
1.9.14.1  wrstuden   99:        return 0;
1.1       thorpej   100: }
                    101:
                    102: /*ARGSUSED*/
                    103: int
1.6       christos  104: i2c_bitbang_send_stop(void *v, int flags, i2c_bitbang_ops_t ops)
1.1       thorpej   105: {
                    106:
1.9.14.1  wrstuden  107:        /* stop condition: put SDL L->H edge during SCL=H */
                    108:
                    109:        /* assume SCL=L, SDL=L here */
1.1       thorpej   110:        DIR(OUTPUT);
1.9.14.1  wrstuden  111:        SETBITS(  0 | SCL);
                    112:        delay(4);               /* stop setup time (4.0 us) */
1.3       christos  113:        SETBITS(SDA | SCL);
1.1       thorpej   114:
1.9.14.1  wrstuden  115:        return 0;
1.1       thorpej   116: }
                    117:
                    118: int
                    119: i2c_bitbang_initiate_xfer(void *v, i2c_addr_t addr, int flags,
                    120:     i2c_bitbang_ops_t ops)
                    121: {
                    122:
1.4       gdamore   123:        if (addr < 0x80) {
                    124:                uint8_t i2caddr;
1.1       thorpej   125:
1.4       gdamore   126:                /* disallow the 10-bit address prefix */
                    127:                if ((addr & 0x78) == 0x78)
                    128:                        return EINVAL;
                    129:                i2caddr = (addr << 1) | ((flags & I2C_F_READ) ? 1 : 0);
                    130:                (void) i2c_bitbang_send_start(v, flags, ops);
                    131:
                    132:                return (i2c_bitbang_write_byte(v, i2caddr,
                    133:                            flags & ~I2C_F_STOP, ops));
                    134:
                    135:        } else if (addr < 0x400) {
                    136:                uint16_t        i2caddr;
                    137:                int             rv;
                    138:
                    139:                i2caddr = (addr << 1) | ((flags & I2C_F_READ) ? 1 : 0) |
                    140:                    0xf000;
                    141:
                    142:                (void) i2c_bitbang_send_start(v, flags, ops);
                    143:                rv = i2c_bitbang_write_byte(v, i2caddr >> 8,
                    144:                    flags & ~I2C_F_STOP, ops);
                    145:                /* did a slave ack the 10-bit prefix? */
                    146:                if (rv != 0)
                    147:                        return rv;
                    148:
                    149:                /* send the lower 7-bits (+ read/write mode) */
                    150:                return (i2c_bitbang_write_byte(v, i2caddr & 0xff,
                    151:                            flags & ~I2C_F_STOP, ops));
1.1       thorpej   152:
1.4       gdamore   153:        } else
                    154:                return EINVAL;
1.1       thorpej   155: }
                    156:
                    157: int
1.9.14.1  wrstuden  158: i2c_bitbang_read_byte(void *v, uint8_t *valp, int flags, i2c_bitbang_ops_t ops)
1.1       thorpej   159: {
                    160:        int i;
                    161:        uint8_t val = 0;
                    162:        uint32_t bit;
                    163:
1.9.14.1  wrstuden  164:        /* assume SCL=L, SDA=L here */
                    165:
                    166:        DIR(INPUT);
1.1       thorpej   167:
                    168:        for (i = 0; i < 8; i++) {
                    169:                val <<= 1;
1.8       kiyohara  170:
1.9.14.1  wrstuden  171:                /* data is set at SCL H->L edge */
1.9.14.2! wrstuden  172:                /* SDA is set here because DIR() is INPUT */
        !           173:                SETBITS(SDA |   0);
1.9.14.1  wrstuden  174:                delay(5);       /* clock low time (4.7 us) */
1.8       kiyohara  175:
1.9.14.1  wrstuden  176:                /* read data at SCL L->H edge */
1.9.14.2! wrstuden  177:                SETBITS(SDA | SCL);
1.7       macallan  178:                if (i2c_wait_for_scl(v, ops) != 0)
                    179:                        return EIO;
1.1       thorpej   180:                if (READ & SDA)
                    181:                        val |= 1;
1.9.14.1  wrstuden  182:                delay(4);       /* clock high time (4.0 us) */
1.1       thorpej   183:        }
1.9.14.1  wrstuden  184:        /* set SCL H->L before set SDA direction OUTPUT */
1.9.14.2! wrstuden  185:        SETBITS(SDA |   0);
1.1       thorpej   186:
1.9.14.1  wrstuden  187:        /* set ack after SCL H->L edge */
1.1       thorpej   188:        bit = (flags & I2C_F_LAST) ? SDA : 0;
                    189:        DIR(OUTPUT);
1.9.14.1  wrstuden  190:        SETBITS(bit |   0);
                    191:        delay(5);       /* clock low time (4.7 us) */
1.8       kiyohara  192:
1.9.14.1  wrstuden  193:        /* ack is checked at SCL L->H edge */
                    194:        SETBITS(bit | SCL);
1.7       macallan  195:        if (i2c_wait_for_scl(v, ops) != 0)
                    196:                return EIO;
1.9.14.1  wrstuden  197:        delay(4);       /* clock high time (4.0 us) */
1.8       kiyohara  198:
1.9.14.1  wrstuden  199:        /* set SCL H->L for next data; don't change SDA here */
                    200:        SETBITS(bit |   0);
1.1       thorpej   201:
1.9.14.1  wrstuden  202:        /* leave SCL=L and SDL=L to avoid unexpected start/stop condition */
                    203:        SETBITS(  0 |   0);
                    204:
1.1       thorpej   205:
                    206:        if ((flags & (I2C_F_STOP | I2C_F_LAST)) == (I2C_F_STOP | I2C_F_LAST))
                    207:                (void) i2c_bitbang_send_stop(v, flags, ops);
                    208:
                    209:        *valp = val;
1.9.14.1  wrstuden  210:        return 0;
1.1       thorpej   211: }
                    212:
                    213: int
1.9.14.1  wrstuden  214: i2c_bitbang_write_byte(void *v, uint8_t val, int flags, i2c_bitbang_ops_t ops)
1.1       thorpej   215: {
                    216:        uint32_t bit;
                    217:        uint8_t mask;
                    218:        int error;
                    219:
1.9.14.1  wrstuden  220:        /* assume at SCL=L, SDA=L here */
                    221:
                    222:        DIR(OUTPUT);
                    223:
1.1       thorpej   224:        for (mask = 0x80; mask != 0; mask >>= 1) {
                    225:                bit = (val & mask) ? SDA : 0;
1.8       kiyohara  226:
1.9.14.1  wrstuden  227:                /* set data after SCL H->L edge */
                    228:                SETBITS(bit |   0);
                    229:                delay(5);       /* clock low time (4.7 us) */
1.8       kiyohara  230:
1.9.14.1  wrstuden  231:                /* data is fetched at SCL L->H edge */
                    232:                SETBITS(bit | SCL);
1.7       macallan  233:                if (i2c_wait_for_scl(v, ops))
                    234:                        return EIO;
1.9.14.1  wrstuden  235:                delay(4);       /* clock high time (4.0 us) */
1.8       kiyohara  236:
1.9.14.1  wrstuden  237:                /* put SCL H->L edge; don't change SDA here */
                    238:                SETBITS(bit |   0);
1.1       thorpej   239:        }
                    240:
1.9.14.1  wrstuden  241:        /* ack is set at H->L edge */
                    242:        DIR(INPUT);
                    243:        delay(5);       /* clock low time (4.7 us) */
1.8       kiyohara  244:
1.9.14.1  wrstuden  245:        /* read ack at L->H edge */
1.9.14.2! wrstuden  246:        /* SDA is set here because DIR() is INPUT */
        !           247:        SETBITS(SDA | SCL);
1.7       macallan  248:        if (i2c_wait_for_scl(v, ops) != 0)
                    249:                return EIO;
1.1       thorpej   250:        error = (READ & SDA) ? EIO : 0;
1.9.14.1  wrstuden  251:        delay(4);       /* clock high time (4.0 us) */
1.8       kiyohara  252:
1.9.14.1  wrstuden  253:        /* set SCL H->L before set SDA direction OUTPUT */
1.9.14.2! wrstuden  254:        SETBITS(SDA |   0);
1.8       kiyohara  255:        DIR(OUTPUT);
1.9.14.2! wrstuden  256:        /* leave SCL=L and SDL=L to avoid unexpected start/stop condition */
        !           257:        SETBITS(  0 |   0);
1.1       thorpej   258:
                    259:        if (flags & I2C_F_STOP)
                    260:                (void) i2c_bitbang_send_stop(v, flags, ops);
                    261:
1.9.14.1  wrstuden  262:        return error;
1.1       thorpej   263: }

CVSweb <webmaster@jp.NetBSD.org>