1 /*
2 * Copyright (c) 2004-2007 Marcus Overhagen <marcus@overhagen.de>
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify,
8 * merge, publish, distribute, sublicense, and/or sell copies of
9 * the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <KernelExport.h>
28 #include "i2c_core.h"
29
30 #define TRACE_I2C
31 #ifdef TRACE_I2C
32 #define TRACE dprintf
33 #else
34 #define TRACE(a...)
35 #endif
36
37 static status_t i2c_writebyte(i2c_bus *bus, uint8 byte, int *ack);
38 static status_t i2c_readbyte(i2c_bus *bus, uint8 *pbyte);
39 static status_t i2c_read_unlocked(i2c_bus *bus, int address, void *data, int size);
40 static status_t i2c_write_unlocked(i2c_bus *bus, int address, const void *data, int size);
41
42
43 struct _i2c_bus
44 {
45 void *cookie;
46 bigtime_t delay;
47 bigtime_t timeout;
48 i2c_set_scl set_scl;
49 i2c_set_sda set_sda;
50 i2c_get_scl get_scl;
51 i2c_get_sda get_sda;
52 sem_id sem;
53 };
54
55
56 i2c_bus *
i2c_create_bus(void * cookie,int frequency,bigtime_t timeout,i2c_set_scl set_scl,i2c_set_sda set_sda,i2c_get_scl get_scl,i2c_get_sda get_sda)57 i2c_create_bus(void *cookie,
58 int frequency,
59 bigtime_t timeout,
60 i2c_set_scl set_scl,
61 i2c_set_sda set_sda,
62 i2c_get_scl get_scl,
63 i2c_get_sda get_sda)
64 {
65 i2c_bus *bus = malloc(sizeof(i2c_bus));
66 if (!bus)
67 return NULL;
68
69 bus->sem = create_sem(1, "i2c bus access");
70 if (bus->sem < 0) {
71 free(bus);
72 return NULL;
73 }
74
75 bus->cookie = cookie;
76 bus->delay = 1000000 / frequency;
77 if (bus->delay == 0)
78 bus->delay = 1;
79 bus->timeout = timeout;
80 bus->set_scl = set_scl;
81 bus->set_sda = set_sda;
82 bus->get_scl = get_scl;
83 bus->get_sda = get_sda;
84
85 set_scl(cookie, 1);
86 set_sda(cookie, 1);
87
88 return bus;
89 }
90
91
92 void
i2c_delete_bus(i2c_bus * bus)93 i2c_delete_bus(i2c_bus *bus)
94 {
95 if (!bus)
96 return;
97 delete_sem(bus->sem);
98 free(bus);
99 }
100
101
102 static inline void
set_sda_low(i2c_bus * bus)103 set_sda_low(i2c_bus *bus)
104 {
105 bus->set_sda(bus->cookie, 0);
106 snooze(bus->delay);
107 }
108
109
110 static inline void
set_sda_high(i2c_bus * bus)111 set_sda_high(i2c_bus *bus)
112 {
113 bus->set_sda(bus->cookie, 1);
114 snooze(bus->delay);
115 }
116
117
118 static inline void
set_scl_low(i2c_bus * bus)119 set_scl_low(i2c_bus *bus)
120 {
121 bus->set_scl(bus->cookie, 0);
122 snooze(bus->delay);
123 }
124
125
126 static inline status_t
set_scl_high(i2c_bus * bus)127 set_scl_high(i2c_bus *bus)
128 {
129 bigtime_t end = system_time() + bus->timeout;
130 bus->set_scl(bus->cookie, 1);
131 while (0 == bus->get_scl(bus->cookie)) {
132 if (system_time() > end)
133 return B_TIMED_OUT;
134 snooze(5);
135 }
136 snooze(bus->delay);
137 return B_OK;
138 }
139
140
141 static inline void
i2c_start(i2c_bus * bus)142 i2c_start(i2c_bus *bus)
143 {
144 set_sda_low(bus);
145 set_scl_low(bus);
146 }
147
148
149 static inline void
i2c_stop(i2c_bus * bus)150 i2c_stop(i2c_bus *bus)
151 {
152 set_sda_low(bus);
153 set_scl_high(bus);
154 set_sda_high(bus);
155 }
156
157
158 static inline status_t
i2c_start_address(i2c_bus * bus,int address,int read)159 i2c_start_address(i2c_bus *bus, int address, int read /* 1 = read, 0 = write */)
160 {
161 status_t res;
162 uint8 addr;
163 int ack;
164 int i;
165
166 // TRACE("i2c_start_address: enter\n");
167
168 addr = (address << 1) | (read & 1);
169 for (i = 0; i < 5; i++) {
170 i2c_start(bus);
171 res = i2c_writebyte(bus, addr, &ack);
172 if (res == B_OK) {
173 if (ack)
174 break;
175 res = B_ERROR;
176 }
177 if (res == B_TIMED_OUT)
178 break;
179 i2c_stop(bus);
180 snooze(50);
181 }
182
183 // TRACE("i2c_start_address: %s, ack %d, i %d\n", strerror(res), ack, i);
184 return res;
185 }
186
187
188 /* write one byte and the ack/nack
189 * return values:
190 * B_OK => byte transmitted
191 * B_ERROR => arbitration lost
192 * B_TIMED_OUT => time out
193 */
194 status_t
i2c_writebyte(i2c_bus * bus,uint8 byte,int * ack)195 i2c_writebyte(i2c_bus *bus, uint8 byte, int *ack)
196 {
197 int has_arbitration;
198
199 int i;
200 has_arbitration = 1;
201 for (i = 7; i >= 0; i--) {
202 int bit = (byte >> i) & 1;
203 if (has_arbitration) {
204 if (bit)
205 set_sda_high(bus);
206 else
207 set_sda_low(bus);
208 }
209 if (set_scl_high(bus) != B_OK) {
210 set_sda_high(bus); // avoid blocking the bus
211 TRACE("i2c_writebyte timeout at bit %d\n", i);
212 return B_TIMED_OUT;
213 }
214 if (has_arbitration) {
215 if (bit == 1 && 0 == bus->get_sda(bus->cookie)) {
216 has_arbitration = 0;
217 // TRACE("i2c_writebyte lost arbitration at bit %d\n", i);
218 }
219 }
220 set_scl_low(bus);
221 }
222 set_sda_high(bus);
223 if (set_scl_high(bus) != B_OK) {
224 TRACE("i2c_writebyte timeout at ack\n");
225 return B_TIMED_OUT;
226 }
227 *ack = 0 == bus->get_sda(bus->cookie);
228 set_scl_low(bus);
229
230 return has_arbitration ? B_OK : B_ERROR;
231 }
232
233
234 /* read one byte, don't generate ack
235 */
236 status_t
i2c_readbyte(i2c_bus * bus,uint8 * pbyte)237 i2c_readbyte(i2c_bus *bus, uint8 *pbyte)
238 {
239 int i;
240 uint8 byte;
241
242 set_sda_high(bus);
243 byte = 0;
244 for (i = 7; i >= 0; i--) {
245 if (set_scl_high(bus) != B_OK) {
246 TRACE("i2c_readbyte timeout at bit %d\n", i);
247 return B_TIMED_OUT;
248 }
249 byte = (byte << 1) | bus->get_sda(bus->cookie);
250 set_scl_low(bus);
251 }
252 *pbyte = byte;
253 return B_OK;
254 }
255
256
257 status_t
i2c_read_unlocked(i2c_bus * bus,int address,void * data,int size)258 i2c_read_unlocked(i2c_bus *bus, int address, void *data, int size)
259 {
260 status_t status;
261 uint8 *bytes;
262
263 if (size <= 0)
264 return B_BAD_VALUE;
265
266 status = i2c_start_address(bus, address, 1 /* 1 = read, 0 = write */);
267 if (status != B_OK) {
268 TRACE("i2c_read: error on i2c_start_address: %s\n", strerror(status));
269 return status;
270 }
271
272 bytes = data;
273 while (size > 0) {
274 if (i2c_readbyte(bus, bytes) != B_OK) { // timeout
275 TRACE("i2c_read: timeout error on byte %ld\n", bytes - (uint8 *)data);
276 return B_TIMED_OUT;
277 }
278 if (size > 0)
279 set_sda_low(bus); // ack
280 else
281 set_sda_high(bus); // nack
282
283 if (set_scl_high(bus) != B_OK) {
284 set_sda_high(bus); // avoid blocking the bus
285 TRACE("i2c_read: timeout at ack\n");
286 return B_TIMED_OUT;
287 }
288 set_scl_low(bus);
289 set_sda_high(bus);
290
291 size--;
292 bytes++;
293 }
294
295 i2c_stop(bus);
296
297 return B_OK;
298 }
299
300
301 status_t
i2c_write_unlocked(i2c_bus * bus,int address,const void * data,int size)302 i2c_write_unlocked(i2c_bus *bus, int address, const void *data, int size)
303 {
304 const uint8 *bytes;
305 status_t status;
306 int ack;
307
308 if (size <= 0)
309 return B_BAD_VALUE;
310
311 status = i2c_start_address(bus, address, 0 /* 1 = read, 0 = write */);
312 if (status != B_OK) {
313 TRACE("i2c_write: error on i2c_start_address: %s\n", strerror(status));
314 return status;
315 }
316
317 bytes = data;
318 while (size > 0) {
319 status = i2c_writebyte(bus, *bytes, &ack);
320 if (status == B_TIMED_OUT) {
321 TRACE("i2c_write: timeout error on byte %ld\n", bytes - (uint8 *)data);
322 return B_TIMED_OUT;
323 }
324 if (status != B_OK) {
325 TRACE("i2c_write: arbitration lost on byte %ld\n", bytes - (uint8 *)data);
326 break;
327 }
328 if (!ack) {
329 TRACE("i2c_write: error, got NACK on byte %ld\n", bytes - (uint8 *)data);
330 break;
331 }
332 bytes++;
333 size--;
334 }
335 i2c_stop(bus);
336
337 if (status != B_OK)
338 return status;
339
340 return ack ? B_OK : B_ERROR;
341 }
342
343
344 status_t
i2c_read(i2c_bus * bus,int address,void * data,int size)345 i2c_read(i2c_bus *bus, int address, void *data, int size)
346 {
347 status_t res;
348 acquire_sem(bus->sem);
349 res = i2c_read_unlocked(bus, address, data, size);
350 release_sem(bus->sem);
351 return res;
352 }
353
354
355 status_t
i2c_write(i2c_bus * bus,int address,const void * data,int size)356 i2c_write(i2c_bus *bus, int address, const void *data, int size)
357 {
358 status_t res;
359 acquire_sem(bus->sem);
360 res = i2c_write_unlocked(bus, address, data, size);
361 release_sem(bus->sem);
362 return res;
363 }
364
365
366 status_t
i2c_xfer(i2c_bus * bus,int address,const void * write_data,int write_size,void * read_data,int read_size)367 i2c_xfer(i2c_bus *bus, int address,
368 const void *write_data, int write_size,
369 void *read_data, int read_size)
370 {
371 status_t res;
372
373 acquire_sem(bus->sem);
374
375 if (write_data != 0 && write_size != 0) {
376 res = i2c_write_unlocked(bus, address, write_data, write_size);
377 if (res != B_OK) {
378 release_sem(bus->sem);
379 return res;
380 }
381 }
382
383 if (read_data != 0 && read_size != 0) {
384 res = i2c_read_unlocked(bus, address, read_data, read_size);
385 if (res != B_OK) {
386 release_sem(bus->sem);
387 return res;
388 }
389 }
390
391 release_sem(bus->sem);
392
393 return B_OK;
394 }
395