source: rtems/bsps/arm/lpc24xx/i2c/i2c.c

Last change on this file was bcef89f2, checked in by Sebastian Huber <sebastian.huber@…>, on 05/19/23 at 06:18:25

Update company name

The embedded brains GmbH & Co. KG is the legal successor of embedded
brains GmbH.

  • Property mode set to 100644
File size: 10.8 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup RTEMSBSPsARMLPC24XXI2C
5 */
6
7/*
8 * SPDX-License-Identifier: BSD-2-Clause
9 *
10 * Copyright (C) 2009, 2019 embedded brains GmbH & Co. KG
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include <bsp/i2c.h>
35#include <bsp.h>
36#include <bsp/io.h>
37#include <bsp/irq.h>
38#include <bsp/irq-generic.h>
39
40#include <rtems/score/assert.h>
41
42#include <dev/i2c/i2c.h>
43
44RTEMS_STATIC_ASSERT(I2C_M_RD == 1, lpc24xx_i2c_read_flag);
45
46typedef struct {
47  i2c_bus base;
48  volatile lpc24xx_i2c *regs;
49  uint8_t *buf;
50  const uint8_t *buf_end;
51  size_t todo;
52  const i2c_msg *msg;
53  const i2c_msg *msg_end;
54  int error;
55  rtems_binary_semaphore sem;
56  lpc24xx_module module;
57  rtems_vector_number irq;
58} lpc24xx_i2c_bus;
59
60typedef struct {
61  volatile lpc24xx_i2c *regs;
62  lpc24xx_module module;
63  rtems_vector_number irq;
64} lpc24xx_i2c_config;
65
66static const i2c_msg *lpc24xx_i2c_msg_inc(lpc24xx_i2c_bus *bus)
67{
68  const i2c_msg *msg;
69
70  msg = bus->msg + 1;
71  bus->msg = msg;
72  return msg;
73}
74
75static void lpc24xx_i2c_msg_inc_and_set_buf(lpc24xx_i2c_bus *bus)
76{
77  const i2c_msg *msg;
78
79  msg = lpc24xx_i2c_msg_inc(bus);
80  bus->buf = msg->buf;
81  bus->buf_end = bus->buf + msg->len;
82}
83
84static void lpc24xx_i2c_buf_inc(lpc24xx_i2c_bus *bus)
85{
86  ++bus->buf;
87  --bus->todo;
88}
89
90static void lpc24xx_i2c_buf_push(lpc24xx_i2c_bus *bus, uint8_t c)
91{
92  while (true) {
93    if (bus->buf != bus->buf_end) {
94      bus->buf[0] = c;
95      lpc24xx_i2c_buf_inc(bus);
96      break;
97    }
98
99    lpc24xx_i2c_msg_inc_and_set_buf(bus);
100  }
101}
102
103static uint8_t lpc24xx_i2c_buf_pop(lpc24xx_i2c_bus *bus)
104{
105  while (true) {
106    if (bus->buf != bus->buf_end) {
107      uint8_t c;
108
109      c = bus->buf[0];
110      lpc24xx_i2c_buf_inc(bus);
111      return c;
112    }
113
114    lpc24xx_i2c_msg_inc_and_set_buf(bus);
115  }
116}
117
118static void lpc24xx_i2c_setup_msg(lpc24xx_i2c_bus *bus, const i2c_msg *msg)
119{
120  int can_continue;
121  size_t todo;
122
123  bus->msg = msg;
124  bus->buf = msg->buf;
125  todo = msg->len;
126  bus->buf_end = bus->buf + todo;
127
128  can_continue = (msg->flags & I2C_M_RD) | I2C_M_NOSTART;
129  ++msg;
130
131  while (msg != bus->msg_end) {
132    if ((msg->flags & (I2C_M_RD | I2C_M_NOSTART)) != can_continue) {
133      break;
134    }
135
136    todo += msg->len;
137    ++msg;
138  }
139
140  bus->todo = todo;
141}
142
143static int lpc24xx_i2c_next_msg(
144  lpc24xx_i2c_bus *bus,
145  volatile lpc24xx_i2c *regs
146)
147{
148  const i2c_msg *msg;
149  int error;
150
151  msg = bus->msg + 1;
152  error = 1;
153
154  if (msg != bus->msg_end) {
155    lpc24xx_i2c_setup_msg(bus, msg);
156
157    if ((msg->flags & I2C_M_NOSTART) == 0) {
158      regs->conset = LPC24XX_I2C_STA;
159      regs->conclr = LPC24XX_I2C_SI;
160    } else {
161      regs->conset = LPC24XX_I2C_STO;
162      regs->conclr = LPC24XX_I2C_SI;
163      error = -EINVAL;
164    }
165  } else {
166    regs->conset = LPC24XX_I2C_STO;
167    regs->conclr = LPC24XX_I2C_SI;
168    error = 0;
169  }
170
171  return error;
172}
173
174static void lpc24xx_i2c_interrupt(void *arg)
175{
176  lpc24xx_i2c_bus *bus;
177  volatile lpc24xx_i2c *regs;
178  const i2c_msg *msg;
179  int error;
180
181  bus = arg;
182  regs = bus->regs;
183  error = 1;
184
185  switch (regs->stat) {
186    case 0x00:
187      /* Bus error */
188    case 0x20:
189      /* Slave address plus write sent, NACK received */
190    case 0x48:
191      /* Slave address plus read sent, NACK received */
192      regs->conset = LPC24XX_I2C_STO | LPC24XX_I2C_AA;
193      regs->conclr = LPC24XX_I2C_SI;
194      error = -EIO;
195      break;
196    case 0x08:
197      /* Start sent */
198    case 0x10:
199      /* Repeated start sent */
200      msg = bus->msg;
201      regs->dat = (uint8_t) ((msg->addr << 1) | (msg->flags & I2C_M_RD));
202      regs->conset = LPC24XX_I2C_AA;
203      regs->conclr = LPC24XX_I2C_STA | LPC24XX_I2C_SI;
204      break;
205    case 0x18:
206      /* Slave address plus write sent, ACK received */
207    case 0x28:
208      /* Data sent, ACK received */
209      if (bus->todo > 0) {
210        regs->dat = lpc24xx_i2c_buf_pop(bus);
211        regs->conset = LPC24XX_I2C_AA;
212        regs->conclr = LPC24XX_I2C_SI;
213      } else {
214        error = lpc24xx_i2c_next_msg(bus, regs);
215      }
216      break;
217    case 0x30:
218      /* Data sent, NACK received */
219      if (bus->todo == 0) {
220        error = lpc24xx_i2c_next_msg(bus, regs);
221      } else {
222        regs->conset = LPC24XX_I2C_STO;
223        regs->conclr = LPC24XX_I2C_SI;
224        error = -EIO;
225      }
226      break;
227    case 0x40:
228      /* Slave address plus read sent, ACK received */
229      if (bus->todo > 1) {
230        regs->conset = LPC24XX_I2C_AA;
231        regs->conclr = LPC24XX_I2C_SI;
232      } else {
233        regs->conclr = LPC24XX_I2C_SI | LPC24XX_I2C_AA;
234      }
235      break;
236    case 0x50:
237      /* Data received, ACK returned */
238    case 0x58:
239      /* Data received, NACK returned */
240      lpc24xx_i2c_buf_push(bus, regs->dat);
241
242      if (bus->todo > 1) {
243        regs->conset = LPC24XX_I2C_AA;
244        regs->conclr = LPC24XX_I2C_SI;
245      } else if (bus->todo == 1) {
246        regs->conclr = LPC24XX_I2C_SI | LPC24XX_I2C_AA;
247      } else {
248        error = lpc24xx_i2c_next_msg(bus, regs);
249      }
250      break;
251    case 0xF8:
252      /* Do nothing */
253      break;
254    default:
255      error = -EIO;
256      break;
257  }
258
259  if (error <= 0) {
260    bus->error = error;
261    bsp_interrupt_vector_disable(bus->irq);
262    rtems_binary_semaphore_post(&bus->sem);
263  }
264}
265
266static int
267lpc24xx_i2c_transfer(i2c_bus *base, i2c_msg *msgs, uint32_t msg_count)
268{
269  lpc24xx_i2c_bus *bus;
270  volatile lpc24xx_i2c *regs;
271  uint16_t supported;
272  uint32_t i;
273  int eno;
274
275  if (msg_count == 0){
276    return 0;
277  }
278
279  supported = I2C_M_RD;
280
281  for (i = 0; i < msg_count; ++i) {
282    if ((msgs[i].flags & ~supported) != 0) {
283      return -EINVAL;
284    }
285
286    supported |= I2C_M_NOSTART;
287  }
288
289  bus = (lpc24xx_i2c_bus *) base;
290  bus->msg_end = msgs + msg_count;
291  lpc24xx_i2c_setup_msg(bus, msgs);
292
293  regs = bus->regs;
294
295  /* Start */
296  regs->conset = LPC24XX_I2C_STA;
297
298  bsp_interrupt_vector_enable(bus->irq);
299  eno = rtems_binary_semaphore_wait_timed_ticks(
300    &bus->sem,
301    bus->base.timeout
302  );
303  if (eno != 0) {
304    regs->conclr = LPC24XX_I2C_EN;
305    regs->conset = LPC24XX_I2C_EN;
306    rtems_binary_semaphore_try_wait(&bus->sem);
307    return -ETIMEDOUT;
308  }
309
310  return bus->error;
311}
312
313/* I2C-Bus Specification and User Manual, Table 10 */
314static const uint16_t lpc24xx_i2c_t_low_high[3][2] = {
315  { 4700, 4000 },
316  { 1300,  600 },
317  {  500,  260 }
318};
319
320static uint32_t lpc24xx_i2c_cycle_count(uint32_t scl, uint32_t x, uint32_t t)
321{
322  scl = (scl * x + t - 1) / t;
323
324  if (scl <= 4) {
325    scl = 4;
326  } else if (scl >= 0xffff) {
327    scl = 0xffff;
328  }
329
330  return scl;
331}
332
333static int lpc24xx_i2c_set_clock(i2c_bus *base, unsigned long clock)
334{
335  lpc24xx_i2c_bus *bus;
336  volatile lpc24xx_i2c *regs;
337  size_t i;
338  uint32_t low;
339  uint32_t high;
340  uint32_t t;
341  uint32_t scl;
342
343  if (clock <= 100000) {
344    i = 0;
345  } else if (clock <= 400000) {
346    i = 1;
347  } else {
348    i = 2;
349  }
350
351  low = lpc24xx_i2c_t_low_high[i][0];
352  high = lpc24xx_i2c_t_low_high[i][1];
353  t = low + high;
354  scl = (LPC24XX_PCLK + clock - 1) / clock;
355
356  bus = (lpc24xx_i2c_bus *) base;
357  regs = bus->regs;
358
359  regs->scll = lpc24xx_i2c_cycle_count(scl, low, t);
360  regs->sclh = lpc24xx_i2c_cycle_count(scl, high, t);
361
362  return 0;
363}
364
365static void
366lpc24xx_i2c_destroy(i2c_bus *base)
367{
368  lpc24xx_i2c_bus *bus;
369  rtems_status_code sc;
370
371  bus = (lpc24xx_i2c_bus *) base;
372
373  sc = rtems_interrupt_handler_remove(bus->irq, lpc24xx_i2c_interrupt, bus);
374  _Assert(sc == RTEMS_SUCCESSFUL);
375  (void) sc;
376
377  /* Disable I2C module */
378  bus->regs->conclr = LPC24XX_I2C_EN;
379
380  sc = lpc24xx_module_disable(bus->module);
381  _Assert(sc == RTEMS_SUCCESSFUL);
382  (void) sc;
383
384  rtems_binary_semaphore_destroy(&bus->sem);
385  i2c_bus_destroy_and_free(&bus->base);
386}
387
388static int lpc24xx_i2c_init(lpc24xx_i2c_bus *bus)
389{
390  rtems_status_code sc;
391
392  sc = lpc24xx_module_enable(bus->module, LPC24XX_MODULE_PCLK_DEFAULT);
393  _Assert(sc == RTEMS_SUCCESSFUL);
394  (void) sc;
395
396  /* Disable I2C module */
397  bus->regs->conclr = LPC24XX_I2C_EN;
398
399  sc = rtems_interrupt_handler_install(
400    bus->irq,
401    "I2C",
402    RTEMS_INTERRUPT_UNIQUE,
403    lpc24xx_i2c_interrupt,
404    bus
405  );
406  if (sc != RTEMS_SUCCESSFUL) {
407    return EAGAIN;
408  }
409
410  rtems_binary_semaphore_init(&bus->sem, "I2C");
411
412  lpc24xx_i2c_set_clock(&bus->base, I2C_BUS_CLOCK_DEFAULT);
413
414  /* Initialize I2C module */
415  bus->regs->conset = LPC24XX_I2C_EN;
416
417  return 0;
418}
419
420static int i2c_bus_register_lpc24xx(
421  const char *bus_path,
422  const lpc24xx_i2c_config *config
423)
424{
425  lpc24xx_i2c_bus *bus;
426  int eno;
427
428  bus = (lpc24xx_i2c_bus *) i2c_bus_alloc_and_init(sizeof(*bus));
429  if (bus == NULL){
430    return -1;
431  }
432
433  bus->regs = config->regs;
434  bus->module = config->module;
435  bus->irq = config->irq;
436
437  eno = lpc24xx_i2c_init(bus);
438  if (eno != 0) {
439    (*bus->base.destroy)(&bus->base);
440    rtems_set_errno_and_return_minus_one(eno);
441  }
442
443  bus->base.transfer = lpc24xx_i2c_transfer;
444  bus->base.set_clock = lpc24xx_i2c_set_clock;
445  bus->base.destroy = lpc24xx_i2c_destroy;
446
447  return i2c_bus_register(&bus->base, bus_path);
448}
449
450int lpc24xx_register_i2c_0(void)
451{
452  static const lpc24xx_i2c_config config = {
453    .regs = (volatile lpc24xx_i2c *) I2C0_BASE_ADDR,
454    .module = LPC24XX_MODULE_I2C_0,
455    .irq = LPC24XX_IRQ_I2C_0
456  };
457
458  return i2c_bus_register_lpc24xx(
459    LPC24XX_I2C_0_BUS_PATH,
460    &config
461  );
462}
463
464int lpc24xx_register_i2c_1(void)
465{
466  static const lpc24xx_i2c_config config = {
467    .regs = (volatile lpc24xx_i2c *) I2C1_BASE_ADDR,
468    .module = LPC24XX_MODULE_I2C_1,
469    .irq = LPC24XX_IRQ_I2C_1
470  };
471
472  return i2c_bus_register_lpc24xx(
473    LPC24XX_I2C_2_BUS_PATH,
474    &config
475  );
476}
477
478int lpc24xx_register_i2c_2(void)
479{
480  static const lpc24xx_i2c_config config = {
481    .regs = (volatile lpc24xx_i2c *) I2C2_BASE_ADDR,
482    .module = LPC24XX_MODULE_I2C_2,
483    .irq = LPC24XX_IRQ_I2C_2
484  };
485
486  return i2c_bus_register_lpc24xx(
487    LPC24XX_I2C_2_BUS_PATH,
488    &config
489  );
490}
Note: See TracBrowser for help on using the repository browser.