/** * @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 * * * 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 #include #include 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; }