diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-23 09:45:28 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-23 15:18:43 +0200 |
commit | a2dad96ab736f66ed54421cad53caf31f250e181 (patch) | |
tree | 1935320b5b52276ed2e52741cdae71637f209f77 /bsps/arm/lpc32xx | |
parent | bsps/arm: Remove unused stm32f* files (diff) | |
download | rtems-a2dad96ab736f66ed54421cad53caf31f250e181.tar.bz2 |
bsps: Move I2C drivers to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps/arm/lpc32xx')
-rw-r--r-- | bsps/arm/lpc32xx/i2c/i2c.c | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/bsps/arm/lpc32xx/i2c/i2c.c b/bsps/arm/lpc32xx/i2c/i2c.c new file mode 100644 index 0000000000..508cd6458a --- /dev/null +++ b/bsps/arm/lpc32xx/i2c/i2c.c @@ -0,0 +1,251 @@ +/** + * @file + * + * @ingroup lpc32xx_i2c + * + * @brief I2C support implementation. + */ + +/* + * Copyright (c) 2010-2011 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Obere Lagerstr. 30 + * 82178 Puchheim + * Germany + * <rtems@embedded-brains.de> + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#include <rtems.h> + +#include <bsp.h> +#include <bsp/i2c.h> + +void lpc32xx_i2c_reset(volatile lpc32xx_i2c *i2c) +{ + i2c->ctrl = I2C_CTRL_RESET; +} + +rtems_status_code lpc32xx_i2c_init( + volatile lpc32xx_i2c *i2c, + unsigned clock_in_hz +) +{ + uint32_t i2cclk = 0; + + if (i2c == &lpc32xx.i2c_1) { + i2cclk |= I2CCLK_1_EN | I2CCLK_1_HIGH_DRIVE; + } else if (i2c == &lpc32xx.i2c_2) { + i2cclk |= I2CCLK_2_EN | I2CCLK_2_HIGH_DRIVE; + } else { + return RTEMS_INVALID_ID; + } + + LPC32XX_I2CCLK_CTRL |= i2cclk; + + lpc32xx_i2c_reset(i2c); + + return lpc32xx_i2c_clock(i2c, clock_in_hz); +} + +rtems_status_code lpc32xx_i2c_clock( + volatile lpc32xx_i2c *i2c, + unsigned clock_in_hz +) +{ + uint32_t clk_div = lpc32xx_hclk() / clock_in_hz; + uint32_t clk_lo = 0; + uint32_t clk_hi = 0; + + switch (clock_in_hz) { + case 100000: + clk_lo = clk_div / 2; + break; + case 400000: + clk_lo = (64 * clk_div) / 100; + break; + default: + return RTEMS_INVALID_CLOCK; + } + + clk_hi = clk_div - clk_lo; + + i2c->clk_lo = clk_lo; + i2c->clk_hi = clk_hi; + + return RTEMS_SUCCESSFUL; +} + +static rtems_status_code wait_for_transaction_done(volatile lpc32xx_i2c *i2c) +{ + uint32_t stat = 0; + + do { + stat = i2c->stat; + } while ((stat & I2C_STAT_TDI) == 0); + + if ((stat & I2C_STAT_TFE) != 0) { + i2c->stat = I2C_STAT_TDI; + + return RTEMS_SUCCESSFUL; + } else { + lpc32xx_i2c_reset(i2c); + + return RTEMS_IO_ERROR; + } +} + +static rtems_status_code tx(volatile lpc32xx_i2c *i2c, uint32_t data) +{ + uint32_t stat = 0; + + do { + stat = i2c->stat; + } while ((stat & (I2C_STAT_TFE | I2C_STAT_TDI)) == 0); + + if ((stat & I2C_STAT_TDI) == 0) { + i2c->rx_or_tx = data; + + return RTEMS_SUCCESSFUL; + } else { + lpc32xx_i2c_reset(i2c); + + return RTEMS_IO_ERROR; + } +} + +rtems_status_code lpc32xx_i2c_write_start( + volatile lpc32xx_i2c *i2c, + unsigned addr +) +{ + return tx(i2c, I2C_TX_ADDR(addr) | I2C_TX_START); +} + +rtems_status_code lpc32xx_i2c_read_start( + volatile lpc32xx_i2c *i2c, + unsigned addr +) +{ + return tx(i2c, I2C_TX_ADDR(addr) | I2C_TX_START | I2C_TX_READ); +} + +rtems_status_code lpc32xx_i2c_write_with_optional_stop( + volatile lpc32xx_i2c *i2c, + const uint8_t *out, + size_t n, + bool stop +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + size_t i = 0; + + for (i = 0; i < n - 1 && sc == RTEMS_SUCCESSFUL; ++i) { + sc = tx(i2c, out [i]); + } + + if (sc == RTEMS_SUCCESSFUL) { + uint32_t stop_flag = stop ? I2C_TX_STOP : 0; + + sc = tx(i2c, out [n - 1] | stop_flag); + } + + if (stop && sc == RTEMS_SUCCESSFUL) { + sc = wait_for_transaction_done(i2c); + } + + return sc; +} + +static bool can_tx_for_rx(volatile lpc32xx_i2c *i2c) +{ + return (i2c->stat & (I2C_STAT_TFF | I2C_STAT_RFF)) == 0; +} + +static bool can_rx(volatile lpc32xx_i2c *i2c) +{ + return (i2c->stat & I2C_STAT_RFE) == 0; +} + +rtems_status_code lpc32xx_i2c_read_with_optional_stop( + volatile lpc32xx_i2c *i2c, + uint8_t *in, + size_t n, + bool stop +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + size_t last = n - 1; + size_t rx = 0; + size_t tx = 0; + + if (!stop) { + return RTEMS_NOT_IMPLEMENTED; + } + + while (rx <= last) { + while (tx < last && can_tx_for_rx(i2c)) { + i2c->rx_or_tx = 0; + ++tx; + } + + if (tx == last && can_tx_for_rx(i2c)) { + uint32_t stop_flag = stop ? I2C_TX_STOP : 0; + + i2c->rx_or_tx = stop_flag; + ++tx; + } + + while (rx <= last && can_rx(i2c)) { + in [rx] = (uint8_t) i2c->rx_or_tx; + ++rx; + } + } + + if (stop) { + sc = wait_for_transaction_done(i2c); + } + + return sc; +} + +rtems_status_code lpc32xx_i2c_write_and_read( + volatile lpc32xx_i2c *i2c, + unsigned addr, + const uint8_t *out, + size_t out_size, + uint8_t *in, + size_t in_size +) +{ + rtems_status_code sc = RTEMS_SUCCESSFUL; + + if (out_size > 0) { + bool stop = in_size == 0; + + sc = lpc32xx_i2c_write_start(i2c, addr); + if (sc != RTEMS_SUCCESSFUL) { + return sc; + } + + sc = lpc32xx_i2c_write_with_optional_stop(i2c, out, out_size, stop); + if (sc != RTEMS_SUCCESSFUL) { + return sc; + } + } + + if (in_size > 0) { + sc = lpc32xx_i2c_read_start(i2c, addr); + if (sc != RTEMS_SUCCESSFUL) { + return sc; + } + + lpc32xx_i2c_read_with_optional_stop(i2c, in, in_size, true); + } + + return RTEMS_SUCCESSFUL; +} |