diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2010-06-23 08:27:57 +0000 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2010-06-23 08:27:57 +0000 |
commit | 3103d4cbad582cc9e62aaf15fd5af308949a83f0 (patch) | |
tree | b1ff4f68908b3177c614349a2f0252e4b64e9b18 /c/src/lib/libbsp/arm/lpc32xx/misc/i2c.c | |
parent | 2010-06-23 Sebastian Huber <sebastian.huber@embedded-brains.de> (diff) | |
download | rtems-3103d4cbad582cc9e62aaf15fd5af308949a83f0.tar.bz2 |
2010-06-23 Sebastian Huber <sebastian.huber@embedded-brains.de>
* make/custom/lpc32xx_mzx_boot_int.cfg,
startup/linkcmds.lpc32xx_mzx_boot_int: Removed files.
* include/boot.h, include/emc.h, include/i2c.h, include/nand-mlc.h,
make/custom/lpc32xx_mzx.cfg, make/custom/lpc32xx_mzx_stage_1.cfg,
make/custom/lpc32xx_mzx_stage_2.cfg, misc/boot.c, misc/emc.c,
misc/i2c.c, misc/nand-mlc.c, misc/nand-mlc-read-blocks.c,
misc/nand-mlc-write-blocks.c, misc/restart.c,
startup/linkcmds.lpc32xx, startup/linkcmds.lpc32xx_mzx,
startup/linkcmds.lpc32xx_mzx_stage_1,
startup/linkcmds.lpc32xx_mzx_stage_2: New files.
* configure.ac, Makefile.am, preinstall.am: Reflect changes above.
* include/bsp.h, include/lpc32xx.h, irq/irq.c, rtc/rtc-config.c,
startup/bspstart.c, startup/bspstarthooks.c,
startup/linkcmds.lpc32xx_phycore: Changes throughout.
Diffstat (limited to 'c/src/lib/libbsp/arm/lpc32xx/misc/i2c.c')
-rw-r--r-- | c/src/lib/libbsp/arm/lpc32xx/misc/i2c.c | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/c/src/lib/libbsp/arm/lpc32xx/misc/i2c.c b/c/src/lib/libbsp/arm/lpc32xx/misc/i2c.c new file mode 100644 index 0000000000..e1ade9d4ac --- /dev/null +++ b/c/src/lib/libbsp/arm/lpc32xx/misc/i2c.c @@ -0,0 +1,259 @@ +/** + * @file + * + * @ingroup lpc32xx_i2c + * + * @brief I2C support implementation. + */ + +/* + * Copyright (c) 2010 + * embedded brains GmbH + * Obere Lagerstr. 30 + * D-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.com/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); +} + +#if LPC32XX_HCLK != 104000000U + #error "unexpected HCLK" +#endif + +rtems_status_code lpc32xx_i2c_clock( + volatile lpc32xx_i2c *i2c, + unsigned clock_in_hz +) +{ + uint32_t clk_lo = 0; + uint32_t clk_hi = 0; + + switch (clock_in_hz) { + case 100000: + clk_lo = 520; + clk_hi = 520; + break; + case 400000: + clk_lo = 166; + clk_hi = 94; + break; + default: + return RTEMS_INVALID_CLOCK; + } + + 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) { + if ((i2c->stat & I2C_STAT_TDI) != 0) { + stop = true; + + break; + } + + 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; +} |