xref: /haiku/src/add-ons/kernel/drivers/dvb/cx23882/i2c_core.c (revision 1caca357daf16c5a31c759d70911ac20247e2714)
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