diff options
Diffstat (limited to 'firmware/export/i2c-async.h')
-rw-r--r-- | firmware/export/i2c-async.h | 302 |
1 files changed, 302 insertions, 0 deletions
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 | */ | ||
137 | typedef 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 | */ | ||
212 | extern 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 | */ | ||
224 | extern 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 | */ | ||
233 | extern 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 | */ | ||
242 | extern int i2c_reg_write(int bus, uint8_t addr, uint8_t reg, | ||
243 | int count, const uint8_t* buf); | ||
244 | extern int i2c_reg_read(int bus, uint8_t addr, uint8_t reg, | ||
245 | int count, uint8_t* buf); | ||
246 | extern 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 */ | ||
250 | inline 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. */ | ||
257 | inline 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 */ | ||
268 | inline 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. */ | ||
283 | extern 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 | */ | ||
293 | extern 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 | */ | ||
299 | extern void __i2c_async_submit(int bus, i2c_descriptor* desc); | ||
300 | |||
301 | #endif /* HAVE_I2C_ASYNC */ | ||
302 | #endif /* __I2C_ASYNC_H__ */ | ||