Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:04

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup lpc32xx_i2c
0007  *
0008  * @brief I2C support implementation.
0009  */
0010 
0011 /*
0012  * Copyright (C) 2010, 2011 embedded brains GmbH & Co. KG
0013  *
0014  * Redistribution and use in source and binary forms, with or without
0015  * modification, are permitted provided that the following conditions
0016  * are met:
0017  * 1. Redistributions of source code must retain the above copyright
0018  *    notice, this list of conditions and the following disclaimer.
0019  * 2. Redistributions in binary form must reproduce the above copyright
0020  *    notice, this list of conditions and the following disclaimer in the
0021  *    documentation and/or other materials provided with the distribution.
0022  *
0023  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0024  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0025  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0026  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0027  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0028  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0029  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0030  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0031  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0032  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0033  * POSSIBILITY OF SUCH DAMAGE.
0034  */
0035 
0036 #include <rtems.h>
0037 
0038 #include <bsp.h>
0039 #include <bsp/i2c.h>
0040 
0041 void lpc32xx_i2c_reset(volatile lpc32xx_i2c *i2c)
0042 {
0043   i2c->ctrl = I2C_CTRL_RESET;
0044 }
0045 
0046 rtems_status_code lpc32xx_i2c_init(
0047   volatile lpc32xx_i2c *i2c,
0048   unsigned clock_in_hz
0049 )
0050 {
0051   uint32_t i2cclk = 0;
0052 
0053   if (i2c == &lpc32xx.i2c_1) {
0054     i2cclk |= I2CCLK_1_EN | I2CCLK_1_HIGH_DRIVE;
0055   } else if (i2c == &lpc32xx.i2c_2) {
0056     i2cclk |= I2CCLK_2_EN | I2CCLK_2_HIGH_DRIVE;
0057   } else {
0058     return RTEMS_INVALID_ID;
0059   }
0060 
0061   LPC32XX_I2CCLK_CTRL |= i2cclk;
0062 
0063   lpc32xx_i2c_reset(i2c);
0064 
0065   return lpc32xx_i2c_clock(i2c, clock_in_hz);
0066 }
0067 
0068 rtems_status_code lpc32xx_i2c_clock(
0069   volatile lpc32xx_i2c *i2c,
0070   unsigned clock_in_hz
0071 )
0072 {
0073   uint32_t clk_div = lpc32xx_hclk() / clock_in_hz;
0074   uint32_t clk_lo = 0;
0075   uint32_t clk_hi = 0;
0076 
0077   switch (clock_in_hz) {
0078     case 100000:
0079       clk_lo = clk_div / 2;
0080       break;
0081     case 400000:
0082       clk_lo = (64 * clk_div) / 100;
0083       break;
0084     default:
0085       return RTEMS_INVALID_CLOCK;
0086   }
0087 
0088   clk_hi = clk_div - clk_lo;
0089 
0090   i2c->clk_lo = clk_lo;
0091   i2c->clk_hi = clk_hi;
0092 
0093   return RTEMS_SUCCESSFUL;
0094 }
0095 
0096 static rtems_status_code wait_for_transaction_done(volatile lpc32xx_i2c *i2c)
0097 {
0098   uint32_t stat = 0;
0099 
0100   do {
0101     stat = i2c->stat;
0102   } while ((stat & I2C_STAT_TDI) == 0);
0103 
0104   if ((stat & I2C_STAT_TFE) != 0) {
0105     i2c->stat = I2C_STAT_TDI;
0106 
0107     return RTEMS_SUCCESSFUL;
0108   } else {
0109     lpc32xx_i2c_reset(i2c);
0110 
0111     return RTEMS_IO_ERROR;
0112   }
0113 }
0114 
0115 static rtems_status_code tx(volatile lpc32xx_i2c *i2c, uint32_t data)
0116 {
0117   uint32_t stat = 0;
0118 
0119   do {
0120     stat = i2c->stat;
0121   } while ((stat & (I2C_STAT_TFE | I2C_STAT_TDI)) == 0);
0122 
0123   if ((stat & I2C_STAT_TDI) == 0) {
0124     i2c->rx_or_tx = data;
0125 
0126     return RTEMS_SUCCESSFUL;
0127   } else {
0128     lpc32xx_i2c_reset(i2c);
0129 
0130     return RTEMS_IO_ERROR;
0131   }
0132 }
0133 
0134 rtems_status_code lpc32xx_i2c_write_start(
0135   volatile lpc32xx_i2c *i2c,
0136   unsigned addr
0137 )
0138 {
0139   return tx(i2c, I2C_TX_ADDR(addr) | I2C_TX_START);
0140 }
0141 
0142 rtems_status_code lpc32xx_i2c_read_start(
0143   volatile lpc32xx_i2c *i2c,
0144   unsigned addr
0145 )
0146 {
0147   return tx(i2c, I2C_TX_ADDR(addr) | I2C_TX_START | I2C_TX_READ);
0148 }
0149 
0150 rtems_status_code lpc32xx_i2c_write_with_optional_stop(
0151   volatile lpc32xx_i2c *i2c,
0152   const uint8_t *out,
0153   size_t n,
0154   bool stop
0155 )
0156 {
0157   rtems_status_code sc = RTEMS_SUCCESSFUL;
0158   size_t i = 0;
0159 
0160   for (i = 0; i < n - 1 && sc == RTEMS_SUCCESSFUL; ++i) {
0161     sc = tx(i2c, out [i]);
0162   }
0163 
0164   if (sc == RTEMS_SUCCESSFUL) {
0165     uint32_t stop_flag = stop ? I2C_TX_STOP : 0;
0166 
0167     sc = tx(i2c, out [n - 1] | stop_flag);
0168   }
0169 
0170   if (stop && sc == RTEMS_SUCCESSFUL) {
0171     sc = wait_for_transaction_done(i2c);
0172   }
0173 
0174   return sc;
0175 }
0176 
0177 static bool can_tx_for_rx(volatile lpc32xx_i2c *i2c)
0178 {
0179   return (i2c->stat & (I2C_STAT_TFF | I2C_STAT_RFF)) == 0;
0180 }
0181 
0182 static bool can_rx(volatile lpc32xx_i2c *i2c)
0183 {
0184   return (i2c->stat & I2C_STAT_RFE) == 0;
0185 }
0186 
0187 rtems_status_code lpc32xx_i2c_read_with_optional_stop(
0188   volatile lpc32xx_i2c *i2c,
0189   uint8_t *in,
0190   size_t n,
0191   bool stop
0192 )
0193 {
0194   rtems_status_code sc = RTEMS_SUCCESSFUL;
0195   size_t last = n - 1;
0196   size_t rx = 0;
0197   size_t tx = 0;
0198 
0199   if (!stop) {
0200     return RTEMS_NOT_IMPLEMENTED;
0201   }
0202 
0203   while (rx <= last) {
0204     while (tx < last && can_tx_for_rx(i2c)) {
0205       i2c->rx_or_tx = 0;
0206       ++tx;
0207     }
0208 
0209     if (tx == last && can_tx_for_rx(i2c)) {
0210       uint32_t stop_flag = stop ? I2C_TX_STOP : 0;
0211 
0212       i2c->rx_or_tx = stop_flag;
0213       ++tx;
0214     }
0215 
0216     while (rx <= last && can_rx(i2c)) {
0217       in [rx] = (uint8_t) i2c->rx_or_tx;
0218       ++rx;
0219     }
0220   }
0221 
0222   if (stop) {
0223     sc = wait_for_transaction_done(i2c);
0224   }
0225 
0226   return sc;
0227 }
0228 
0229 rtems_status_code lpc32xx_i2c_write_and_read(
0230   volatile lpc32xx_i2c *i2c,
0231   unsigned addr,
0232   const uint8_t *out,
0233   size_t out_size,
0234   uint8_t *in,
0235   size_t in_size
0236 )
0237 {
0238   rtems_status_code sc = RTEMS_SUCCESSFUL;
0239 
0240   if (out_size > 0) {
0241     bool stop = in_size == 0;
0242 
0243     sc = lpc32xx_i2c_write_start(i2c, addr);
0244     if (sc != RTEMS_SUCCESSFUL) {
0245       return sc;
0246     }
0247 
0248     sc = lpc32xx_i2c_write_with_optional_stop(i2c, out, out_size, stop);
0249     if (sc != RTEMS_SUCCESSFUL) {
0250       return sc;
0251     }
0252   }
0253 
0254   if (in_size > 0) {
0255     sc = lpc32xx_i2c_read_start(i2c, addr);
0256     if (sc != RTEMS_SUCCESSFUL) {
0257       return sc;
0258     }
0259 
0260     lpc32xx_i2c_read_with_optional_stop(i2c, in, in_size, true);
0261   }
0262 
0263   return RTEMS_SUCCESSFUL;
0264 }