1 | /* |
---|
2 | * Copyright (c) 2016-2017 Chris Johns <chrisj@rtems.org> All rights reserved. |
---|
3 | * |
---|
4 | * The license and distribution terms for this file may be |
---|
5 | * found in the file LICENSE in this distribution or at |
---|
6 | * http://www.rtems.org/license/LICENSE. |
---|
7 | */ |
---|
8 | |
---|
9 | /* |
---|
10 | * Xilinx AXI IIC Interface v2.0. See PG090.pdf. |
---|
11 | * |
---|
12 | * Note, only master support is provided and no dynamic mode by design. |
---|
13 | */ |
---|
14 | |
---|
15 | #include <stdarg.h> |
---|
16 | |
---|
17 | #include <rtems.h> |
---|
18 | #include <rtems/bspIo.h> |
---|
19 | #include <rtems/irq-extension.h> |
---|
20 | #include <rtems/score/assert.h> |
---|
21 | |
---|
22 | #include <dev/i2c/i2c.h> |
---|
23 | #include <dev/i2c/xilinx-axi-i2c.h> |
---|
24 | |
---|
25 | /* |
---|
26 | * Register map. |
---|
27 | */ |
---|
28 | #define REG_GIE 0x01c |
---|
29 | #define REG_ISR 0x020 |
---|
30 | #define REG_IER 0x028 |
---|
31 | #define REG_SOFTR 0x040 |
---|
32 | #define REG_CR 0x100 |
---|
33 | #define REG_SR 0x104 |
---|
34 | #define REG_TX_FIFO 0x108 |
---|
35 | #define REG_RX_FIFO 0x10c |
---|
36 | #define REG_ADR 0x110 |
---|
37 | #define REG_TX_FIFO_OCY 0x114 |
---|
38 | #define REG_RX_FIFO_OCY 0x118 |
---|
39 | #define REG_TEN_ADR 0x11c |
---|
40 | #define REG_RX_FIFO_PIRQ 0x120 |
---|
41 | #define REG_GPO 0x124 |
---|
42 | #define REG_TSUSTA 0x128 |
---|
43 | #define REG_TSUSTO 0x12c |
---|
44 | #define REG_THDSTA 0x130 |
---|
45 | #define REG_TSUDAT 0x134 |
---|
46 | #define REG_TBUF 0x138 |
---|
47 | #define REG_THIGH 0x13c |
---|
48 | #define REG_TLOW 0x140 |
---|
49 | #define REG_THDDAT 0x144 |
---|
50 | |
---|
51 | /* |
---|
52 | * Interrupts. |
---|
53 | */ |
---|
54 | #define INT_ARB_LOST (1 << 0) |
---|
55 | #define INT_TX_ERROR (1 << 1) |
---|
56 | #define INT_TX_FIFO_EMPTY (1 << 2) |
---|
57 | #define INT_RX_FIFO_FULL (1 << 3) |
---|
58 | #define INT_BUS_NOT_BUSY (1 << 4) |
---|
59 | #define INT_ADDRESSED_AS_SLAVE (1 << 5) |
---|
60 | #define INT_NOT_ADDRESSED_AS_SLAVE (1 << 6) |
---|
61 | #define INT_TX_FIFO_HALF_FULL (1 << 7) |
---|
62 | #define INT_ALL (0xff) |
---|
63 | |
---|
64 | /* |
---|
65 | * Command register. |
---|
66 | */ |
---|
67 | #define CR_EN (1 << 0) |
---|
68 | #define CR_TX_FIFO_RESET (1 << 1) |
---|
69 | #define CR_MSMS (1 << 2) |
---|
70 | #define CR_TX (1 << 3) |
---|
71 | #define CR_TXAK (1 << 4) |
---|
72 | #define CR_RSTA (1 << 5) |
---|
73 | #define CR_GC_EN (1 << 6) |
---|
74 | |
---|
75 | /* |
---|
76 | * Status register. |
---|
77 | */ |
---|
78 | #define SR_ABGC (1 << 0) |
---|
79 | #define SR_AAS (1 << 1) |
---|
80 | #define SR_BB (1 << 2) |
---|
81 | #define SR_SRW (1 << 3) |
---|
82 | #define SR_TX_FIFO_FULL (1 << 4) |
---|
83 | #define SR_RX_FIFO_FULL (1 << 5) |
---|
84 | #define SR_RX_FIFO_EMPTY (1 << 6) |
---|
85 | #define SR_TX_FIFO_EMPTY (1 << 7) |
---|
86 | |
---|
87 | /* |
---|
88 | * FIFO Sizes. |
---|
89 | */ |
---|
90 | #define TX_FIFO_SIZE 16 |
---|
91 | #define TX_FIFO_HALF_SIZE (TX_FIFO_SIZE / 2) |
---|
92 | #define RX_FIFO_SIZE 16 |
---|
93 | |
---|
94 | /* |
---|
95 | * Address flags. |
---|
96 | */ |
---|
97 | #define ADDR_TEN (1 << 31) |
---|
98 | #define ADDR_GPO (1 << 30) |
---|
99 | |
---|
100 | typedef struct { |
---|
101 | i2c_bus base; |
---|
102 | uintptr_t regs; |
---|
103 | i2c_msg* msgs; |
---|
104 | uint32_t msgs_remaining; |
---|
105 | i2c_msg* current_msg; |
---|
106 | uint32_t current_msg_todo; |
---|
107 | uint8_t* current_msg_byte; |
---|
108 | uint32_t current_todo; |
---|
109 | uint32_t irqstatus; |
---|
110 | bool read; |
---|
111 | uint32_t addr; |
---|
112 | rtems_id task_id; |
---|
113 | bool gpo_address; |
---|
114 | xilinx_aix_i2c_timing timing; |
---|
115 | rtems_vector_number irq; |
---|
116 | } xilinx_axi_i2c_bus; |
---|
117 | |
---|
118 | xilinx_axi_i2c_bus* axi_i2c_bus; |
---|
119 | |
---|
120 | /* |
---|
121 | * Real-time trace buffering with a small overhead. The data can be dumped from |
---|
122 | * gdb with: |
---|
123 | * |
---|
124 | * define axi-i2c |
---|
125 | * set $i = 0 |
---|
126 | * while $i < axi_trace_in |
---|
127 | * printf "%4d %08x %08x %08x : ", \ |
---|
128 | * $i, axi_trace[$i].vars[0], axi_trace[$i].vars[1], axi_trace[$i].vars[2] |
---|
129 | * output axi_trace[$i].state |
---|
130 | * printf "\n" |
---|
131 | * set $i = $i + 1 |
---|
132 | * end |
---|
133 | * end |
---|
134 | */ |
---|
135 | |
---|
136 | typedef enum |
---|
137 | { |
---|
138 | AXI_I2C_NOP, |
---|
139 | AXI_I2C_BEGIN, |
---|
140 | AXI_I2C_END, |
---|
141 | AXI_I2C_TRANSFER, |
---|
142 | AXI_I2C_ADDRESS, |
---|
143 | AXI_I2C_START_TRANSFER, |
---|
144 | AXI_I2C_WRITE, |
---|
145 | AXI_I2C_READ, |
---|
146 | AXI_I2C_TX_FIFO, |
---|
147 | AXI_I2C_RX_FIFO, |
---|
148 | AXI_I2C_RX_FIFO_LEVEL, |
---|
149 | AXI_I2C_INT, |
---|
150 | AXI_I2C_INT_DONE, |
---|
151 | AXI_I2C_INT_ERROR, |
---|
152 | AXI_I2C_BUS_NOT_BUSY, |
---|
153 | AXI_I2C_REG_WRITE, |
---|
154 | AXI_I2C_REG_READ, |
---|
155 | AXI_I2C_TIMEOUT, |
---|
156 | AXI_I2C_WAKE |
---|
157 | } axi_i2c_state; |
---|
158 | |
---|
159 | #define RTEMS_AXI_I2C_TRACE 0 |
---|
160 | #if RTEMS_AXI_I2C_TRACE |
---|
161 | |
---|
162 | #define DRIVER_REG_TRACE 1 |
---|
163 | |
---|
164 | typedef struct |
---|
165 | { |
---|
166 | axi_i2c_state state; |
---|
167 | uint32_t vars[3]; |
---|
168 | } axi_i2c_trace; |
---|
169 | |
---|
170 | #define AXI_I2C_TRACE 5000 |
---|
171 | axi_i2c_trace axi_trace[AXI_I2C_TRACE]; |
---|
172 | int axi_trace_in; |
---|
173 | |
---|
174 | static inline void axi_trace_reset(void) |
---|
175 | { |
---|
176 | axi_trace_in = 0; |
---|
177 | } |
---|
178 | |
---|
179 | static inline void axi_trace_append(axi_i2c_state state, |
---|
180 | uint32_t v1, |
---|
181 | uint32_t v2, |
---|
182 | uint32_t v3) |
---|
183 | { |
---|
184 | if (axi_trace_in < AXI_I2C_TRACE) { |
---|
185 | axi_i2c_trace rec = { state, { v1, v2, v3 } }; |
---|
186 | axi_trace[axi_trace_in++] = rec; |
---|
187 | } |
---|
188 | } |
---|
189 | #else |
---|
190 | #define axi_trace_reset() |
---|
191 | #define axi_trace_append(s, v1, v2, v3) |
---|
192 | #endif |
---|
193 | |
---|
194 | #define DRIVER_DEBUG 0 |
---|
195 | #define DRIVER_DEBUG_DEFAULT true |
---|
196 | #if DRIVER_DEBUG |
---|
197 | #ifndef RTEMS_PRINTFLIKE |
---|
198 | #define RTEMS_PRINTFLIKE( _format_pos, _ap_pos ) \ |
---|
199 | __attribute__((__format__(__printf__, _format_pos, _ap_pos))) |
---|
200 | #endif |
---|
201 | static bool drv_printk_enable = DRIVER_DEBUG_DEFAULT; |
---|
202 | static void drv_printk(const char* format, ...) RTEMS_PRINTFLIKE(1, 2); |
---|
203 | static void |
---|
204 | drv_printk(const char* format, ...) |
---|
205 | { |
---|
206 | va_list ap; |
---|
207 | va_start(ap, format); |
---|
208 | if (drv_printk_enable) |
---|
209 | vprintk(format, ap); |
---|
210 | va_end(ap); |
---|
211 | } |
---|
212 | #else |
---|
213 | #define drv_printk(_fmt, ...) |
---|
214 | #endif |
---|
215 | |
---|
216 | static inline void |
---|
217 | xilinx_axi_i2c_reg_write(const xilinx_axi_i2c_bus* bus, uint32_t reg, uint32_t value) |
---|
218 | { |
---|
219 | #if DRIVER_REG_TRACE |
---|
220 | axi_trace_append(AXI_I2C_REG_WRITE, reg, value, 0); |
---|
221 | #endif |
---|
222 | *((volatile uint32_t*) (bus->regs + reg))= value; |
---|
223 | } |
---|
224 | |
---|
225 | static inline uint32_t |
---|
226 | xilinx_axi_i2c_reg_read(const xilinx_axi_i2c_bus* bus, uint32_t reg) |
---|
227 | { |
---|
228 | uint32_t value = *((volatile uint32_t*) (bus->regs + reg)); |
---|
229 | #if DRIVER_REG_TRACE |
---|
230 | axi_trace_append(AXI_I2C_REG_READ, reg, value, 0); |
---|
231 | #endif |
---|
232 | return value; |
---|
233 | } |
---|
234 | |
---|
235 | static uint32_t |
---|
236 | xilinx_axi_i2c_read_irq_status(const xilinx_axi_i2c_bus* bus) |
---|
237 | { |
---|
238 | return xilinx_axi_i2c_reg_read(bus, REG_ISR); |
---|
239 | } |
---|
240 | |
---|
241 | static uint32_t |
---|
242 | xilinx_axi_i2c_read_irq_enabled(const xilinx_axi_i2c_bus* bus) |
---|
243 | { |
---|
244 | return xilinx_axi_i2c_reg_read(bus, REG_IER); |
---|
245 | } |
---|
246 | |
---|
247 | static void |
---|
248 | xilinx_axi_i2c_clear_irq(const xilinx_axi_i2c_bus* bus, uint32_t mask) |
---|
249 | { |
---|
250 | /* |
---|
251 | * The ISR bits can be toggled so only write a 1 if set. |
---|
252 | */ |
---|
253 | xilinx_axi_i2c_reg_write(bus, REG_ISR, |
---|
254 | xilinx_axi_i2c_reg_read(bus, REG_ISR) & mask); |
---|
255 | } |
---|
256 | |
---|
257 | static inline void |
---|
258 | xilinx_axi_i2c_enable_irq(const xilinx_axi_i2c_bus* bus, uint32_t mask) |
---|
259 | { |
---|
260 | xilinx_axi_i2c_reg_write(bus, REG_IER, |
---|
261 | xilinx_axi_i2c_reg_read(bus, REG_IER) | mask); |
---|
262 | } |
---|
263 | |
---|
264 | static inline void |
---|
265 | xilinx_axi_i2c_disable_irq(const xilinx_axi_i2c_bus* bus, uint32_t mask) |
---|
266 | { |
---|
267 | xilinx_axi_i2c_reg_write(bus, REG_IER, |
---|
268 | xilinx_axi_i2c_reg_read(bus, REG_IER) & ~mask); |
---|
269 | } |
---|
270 | |
---|
271 | static void |
---|
272 | xilinx_axi_i2c_clear_enable_irq(const xilinx_axi_i2c_bus* bus, uint32_t mask) |
---|
273 | { |
---|
274 | xilinx_axi_i2c_clear_irq(bus, mask); |
---|
275 | xilinx_axi_i2c_enable_irq(bus, mask); |
---|
276 | } |
---|
277 | |
---|
278 | static void |
---|
279 | xilinx_axi_i2c_disable_clear_irq(const xilinx_axi_i2c_bus* bus, uint32_t mask) |
---|
280 | { |
---|
281 | xilinx_axi_i2c_disable_irq(bus, mask); |
---|
282 | xilinx_axi_i2c_clear_irq(bus, mask); |
---|
283 | } |
---|
284 | |
---|
285 | static inline void |
---|
286 | xilinx_axi_i2c_disable_all_irq(const xilinx_axi_i2c_bus* bus) |
---|
287 | { |
---|
288 | xilinx_axi_i2c_reg_write(bus, REG_GIE, 0); |
---|
289 | } |
---|
290 | |
---|
291 | static void |
---|
292 | xilinx_axi_i2c_enable_interrupts(const xilinx_axi_i2c_bus* bus) |
---|
293 | { |
---|
294 | xilinx_axi_i2c_reg_write(bus, REG_GIE, 1 << 31); |
---|
295 | } |
---|
296 | |
---|
297 | static void |
---|
298 | xilinx_axi_i2c_disable_interrupts(const xilinx_axi_i2c_bus* bus) |
---|
299 | { |
---|
300 | xilinx_axi_i2c_reg_write(bus, REG_GIE, 0); |
---|
301 | xilinx_axi_i2c_reg_write(bus, REG_IER, 0); |
---|
302 | } |
---|
303 | |
---|
304 | static inline void |
---|
305 | xilinx_axi_i2c_write_cr(const xilinx_axi_i2c_bus* bus, uint32_t value) |
---|
306 | { |
---|
307 | xilinx_axi_i2c_reg_write(bus, REG_CR, value); |
---|
308 | } |
---|
309 | |
---|
310 | static inline uint32_t |
---|
311 | xilinx_axi_i2c_read_cr(const xilinx_axi_i2c_bus* bus) |
---|
312 | { |
---|
313 | return xilinx_axi_i2c_reg_read(bus, REG_CR); |
---|
314 | } |
---|
315 | |
---|
316 | static inline void |
---|
317 | xilinx_axi_i2c_set_cr(const xilinx_axi_i2c_bus* bus, uint32_t mask) |
---|
318 | { |
---|
319 | xilinx_axi_i2c_reg_write(bus, REG_CR, |
---|
320 | xilinx_axi_i2c_reg_read(bus, REG_CR) | mask); |
---|
321 | } |
---|
322 | |
---|
323 | static inline void |
---|
324 | xilinx_axi_i2c_clear_cr(const xilinx_axi_i2c_bus* bus, uint32_t mask) |
---|
325 | { |
---|
326 | xilinx_axi_i2c_write_cr(bus, xilinx_axi_i2c_read_cr(bus) & ~mask); |
---|
327 | } |
---|
328 | |
---|
329 | static inline uint32_t |
---|
330 | xilinx_axi_i2c_read_sr(const xilinx_axi_i2c_bus* bus) |
---|
331 | { |
---|
332 | return xilinx_axi_i2c_reg_read(bus, REG_SR); |
---|
333 | } |
---|
334 | |
---|
335 | static inline uint32_t |
---|
336 | xilinx_axi_i2c_read_rx_level(const xilinx_axi_i2c_bus* bus) |
---|
337 | { |
---|
338 | if ((xilinx_axi_i2c_read_sr(bus) & SR_RX_FIFO_EMPTY) != 0) |
---|
339 | return 0; |
---|
340 | return xilinx_axi_i2c_reg_read(bus, REG_RX_FIFO_OCY) + 1; |
---|
341 | } |
---|
342 | |
---|
343 | static inline void |
---|
344 | xilinx_axi_i2c_write_rx_pirq(const xilinx_axi_i2c_bus* bus, uint32_t level) |
---|
345 | { |
---|
346 | if (level != 0) |
---|
347 | xilinx_axi_i2c_reg_write(bus, REG_RX_FIFO_PIRQ, level - 1); |
---|
348 | else |
---|
349 | xilinx_axi_i2c_reg_write(bus, REG_RX_FIFO_PIRQ, level); |
---|
350 | } |
---|
351 | |
---|
352 | static inline uint32_t |
---|
353 | xilinx_axi_i2c_read_tx_space(const xilinx_axi_i2c_bus* bus) |
---|
354 | { |
---|
355 | if ((xilinx_axi_i2c_read_sr(bus) & SR_TX_FIFO_EMPTY) != 0) |
---|
356 | return TX_FIFO_SIZE; |
---|
357 | return TX_FIFO_SIZE - xilinx_axi_i2c_reg_read(bus, REG_TX_FIFO_OCY) - 1; |
---|
358 | } |
---|
359 | |
---|
360 | static void |
---|
361 | xilinx_axi_i2c_write_tx_fifo_data(xilinx_axi_i2c_bus* bus, uint32_t data) |
---|
362 | { |
---|
363 | axi_trace_append(AXI_I2C_TX_FIFO, data, 0, 0); |
---|
364 | xilinx_axi_i2c_reg_write(bus, REG_TX_FIFO, data); |
---|
365 | } |
---|
366 | |
---|
367 | static inline uint32_t |
---|
368 | xilinx_axi_i2c_read_rx_fifo_data(xilinx_axi_i2c_bus* bus) |
---|
369 | { |
---|
370 | uint32_t data = xilinx_axi_i2c_reg_read(bus, REG_RX_FIFO); |
---|
371 | axi_trace_append(AXI_I2C_RX_FIFO, data, 0, 0); |
---|
372 | return data; |
---|
373 | } |
---|
374 | |
---|
375 | static void |
---|
376 | xilinx_axi_i2c_reset(xilinx_axi_i2c_bus* bus) |
---|
377 | { |
---|
378 | xilinx_axi_i2c_reg_write(bus, REG_SOFTR, 0x0a); |
---|
379 | if ((bus->timing.valid_mask & XILINX_AIX_I2C_ALL_REGS) != 0) |
---|
380 | { |
---|
381 | static const uint32_t r[8] = { |
---|
382 | REG_TSUSTA, |
---|
383 | REG_TSUSTO, |
---|
384 | REG_THDSTA, |
---|
385 | REG_TSUDAT, |
---|
386 | REG_TBUF, |
---|
387 | REG_THIGH, |
---|
388 | REG_TLOW, |
---|
389 | REG_THDDAT |
---|
390 | }; |
---|
391 | static const uint32_t m[8] = { |
---|
392 | XILINX_AIX_I2C_TSUSTA, |
---|
393 | XILINX_AIX_I2C_TSUSTO, |
---|
394 | XILINX_AIX_I2C_THDSTA, |
---|
395 | XILINX_AIX_I2C_TSUDAT, |
---|
396 | XILINX_AIX_I2C_TBUF, |
---|
397 | XILINX_AIX_I2C_THIGH, |
---|
398 | XILINX_AIX_I2C_TLOW, |
---|
399 | XILINX_AIX_I2C_THDDAT |
---|
400 | }; |
---|
401 | uint32_t vm = bus->timing.valid_mask; |
---|
402 | const uint32_t* u = &bus->timing.TSUSTA; |
---|
403 | size_t i; |
---|
404 | for (i = 0; i < (sizeof(r) / sizeof(r[0])); ++i, ++u) { |
---|
405 | if ((vm & m[i]) != 0) { |
---|
406 | xilinx_axi_i2c_reg_write(bus, r[i], *u); |
---|
407 | } |
---|
408 | } |
---|
409 | } |
---|
410 | } |
---|
411 | |
---|
412 | |
---|
413 | static void |
---|
414 | xilinx_axi_i2c_reinit(xilinx_axi_i2c_bus* bus) |
---|
415 | { |
---|
416 | drv_printk("axi-i2c: reinit\n"); |
---|
417 | xilinx_axi_i2c_reset(bus); |
---|
418 | xilinx_axi_i2c_write_rx_pirq(bus, RX_FIFO_SIZE); |
---|
419 | xilinx_axi_i2c_write_cr(bus, CR_TX_FIFO_RESET); |
---|
420 | xilinx_axi_i2c_write_cr(bus, CR_EN); |
---|
421 | xilinx_axi_i2c_clear_enable_irq(bus, INT_ARB_LOST); |
---|
422 | } |
---|
423 | |
---|
424 | static void |
---|
425 | xilinx_axi_i2c_wakeup(xilinx_axi_i2c_bus* bus) |
---|
426 | { |
---|
427 | axi_trace_append(AXI_I2C_WAKE, bus->task_id, bus->irqstatus, 0); |
---|
428 | drv_printk("axi-i2c: wakeup: irqstatus: %08lx\n", bus->irqstatus); |
---|
429 | rtems_status_code sc = rtems_event_transient_send(bus->task_id); |
---|
430 | _Assert(sc == RTEMS_SUCCESSFUL); |
---|
431 | (void) sc; |
---|
432 | } |
---|
433 | |
---|
434 | static void |
---|
435 | xilinx_axi_i2c_next_byte(xilinx_axi_i2c_bus* bus) |
---|
436 | { |
---|
437 | --bus->current_todo; |
---|
438 | --bus->current_msg_todo; |
---|
439 | ++bus->current_msg_byte; |
---|
440 | if (bus->current_msg_todo == 0) { |
---|
441 | if (bus->msgs_remaining != 0 && |
---|
442 | (bus->msgs[0].flags & I2C_M_NOSTART) != 0) { |
---|
443 | bus->current_msg_todo = bus->msgs[0].len; |
---|
444 | bus->current_msg_byte = bus->msgs[0].buf; |
---|
445 | ++bus->msgs; |
---|
446 | --bus->msgs_remaining; |
---|
447 | } |
---|
448 | } |
---|
449 | } |
---|
450 | |
---|
451 | static void |
---|
452 | xilinx_axi_i2c_read_rx_byte(xilinx_axi_i2c_bus* bus) |
---|
453 | { |
---|
454 | *bus->current_msg_byte = (uint8_t) xilinx_axi_i2c_read_rx_fifo_data(bus); |
---|
455 | xilinx_axi_i2c_next_byte(bus); |
---|
456 | } |
---|
457 | |
---|
458 | static void |
---|
459 | xilinx_axi_i2c_read_rx_bytes(xilinx_axi_i2c_bus* bus, uint32_t count) |
---|
460 | { |
---|
461 | while (count-- > 0) |
---|
462 | xilinx_axi_i2c_read_rx_byte(bus); |
---|
463 | } |
---|
464 | |
---|
465 | static void |
---|
466 | xilinx_axi_i2c_set_rx_fifo_level(xilinx_axi_i2c_bus* bus) |
---|
467 | { |
---|
468 | uint32_t size; |
---|
469 | if (bus->current_todo > RX_FIFO_SIZE) { |
---|
470 | size = RX_FIFO_SIZE; |
---|
471 | } else { |
---|
472 | size = bus->current_todo - 1; |
---|
473 | } |
---|
474 | axi_trace_append(AXI_I2C_RX_FIFO_LEVEL, size, 0, 0); |
---|
475 | xilinx_axi_i2c_write_rx_pirq(bus, size); |
---|
476 | } |
---|
477 | |
---|
478 | static bool xilinx_axi_i2c_start_transfer(xilinx_axi_i2c_bus* bus); |
---|
479 | |
---|
480 | static bool |
---|
481 | xilinx_axi_i2c_read_rx_fifo(xilinx_axi_i2c_bus* bus) |
---|
482 | { |
---|
483 | drv_printk("axi-i2c: read rx fifo: length:%lu\n", bus->current_todo); |
---|
484 | |
---|
485 | if (bus->current_todo == 0) { |
---|
486 | return false; |
---|
487 | } |
---|
488 | |
---|
489 | if ((xilinx_axi_i2c_read_sr(bus) & SR_RX_FIFO_EMPTY) == 0) { |
---|
490 | uint32_t level = xilinx_axi_i2c_read_rx_level(bus); |
---|
491 | bool active; |
---|
492 | |
---|
493 | drv_printk("axi-i2c: read rx fifo: level:%lu\n", level); |
---|
494 | |
---|
495 | if (level > bus->current_todo) |
---|
496 | level = bus->current_todo; |
---|
497 | |
---|
498 | switch (bus->current_todo - level) { |
---|
499 | case 1: |
---|
500 | drv_printk("axi-i2c: read rx fifo: one more\n"); |
---|
501 | /* |
---|
502 | * One more byte to be received. This is set up by programming the RX |
---|
503 | * FIFO programmable depth interrupt register with a value that is 2 |
---|
504 | * less than the number we need (the register is minus 1). When we have |
---|
505 | * one byte left disable the TX error interrupt because setting the NO |
---|
506 | * ACK bit in the command register causes a TX error interrupt. Set the |
---|
507 | * TXAK bit in the CR to not-acknowledge the next byte received telling |
---|
508 | * the slave sender the master accepts no more data, then read the |
---|
509 | * FIFO. If the FIFO is ready before the TXAK bit is set the slave will |
---|
510 | * see a request for more data. We will come back to the next case |
---|
511 | * statement for the last byte once it has been received. |
---|
512 | */ |
---|
513 | xilinx_axi_i2c_disable_clear_irq(bus, INT_TX_ERROR); |
---|
514 | xilinx_axi_i2c_set_cr(bus, CR_TXAK); |
---|
515 | xilinx_axi_i2c_read_rx_bytes(bus, level); |
---|
516 | /* |
---|
517 | * Set the RX PIRQ to 0 after the RX data has been read. There is an |
---|
518 | * observed timing issue and glitch if written before. |
---|
519 | */ |
---|
520 | xilinx_axi_i2c_write_rx_pirq(bus, 0); |
---|
521 | break; |
---|
522 | |
---|
523 | case 0: |
---|
524 | drv_printk("axi-i2c: read rx fifo: no more\n"); |
---|
525 | /* |
---|
526 | * We should have 1 byte in the FIFO which is the last byte received |
---|
527 | * with a NACK. If there are no more message we need to send a STOP by |
---|
528 | * clearing he MSMS bit in the CR and then waiting for the bus to not |
---|
529 | * be busy. |
---|
530 | */ |
---|
531 | xilinx_axi_i2c_disable_clear_irq(bus, |
---|
532 | INT_RX_FIFO_FULL | INT_TX_ERROR); |
---|
533 | if (bus->msgs_remaining == 0) { |
---|
534 | xilinx_axi_i2c_clear_cr(bus, CR_MSMS); |
---|
535 | xilinx_axi_i2c_clear_enable_irq(bus, INT_BUS_NOT_BUSY); |
---|
536 | active = true; |
---|
537 | } |
---|
538 | xilinx_axi_i2c_read_rx_byte(bus); |
---|
539 | if (bus->msgs_remaining != 0) |
---|
540 | active = xilinx_axi_i2c_start_transfer(bus); |
---|
541 | return active; |
---|
542 | |
---|
543 | default: |
---|
544 | drv_printk("axi-i2c: read rx fifo: more:%lu\n", bus->current_todo - level); |
---|
545 | /* |
---|
546 | * All the requested data is in the FIFO so read it and update the PIRQ |
---|
547 | * level. The PIRQ size is always one less than the maximum size. |
---|
548 | */ |
---|
549 | xilinx_axi_i2c_read_rx_bytes(bus, level); |
---|
550 | if (bus->current_todo > RX_FIFO_SIZE) { |
---|
551 | xilinx_axi_i2c_write_rx_pirq(bus, RX_FIFO_SIZE); |
---|
552 | } else { |
---|
553 | xilinx_axi_i2c_write_rx_pirq(bus, bus->current_todo - 1); |
---|
554 | } |
---|
555 | break; |
---|
556 | } |
---|
557 | } |
---|
558 | |
---|
559 | return true; |
---|
560 | } |
---|
561 | |
---|
562 | static void |
---|
563 | xilinx_axi_i2c_write_tx_byte(xilinx_axi_i2c_bus* bus) |
---|
564 | { |
---|
565 | xilinx_axi_i2c_write_tx_fifo_data(bus, *bus->current_msg_byte); |
---|
566 | xilinx_axi_i2c_next_byte(bus); |
---|
567 | } |
---|
568 | |
---|
569 | static void |
---|
570 | xilinx_axi_i2c_write_tx_bytes(xilinx_axi_i2c_bus* bus) |
---|
571 | { |
---|
572 | uint32_t space = xilinx_axi_i2c_read_tx_space(bus); |
---|
573 | uint32_t level = bus->current_todo - 1; |
---|
574 | uint32_t i; |
---|
575 | drv_printk("axi-i2c: tx fifo load: space:%lu level:%lu\n", space, level); |
---|
576 | if (level < space) |
---|
577 | space = level; |
---|
578 | for (i = 0; i < space; ++i) |
---|
579 | xilinx_axi_i2c_write_tx_byte(bus); |
---|
580 | } |
---|
581 | |
---|
582 | static bool |
---|
583 | xilinx_axi_i2c_write_tx_fifo(xilinx_axi_i2c_bus* bus) |
---|
584 | { |
---|
585 | bool more = true; |
---|
586 | drv_printk("axi-i2c: write tx fifo: current_todo: %lu\n", bus->current_todo); |
---|
587 | switch (bus->current_todo) { |
---|
588 | case 0: |
---|
589 | xilinx_axi_i2c_disable_clear_irq(bus, |
---|
590 | INT_TX_FIFO_EMPTY | |
---|
591 | INT_TX_FIFO_HALF_FULL | |
---|
592 | INT_TX_ERROR | |
---|
593 | INT_BUS_NOT_BUSY); |
---|
594 | more = xilinx_axi_i2c_start_transfer(bus); |
---|
595 | break; |
---|
596 | case 1: |
---|
597 | /* |
---|
598 | * If transmitting and the last byte issue a stop and wait for the bus to |
---|
599 | * not be busy. |
---|
600 | */ |
---|
601 | if (!bus->read && bus->msgs_remaining == 0) { |
---|
602 | xilinx_axi_i2c_clear_cr(bus, CR_MSMS); |
---|
603 | xilinx_axi_i2c_clear_enable_irq(bus, INT_BUS_NOT_BUSY); |
---|
604 | } |
---|
605 | xilinx_axi_i2c_write_tx_byte(bus); |
---|
606 | break; |
---|
607 | default: |
---|
608 | xilinx_axi_i2c_write_tx_bytes(bus); |
---|
609 | break; |
---|
610 | } |
---|
611 | return more; |
---|
612 | } |
---|
613 | |
---|
614 | static void |
---|
615 | xilinx_axi_i2c_write_address(xilinx_axi_i2c_bus* bus) |
---|
616 | { |
---|
617 | if ((bus->addr & ADDR_GPO) != 0) |
---|
618 | xilinx_axi_i2c_reg_write(bus, REG_GPO, (bus->addr >> 12) & 0xf); |
---|
619 | if ((bus->addr & ADDR_TEN) != 0) |
---|
620 | xilinx_axi_i2c_write_tx_fifo_data(bus, (bus->addr >> 8) & 0xff); |
---|
621 | xilinx_axi_i2c_write_tx_fifo_data(bus, bus->addr & 0xff); |
---|
622 | } |
---|
623 | |
---|
624 | static void |
---|
625 | xilinx_axi_i2c_start_read(xilinx_axi_i2c_bus* bus) |
---|
626 | { |
---|
627 | uint32_t cr; |
---|
628 | uint32_t set = INT_RX_FIFO_FULL; |
---|
629 | axi_trace_append(AXI_I2C_READ, bus->current_todo, 0, 0); |
---|
630 | drv_printk("axi-i2c: start read: size: %lu\n", bus->current_todo); |
---|
631 | /* |
---|
632 | * Is this a restart? If there is no active STOP it is a restart. |
---|
633 | */ |
---|
634 | cr = xilinx_axi_i2c_read_cr(bus); |
---|
635 | if ((cr & CR_MSMS) != 0) { |
---|
636 | cr |= CR_RSTA; |
---|
637 | xilinx_axi_i2c_write_cr(bus, cr); |
---|
638 | } |
---|
639 | xilinx_axi_i2c_write_address(bus); |
---|
640 | xilinx_axi_i2c_set_rx_fifo_level(bus); |
---|
641 | /* |
---|
642 | * We must NACK the last byte so if we are receiving a single byte issue a |
---|
643 | * NACK. |
---|
644 | */ |
---|
645 | cr &= ~(CR_TX | CR_TXAK); |
---|
646 | if (bus->current_todo == 1) { |
---|
647 | cr |= CR_TXAK; |
---|
648 | } else { |
---|
649 | set |= INT_TX_ERROR; |
---|
650 | } |
---|
651 | /* |
---|
652 | * Issue a start. |
---|
653 | */ |
---|
654 | cr |= CR_MSMS; |
---|
655 | xilinx_axi_i2c_clear_enable_irq(bus, set); |
---|
656 | xilinx_axi_i2c_write_cr(bus, cr); |
---|
657 | } |
---|
658 | |
---|
659 | static void |
---|
660 | xilinx_axi_i2c_start_write(xilinx_axi_i2c_bus* bus) |
---|
661 | { |
---|
662 | uint32_t space; |
---|
663 | uint32_t enable; |
---|
664 | uint32_t cr; |
---|
665 | axi_trace_append(AXI_I2C_WRITE, bus->current_todo, 0, 0); |
---|
666 | cr = xilinx_axi_i2c_read_cr(bus); |
---|
667 | /* |
---|
668 | * If a master issue a restart if there is no active STOP on the bus. |
---|
669 | */ |
---|
670 | if ((cr & CR_MSMS) != 0) { |
---|
671 | cr |= CR_RSTA; |
---|
672 | xilinx_axi_i2c_write_cr(bus, cr); |
---|
673 | } |
---|
674 | xilinx_axi_i2c_write_address(bus); |
---|
675 | if (bus->current_todo > 1) |
---|
676 | xilinx_axi_i2c_write_tx_bytes(bus); |
---|
677 | space = xilinx_axi_i2c_read_tx_space(bus); |
---|
678 | enable = INT_TX_FIFO_EMPTY | INT_TX_ERROR; |
---|
679 | if (space > TX_FIFO_HALF_SIZE && bus->current_todo > 1) { |
---|
680 | enable |= INT_TX_FIFO_HALF_FULL; |
---|
681 | } |
---|
682 | xilinx_axi_i2c_clear_enable_irq(bus, enable); |
---|
683 | cr &= ~CR_TXAK; |
---|
684 | cr |= CR_MSMS | CR_TX; |
---|
685 | xilinx_axi_i2c_write_cr(bus, cr); |
---|
686 | } |
---|
687 | |
---|
688 | static bool |
---|
689 | xilinx_axi_i2c_start_transfer(xilinx_axi_i2c_bus* bus) |
---|
690 | { |
---|
691 | const i2c_msg* msgs = bus->msgs; |
---|
692 | uint32_t msg_todo = bus->msgs_remaining; |
---|
693 | uint32_t i; |
---|
694 | |
---|
695 | axi_trace_append(AXI_I2C_START_TRANSFER, msg_todo, 0, 0); |
---|
696 | drv_printk("axi-i2c: start transfer: messages: %lu\n", msg_todo); |
---|
697 | |
---|
698 | if (msg_todo == 0) { |
---|
699 | xilinx_axi_i2c_clear_cr(bus, CR_MSMS); |
---|
700 | return false; |
---|
701 | } |
---|
702 | |
---|
703 | /* |
---|
704 | * Get the amount of data to transfer. It can span message buffers if the |
---|
705 | * I2C_M_NOSTART flag is set. |
---|
706 | */ |
---|
707 | bus->current_todo = msgs[0].len; |
---|
708 | for (i = 1; i < msg_todo && (msgs[i].flags & I2C_M_NOSTART) != 0; ++i) { |
---|
709 | bus->current_todo += msgs[i].len; |
---|
710 | } |
---|
711 | |
---|
712 | bus->read = (msgs->flags & I2C_M_RD) != 0; |
---|
713 | |
---|
714 | if ((msgs->flags & I2C_M_TEN) != 0) { |
---|
715 | bus->addr = (ADDR_TEN | |
---|
716 | ((msgs->addr & (3 << 8)) << 1) | |
---|
717 | ((bus->read ? 1 : 0) << 8) | |
---|
718 | (msgs->addr & 0xff)); |
---|
719 | } |
---|
720 | else { |
---|
721 | bus->addr = (msgs->addr & 0x7f) << 1 | (bus->read ? 1 : 0); |
---|
722 | } |
---|
723 | |
---|
724 | if (bus->gpo_address) |
---|
725 | bus->addr |= ADDR_GPO | (msgs->addr & 0xf000); |
---|
726 | |
---|
727 | axi_trace_append(AXI_I2C_TRANSFER, |
---|
728 | bus->msgs_remaining, |
---|
729 | bus->current_todo, bus->addr); |
---|
730 | |
---|
731 | /* |
---|
732 | * The bus->msgs is left pointing to the next message because we may need to |
---|
733 | * start a new message while completing the current message. |
---|
734 | */ |
---|
735 | bus->current_msg_todo = msgs[0].len; |
---|
736 | bus->current_msg_byte = msgs[0].buf; |
---|
737 | ++bus->msgs; |
---|
738 | --bus->msgs_remaining; |
---|
739 | |
---|
740 | if (bus->read) { |
---|
741 | xilinx_axi_i2c_start_read(bus); |
---|
742 | } else { |
---|
743 | xilinx_axi_i2c_start_write(bus); |
---|
744 | } |
---|
745 | |
---|
746 | return true; |
---|
747 | } |
---|
748 | |
---|
749 | static void |
---|
750 | xilinx_axi_i2c_interrupt(void* arg) |
---|
751 | { |
---|
752 | xilinx_axi_i2c_bus* bus = arg; |
---|
753 | uint32_t status = xilinx_axi_i2c_read_irq_status(bus); |
---|
754 | uint32_t enabled = xilinx_axi_i2c_read_irq_enabled(bus); |
---|
755 | uint32_t active = status & enabled; |
---|
756 | uint32_t clear = 0; |
---|
757 | int done = 0; |
---|
758 | |
---|
759 | axi_trace_append(AXI_I2C_INT, active, status, enabled); |
---|
760 | |
---|
761 | drv_printk("axi-i2c: interrupt: active:%02lx isr:%02lx ier:%02lx\n", |
---|
762 | active, status, enabled); |
---|
763 | |
---|
764 | /* |
---|
765 | * An error or we lost arbitration. If transmitting and there is more data to |
---|
766 | * send a INT_TX_ERROR means the slave issue a NOT ACK because there was not |
---|
767 | * slave at the address or the addressed slave will not accept any more data. |
---|
768 | * |
---|
769 | * Clean up and wake the user. |
---|
770 | */ |
---|
771 | if (((active & INT_ARB_LOST) != 0) || |
---|
772 | (!bus->read && (active & INT_TX_ERROR) != 0)) { |
---|
773 | bus->irqstatus = active & (INT_ARB_LOST | INT_TX_ERROR); |
---|
774 | axi_trace_append(AXI_I2C_INT_ERROR, bus->irqstatus, 0, 0); |
---|
775 | xilinx_axi_i2c_reinit(bus); |
---|
776 | xilinx_axi_i2c_clear_cr(bus, CR_EN); |
---|
777 | xilinx_axi_i2c_wakeup(bus); |
---|
778 | return; |
---|
779 | } |
---|
780 | |
---|
781 | /* |
---|
782 | * RX FIFO full? |
---|
783 | */ |
---|
784 | if ((active & INT_RX_FIFO_FULL) != 0) { |
---|
785 | clear |= INT_RX_FIFO_FULL; |
---|
786 | |
---|
787 | if (bus->read && !xilinx_axi_i2c_read_rx_fifo(bus)) { |
---|
788 | ++done; |
---|
789 | axi_trace_append(AXI_I2C_INT_DONE, done, clear, 0); |
---|
790 | } |
---|
791 | |
---|
792 | if (bus->current_todo == 0) { |
---|
793 | clear |= status & INT_TX_ERROR; |
---|
794 | } |
---|
795 | } |
---|
796 | |
---|
797 | /* |
---|
798 | * TX FIFO empty or half empty? |
---|
799 | */ |
---|
800 | if ((active & (INT_TX_FIFO_EMPTY | INT_TX_FIFO_HALF_FULL)) != 0) { |
---|
801 | clear |= active & (INT_TX_FIFO_EMPTY | INT_TX_FIFO_HALF_FULL); |
---|
802 | |
---|
803 | if (!bus->read && !xilinx_axi_i2c_write_tx_fifo(bus)) { |
---|
804 | ++done; |
---|
805 | axi_trace_append(AXI_I2C_INT_DONE, done, clear, 1); |
---|
806 | } |
---|
807 | } |
---|
808 | |
---|
809 | /* |
---|
810 | * Gate the bus not busy interrupt with the bus busy status to know the bus |
---|
811 | * is really not busy. It could be an interrupt left over from starting the |
---|
812 | * transmission. |
---|
813 | */ |
---|
814 | if ((active & INT_BUS_NOT_BUSY) != 0) { |
---|
815 | if ((xilinx_axi_i2c_read_sr(bus) & SR_BB) == 0) { |
---|
816 | xilinx_axi_i2c_disable_clear_irq(bus, INT_BUS_NOT_BUSY); |
---|
817 | if (bus->read && !xilinx_axi_i2c_read_rx_fifo(bus)) { |
---|
818 | ++done; |
---|
819 | axi_trace_append(AXI_I2C_BUS_NOT_BUSY, done, clear, 0); |
---|
820 | } |
---|
821 | else if (!bus->read && !xilinx_axi_i2c_write_tx_fifo(bus)) { |
---|
822 | ++done; |
---|
823 | axi_trace_append(AXI_I2C_BUS_NOT_BUSY, done, clear, 1); |
---|
824 | } |
---|
825 | } |
---|
826 | else { |
---|
827 | clear |= INT_BUS_NOT_BUSY; |
---|
828 | } |
---|
829 | } |
---|
830 | |
---|
831 | if (clear != 0) |
---|
832 | xilinx_axi_i2c_clear_irq(bus, clear); |
---|
833 | |
---|
834 | if (done != 0) { |
---|
835 | xilinx_axi_i2c_disable_interrupts(bus); |
---|
836 | xilinx_axi_i2c_clear_cr(bus, CR_EN); |
---|
837 | xilinx_axi_i2c_wakeup(bus); |
---|
838 | } |
---|
839 | } |
---|
840 | |
---|
841 | static int xilinx_axi_i2c_transfer(i2c_bus* base, |
---|
842 | i2c_msg* msgs, |
---|
843 | uint32_t msg_count) |
---|
844 | { |
---|
845 | xilinx_axi_i2c_bus* bus = (xilinx_axi_i2c_bus *) base; |
---|
846 | rtems_status_code sc; |
---|
847 | int r = 0; |
---|
848 | |
---|
849 | axi_trace_reset(); |
---|
850 | axi_trace_append(AXI_I2C_BEGIN, msg_count, 0, 0); |
---|
851 | |
---|
852 | drv_printk("axi-i2c: i2c transfer\n"); |
---|
853 | |
---|
854 | _Assert(msg_count > 0); |
---|
855 | |
---|
856 | bus->msgs = &msgs[0]; |
---|
857 | bus->msgs_remaining = msg_count; |
---|
858 | bus->irqstatus = 0; |
---|
859 | bus->task_id = rtems_task_self(); |
---|
860 | |
---|
861 | xilinx_axi_i2c_reinit(bus); |
---|
862 | xilinx_axi_i2c_start_transfer(bus); |
---|
863 | xilinx_axi_i2c_enable_interrupts(bus); |
---|
864 | |
---|
865 | sc = rtems_event_transient_receive(RTEMS_WAIT, bus->base.timeout); |
---|
866 | if (sc != RTEMS_SUCCESSFUL) { |
---|
867 | axi_trace_append(AXI_I2C_TIMEOUT, 0, 0, 0); |
---|
868 | xilinx_axi_i2c_reinit(bus); |
---|
869 | rtems_event_transient_clear(); |
---|
870 | r = -ETIMEDOUT; |
---|
871 | } |
---|
872 | |
---|
873 | if (r == 0 && bus->irqstatus != 0) |
---|
874 | r = -EIO; |
---|
875 | |
---|
876 | axi_trace_append(AXI_I2C_END, bus->irqstatus, r, 0); |
---|
877 | |
---|
878 | return r; |
---|
879 | } |
---|
880 | |
---|
881 | static int xilinx_axi_i2c_set_clock(i2c_bus *base, unsigned long clock) |
---|
882 | { |
---|
883 | xilinx_axi_i2c_bus* bus = (xilinx_axi_i2c_bus*) base; |
---|
884 | |
---|
885 | if ((bus->timing.valid_mask & XILINX_AIX_I2C_AXI_CLOCK) == 0) |
---|
886 | return -EIO; |
---|
887 | |
---|
888 | bus->timing.THIGH = |
---|
889 | (bus->timing.AXI_CLOCK / (2 * clock)) - 7 - bus->timing.SCL_INERTIAL_DELAY; |
---|
890 | bus->timing.TLOW = bus->timing.THIGH; |
---|
891 | |
---|
892 | bus->timing.valid_mask |= XILINX_AIX_I2C_THIGH | XILINX_AIX_I2C_TLOW; |
---|
893 | |
---|
894 | return 0; |
---|
895 | } |
---|
896 | |
---|
897 | static void xilinx_axi_i2c_destroy(i2c_bus* base) |
---|
898 | { |
---|
899 | xilinx_axi_i2c_bus* bus = (xilinx_axi_i2c_bus*) base; |
---|
900 | rtems_status_code sc; |
---|
901 | |
---|
902 | sc = rtems_interrupt_handler_remove(bus->irq, xilinx_axi_i2c_interrupt, bus); |
---|
903 | _Assert(sc == RTEMS_SUCCESSFUL); |
---|
904 | (void) sc; |
---|
905 | |
---|
906 | i2c_bus_destroy_and_free(&bus->base); |
---|
907 | } |
---|
908 | |
---|
909 | int |
---|
910 | i2c_bus_register_xilinx_aix_i2c(const char* bus_path, |
---|
911 | uintptr_t register_base, |
---|
912 | rtems_vector_number irq, |
---|
913 | bool gpo_address, |
---|
914 | const xilinx_aix_i2c_timing* timing) |
---|
915 | { |
---|
916 | xilinx_axi_i2c_bus* bus; |
---|
917 | rtems_status_code sc; |
---|
918 | |
---|
919 | bus = (xilinx_axi_i2c_bus*) i2c_bus_alloc_and_init(sizeof(*bus)); |
---|
920 | if (bus == NULL) { |
---|
921 | return -1; |
---|
922 | } |
---|
923 | |
---|
924 | bus->regs = register_base; |
---|
925 | bus->irq = irq; |
---|
926 | bus->gpo_address = gpo_address; |
---|
927 | bus->timing = *timing; |
---|
928 | |
---|
929 | sc = rtems_interrupt_handler_install(irq, |
---|
930 | "Xilinx AXI I2C", |
---|
931 | RTEMS_INTERRUPT_UNIQUE, |
---|
932 | xilinx_axi_i2c_interrupt, |
---|
933 | bus); |
---|
934 | if (sc != RTEMS_SUCCESSFUL) { |
---|
935 | drv_printk("axi-i2c: interrupt attach failed\n"); |
---|
936 | (*bus->base.destroy)(&bus->base); |
---|
937 | rtems_set_errno_and_return_minus_one(EIO); |
---|
938 | } |
---|
939 | |
---|
940 | bus->base.transfer = xilinx_axi_i2c_transfer; |
---|
941 | bus->base.set_clock = xilinx_axi_i2c_set_clock; |
---|
942 | bus->base.destroy = xilinx_axi_i2c_destroy; |
---|
943 | |
---|
944 | axi_i2c_bus = bus; |
---|
945 | |
---|
946 | return i2c_bus_register(&bus->base, bus_path); |
---|
947 | } |
---|