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