1 1.14 pgoyette /* $NetBSD: i2c_bitbang.c,v 1.14 2016/06/07 01:06:27 pgoyette Exp $ */ 2 1.1 thorpej 3 1.1 thorpej /* 4 1.1 thorpej * Copyright (c) 2003 Wasabi Systems, Inc. 5 1.1 thorpej * All rights reserved. 6 1.1 thorpej * 7 1.1 thorpej * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8 1.1 thorpej * 9 1.1 thorpej * Redistribution and use in source and binary forms, with or without 10 1.1 thorpej * modification, are permitted provided that the following conditions 11 1.1 thorpej * are met: 12 1.1 thorpej * 1. Redistributions of source code must retain the above copyright 13 1.1 thorpej * notice, this list of conditions and the following disclaimer. 14 1.1 thorpej * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 thorpej * notice, this list of conditions and the following disclaimer in the 16 1.1 thorpej * documentation and/or other materials provided with the distribution. 17 1.1 thorpej * 3. All advertising materials mentioning features or use of this software 18 1.1 thorpej * must display the following acknowledgement: 19 1.1 thorpej * This product includes software developed for the NetBSD Project by 20 1.1 thorpej * Wasabi Systems, Inc. 21 1.1 thorpej * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22 1.1 thorpej * or promote products derived from this software without specific prior 23 1.1 thorpej * written permission. 24 1.1 thorpej * 25 1.1 thorpej * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26 1.1 thorpej * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27 1.1 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28 1.1 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29 1.1 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 1.1 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 1.1 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 1.1 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 1.1 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 1.1 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 1.1 thorpej * POSSIBILITY OF SUCH DAMAGE. 36 1.1 thorpej */ 37 1.1 thorpej 38 1.1 thorpej /* 39 1.1 thorpej * Common module for bit-bang'ing an I2C bus. 40 1.1 thorpej */ 41 1.1 thorpej 42 1.9 lukem #include <sys/cdefs.h> 43 1.14 pgoyette __KERNEL_RCSID(0, "$NetBSD: i2c_bitbang.c,v 1.14 2016/06/07 01:06:27 pgoyette Exp $"); 44 1.9 lukem 45 1.14 pgoyette #include <sys/module.h> 46 1.1 thorpej #include <sys/param.h> 47 1.1 thorpej 48 1.1 thorpej #include <dev/i2c/i2cvar.h> 49 1.1 thorpej #include <dev/i2c/i2c_bitbang.h> 50 1.1 thorpej 51 1.3 christos #define SETBITS(x) ops->ibo_set_bits(v, (x)) 52 1.1 thorpej #define DIR(x) ops->ibo_set_dir(v, (x)) 53 1.1 thorpej #define READ ops->ibo_read_bits(v) 54 1.1 thorpej 55 1.1 thorpej #define SDA ops->ibo_bits[I2C_BIT_SDA] /* i2c signal */ 56 1.1 thorpej #define SCL ops->ibo_bits[I2C_BIT_SCL] /* i2c signal */ 57 1.1 thorpej #define OUTPUT ops->ibo_bits[I2C_BIT_OUTPUT] /* SDA is output */ 58 1.1 thorpej #define INPUT ops->ibo_bits[I2C_BIT_INPUT] /* SDA is input */ 59 1.1 thorpej 60 1.7 macallan #ifndef SCL_BAIL_COUNT 61 1.7 macallan #define SCL_BAIL_COUNT 1000 62 1.7 macallan #endif 63 1.7 macallan 64 1.7 macallan static inline int i2c_wait_for_scl(void *, i2c_bitbang_ops_t); 65 1.7 macallan 66 1.7 macallan static inline int 67 1.7 macallan i2c_wait_for_scl(void *v, i2c_bitbang_ops_t ops) 68 1.7 macallan { 69 1.7 macallan int bail = 0; 70 1.7 macallan 71 1.7 macallan while (((READ & SCL) == 0) && (bail < SCL_BAIL_COUNT)) { 72 1.7 macallan delay(1); 73 1.7 macallan bail++; 74 1.7 macallan } 75 1.7 macallan if (bail == SCL_BAIL_COUNT) { 76 1.7 macallan i2c_bitbang_send_stop(v, 0, ops); 77 1.7 macallan return EIO; 78 1.7 macallan } 79 1.7 macallan return 0; 80 1.7 macallan } 81 1.7 macallan 82 1.1 thorpej /*ARGSUSED*/ 83 1.1 thorpej int 84 1.6 christos i2c_bitbang_send_start(void *v, int flags, i2c_bitbang_ops_t ops) 85 1.1 thorpej { 86 1.1 thorpej 87 1.13 tsutsui /* start condition: put SDA H->L edge during SCL=H */ 88 1.11 tsutsui 89 1.1 thorpej DIR(OUTPUT); 90 1.3 christos SETBITS(SDA | SCL); 91 1.10 tsutsui delay(5); /* bus free time (4.7 us) */ 92 1.11 tsutsui SETBITS( 0 | SCL); 93 1.7 macallan if (i2c_wait_for_scl(v, ops) != 0) 94 1.7 macallan return EIO; 95 1.10 tsutsui delay(4); /* start hold time (4.0 us) */ 96 1.8 kiyohara 97 1.13 tsutsui /* leave SCL=L and SDA=L to avoid unexpected start/stop condition */ 98 1.11 tsutsui SETBITS( 0 | 0); 99 1.1 thorpej 100 1.10 tsutsui return 0; 101 1.1 thorpej } 102 1.1 thorpej 103 1.1 thorpej /*ARGSUSED*/ 104 1.1 thorpej int 105 1.6 christos i2c_bitbang_send_stop(void *v, int flags, i2c_bitbang_ops_t ops) 106 1.1 thorpej { 107 1.1 thorpej 108 1.13 tsutsui /* stop condition: put SDA L->H edge during SCL=H */ 109 1.11 tsutsui 110 1.13 tsutsui /* assume SCL=L, SDA=L here */ 111 1.1 thorpej DIR(OUTPUT); 112 1.11 tsutsui SETBITS( 0 | SCL); 113 1.10 tsutsui delay(4); /* stop setup time (4.0 us) */ 114 1.3 christos SETBITS(SDA | SCL); 115 1.1 thorpej 116 1.10 tsutsui return 0; 117 1.1 thorpej } 118 1.1 thorpej 119 1.1 thorpej int 120 1.1 thorpej i2c_bitbang_initiate_xfer(void *v, i2c_addr_t addr, int flags, 121 1.1 thorpej i2c_bitbang_ops_t ops) 122 1.1 thorpej { 123 1.1 thorpej 124 1.4 gdamore if (addr < 0x80) { 125 1.4 gdamore uint8_t i2caddr; 126 1.1 thorpej 127 1.4 gdamore /* disallow the 10-bit address prefix */ 128 1.4 gdamore if ((addr & 0x78) == 0x78) 129 1.4 gdamore return EINVAL; 130 1.4 gdamore i2caddr = (addr << 1) | ((flags & I2C_F_READ) ? 1 : 0); 131 1.4 gdamore (void) i2c_bitbang_send_start(v, flags, ops); 132 1.4 gdamore 133 1.4 gdamore return (i2c_bitbang_write_byte(v, i2caddr, 134 1.4 gdamore flags & ~I2C_F_STOP, ops)); 135 1.4 gdamore 136 1.4 gdamore } else if (addr < 0x400) { 137 1.4 gdamore uint16_t i2caddr; 138 1.4 gdamore int rv; 139 1.4 gdamore 140 1.4 gdamore i2caddr = (addr << 1) | ((flags & I2C_F_READ) ? 1 : 0) | 141 1.4 gdamore 0xf000; 142 1.4 gdamore 143 1.4 gdamore (void) i2c_bitbang_send_start(v, flags, ops); 144 1.4 gdamore rv = i2c_bitbang_write_byte(v, i2caddr >> 8, 145 1.4 gdamore flags & ~I2C_F_STOP, ops); 146 1.4 gdamore /* did a slave ack the 10-bit prefix? */ 147 1.4 gdamore if (rv != 0) 148 1.4 gdamore return rv; 149 1.4 gdamore 150 1.4 gdamore /* send the lower 7-bits (+ read/write mode) */ 151 1.4 gdamore return (i2c_bitbang_write_byte(v, i2caddr & 0xff, 152 1.4 gdamore flags & ~I2C_F_STOP, ops)); 153 1.1 thorpej 154 1.4 gdamore } else 155 1.4 gdamore return EINVAL; 156 1.1 thorpej } 157 1.1 thorpej 158 1.1 thorpej int 159 1.10 tsutsui i2c_bitbang_read_byte(void *v, uint8_t *valp, int flags, i2c_bitbang_ops_t ops) 160 1.1 thorpej { 161 1.1 thorpej int i; 162 1.1 thorpej uint8_t val = 0; 163 1.1 thorpej uint32_t bit; 164 1.1 thorpej 165 1.11 tsutsui /* assume SCL=L, SDA=L here */ 166 1.11 tsutsui 167 1.11 tsutsui DIR(INPUT); 168 1.1 thorpej 169 1.1 thorpej for (i = 0; i < 8; i++) { 170 1.1 thorpej val <<= 1; 171 1.8 kiyohara 172 1.11 tsutsui /* data is set at SCL H->L edge */ 173 1.12 tsutsui /* SDA is set here because DIR() is INPUT */ 174 1.12 tsutsui SETBITS(SDA | 0); 175 1.11 tsutsui delay(5); /* clock low time (4.7 us) */ 176 1.8 kiyohara 177 1.11 tsutsui /* read data at SCL L->H edge */ 178 1.12 tsutsui SETBITS(SDA | SCL); 179 1.7 macallan if (i2c_wait_for_scl(v, ops) != 0) 180 1.7 macallan return EIO; 181 1.1 thorpej if (READ & SDA) 182 1.1 thorpej val |= 1; 183 1.11 tsutsui delay(4); /* clock high time (4.0 us) */ 184 1.1 thorpej } 185 1.11 tsutsui /* set SCL H->L before set SDA direction OUTPUT */ 186 1.12 tsutsui SETBITS(SDA | 0); 187 1.1 thorpej 188 1.11 tsutsui /* set ack after SCL H->L edge */ 189 1.1 thorpej bit = (flags & I2C_F_LAST) ? SDA : 0; 190 1.11 tsutsui DIR(OUTPUT); 191 1.11 tsutsui SETBITS(bit | 0); 192 1.11 tsutsui delay(5); /* clock low time (4.7 us) */ 193 1.8 kiyohara 194 1.11 tsutsui /* ack is checked at SCL L->H edge */ 195 1.3 christos SETBITS(bit | SCL); 196 1.7 macallan if (i2c_wait_for_scl(v, ops) != 0) 197 1.7 macallan return EIO; 198 1.10 tsutsui delay(4); /* clock high time (4.0 us) */ 199 1.8 kiyohara 200 1.11 tsutsui /* set SCL H->L for next data; don't change SDA here */ 201 1.11 tsutsui SETBITS(bit | 0); 202 1.1 thorpej 203 1.13 tsutsui /* leave SCL=L and SDA=L to avoid unexpected start/stop condition */ 204 1.11 tsutsui SETBITS( 0 | 0); 205 1.11 tsutsui 206 1.1 thorpej 207 1.1 thorpej if ((flags & (I2C_F_STOP | I2C_F_LAST)) == (I2C_F_STOP | I2C_F_LAST)) 208 1.1 thorpej (void) i2c_bitbang_send_stop(v, flags, ops); 209 1.1 thorpej 210 1.1 thorpej *valp = val; 211 1.10 tsutsui return 0; 212 1.1 thorpej } 213 1.1 thorpej 214 1.1 thorpej int 215 1.10 tsutsui i2c_bitbang_write_byte(void *v, uint8_t val, int flags, i2c_bitbang_ops_t ops) 216 1.1 thorpej { 217 1.1 thorpej uint32_t bit; 218 1.1 thorpej uint8_t mask; 219 1.1 thorpej int error; 220 1.1 thorpej 221 1.11 tsutsui /* assume at SCL=L, SDA=L here */ 222 1.11 tsutsui 223 1.11 tsutsui DIR(OUTPUT); 224 1.11 tsutsui 225 1.1 thorpej for (mask = 0x80; mask != 0; mask >>= 1) { 226 1.1 thorpej bit = (val & mask) ? SDA : 0; 227 1.8 kiyohara 228 1.11 tsutsui /* set data after SCL H->L edge */ 229 1.11 tsutsui SETBITS(bit | 0); 230 1.11 tsutsui delay(5); /* clock low time (4.7 us) */ 231 1.11 tsutsui 232 1.11 tsutsui /* data is fetched at SCL L->H edge */ 233 1.3 christos SETBITS(bit | SCL); 234 1.7 macallan if (i2c_wait_for_scl(v, ops)) 235 1.7 macallan return EIO; 236 1.10 tsutsui delay(4); /* clock high time (4.0 us) */ 237 1.8 kiyohara 238 1.11 tsutsui /* put SCL H->L edge; don't change SDA here */ 239 1.11 tsutsui SETBITS(bit | 0); 240 1.1 thorpej } 241 1.1 thorpej 242 1.11 tsutsui /* ack is set at H->L edge */ 243 1.11 tsutsui DIR(INPUT); 244 1.11 tsutsui delay(5); /* clock low time (4.7 us) */ 245 1.8 kiyohara 246 1.11 tsutsui /* read ack at L->H edge */ 247 1.12 tsutsui /* SDA is set here because DIR() is INPUT */ 248 1.12 tsutsui SETBITS(SDA | SCL); 249 1.7 macallan if (i2c_wait_for_scl(v, ops) != 0) 250 1.7 macallan return EIO; 251 1.1 thorpej error = (READ & SDA) ? EIO : 0; 252 1.11 tsutsui delay(4); /* clock high time (4.0 us) */ 253 1.8 kiyohara 254 1.12 tsutsui /* set SCL H->L before set SDA direction OUTPUT */ 255 1.12 tsutsui SETBITS(SDA | 0); 256 1.12 tsutsui DIR(OUTPUT); 257 1.13 tsutsui /* leave SCL=L and SDA=L to avoid unexpected start/stop condition */ 258 1.11 tsutsui SETBITS( 0 | 0); 259 1.1 thorpej 260 1.1 thorpej if (flags & I2C_F_STOP) 261 1.1 thorpej (void) i2c_bitbang_send_stop(v, flags, ops); 262 1.1 thorpej 263 1.10 tsutsui return error; 264 1.1 thorpej } 265 1.14 pgoyette 266 1.14 pgoyette MODULE(MODULE_CLASS_MISC, i2c_bitbang, NULL); 267 1.14 pgoyette 268 1.14 pgoyette static int 269 1.14 pgoyette i2c_bitbang_modcmd(modcmd_t cmd, void *opaque) 270 1.14 pgoyette { 271 1.14 pgoyette 272 1.14 pgoyette switch (cmd) { 273 1.14 pgoyette case MODULE_CMD_INIT: 274 1.14 pgoyette return 0; 275 1.14 pgoyette case MODULE_CMD_FINI: 276 1.14 pgoyette return 0; 277 1.14 pgoyette default: 278 1.14 pgoyette return ENOTTY; 279 1.14 pgoyette } 280 1.14 pgoyette } 281