123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971 |
- /*
- * Copyright (c) 2016-2024, STMicroelectronics - All Rights Reserved
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
- #include <errno.h>
- #include <stdbool.h>
- #include <stdlib.h>
- #include <common/debug.h>
- #include <common/fdt_wrappers.h>
- #include <drivers/clk.h>
- #include <drivers/delay_timer.h>
- #include <drivers/st/stm32_gpio.h>
- #include <drivers/st/stm32_i2c.h>
- #include <lib/mmio.h>
- #include <lib/utils.h>
- #include <libfdt.h>
- #include <platform_def.h>
- /* STM32 I2C registers offsets */
- #define I2C_CR1 0x00U
- #define I2C_CR2 0x04U
- #define I2C_OAR1 0x08U
- #define I2C_OAR2 0x0CU
- #define I2C_TIMINGR 0x10U
- #define I2C_TIMEOUTR 0x14U
- #define I2C_ISR 0x18U
- #define I2C_ICR 0x1CU
- #define I2C_PECR 0x20U
- #define I2C_RXDR 0x24U
- #define I2C_TXDR 0x28U
- #define TIMINGR_CLEAR_MASK 0xF0FFFFFFU
- #define MAX_NBYTE_SIZE 255U
- #define I2C_NSEC_PER_SEC 1000000000L
- /* I2C Timing hard-coded value, for I2C clock source is HSI at 64MHz */
- #define I2C_TIMING 0x10D07DB5
- static void notif_i2c_timeout(struct i2c_handle_s *hi2c)
- {
- hi2c->i2c_err |= I2C_ERROR_TIMEOUT;
- hi2c->i2c_mode = I2C_MODE_NONE;
- hi2c->i2c_state = I2C_STATE_READY;
- }
- /*
- * @brief Configure I2C Analog noise filter.
- * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
- * the configuration information for the specified I2C peripheral.
- * @param analog_filter: New state of the Analog filter
- * @retval 0 if OK, negative value else
- */
- static int i2c_config_analog_filter(struct i2c_handle_s *hi2c,
- uint32_t analog_filter)
- {
- if ((hi2c->i2c_state != I2C_STATE_READY) || (hi2c->lock != 0U)) {
- return -EBUSY;
- }
- hi2c->lock = 1;
- hi2c->i2c_state = I2C_STATE_BUSY;
- /* Disable the selected I2C peripheral */
- mmio_clrbits_32(hi2c->i2c_base_addr + I2C_CR1, I2C_CR1_PE);
- /* Reset I2Cx ANOFF bit */
- mmio_clrbits_32(hi2c->i2c_base_addr + I2C_CR1, I2C_CR1_ANFOFF);
- /* Set analog filter bit*/
- mmio_setbits_32(hi2c->i2c_base_addr + I2C_CR1, analog_filter);
- /* Enable the selected I2C peripheral */
- mmio_setbits_32(hi2c->i2c_base_addr + I2C_CR1, I2C_CR1_PE);
- hi2c->i2c_state = I2C_STATE_READY;
- hi2c->lock = 0;
- return 0;
- }
- /*
- * @brief Get I2C setup information from the device tree and set pinctrl
- * configuration.
- * @param fdt: Pointer to the device tree
- * @param node: I2C node offset
- * @param init: Ref to the initialization configuration structure
- * @retval 0 if OK, negative value else
- */
- int stm32_i2c_get_setup_from_fdt(void *fdt, int node,
- struct stm32_i2c_init_s *init)
- {
- uint32_t read_val;
- init->rise_time = fdt_read_uint32_default(fdt, node,
- "i2c-scl-rising-time-ns",
- STM32_I2C_RISE_TIME_DEFAULT);
- init->fall_time = fdt_read_uint32_default(fdt, node,
- "i2c-scl-falling-time-ns",
- STM32_I2C_FALL_TIME_DEFAULT);
- read_val = fdt_read_uint32_default(fdt, node, "clock-frequency",
- STANDARD_RATE);
- switch (read_val) {
- case FAST_PLUS_RATE:
- init->speed_mode = I2C_SPEED_FAST_PLUS;
- break;
- case FAST_RATE:
- init->speed_mode = I2C_SPEED_FAST;
- break;
- case STANDARD_RATE:
- default:
- init->speed_mode = I2C_SPEED_STANDARD;
- break;
- }
- return dt_set_pinctrl_config(node);
- }
- /*
- * @brief Initialize the I2C device.
- * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
- * the configuration information for the specified I2C.
- * @param init_data: Initialization configuration structure
- * @retval 0 if OK, negative value else
- */
- int stm32_i2c_init(struct i2c_handle_s *hi2c,
- struct stm32_i2c_init_s *init_data)
- {
- int rc = 0;
- uint32_t timing = I2C_TIMING;
- if (hi2c == NULL) {
- return -ENOENT;
- }
- if (hi2c->i2c_state == I2C_STATE_RESET) {
- hi2c->lock = 0;
- }
- hi2c->i2c_state = I2C_STATE_BUSY;
- clk_enable(hi2c->clock);
- /* Disable the selected I2C peripheral */
- mmio_clrbits_32(hi2c->i2c_base_addr + I2C_CR1, I2C_CR1_PE);
- /* Configure I2Cx: Frequency range */
- mmio_write_32(hi2c->i2c_base_addr + I2C_TIMINGR,
- timing & TIMINGR_CLEAR_MASK);
- /* Disable Own Address1 before set the Own Address1 configuration */
- mmio_clrbits_32(hi2c->i2c_base_addr + I2C_OAR1, I2C_OAR1_OA1EN);
- /* Configure I2Cx: Own Address1 and ack own address1 mode */
- if (init_data->addressing_mode == I2C_ADDRESSINGMODE_7BIT) {
- mmio_write_32(hi2c->i2c_base_addr + I2C_OAR1,
- I2C_OAR1_OA1EN | init_data->own_address1);
- } else { /* I2C_ADDRESSINGMODE_10BIT */
- mmio_write_32(hi2c->i2c_base_addr + I2C_OAR1,
- I2C_OAR1_OA1EN | I2C_OAR1_OA1MODE |
- init_data->own_address1);
- }
- mmio_write_32(hi2c->i2c_base_addr + I2C_CR2, 0);
- /* Configure I2Cx: Addressing Master mode */
- if (init_data->addressing_mode == I2C_ADDRESSINGMODE_10BIT) {
- mmio_setbits_32(hi2c->i2c_base_addr + I2C_CR2, I2C_CR2_ADD10);
- }
- /*
- * Enable the AUTOEND by default, and enable NACK
- * (should be disabled only during Slave process).
- */
- mmio_setbits_32(hi2c->i2c_base_addr + I2C_CR2,
- I2C_CR2_AUTOEND | I2C_CR2_NACK);
- /* Disable Own Address2 before set the Own Address2 configuration */
- mmio_clrbits_32(hi2c->i2c_base_addr + I2C_OAR2, I2C_DUALADDRESS_ENABLE);
- /* Configure I2Cx: Dual mode and Own Address2 */
- mmio_write_32(hi2c->i2c_base_addr + I2C_OAR2,
- init_data->dual_address_mode |
- init_data->own_address2 |
- (init_data->own_address2_masks << 8));
- /* Configure I2Cx: Generalcall and NoStretch mode */
- mmio_write_32(hi2c->i2c_base_addr + I2C_CR1,
- init_data->general_call_mode |
- init_data->no_stretch_mode);
- /* Enable the selected I2C peripheral */
- mmio_setbits_32(hi2c->i2c_base_addr + I2C_CR1, I2C_CR1_PE);
- hi2c->i2c_err = I2C_ERROR_NONE;
- hi2c->i2c_state = I2C_STATE_READY;
- hi2c->i2c_mode = I2C_MODE_NONE;
- rc = i2c_config_analog_filter(hi2c, init_data->analog_filter ?
- I2C_ANALOGFILTER_ENABLE :
- I2C_ANALOGFILTER_DISABLE);
- if (rc != 0) {
- ERROR("Cannot initialize I2C analog filter (%d)\n", rc);
- clk_disable(hi2c->clock);
- return rc;
- }
- clk_disable(hi2c->clock);
- return rc;
- }
- /*
- * @brief I2C Tx data register flush process.
- * @param hi2c: I2C handle
- * @retval None
- */
- static void i2c_flush_txdr(struct i2c_handle_s *hi2c)
- {
- /*
- * If a pending TXIS flag is set,
- * write a dummy data in TXDR to clear it.
- */
- if ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) & I2C_FLAG_TXIS) !=
- 0U) {
- mmio_write_32(hi2c->i2c_base_addr + I2C_TXDR, 0);
- }
- /* Flush TX register if not empty */
- if ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) & I2C_FLAG_TXE) ==
- 0U) {
- mmio_setbits_32(hi2c->i2c_base_addr + I2C_ISR,
- I2C_FLAG_TXE);
- }
- }
- /*
- * @brief This function handles I2C Communication timeout.
- * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
- * the configuration information for the specified I2C.
- * @param flag: Specifies the I2C flag to check
- * @param awaited_value: The awaited bit value for the flag (0 or 1)
- * @param timeout_ref: Reference to target timeout
- * @retval 0 if OK, negative value else
- */
- static int i2c_wait_flag(struct i2c_handle_s *hi2c, uint32_t flag,
- uint8_t awaited_value, uint64_t timeout_ref)
- {
- for ( ; ; ) {
- uint32_t isr = mmio_read_32(hi2c->i2c_base_addr + I2C_ISR);
- if (!!(isr & flag) != !!awaited_value) {
- return 0;
- }
- if (timeout_elapsed(timeout_ref)) {
- notif_i2c_timeout(hi2c);
- hi2c->lock = 0;
- return -EIO;
- }
- }
- }
- /*
- * @brief This function handles Acknowledge failed detection during
- * an I2C Communication.
- * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
- * the configuration information for the specified I2C.
- * @param timeout_ref: Reference to target timeout
- * @retval 0 if OK, negative value else
- */
- static int i2c_ack_failed(struct i2c_handle_s *hi2c, uint64_t timeout_ref)
- {
- if ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) & I2C_FLAG_AF) == 0U) {
- return 0;
- }
- /*
- * Wait until STOP Flag is reset.
- * AutoEnd should be initiate after AF.
- */
- while ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) &
- I2C_FLAG_STOPF) == 0U) {
- if (timeout_elapsed(timeout_ref)) {
- notif_i2c_timeout(hi2c);
- hi2c->lock = 0;
- return -EIO;
- }
- }
- mmio_write_32(hi2c->i2c_base_addr + I2C_ICR, I2C_FLAG_AF);
- mmio_write_32(hi2c->i2c_base_addr + I2C_ICR, I2C_FLAG_STOPF);
- i2c_flush_txdr(hi2c);
- mmio_clrbits_32(hi2c->i2c_base_addr + I2C_CR2, I2C_RESET_CR2);
- hi2c->i2c_err |= I2C_ERROR_AF;
- hi2c->i2c_state = I2C_STATE_READY;
- hi2c->i2c_mode = I2C_MODE_NONE;
- hi2c->lock = 0;
- return -EIO;
- }
- /*
- * @brief This function handles I2C Communication timeout for specific usage
- * of TXIS flag.
- * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
- * the configuration information for the specified I2C.
- * @param timeout_ref: Reference to target timeout
- * @retval 0 if OK, negative value else
- */
- static int i2c_wait_txis(struct i2c_handle_s *hi2c, uint64_t timeout_ref)
- {
- while ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) &
- I2C_FLAG_TXIS) == 0U) {
- if (i2c_ack_failed(hi2c, timeout_ref) != 0) {
- return -EIO;
- }
- if (timeout_elapsed(timeout_ref)) {
- notif_i2c_timeout(hi2c);
- hi2c->lock = 0;
- return -EIO;
- }
- }
- return 0;
- }
- /*
- * @brief This function handles I2C Communication timeout for specific
- * usage of STOP flag.
- * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
- * the configuration information for the specified I2C.
- * @param timeout_ref: Reference to target timeout
- * @retval 0 if OK, negative value else
- */
- static int i2c_wait_stop(struct i2c_handle_s *hi2c, uint64_t timeout_ref)
- {
- while ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) &
- I2C_FLAG_STOPF) == 0U) {
- if (i2c_ack_failed(hi2c, timeout_ref) != 0) {
- return -EIO;
- }
- if (timeout_elapsed(timeout_ref)) {
- notif_i2c_timeout(hi2c);
- hi2c->lock = 0;
- return -EIO;
- }
- }
- return 0;
- }
- /*
- * @brief Handles I2Cx communication when starting transfer or during transfer
- * (TC or TCR flag are set).
- * @param hi2c: I2C handle
- * @param dev_addr: Specifies the slave address to be programmed
- * @param size: Specifies the number of bytes to be programmed.
- * This parameter must be a value between 0 and 255.
- * @param i2c_mode: New state of the I2C START condition generation.
- * This parameter can be one of the following values:
- * @arg @ref I2C_RELOAD_MODE: Enable Reload mode.
- * @arg @ref I2C_AUTOEND_MODE: Enable Automatic end mode.
- * @arg @ref I2C_SOFTEND_MODE: Enable Software end mode.
- * @param request: New state of the I2C START condition generation.
- * This parameter can be one of the following values:
- * @arg @ref I2C_NO_STARTSTOP: Don't Generate stop and start condition.
- * @arg @ref I2C_GENERATE_STOP: Generate stop condition
- * (size should be set to 0).
- * @arg @ref I2C_GENERATE_START_READ: Generate Restart for read request.
- * @arg @ref I2C_GENERATE_START_WRITE: Generate Restart for write request.
- * @retval None
- */
- static void i2c_transfer_config(struct i2c_handle_s *hi2c, uint16_t dev_addr,
- uint16_t size, uint32_t i2c_mode,
- uint32_t request)
- {
- uint32_t clr_value, set_value;
- clr_value = (I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD |
- I2C_CR2_AUTOEND | I2C_CR2_START | I2C_CR2_STOP) |
- (I2C_CR2_RD_WRN & (request >> (31U - I2C_CR2_RD_WRN_OFFSET)));
- set_value = ((uint32_t)dev_addr & I2C_CR2_SADD) |
- (((uint32_t)size << I2C_CR2_NBYTES_OFFSET) & I2C_CR2_NBYTES) |
- i2c_mode | request;
- mmio_clrsetbits_32(hi2c->i2c_base_addr + I2C_CR2, clr_value, set_value);
- }
- /*
- * @brief Master sends target device address followed by internal memory
- * address for write request.
- * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
- * the configuration information for the specified I2C.
- * @param dev_addr: Target device address
- * @param mem_addr: Internal memory address
- * @param mem_add_size: Size of internal memory address
- * @param timeout_ref: Reference to target timeout
- * @retval 0 if OK, negative value else
- */
- static int i2c_request_memory_write(struct i2c_handle_s *hi2c,
- uint16_t dev_addr, uint16_t mem_addr,
- uint16_t mem_add_size, uint64_t timeout_ref)
- {
- i2c_transfer_config(hi2c, dev_addr, mem_add_size, I2C_RELOAD_MODE,
- I2C_GENERATE_START_WRITE);
- if (i2c_wait_txis(hi2c, timeout_ref) != 0) {
- return -EIO;
- }
- if (mem_add_size == I2C_MEMADD_SIZE_8BIT) {
- /* Send Memory Address */
- mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR,
- (uint8_t)(mem_addr & 0x00FFU));
- } else {
- /* Send MSB of Memory Address */
- mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR,
- (uint8_t)((mem_addr & 0xFF00U) >> 8));
- if (i2c_wait_txis(hi2c, timeout_ref) != 0) {
- return -EIO;
- }
- /* Send LSB of Memory Address */
- mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR,
- (uint8_t)(mem_addr & 0x00FFU));
- }
- if (i2c_wait_flag(hi2c, I2C_FLAG_TCR, 0, timeout_ref) != 0) {
- return -EIO;
- }
- return 0;
- }
- /*
- * @brief Master sends target device address followed by internal memory
- * address for read request.
- * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
- * the configuration information for the specified I2C.
- * @param dev_addr: Target device address
- * @param mem_addr: Internal memory address
- * @param mem_add_size: Size of internal memory address
- * @param timeout_ref: Reference to target timeout
- * @retval 0 if OK, negative value else
- */
- static int i2c_request_memory_read(struct i2c_handle_s *hi2c, uint16_t dev_addr,
- uint16_t mem_addr, uint16_t mem_add_size,
- uint64_t timeout_ref)
- {
- i2c_transfer_config(hi2c, dev_addr, mem_add_size, I2C_SOFTEND_MODE,
- I2C_GENERATE_START_WRITE);
- if (i2c_wait_txis(hi2c, timeout_ref) != 0) {
- return -EIO;
- }
- if (mem_add_size == I2C_MEMADD_SIZE_8BIT) {
- /* Send Memory Address */
- mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR,
- (uint8_t)(mem_addr & 0x00FFU));
- } else {
- /* Send MSB of Memory Address */
- mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR,
- (uint8_t)((mem_addr & 0xFF00U) >> 8));
- if (i2c_wait_txis(hi2c, timeout_ref) != 0) {
- return -EIO;
- }
- /* Send LSB of Memory Address */
- mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR,
- (uint8_t)(mem_addr & 0x00FFU));
- }
- if (i2c_wait_flag(hi2c, I2C_FLAG_TC, 0, timeout_ref) != 0) {
- return -EIO;
- }
- return 0;
- }
- /*
- * @brief Generic function to write an amount of data in blocking mode
- * (for Memory Mode and Master Mode)
- * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
- * the configuration information for the specified I2C.
- * @param dev_addr: Target device address
- * @param mem_addr: Internal memory address (if Memory Mode)
- * @param mem_add_size: Size of internal memory address (if Memory Mode)
- * @param p_data: Pointer to data buffer
- * @param size: Amount of data to be sent
- * @param timeout_ms: Timeout duration in milliseconds
- * @param mode: Communication mode
- * @retval 0 if OK, negative value else
- */
- static int i2c_write(struct i2c_handle_s *hi2c, uint16_t dev_addr,
- uint16_t mem_addr, uint16_t mem_add_size,
- uint8_t *p_data, uint16_t size, uint32_t timeout_ms,
- enum i2c_mode_e mode)
- {
- uint64_t timeout_ref;
- int rc = -EIO;
- uint8_t *p_buff = p_data;
- uint32_t xfer_size;
- uint32_t xfer_count = size;
- if ((mode != I2C_MODE_MASTER) && (mode != I2C_MODE_MEM)) {
- return -1;
- }
- if ((hi2c->i2c_state != I2C_STATE_READY) || (hi2c->lock != 0U)) {
- return -EBUSY;
- }
- if ((p_data == NULL) || (size == 0U)) {
- return -EINVAL;
- }
- clk_enable(hi2c->clock);
- hi2c->lock = 1;
- timeout_ref = timeout_init_us(I2C_TIMEOUT_BUSY_MS * 1000);
- if (i2c_wait_flag(hi2c, I2C_FLAG_BUSY, 1, timeout_ref) != 0) {
- goto bail;
- }
- hi2c->i2c_state = I2C_STATE_BUSY_TX;
- hi2c->i2c_mode = mode;
- hi2c->i2c_err = I2C_ERROR_NONE;
- timeout_ref = timeout_init_us(timeout_ms * 1000);
- if (mode == I2C_MODE_MEM) {
- /* In Memory Mode, Send Slave Address and Memory Address */
- if (i2c_request_memory_write(hi2c, dev_addr, mem_addr,
- mem_add_size, timeout_ref) != 0) {
- goto bail;
- }
- if (xfer_count > MAX_NBYTE_SIZE) {
- xfer_size = MAX_NBYTE_SIZE;
- i2c_transfer_config(hi2c, dev_addr, xfer_size,
- I2C_RELOAD_MODE, I2C_NO_STARTSTOP);
- } else {
- xfer_size = xfer_count;
- i2c_transfer_config(hi2c, dev_addr, xfer_size,
- I2C_AUTOEND_MODE, I2C_NO_STARTSTOP);
- }
- } else {
- /* In Master Mode, Send Slave Address */
- if (xfer_count > MAX_NBYTE_SIZE) {
- xfer_size = MAX_NBYTE_SIZE;
- i2c_transfer_config(hi2c, dev_addr, xfer_size,
- I2C_RELOAD_MODE,
- I2C_GENERATE_START_WRITE);
- } else {
- xfer_size = xfer_count;
- i2c_transfer_config(hi2c, dev_addr, xfer_size,
- I2C_AUTOEND_MODE,
- I2C_GENERATE_START_WRITE);
- }
- }
- do {
- if (i2c_wait_txis(hi2c, timeout_ref) != 0) {
- goto bail;
- }
- mmio_write_8(hi2c->i2c_base_addr + I2C_TXDR, *p_buff);
- p_buff++;
- xfer_count--;
- xfer_size--;
- if ((xfer_count != 0U) && (xfer_size == 0U)) {
- /* Wait until TCR flag is set */
- if (i2c_wait_flag(hi2c, I2C_FLAG_TCR, 0,
- timeout_ref) != 0) {
- goto bail;
- }
- if (xfer_count > MAX_NBYTE_SIZE) {
- xfer_size = MAX_NBYTE_SIZE;
- i2c_transfer_config(hi2c, dev_addr,
- xfer_size,
- I2C_RELOAD_MODE,
- I2C_NO_STARTSTOP);
- } else {
- xfer_size = xfer_count;
- i2c_transfer_config(hi2c, dev_addr,
- xfer_size,
- I2C_AUTOEND_MODE,
- I2C_NO_STARTSTOP);
- }
- }
- } while (xfer_count > 0U);
- /*
- * No need to Check TC flag, with AUTOEND mode the stop
- * is automatically generated.
- * Wait until STOPF flag is reset.
- */
- if (i2c_wait_stop(hi2c, timeout_ref) != 0) {
- goto bail;
- }
- mmio_write_32(hi2c->i2c_base_addr + I2C_ICR, I2C_FLAG_STOPF);
- mmio_clrbits_32(hi2c->i2c_base_addr + I2C_CR2, I2C_RESET_CR2);
- hi2c->i2c_state = I2C_STATE_READY;
- hi2c->i2c_mode = I2C_MODE_NONE;
- rc = 0;
- bail:
- hi2c->lock = 0;
- clk_disable(hi2c->clock);
- return rc;
- }
- /*
- * @brief Write an amount of data in blocking mode to a specific memory
- * address.
- * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
- * the configuration information for the specified I2C.
- * @param dev_addr: Target device address
- * @param mem_addr: Internal memory address
- * @param mem_add_size: Size of internal memory address
- * @param p_data: Pointer to data buffer
- * @param size: Amount of data to be sent
- * @param timeout_ms: Timeout duration in milliseconds
- * @retval 0 if OK, negative value else
- */
- int stm32_i2c_mem_write(struct i2c_handle_s *hi2c, uint16_t dev_addr,
- uint16_t mem_addr, uint16_t mem_add_size,
- uint8_t *p_data, uint16_t size, uint32_t timeout_ms)
- {
- return i2c_write(hi2c, dev_addr, mem_addr, mem_add_size,
- p_data, size, timeout_ms, I2C_MODE_MEM);
- }
- /*
- * @brief Transmits in master mode an amount of data in blocking mode.
- * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
- * the configuration information for the specified I2C.
- * @param dev_addr: Target device address
- * @param p_data: Pointer to data buffer
- * @param size: Amount of data to be sent
- * @param timeout_ms: Timeout duration in milliseconds
- * @retval 0 if OK, negative value else
- */
- int stm32_i2c_master_transmit(struct i2c_handle_s *hi2c, uint16_t dev_addr,
- uint8_t *p_data, uint16_t size,
- uint32_t timeout_ms)
- {
- return i2c_write(hi2c, dev_addr, 0, 0,
- p_data, size, timeout_ms, I2C_MODE_MASTER);
- }
- /*
- * @brief Generic function to read an amount of data in blocking mode
- * (for Memory Mode and Master Mode)
- * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
- * the configuration information for the specified I2C.
- * @param dev_addr: Target device address
- * @param mem_addr: Internal memory address (if Memory Mode)
- * @param mem_add_size: Size of internal memory address (if Memory Mode)
- * @param p_data: Pointer to data buffer
- * @param size: Amount of data to be sent
- * @param timeout_ms: Timeout duration in milliseconds
- * @param mode: Communication mode
- * @retval 0 if OK, negative value else
- */
- static int i2c_read(struct i2c_handle_s *hi2c, uint16_t dev_addr,
- uint16_t mem_addr, uint16_t mem_add_size,
- uint8_t *p_data, uint16_t size, uint32_t timeout_ms,
- enum i2c_mode_e mode)
- {
- uint64_t timeout_ref;
- int rc = -EIO;
- uint8_t *p_buff = p_data;
- uint32_t xfer_count = size;
- uint32_t xfer_size;
- if ((mode != I2C_MODE_MASTER) && (mode != I2C_MODE_MEM)) {
- return -1;
- }
- if ((hi2c->i2c_state != I2C_STATE_READY) || (hi2c->lock != 0U)) {
- return -EBUSY;
- }
- if ((p_data == NULL) || (size == 0U)) {
- return -EINVAL;
- }
- clk_enable(hi2c->clock);
- hi2c->lock = 1;
- timeout_ref = timeout_init_us(I2C_TIMEOUT_BUSY_MS * 1000);
- if (i2c_wait_flag(hi2c, I2C_FLAG_BUSY, 1, timeout_ref) != 0) {
- goto bail;
- }
- hi2c->i2c_state = I2C_STATE_BUSY_RX;
- hi2c->i2c_mode = mode;
- hi2c->i2c_err = I2C_ERROR_NONE;
- if (mode == I2C_MODE_MEM) {
- /* Send Memory Address */
- if (i2c_request_memory_read(hi2c, dev_addr, mem_addr,
- mem_add_size, timeout_ref) != 0) {
- goto bail;
- }
- }
- /*
- * Send Slave Address.
- * Set NBYTES to write and reload if xfer_count > MAX_NBYTE_SIZE
- * and generate RESTART.
- */
- if (xfer_count > MAX_NBYTE_SIZE) {
- xfer_size = MAX_NBYTE_SIZE;
- i2c_transfer_config(hi2c, dev_addr, xfer_size,
- I2C_RELOAD_MODE, I2C_GENERATE_START_READ);
- } else {
- xfer_size = xfer_count;
- i2c_transfer_config(hi2c, dev_addr, xfer_size,
- I2C_AUTOEND_MODE, I2C_GENERATE_START_READ);
- }
- do {
- if (i2c_wait_flag(hi2c, I2C_FLAG_RXNE, 0, timeout_ref) != 0) {
- goto bail;
- }
- *p_buff = mmio_read_8(hi2c->i2c_base_addr + I2C_RXDR);
- p_buff++;
- xfer_size--;
- xfer_count--;
- if ((xfer_count != 0U) && (xfer_size == 0U)) {
- if (i2c_wait_flag(hi2c, I2C_FLAG_TCR, 0,
- timeout_ref) != 0) {
- goto bail;
- }
- if (xfer_count > MAX_NBYTE_SIZE) {
- xfer_size = MAX_NBYTE_SIZE;
- i2c_transfer_config(hi2c, dev_addr,
- xfer_size,
- I2C_RELOAD_MODE,
- I2C_NO_STARTSTOP);
- } else {
- xfer_size = xfer_count;
- i2c_transfer_config(hi2c, dev_addr,
- xfer_size,
- I2C_AUTOEND_MODE,
- I2C_NO_STARTSTOP);
- }
- }
- } while (xfer_count > 0U);
- /*
- * No need to Check TC flag, with AUTOEND mode the stop
- * is automatically generated.
- * Wait until STOPF flag is reset.
- */
- if (i2c_wait_stop(hi2c, timeout_ref) != 0) {
- goto bail;
- }
- mmio_write_32(hi2c->i2c_base_addr + I2C_ICR, I2C_FLAG_STOPF);
- mmio_clrbits_32(hi2c->i2c_base_addr + I2C_CR2, I2C_RESET_CR2);
- hi2c->i2c_state = I2C_STATE_READY;
- hi2c->i2c_mode = I2C_MODE_NONE;
- rc = 0;
- bail:
- hi2c->lock = 0;
- clk_disable(hi2c->clock);
- return rc;
- }
- /*
- * @brief Read an amount of data in blocking mode from a specific memory
- * address.
- * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
- * the configuration information for the specified I2C.
- * @param dev_addr: Target device address
- * @param mem_addr: Internal memory address
- * @param mem_add_size: Size of internal memory address
- * @param p_data: Pointer to data buffer
- * @param size: Amount of data to be sent
- * @param timeout_ms: Timeout duration in milliseconds
- * @retval 0 if OK, negative value else
- */
- int stm32_i2c_mem_read(struct i2c_handle_s *hi2c, uint16_t dev_addr,
- uint16_t mem_addr, uint16_t mem_add_size,
- uint8_t *p_data, uint16_t size, uint32_t timeout_ms)
- {
- return i2c_read(hi2c, dev_addr, mem_addr, mem_add_size,
- p_data, size, timeout_ms, I2C_MODE_MEM);
- }
- /*
- * @brief Receives in master mode an amount of data in blocking mode.
- * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
- * the configuration information for the specified I2C.
- * @param dev_addr: Target device address
- * @param p_data: Pointer to data buffer
- * @param size: Amount of data to be sent
- * @param timeout_ms: Timeout duration in milliseconds
- * @retval 0 if OK, negative value else
- */
- int stm32_i2c_master_receive(struct i2c_handle_s *hi2c, uint16_t dev_addr,
- uint8_t *p_data, uint16_t size,
- uint32_t timeout_ms)
- {
- return i2c_read(hi2c, dev_addr, 0, 0,
- p_data, size, timeout_ms, I2C_MODE_MASTER);
- }
- /*
- * @brief Checks if target device is ready for communication.
- * @note This function is used with Memory devices
- * @param hi2c: Pointer to a struct i2c_handle_s structure that contains
- * the configuration information for the specified I2C.
- * @param dev_addr: Target device address
- * @param trials: Number of trials
- * @param timeout_ms: Timeout duration in milliseconds
- * @retval True if device is ready, false else
- */
- bool stm32_i2c_is_device_ready(struct i2c_handle_s *hi2c,
- uint16_t dev_addr, uint32_t trials,
- uint32_t timeout_ms)
- {
- uint32_t i2c_trials = 0U;
- bool rc = false;
- if ((hi2c->i2c_state != I2C_STATE_READY) || (hi2c->lock != 0U)) {
- return rc;
- }
- clk_enable(hi2c->clock);
- hi2c->lock = 1;
- hi2c->i2c_mode = I2C_MODE_NONE;
- if ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) & I2C_FLAG_BUSY) !=
- 0U) {
- goto bail;
- }
- hi2c->i2c_state = I2C_STATE_BUSY;
- hi2c->i2c_err = I2C_ERROR_NONE;
- do {
- uint64_t timeout_ref;
- /* Generate Start */
- if ((mmio_read_32(hi2c->i2c_base_addr + I2C_OAR1) &
- I2C_OAR1_OA1MODE) == 0) {
- mmio_write_32(hi2c->i2c_base_addr + I2C_CR2,
- (((uint32_t)dev_addr & I2C_CR2_SADD) |
- I2C_CR2_START | I2C_CR2_AUTOEND) &
- ~I2C_CR2_RD_WRN);
- } else {
- mmio_write_32(hi2c->i2c_base_addr + I2C_CR2,
- (((uint32_t)dev_addr & I2C_CR2_SADD) |
- I2C_CR2_START | I2C_CR2_ADD10) &
- ~I2C_CR2_RD_WRN);
- }
- /*
- * No need to Check TC flag, with AUTOEND mode the stop
- * is automatically generated.
- * Wait until STOPF flag is set or a NACK flag is set.
- */
- timeout_ref = timeout_init_us(timeout_ms * 1000);
- do {
- if ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) &
- (I2C_FLAG_STOPF | I2C_FLAG_AF)) != 0U) {
- break;
- }
- if (timeout_elapsed(timeout_ref)) {
- notif_i2c_timeout(hi2c);
- goto bail;
- }
- } while (true);
- if ((mmio_read_32(hi2c->i2c_base_addr + I2C_ISR) &
- I2C_FLAG_AF) == 0U) {
- if (i2c_wait_flag(hi2c, I2C_FLAG_STOPF, 0,
- timeout_ref) != 0) {
- goto bail;
- }
- mmio_write_32(hi2c->i2c_base_addr + I2C_ICR,
- I2C_FLAG_STOPF);
- hi2c->i2c_state = I2C_STATE_READY;
- rc = true;
- goto bail;
- }
- if (i2c_wait_flag(hi2c, I2C_FLAG_STOPF, 0, timeout_ref) != 0) {
- goto bail;
- }
- mmio_write_32(hi2c->i2c_base_addr + I2C_ICR, I2C_FLAG_AF);
- mmio_write_32(hi2c->i2c_base_addr + I2C_ICR, I2C_FLAG_STOPF);
- if (i2c_trials == trials) {
- mmio_setbits_32(hi2c->i2c_base_addr + I2C_CR2,
- I2C_CR2_STOP);
- if (i2c_wait_flag(hi2c, I2C_FLAG_STOPF, 0,
- timeout_ref) != 0) {
- goto bail;
- }
- mmio_write_32(hi2c->i2c_base_addr + I2C_ICR,
- I2C_FLAG_STOPF);
- }
- i2c_trials++;
- } while (i2c_trials < trials);
- notif_i2c_timeout(hi2c);
- bail:
- hi2c->lock = 0;
- clk_disable(hi2c->clock);
- return rc;
- }
|