source: rtems/bsps/arm/xilinx-zynq/i2c/cadence-i2c.c @ a2dad96

5
Last change on this file since a2dad96 was a2dad96, checked in by Sebastian Huber <sebastian.huber@…>, on Apr 23, 2018 at 7:45:28 AM

bsps: Move I2C drivers to bsps

This patch is a part of the BSP source reorganization.

Update #3285.

  • Property mode set to 100644
File size: 10.6 KB
Line 
1/*
2 * Copyright (c) 2014 embedded brains GmbH.  All rights reserved.
3 *
4 *  embedded brains GmbH
5 *  Dornierstr. 4
6 *  82178 Puchheim
7 *  Germany
8 *  <info@embedded-brains.de>
9 *
10 * The license and distribution terms for this file may be
11 * found in the file LICENSE in this distribution or at
12 * http://www.rtems.org/license/LICENSE.
13 */
14
15#include <bsp/cadence-i2c.h>
16#include <bsp/cadence-i2c-regs.h>
17
18#include <rtems/irq-extension.h>
19#include <rtems/score/assert.h>
20
21#include <dev/i2c/i2c.h>
22
23#define CADENCE_I2C_DIV_A_MAX 4
24
25#define CADENCE_I2C_DIV_B_MAX 64
26
27#define CADENCE_I2C_FIFO_DEPTH 16
28
29#define CADENCE_I2C_DATA_IRQ_DEPTH (CADENCE_I2C_FIFO_DEPTH - 2)
30
31#define CADENCE_I2C_TRANSFER_SIZE_MAX 255
32
33#define CADENCE_I2C_TRANSFER_SIZE_ONCE_MAX (18 * CADENCE_I2C_DATA_IRQ_DEPTH)
34
35#define CADENCE_I2C_IRQ_ERROR \
36  (CADENCE_I2C_IXR_ARB_LOST \
37    | CADENCE_I2C_IXR_RX_UNF \
38    | CADENCE_I2C_IXR_TX_OVR \
39    | CADENCE_I2C_IXR_RX_OVR \
40    | CADENCE_I2C_IXR_NACK)
41
42#define CADENCE_I2C_IRQ_USED \
43  (CADENCE_I2C_IRQ_ERROR \
44    | CADENCE_I2C_IXR_DATA \
45    | CADENCE_I2C_IXR_COMP)
46
47typedef struct {
48  i2c_bus base;
49  volatile cadence_i2c *regs;
50  i2c_msg *msgs;
51  uint32_t msg_todo;
52  uint32_t current_msg_todo;
53  uint8_t *current_msg_byte;
54  uint32_t current_todo;
55  uint32_t irqstatus;
56  bool read;
57  bool hold;
58  rtems_id task_id;
59  uint32_t input_clock;
60  rtems_vector_number irq;
61} cadence_i2c_bus;
62
63static void cadence_i2c_disable_interrupts(volatile cadence_i2c *regs)
64{
65  regs->irqdisable = 0xffff;
66}
67
68static void cadence_i2c_clear_irq_status(volatile cadence_i2c *regs)
69{
70  regs->irqstatus = regs->irqstatus;
71}
72
73static void cadence_i2c_reset(cadence_i2c_bus *bus)
74{
75  volatile cadence_i2c *regs = bus->regs;
76  uint32_t val;
77
78  cadence_i2c_disable_interrupts(regs);
79
80  val = regs->control;
81  val &= ~CADENCE_I2C_CONTROL_HOLD;
82  val |= CADENCE_I2C_CONTROL_ACKEN
83    | CADENCE_I2C_CONTROL_MS
84    | CADENCE_I2C_CONTROL_CLR_FIFO;
85  regs->control = val;
86
87  regs->transfer_size = 0;
88  regs->status = regs->status;
89
90  cadence_i2c_clear_irq_status(regs);
91}
92
93static uint32_t cadence_i2c_set_address_size(
94  const i2c_msg *msg,
95  uint32_t control
96)
97{
98  if ((msg->flags & I2C_M_TEN) == 0) {
99    control |= CADENCE_I2C_CONTROL_NEA;
100  } else {
101    control &= ~CADENCE_I2C_CONTROL_NEA;
102  }
103
104  return control;
105}
106
107static void cadence_i2c_setup_read_transfer(
108  cadence_i2c_bus *bus,
109  volatile cadence_i2c *regs,
110  uint32_t control
111)
112{
113  control |= CADENCE_I2C_CONTROL_RW;
114  regs->control = control;
115
116  if (bus->current_todo <= CADENCE_I2C_TRANSFER_SIZE_MAX) {
117    regs->transfer_size = bus->current_todo;
118  } else {
119    regs->transfer_size = CADENCE_I2C_TRANSFER_SIZE_ONCE_MAX;
120  }
121}
122
123static void cadence_i2c_next_byte(cadence_i2c_bus *bus)
124{
125  --bus->current_msg_todo;
126  ++bus->current_msg_byte;
127
128  if (bus->current_msg_todo == 0) {
129    i2c_msg *msg;
130
131    ++bus->msgs;
132    --bus->msg_todo;
133
134    msg = &bus->msgs[0];
135
136    bus->current_msg_todo = msg->len;
137    bus->current_msg_byte = msg->buf;
138  }
139}
140
141static void cadence_i2c_write_to_fifo(
142  cadence_i2c_bus *bus,
143  volatile cadence_i2c *regs
144)
145{
146  uint32_t space_available;
147  uint32_t todo_now;
148  uint32_t i;
149
150  space_available = CADENCE_I2C_FIFO_DEPTH - regs->transfer_size;
151
152  if (bus->current_todo > space_available) {
153    todo_now = space_available;
154  } else {
155    todo_now = bus->current_todo;
156  }
157
158  bus->current_todo -= todo_now;
159
160  for (i = 0; i < todo_now; ++i) {
161    regs->data = *bus->current_msg_byte;
162
163    cadence_i2c_next_byte(bus);
164  }
165}
166
167static void cadence_i2c_setup_write_transfer(
168  cadence_i2c_bus *bus,
169  volatile cadence_i2c *regs,
170  uint32_t control
171)
172{
173  control &= ~CADENCE_I2C_CONTROL_RW;
174  regs->control = control;
175
176  cadence_i2c_write_to_fifo(bus, regs);
177}
178
179static void cadence_i2c_setup_transfer(
180  cadence_i2c_bus *bus,
181  volatile cadence_i2c *regs
182)
183{
184  const i2c_msg *msgs = bus->msgs;
185  uint32_t msg_todo = bus->msg_todo;
186  uint32_t i;
187  uint32_t control;
188
189  bus->current_todo = msgs[0].len;
190  for (i = 1; i < msg_todo && (msgs[i].flags & I2C_M_NOSTART) != 0; ++i) {
191    bus->current_todo += msgs[i].len;
192  }
193
194  regs = bus->regs;
195
196  control = regs->control;
197  control |= CADENCE_I2C_CONTROL_CLR_FIFO;
198
199  bus->hold = i < msg_todo;
200
201  if (bus->hold || bus->current_todo > CADENCE_I2C_FIFO_DEPTH) {
202    control |= CADENCE_I2C_CONTROL_HOLD;
203  } else {
204    control &= ~CADENCE_I2C_CONTROL_HOLD;
205  }
206
207  control = cadence_i2c_set_address_size(msgs, control);
208
209  bus->read = (msgs->flags & I2C_M_RD) != 0;
210  if (bus->read) {
211    cadence_i2c_setup_read_transfer(bus, regs, control);
212  } else {
213    cadence_i2c_setup_write_transfer(bus, regs, control);
214  }
215
216  cadence_i2c_clear_irq_status(regs);
217
218  regs->address = CADENCE_I2C_ADDRESS(msgs->addr);
219}
220
221static void cadence_i2c_continue_read_transfer(
222  cadence_i2c_bus *bus,
223  volatile cadence_i2c *regs
224)
225{
226  uint32_t i;
227
228  bus->current_todo -= CADENCE_I2C_DATA_IRQ_DEPTH;
229
230  /*
231   * This works since CADENCE_I2C_TRANSFER_SIZE_ONCE_MAX is an integral
232   * multiple of CADENCE_I2C_DATA_IRQ_DEPTH.
233   *
234   * FIXME: Tests with a 1024 byte EEPROM show that this doesn't work.  Needs
235   * further investigations with an I2C analyser or an oscilloscope.
236   */
237  if (regs->transfer_size == 0) {
238    if (bus->current_todo <= CADENCE_I2C_TRANSFER_SIZE_MAX) {
239      regs->transfer_size = bus->current_todo;
240    } else {
241      regs->transfer_size = CADENCE_I2C_TRANSFER_SIZE_ONCE_MAX;
242    }
243  }
244
245  for (i = 0; i < CADENCE_I2C_DATA_IRQ_DEPTH; ++i) {
246    *bus->current_msg_byte = (uint8_t) regs->data;
247
248    cadence_i2c_next_byte(bus);
249  }
250
251  if (!bus->hold && bus->current_todo <= CADENCE_I2C_FIFO_DEPTH) {
252    regs->control &= ~CADENCE_I2C_CONTROL_HOLD;
253  }
254}
255
256static void cadence_i2c_interrupt(void *arg)
257{
258  cadence_i2c_bus *bus = arg;
259  volatile cadence_i2c *regs = bus->regs;
260  uint32_t irqstatus = regs->irqstatus;
261  bool done = false;
262
263  /* Clear interrupts */
264  regs->irqstatus = irqstatus;
265
266  if ((irqstatus & (CADENCE_I2C_IXR_ARB_LOST | CADENCE_I2C_IXR_NACK)) != 0) {
267    done = true;
268  }
269
270  if (
271    (irqstatus & CADENCE_I2C_IXR_DATA) != 0
272      && bus->read
273      && bus->current_todo >= CADENCE_I2C_DATA_IRQ_DEPTH
274  ) {
275    cadence_i2c_continue_read_transfer(bus, regs);
276  }
277
278  if ((irqstatus & CADENCE_I2C_IXR_COMP) != 0) {
279    if (bus->read) {
280      uint32_t todo_now = bus->current_todo;
281      uint32_t i;
282
283      for (i = 0; i < todo_now; ++i) {
284        *bus->current_msg_byte = (uint8_t) regs->data;
285
286        cadence_i2c_next_byte(bus);
287      }
288
289      bus->current_todo = 0;
290
291      done = true;
292    } else {
293      if (bus->current_todo > 0) {
294        cadence_i2c_write_to_fifo(bus, regs);
295      } else {
296        done = true;
297      }
298
299      if (!bus->hold && bus->current_todo == 0) {
300        regs->control &= ~CADENCE_I2C_CONTROL_HOLD;
301      }
302    }
303  }
304
305  if (done) {
306    uint32_t err = irqstatus & CADENCE_I2C_IRQ_ERROR;
307
308    if (bus->msg_todo == 0 || err != 0) {
309      rtems_status_code sc;
310
311      cadence_i2c_disable_interrupts(regs);
312
313      bus->irqstatus = err;
314
315      sc = rtems_event_transient_send(bus->task_id);
316      _Assert(sc == RTEMS_SUCCESSFUL);
317      (void) sc;
318    } else {
319      cadence_i2c_setup_transfer(bus, regs);
320    }
321  }
322}
323
324static int cadence_i2c_transfer(
325  i2c_bus *base,
326  i2c_msg *msgs,
327  uint32_t msg_count
328)
329{
330  cadence_i2c_bus *bus = (cadence_i2c_bus *) base;
331  volatile cadence_i2c *regs;
332  rtems_status_code sc;
333  uint32_t i;
334
335  _Assert(msg_count > 0);
336
337  for (i = 0; i < msg_count; ++i) {
338    /* FIXME: Not sure if we can support this. */
339    if ((msgs[i].flags & I2C_M_RECV_LEN) != 0) {
340      return -EINVAL;
341    }
342  }
343
344  bus->msgs = &msgs[0];
345  bus->msg_todo = msg_count;
346  bus->current_msg_todo = msgs[0].len;
347  bus->current_msg_byte = msgs[0].buf;
348  bus->task_id = rtems_task_self();
349
350  regs = bus->regs;
351  cadence_i2c_setup_transfer(bus, regs);
352  regs->irqenable = CADENCE_I2C_IRQ_USED;
353
354  sc = rtems_event_transient_receive(RTEMS_WAIT, bus->base.timeout);
355  if (sc != RTEMS_SUCCESSFUL) {
356    cadence_i2c_reset(bus);
357    rtems_event_transient_clear();
358
359    return -ETIMEDOUT;
360  }
361
362  return bus->irqstatus == 0 ? 0 : -EIO;
363}
364
365static int cadence_i2c_set_clock(i2c_bus *base, unsigned long clock)
366{
367  cadence_i2c_bus *bus = (cadence_i2c_bus *) base;
368  volatile cadence_i2c *regs = bus->regs;
369  uint32_t error = 0xffffffff;
370  uint32_t best_div_a = CADENCE_I2C_DIV_A_MAX - 1;
371  uint32_t best_div_b = CADENCE_I2C_DIV_B_MAX - 1;
372  uint32_t div = bus->input_clock / (22 * clock);
373  uint32_t div_a;
374  uint32_t control;
375
376  if (div <= 0 || div > (CADENCE_I2C_DIV_A_MAX * CADENCE_I2C_DIV_B_MAX)) {
377    return -EIO;
378  }
379
380  for (div_a = 0; div_a < CADENCE_I2C_DIV_A_MAX; ++div_a) {
381    uint32_t a = 22 * clock * (div_a + 1);
382    uint32_t b = (bus->input_clock + a - 1) / a;
383
384    if (b > 0 && b <= CADENCE_I2C_DIV_B_MAX) {
385      uint32_t actual_clock = bus->input_clock / (22 * (div_a + 1) * b);
386      uint32_t e = clock < actual_clock ?
387        actual_clock - clock : clock - actual_clock;
388
389      /*
390       * Favour greater div_a values according to UG585, Zynq-7000 AP SoC
391       * Technical Reference Manual, Table 20-1: Calculated Values for Standard
392       * and High Speed SCL Clock Values".
393       */
394      if (e <= error && actual_clock <= clock) {
395        error = e;
396        best_div_a = div_a;
397        best_div_b = b - 1;
398      }
399    }
400  }
401
402  control = regs->control;
403  control = CADENCE_I2C_CONTROL_DIV_A_SET(control, best_div_a);
404  control = CADENCE_I2C_CONTROL_DIV_B_SET(control, best_div_b);
405  regs->control = control;
406
407  return 0;
408}
409
410static void cadence_i2c_destroy(i2c_bus *base)
411{
412  cadence_i2c_bus *bus = (cadence_i2c_bus *) base;
413  rtems_status_code sc;
414
415  sc = rtems_interrupt_handler_remove(bus->irq, cadence_i2c_interrupt, bus);
416  _Assert(sc == RTEMS_SUCCESSFUL);
417  (void) sc;
418
419  i2c_bus_destroy_and_free(&bus->base);
420}
421
422int i2c_bus_register_cadence(
423  const char *bus_path,
424  uintptr_t register_base,
425  uint32_t input_clock,
426  rtems_vector_number irq
427)
428{
429  cadence_i2c_bus *bus;
430  rtems_status_code sc;
431  int err;
432
433  bus = (cadence_i2c_bus *) i2c_bus_alloc_and_init(sizeof(*bus));
434  if (bus == NULL) {
435    return -1;
436  }
437
438  bus->regs = (volatile cadence_i2c *) register_base;
439  bus->input_clock = input_clock;
440  bus->irq = irq;
441
442  cadence_i2c_reset(bus);
443
444  err = cadence_i2c_set_clock(&bus->base, I2C_BUS_CLOCK_DEFAULT);
445  if (err != 0) {
446    (*bus->base.destroy)(&bus->base);
447
448    rtems_set_errno_and_return_minus_one(-err);
449  }
450
451  sc = rtems_interrupt_handler_install(
452    irq,
453    "Cadence I2C",
454    RTEMS_INTERRUPT_UNIQUE,
455    cadence_i2c_interrupt,
456    bus
457  );
458  if (sc != RTEMS_SUCCESSFUL) {
459    (*bus->base.destroy)(&bus->base);
460
461    rtems_set_errno_and_return_minus_one(EIO);
462  }
463
464  bus->base.transfer = cadence_i2c_transfer;
465  bus->base.set_clock = cadence_i2c_set_clock;
466  bus->base.destroy = cadence_i2c_destroy;
467
468  return i2c_bus_register(&bus->base, bus_path);
469}
Note: See TracBrowser for help on using the repository browser.