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
37static status_t i2c_writebyte(i2c_bus *bus, uint8 byte, int *ack);
38static status_t i2c_readbyte(i2c_bus *bus, uint8 *pbyte);
39static status_t i2c_read_unlocked(i2c_bus *bus, int address, void *data, int size);
40static status_t i2c_write_unlocked(i2c_bus *bus, int address, const void *data, int size);
41
42
43struct _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
56i2c_bus *
57i2c_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
92void
93i2c_delete_bus(i2c_bus *bus)
94{
95	if (!bus)
96		return;
97	delete_sem(bus->sem);
98	free(bus);
99}
100
101
102static inline void
103set_sda_low(i2c_bus *bus)
104{
105	bus->set_sda(bus->cookie, 0);
106	snooze(bus->delay);
107}
108
109
110static inline void
111set_sda_high(i2c_bus *bus)
112{
113	bus->set_sda(bus->cookie, 1);
114	snooze(bus->delay);
115}
116
117
118static inline void
119set_scl_low(i2c_bus *bus)
120{
121	bus->set_scl(bus->cookie, 0);
122	snooze(bus->delay);
123}
124
125
126static inline status_t
127set_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
141static inline void
142i2c_start(i2c_bus *bus)
143{
144	set_sda_low(bus);
145	set_scl_low(bus);
146}
147
148
149static inline void
150i2c_stop(i2c_bus *bus)
151{
152	set_sda_low(bus);
153	set_scl_high(bus);
154	set_sda_high(bus);
155}
156
157
158static inline status_t
159i2c_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 */
194status_t
195i2c_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 */
236status_t
237i2c_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
257status_t
258i2c_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
301status_t
302i2c_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
344status_t
345i2c_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
355status_t
356i2c_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
366status_t
367i2c_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