summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
Diffstat (limited to 'firmware')
-rw-r--r--firmware/SOURCES4
-rw-r--r--firmware/drivers/i2c-async.c398
-rw-r--r--firmware/export/i2c-async.h302
3 files changed, 704 insertions, 0 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 430eb4119b..236933bdb7 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -1965,6 +1965,10 @@ target/hosted/sdl/filesystem-sdl.c
1965drivers/touchpad.c 1965drivers/touchpad.c
1966#endif 1966#endif
1967 1967
1968#ifdef HAVE_I2C_ASYNC
1969drivers/i2c-async.c
1970#endif
1971
1968/* firmware/kernel section */ 1972/* firmware/kernel section */
1969#ifdef HAVE_CORELOCK_OBJECT 1973#ifdef HAVE_CORELOCK_OBJECT
1970kernel/corelock.c 1974kernel/corelock.c
diff --git a/firmware/drivers/i2c-async.c b/firmware/drivers/i2c-async.c
new file mode 100644
index 0000000000..f18bea565f
--- /dev/null
+++ b/firmware/drivers/i2c-async.c
@@ -0,0 +1,398 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "i2c-async.h"
23#include "config.h"
24#include "system.h"
25#include "kernel.h"
26
27/* To decide on the queue size, typically you should use the formula:
28 *
29 * queue size = max { D_1, ..., D_n } * max { T_1, ..., T_m }
30 *
31 * where
32 *
33 * n = number of busses
34 * m = number of devices across all busses
35 * D_i = number of devices on bus i
36 * T_j = number of queued transactions needed for device j
37 *
38 * The idea is to ensure nobody waits for a queue slot, but keep the queue
39 * size to a minimum so that queue management features don't take too long.
40 * (They have to iterate over the entire queue with IRQs disabled, so...)
41 *
42 * If you don't use the asychronous features then waiting for a queue slot
43 * is a moot point since you'll be blocking until completion anyway; in that
44 * case feel free to use single-entry queues.
45 *
46 * Note that each bus gets the same sized queue. If this isn't good for
47 * your target for whatever reason, it might be worth implementing per-bus
48 * queue sizes here.
49 */
50#if !defined(I2C_ASYNC_BUS_COUNT) || !defined(I2C_ASYNC_QUEUE_SIZE)
51# error "Need to add #defines for i2c-async frontend"
52#endif
53
54#if I2C_ASYNC_QUEUE_SIZE < 1
55# error "i2c-async queue size must be >= 1"
56#endif
57
58/* Singly-linked list for queuing up transactions */
59typedef struct i2c_async_txn {
60 struct i2c_async_txn* next;
61 i2c_descriptor* desc;
62 int cookie;
63} i2c_async_txn;
64
65typedef struct i2c_async_bus {
66 /* Head/tail of transaction queue. The head always points to the
67 * currently running transaction, or NULL if the bus is idle.
68 */
69 i2c_async_txn* head;
70 i2c_async_txn* tail;
71
72 /* Head of a free list used to allocate nodes. */
73 i2c_async_txn* free;
74
75 /* Next unallocated cookie value */
76 int nextcookie;
77
78 /* Semaphore for reserving a node in the free list. Nodes can only be
79 * allocated after a successful semaphore_wait(), and after being freed
80 * semaphore_release() must be called.
81 */
82 struct semaphore sema;
83
84#ifdef HAVE_CORELOCK_OBJECT
85 /* Corelock is required for multi-core CPUs */
86 struct corelock cl;
87#endif
88} i2c_async_bus;
89
90static i2c_async_txn i2c_async_txns[I2C_ASYNC_BUS_COUNT][I2C_ASYNC_QUEUE_SIZE];
91static i2c_async_bus i2c_async_busses[I2C_ASYNC_BUS_COUNT];
92
93static i2c_async_txn* i2c_async_popfree(i2c_async_txn** head)
94{
95 i2c_async_txn* node = *head;
96 *head = node->next;
97 return node;
98}
99
100static void i2c_async_pushfree(i2c_async_txn** head, i2c_async_txn* node)
101{
102 node->next = *head;
103 *head = node;
104}
105
106static i2c_async_txn* i2c_async_pool_init(i2c_async_txn* array, int count)
107{
108 /* Populate forward pointers */
109 for(int i = 0; i < count - 1; ++i)
110 array[i].next = &array[i+1];
111
112 /* Last pointer is NULL */
113 array[count - 1].next = NULL;
114
115 /* Return head of pool */
116 return &array[0];
117}
118
119static void i2c_async_bus_init(int busnr)
120{
121 const int q_size = I2C_ASYNC_QUEUE_SIZE;
122
123 i2c_async_bus* bus = &i2c_async_busses[busnr];
124 bus->head = bus->tail = NULL;
125 bus->free = i2c_async_pool_init(i2c_async_txns[busnr], q_size);
126 bus->nextcookie = 1;
127 semaphore_init(&bus->sema, q_size, q_size);
128 corelock_init(&bus->cl);
129}
130
131/* Add a node to the end of the transaction queue */
132static void i2c_async_bus_enqueue(i2c_async_bus* bus, i2c_async_txn* node)
133{
134 node->next = NULL;
135 if(bus->head == NULL)
136 bus->head = bus->tail = node;
137 else {
138 bus->tail->next = node;
139 bus->tail = node;
140 }
141}
142
143/* Helper function called to run descriptor completion tasks */
144static void i2c_async_bus_complete_desc(i2c_async_bus* bus, int status)
145{
146 i2c_descriptor* d = bus->head->desc;
147 if(d->callback)
148 d->callback(status, d);
149
150 bus->head->desc = d->next;
151}
152
153void __i2c_async_complete_callback(int busnr, int status)
154{
155 i2c_async_bus* bus = &i2c_async_busses[busnr];
156 corelock_lock(&bus->cl);
157
158 i2c_async_bus_complete_desc(bus, status);
159 if(status != I2C_STATUS_OK) {
160 /* Skip remainder of transaction after an error */
161 while(bus->head->desc)
162 i2c_async_bus_complete_desc(bus, I2C_STATUS_SKIPPED);
163 }
164
165 /* Dequeue next transaction if we finished the current one */
166 if(!bus->head->desc) {
167 i2c_async_txn* node = bus->head;
168 bus->head = node->next;
169 i2c_async_pushfree(&bus->free, node);
170 semaphore_release(&bus->sema);
171 }
172
173 if(bus->head) {
174 /* Submit the next descriptor */
175 __i2c_async_submit(busnr, bus->head->desc);
176 } else {
177 /* Fixup tail after last transaction */
178 bus->tail = NULL;
179 }
180
181 corelock_unlock(&bus->cl);
182}
183
184void __i2c_async_init(void)
185{
186 for(int i = 0; i < I2C_ASYNC_BUS_COUNT; ++i)
187 i2c_async_bus_init(i);
188}
189
190int i2c_async_queue(int busnr, int timeout, int q_mode,
191 int cookie, i2c_descriptor* desc)
192{
193 i2c_async_txn* node;
194 i2c_async_bus* bus = &i2c_async_busses[busnr];
195 int rc = I2C_RC_OK;
196
197 int irq = disable_irq_save();
198 corelock_lock(&bus->cl);
199
200 /* Scan the queue unless q_mode is a simple ADD */
201 if(q_mode != I2C_Q_ADD) {
202 for(node = bus->head; node != NULL; node = node->next) {
203 if(node->cookie == cookie) {
204 if(q_mode == I2C_Q_REPLACE && node != bus->head)
205 node->desc = desc;
206 else
207 rc = I2C_RC_NOTADDED;
208 goto _exit;
209 }
210 }
211 }
212
213 /* Try to claim a queue node without blocking if we can */
214 if(semaphore_wait(&bus->sema, TIMEOUT_NOBLOCK) != OBJ_WAIT_SUCCEEDED) {
215 /* Bail out now if caller doesn't want to wait */
216 if(timeout == TIMEOUT_NOBLOCK) {
217 rc = I2C_RC_BUSY;
218 goto _exit;
219 }
220
221 /* Wait on the semaphore */
222 corelock_unlock(&bus->cl);
223 restore_irq(irq);
224 if(semaphore_wait(&bus->sema, timeout) == OBJ_WAIT_TIMEDOUT)
225 return I2C_RC_BUSY;
226
227 /* Got a node; re-lock */
228 irq = disable_irq_save();
229 corelock_lock(&bus->cl);
230 }
231
232 /* Alloc the node and push it to queue */
233 node = i2c_async_popfree(&bus->free);
234 node->desc = desc;
235 node->cookie = cookie;
236 i2c_async_bus_enqueue(bus, node);
237
238 /* Start the first descriptor if the bus is idle */
239 if(node == bus->head)
240 __i2c_async_submit(busnr, desc);
241
242 _exit:
243 corelock_unlock(&bus->cl);
244 restore_irq(irq);
245 return rc;
246}
247
248int i2c_async_cancel(int busnr, int cookie)
249{
250 i2c_async_bus* bus = &i2c_async_busses[busnr];
251 int rc = I2C_RC_NOTFOUND;
252
253 int irq = disable_irq_save();
254 corelock_lock(&bus->cl);
255
256 /* Bail if queue is empty */
257 if(!bus->head)
258 goto _exit;
259
260 /* Check the running transaction for a match */
261 if(bus->head->cookie == cookie) {
262 rc = I2C_RC_BUSY;
263 goto _exit;
264 }
265
266 /* Walk the queue, starting after the head */
267 i2c_async_txn* prev = bus->head;
268 i2c_async_txn* node = prev->next;
269 while(node) {
270 if(node->cookie == cookie) {
271 prev->next = node->next;
272 rc = I2C_RC_OK;
273 goto _exit;
274 }
275
276 prev = node;
277 node = node->next;
278 }
279
280 _exit:
281 corelock_unlock(&bus->cl);
282 restore_irq(irq);
283 return rc;
284}
285
286int i2c_async_reserve_cookies(int busnr, int count)
287{
288 i2c_async_bus* bus = &i2c_async_busses[busnr];
289 int ret = bus->nextcookie;
290 bus->nextcookie += count;
291 return ret;
292}
293
294static void i2c_sync_callback(int status, i2c_descriptor* d)
295{
296 struct semaphore* sem = (struct semaphore*)d->arg;
297 d->arg = status;
298 semaphore_release(sem);
299}
300
301static void i2c_reg_modify1_callback(int status, i2c_descriptor* d)
302{
303 if(status == I2C_STATUS_OK) {
304 uint8_t* buf = (uint8_t*)d->buffer[1];
305 uint8_t val = *buf;
306 *buf = (val & ~(d->arg >> 8)) | (d->arg & 0xff);
307 d->arg = val;
308 }
309}
310
311int i2c_reg_write(int bus, uint8_t addr, uint8_t reg,
312 int count, const uint8_t* buf)
313{
314 struct semaphore sem;
315 semaphore_init(&sem, 1, 0);
316
317 i2c_descriptor desc;
318 desc.slave_addr = addr;
319 desc.bus_cond = I2C_START | I2C_STOP;
320 desc.tran_mode = I2C_WRITE;
321 desc.buffer[0] = &reg;
322 desc.count[0] = 1;
323 desc.buffer[1] = (uint8_t*)buf;
324 desc.count[1] = count;
325 desc.callback = &i2c_sync_callback;
326 desc.arg = (intptr_t)&sem;
327 desc.next = NULL;
328
329 i2c_async_queue(bus, TIMEOUT_BLOCK, I2C_Q_ADD, 0, &desc);
330 semaphore_wait(&sem, TIMEOUT_BLOCK);
331
332 return desc.arg;
333}
334
335int i2c_reg_read(int bus, uint8_t addr, uint8_t reg,
336 int count, uint8_t* buf)
337{
338 struct semaphore sem;
339 semaphore_init(&sem, 1, 0);
340
341 i2c_descriptor desc;
342 desc.slave_addr = addr;
343 desc.bus_cond = I2C_START | I2C_STOP;
344 desc.tran_mode = I2C_READ;
345 desc.buffer[0] = &reg;
346 desc.count[0] = 1;
347 desc.buffer[1] = buf;
348 desc.count[1] = count;
349 desc.callback = &i2c_sync_callback;
350 desc.arg = (intptr_t)&sem;
351 desc.next = NULL;
352
353 i2c_async_queue(bus, TIMEOUT_BLOCK, I2C_Q_ADD, 0, &desc);
354 semaphore_wait(&sem, TIMEOUT_BLOCK);
355
356 return desc.arg;
357}
358
359int i2c_reg_modify1(int bus, uint8_t addr, uint8_t reg,
360 uint8_t clr, uint8_t set, uint8_t* val)
361{
362 struct semaphore sem;
363 semaphore_init(&sem, 1, 0);
364
365 uint8_t buf[2];
366 buf[0] = reg;
367
368 i2c_descriptor desc[2];
369 desc[0].slave_addr = addr;
370 desc[0].bus_cond = I2C_START | I2C_STOP;
371 desc[0].tran_mode = I2C_READ;
372 desc[0].buffer[0] = &buf[0];
373 desc[0].count[0] = 1;
374 desc[0].buffer[1] = &buf[1];
375 desc[0].count[1] = 1;
376 desc[0].callback = &i2c_reg_modify1_callback;
377 desc[0].arg = (clr << 8) | set;
378 desc[0].next = &desc[1];
379
380 desc[1].slave_addr = addr;
381 desc[1].bus_cond = I2C_START | I2C_STOP;
382 desc[1].tran_mode = I2C_WRITE;
383 desc[1].buffer[0] = &buf[0];
384 desc[1].count[0] = 2;
385 desc[1].buffer[1] = NULL;
386 desc[1].count[1] = 0;
387 desc[1].callback = &i2c_sync_callback;
388 desc[1].arg = (intptr_t)&sem;
389 desc[1].next = NULL;
390
391 i2c_async_queue(bus, TIMEOUT_BLOCK, I2C_Q_ADD, 0, &desc[0]);
392 semaphore_wait(&sem, TIMEOUT_BLOCK);
393
394 if(val && desc[1].arg == I2C_STATUS_OK)
395 *val = desc[0].arg;
396
397 return desc[1].arg;
398}
diff --git a/firmware/export/i2c-async.h b/firmware/export/i2c-async.h
new file mode 100644
index 0000000000..2877d1875c
--- /dev/null
+++ b/firmware/export/i2c-async.h
@@ -0,0 +1,302 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef __I2C_ASYNC_H__
23#define __I2C_ASYNC_H__
24
25#include "config.h"
26
27/* i2c-async provides an API for asynchronous communication over an I2C bus.
28 * It's not linked to a specific target, so device drivers using this API can
29 * be shared more easily among multiple targets.
30 *
31 * Transactions are built using descriptors, and callbacks can be used to
32 * perform work directly from interrupt context. Callbacks can even change
33 * the descriptor chain on the fly, so the transaction can be altered based
34 * on data recieved over the I2C bus.
35 *
36 * There's an API for synchronous operations on devices using 8-bit register
37 * addresses. This API demonstrates how you can build more specialized routines
38 * on top of the asynchronous API, and is useful in its own right for dealing
39 * with simple devices.
40 */
41
42#ifdef HAVE_I2C_ASYNC
43
44#include "i2c-target.h"
45#include <stdint.h>
46#include <stddef.h>
47#include <stdbool.h>
48
49/* Queueing codes */
50#define I2C_RC_OK 0
51#define I2C_RC_BUSY 1
52#define I2C_RC_NOTADDED 2
53#define I2C_RC_NOTFOUND 3
54
55/* Descriptor status codes */
56#define I2C_STATUS_OK 0
57#define I2C_STATUS_ERROR 1
58#define I2C_STATUS_TIMEOUT 2
59#define I2C_STATUS_SKIPPED 3
60
61/* I2C bus end conditions */
62#define I2C_START (1 << 0)
63#define I2C_RESTART (1 << 1)
64#define I2C_CONTINUE (1 << 2)
65#define I2C_STOP (1 << 3)
66#define I2C_HOLD (1 << 4)
67
68/* Transfer modes */
69#define I2C_READ 0
70#define I2C_WRITE 1
71
72/* Queue modes */
73#define I2C_Q_ADD 0
74#define I2C_Q_ONCE 1
75#define I2C_Q_REPLACE 2
76
77/* Flag for using 10-bit addresses */
78#define I2C_10BIT_ADDR 0x8000
79
80/* Descriptors are used to set up an I2C transfer. The transfer mode is
81 * specified by 'tran_mode', and is normally a READ or WRITE operation.
82 * The transfer mode dictates how the buffer/count fields are interpreted.
83 *
84 * - I2C_WRITE
85 * buffer[0] must be non-NULL and count[0] must be at least 1.
86 * The transfer sends count[0] bytes from buffer[0] on the bus.
87 *
88 * buffer[1] and count[1] can be used to specify a second buffer
89 * whose contents are written after the buffer[0]'s last byte.
90 * No start/stop conditions are issued between the buffers; it is
91 * as if you had appended the contents of buffer[1] to buffer[0].
92 *
93 * If not used, buffer[1] must be NULL and count[1] must be 0.
94 * If used, buffer[1] must be non-NULL and count[1] must be >= 1.
95 *
96 * - I2C_READ
97 * buffer[1] must be non-NULL and count[1] must be at least 1.
98 * The transfer will request count[1] bytes and writes the data
99 * into buffer[1].
100 *
101 * This type of transfer can send some data bytes before the
102 * read operation, eg. to send a register address for the read.
103 * If this feature is not used, set buffer[0] to NULL and set
104 * count[0] to zero.
105 *
106 * If used, buffer[0] must be non-NULL and count[0] must be >= 1.
107 * Between the last byte written and the first byte read, the bus
108 * will automatically issue a RESTART condition.
109 *
110 * Bus conditions are divided into two classes: start and end conditions.
111 * You MUST supply both a start and end condition in the 'bus_cond' field,
112 * by OR'ing together one start condition and one end condition:
113 *
114 * - I2C_START
115 * Issue a START condition before the first data byte. This must be
116 * specified on the first descriptor of a transaction.
117 *
118 * - I2C_RESTART
119 * Issue a RESTART condition before the first data byte. On the bus this
120 * is physically identical to a START condition, but drivers might need
121 * this to distinguish between these two cases.
122 *
123 * - I2C_CONTINUE
124 * Do not issue any condition before the first data byte. This is only
125 * valid if the descriptor continues a previous descriptor which ended
126 * with the HOLD condition; otherwise the results are undefined.
127 *
128 * - I2C_STOP
129 * Issue a STOP condition after the last data byte. This must be set on
130 * the final descriptor in a transaction, so the bus is left in a usable
131 * state when the descriptor finishes.
132 *
133 * - I2C_HOLD
134 * Do not issue any condition after the last data byte. This is only
135 * valid if the next descriptor starts with an I2C_CONTINUE condition.
136 */
137typedef struct i2c_descriptor {
138 /* Address of the target device. To use 10-bit addresses, simply
139 * OR the address with the I2C_10BIT_ADDR. */
140 uint16_t slave_addr;
141
142 /* What to do at the ends of the data transfer */
143 uint8_t bus_cond;
144
145 /* Transfer mode */
146 uint8_t tran_mode;
147
148 /* Buffer/length fields. Their use depends on the transfer mode. */
149 void* buffer[2];
150 int count[2];
151
152 /* Callback which is invoked when the descriptor completes.
153 *
154 * The first argument is a status code and the second argument is a
155 * pointer to the completed descriptor. The status code is the only
156 * way of checking whether the descriptor completed successfully,
157 * and it is NOT saved, so ensure you save it yourself if needed.
158 */
159 void(*callback)(int, struct i2c_descriptor*);
160
161 /* Argument field reserved for the user; not touched by the driver. */
162 intptr_t arg;
163
164 /* Pointer to the next descriptor. */
165 struct i2c_descriptor* next;
166} i2c_descriptor;
167
168/* Public API */
169
170/* Aysnchronously enqueue a descriptor, optionally waiting on a timeout
171 * if the queue is full. The exact behavior depends on 'q_mode':
172 *
173 * - I2C_Q_ADD
174 * Always try to enqueue the descriptor.
175 *
176 * - I2C_Q_ONCE
177 * Only attempt to enqueue the descriptor if no descriptor with the same
178 * cookie is already running or queued. If this is not the case, then
179 * returns I2C_RC_NOTADDED.
180 *
181 * - I2C_Q_REPLACE
182 * If a descriptor with the same cookie is queued, replace it with this
183 * descriptor and do not run the old descriptor's callbacks. If the
184 * matching descriptor is running, returns I2C_RC_NOTADDED and does not
185 * queue the new descriptor. If no match was found, then simply add the
186 * new descriptor to the queue.
187 *
188 * The 'cookie' is only useful if you want to use the ONCE or REPLACE queue
189 * modes, or if you want to use i2c_async_cancel(). Cookies used for queue
190 * management must be reserved with i2c_async_reserve_cookies(), to prevent
191 * different drivers from stepping on each other's toes.
192 *
193 * When you do not need queue management, you can use a 'cookie' of 0, which
194 * is reserved for unmanaged transactions. Only use I2C_Q_ADD if you do this.
195 *
196 * Queuing is only successful if I2C_RC_OK is returned. All other codes
197 * indicate that the descriptor was not queued, and therefore will not be
198 * executed.
199 *
200 * Be careful about how/when you modify and queue descriptors. It's unsafe to
201 * modify a queued descriptor: it could start running at any time, and the bus
202 * might see a half-rewritten version of the descriptor. You _can_ queue the
203 * same descriptor twice, since the i2c-async driver is not allowed to modify
204 * any fields, but your callbacks need to be written with this case in mind.
205 *
206 * You can use queue management to help efficiently re-use descriptors.
207 * Typically you can alternate between multiple descriptors, always keeping one
208 * free to modify, and using your completion callbacks to cycle the free slot.
209 * You can also probe with i2c_async_cancel() to ensure a specific descriptor
210 * is not running before modifying it.
211 */
212extern int i2c_async_queue(int bus, int timeout, int q_mode,
213 int cookie, i2c_descriptor* desc);
214
215/* Cancel a queued descriptor. Searches the queue, starting with the running
216 * descriptor, for a descriptor with a matching cookie, and attempts to remove
217 * it from the queue.
218 *
219 * - Returns I2C_RC_NOTFOUND if no match was found.
220 * - Returns I2C_RC_BUSY if the match is the currently running transaction.
221 * - Returns I2C_RC_OK if the match was found in the pending queue and was
222 * successfully removed from the queue.
223 */
224extern int i2c_async_cancel(int bus, int cookie);
225
226/* Reserve a range of cookie values for use by a driver. This should only be
227 * done once at startup. The driver doesn't care what cookies are used, so you
228 * can manage them any way you like.
229 *
230 * A range [r, r+count) will be allocated, disjoint from all other allocated
231 * ranges and with r >= 1. Returns 'r'.
232 */
233extern int i2c_async_reserve_cookies(int bus, int count);
234
235/* Synchronous API to read, write, and modify registers. The register address
236 * width is limited to 8 bits, although you can read and write multiple bytes.
237 *
238 * The modify operation can do a clear-and-set on a register with 8-bit values.
239 * It also returns the original value of the register before modification, if
240 * val != NULL.
241 */
242extern int i2c_reg_write(int bus, uint8_t addr, uint8_t reg,
243 int count, const uint8_t* buf);
244extern int i2c_reg_read(int bus, uint8_t addr, uint8_t reg,
245 int count, uint8_t* buf);
246extern int i2c_reg_modify1(int bus, uint8_t addr, uint8_t reg,
247 uint8_t clr, uint8_t set, uint8_t* val);
248
249/* Variant to write a single 8-bit value to a register */
250inline int i2c_reg_write1(int bus, uint8_t addr, uint8_t reg, uint8_t val)
251{
252 return i2c_reg_write(bus, addr, reg, 1, &val);
253}
254
255/* Variant to read an 8-bit value from a register; returns the value
256 * directly, or returns -1 on any error. */
257inline int i2c_reg_read1(int bus, uint8_t addr, uint8_t reg)
258{
259 uint8_t v;
260 int i = i2c_reg_read(bus, addr, reg, 1, &v);
261 if(i == I2C_STATUS_OK)
262 return v;
263 else
264 return -1;
265}
266
267/* Variant to set or clear one bit in an 8-bit register */
268inline int i2c_reg_setbit1(int bus, uint8_t addr, uint8_t reg,
269 int bit, int value, uint8_t* val)
270{
271 uint8_t clr = 0, set = 0;
272 if(value)
273 set = 1 << bit;
274 else
275 clr = 1 << bit;
276
277 return i2c_reg_modify1(bus, addr, reg, clr, set, val);
278}
279
280/* Internal API */
281
282/* Must be called by the target's i2c_init() before anyone uses I2C. */
283extern void __i2c_async_init(void);
284
285/* Called by the target's interrupt handlers to signal completion of the
286 * currently running descriptor. You must ensure IRQs are disabled before
287 * calling this function.
288 *
289 * If another descriptor is queued for submission, either as part of the
290 * same transaction or another one, then this may call __i2c_async_submit()
291 * to start the next descriptor.
292 */
293extern void __i2c_async_complete_callback(int bus, int status);
294
295/* Called by the i2c-async core to submit a descriptor to the hardware bus.
296 * This function is implemented by the target. Just start the transfer and
297 * unmask needed interrupts here, and try to return as quickly as possible.
298 */
299extern void __i2c_async_submit(int bus, i2c_descriptor* desc);
300
301#endif /* HAVE_I2C_ASYNC */
302#endif /* __I2C_ASYNC_H__ */