/* * Copyright (c) 2019-2023 Beijing Hanwei Innovation Technology Ltd. Co. and * its subsidiaries and affiliates (collectly called MKSEMI). * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form, except as embedded into an MKSEMI * integrated circuit in a product or a software update for such product, * must reproduce the above copyright notice, this list of conditions and * the following disclaimer in the documentation and/or other materials * provided with the distribution. * * 3. Neither the name of MKSEMI nor the names of its contributors may be used * to endorse or promote products derived from this software without * specific prior written permission. * * 4. This software, with or without modification, must only be used with a * MKSEMI integrated circuit. * * 5. Any software provided in binary form under this license must not be * reverse engineered, decompiled, modified and/or disassembled. * * THIS SOFTWARE IS PROVIDED BY MKSEMI "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL MKSEMI OR CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "mk_i2c.h" #include "mk_trace.h" #include "mk_clock.h" #include "mk_reset.h" #include "mk_misc.h" #if defined(__ICCARM__) #pragma diag_suppress = Pa082 #endif static struct I2C_HANDLE_T i2c_handle[I2C_MAX_NUM] = { { .base = I2C0, .irq = I2C0_IRQn, .config = { .mode = I2C_MASTER, .speed_mode = I2C_SPEED_STANDARD, .addr_mode = I2C_7BIT_ADDR, .local_addr = MK_DEV_ADDRESS, .target_addr = 0x33, .rx_level = I2C_RXFIFO_CHAR_1, .tx_level = I2C_TXFIFO_CHAR_1, .int_rx = false, .int_tx = false, }, }, }; // Utility function which programs the clock count registers for a given input clock frequency. static void i2c_clock_setup(enum I2C_DEV_T id, uint32_t i2c_clk) { uint16_t ss_scl_high, ss_scl_low; uint16_t fs_scl_high, fs_scl_low; uint16_t hs_scl_high, hs_scl_low; // ic_clk is the clock speed (in Hz) that is being supplied to the // i2c device. The correct clock count values are determined // by using this inconjunction with the minimum high and low signal // hold times as per the I2C bus specification. ss_scl_high = ((uint16_t)(((I2C_MIN_SS_SCL_HIGH_TIME * (i2c_clk / 1000)) / 100000U) + 1)); ss_scl_low = ((uint16_t)(((I2C_MIN_SS_SCL_LOW_TIME * (i2c_clk / 1000)) / 100000U) + 1)); fs_scl_high = ((uint16_t)(((I2C_MIN_FS_SCL_HIGH_TIME * (i2c_clk / 1000)) / 100000U) + 1)); fs_scl_low = ((uint16_t)(((I2C_MIN_FS_SCL_LOW_TIME * (i2c_clk / 1000)) / 100000U) + 1)); hs_scl_high = ((uint16_t)(((I2C_MIN_HS_SCL_HIGH_TIME * (i2c_clk / 1000)) / 100000U) + 1)); hs_scl_low = ((uint16_t)(((I2C_MIN_HS_SCL_LOW_TIME * (i2c_clk / 1000)) / 100000U) + 1)); i2c_handle[id].base->SSCL = I2C_SSCL_LCNT(ss_scl_low) | I2C_SSCL_HCNT(ss_scl_high); i2c_handle[id].base->FSSCL = I2C_FSSCL_LCNT(fs_scl_low) | I2C_FSSCL_HCNT(fs_scl_high); i2c_handle[id].base->HSSCL = I2C_HSSCL_LCNT(hs_scl_low) | I2C_HSSCL_HCNT(hs_scl_high); } static void i2c_clear_special_tx_abrt(enum I2C_DEV_T id) { uint32_t abrt = i2c_handle[id].base->TX_ABORT; i2c_handle[id].base->INTR_CLR = I2C_INTR_CLR_TX_ABRT_MSK; if (abrt & I2C_TX_ABORT_SBYTE_NORSTRT_MSK) { i2c_handle[id].base->CTRL1 |= I2C_CTRL1_RESTART_EN_MSK; i2c_handle[id].base->ADDR &= ~(I2C_ADDR_SPECIAL_MSK | I2C_ADDR_GC_OR_START_MSK); i2c_handle[id].base->INTR_CLR = I2C_INTR_CLR_TX_ABRT_MSK; } } static int i2c_check_error(enum I2C_DEV_T id, uint32_t error) { int ret = 0; if (i2c_handle[id].base->STATUS0 & error) { i2c_handle[id].base->INTR_CLR = I2C_INTR_CLR_ALL_MSK; i2c_clear_special_tx_abrt(id); ret = DRV_ERROR; } return ret; } static uint32_t i2c_wait_status(enum I2C_DEV_T id, uint32_t mask, uint32_t value, uint32_t timeout) { uint32_t ret = 0; uint32_t time_cnt = timeout; uint8_t skip = timeout ? 0 : 1; while ((i2c_handle[id].base->STATUS1 & mask) != value) { if (i2c_check_error(id, I2C_INTR_TX_ABRT_MSK)) { ret = I2C_INTR_TX_ABRT_MSK; break; } if ((time_cnt == 0) && (skip == 0)) { // timeout error code ret = (mask << 12); break; } time_cnt--; delay_us(I2C_DLY_10US); } return ret; } static int i2c_update_dev_addr(enum I2C_DEV_T id, uint16_t dev_addr) { int ret = 0; uint32_t reg = i2c_handle[id].base->ADDR; if ((reg & I2C_ADDR_TGT_ADDR_MSK) != dev_addr) { reg &= ~I2C_ADDR_TGT_ADDR_MSK; reg |= (dev_addr & I2C_ADDR_TGT_ADDR_MSK); // wait for I2C idle if (i2c_wait_status(id, I2C_STATUS1_ACTIVITY_MSK, 0, I2C_WAIT_ACT_CNT)) { return DRV_BUSY; } i2c_handle[id].base->CTRL0 &= ~I2C_CTRL0_ENABLE_MSK; i2c_handle[id].base->ADDR = reg; i2c_handle[id].base->CTRL0 |= I2C_CTRL0_ENABLE_MSK; } return ret; } static int i2c_state_set(enum I2C_DEV_T id, enum I2C_STATE_T state) { int ret = DRV_OK; uint32_t lock = int_lock(); // update state switch (i2c_handle[id].state) { case I2C_STATE_READY: i2c_handle[id].state = state; break; case I2C_STATE_BUSY_TX: case I2C_STATE_BUSY_RX: case I2C_STATE_BUSY_TX_RX: ret = DRV_BUSY; break; case I2C_STATE_RESET: case I2C_STATE_TIMEOUT: case I2C_STATE_ERROR: ret = DRV_ERROR; break; } int_unlock(lock); return ret; } static void i2c_state_clear(enum I2C_DEV_T id, enum I2C_STATE_T state) { uint32_t lock = int_lock(); // update state i2c_handle[id].state &= ~state; if (i2c_handle[id].state == 0) { i2c_handle[id].state = I2C_STATE_READY; } int_unlock(lock); } enum I2C_STATE_T i2c_state_get(enum I2C_DEV_T id) { return i2c_handle[id].state; } int i2c_open(enum I2C_DEV_T id, struct I2C_CFG_T *config) { if (id >= I2C_MAX_NUM) { return DRV_ERROR; } else { // enable I2C clock clock_enable(CLOCK_I2C); // reset I2C module reset_module(RESET_MODULE_I2C); } if (config) { memcpy(&i2c_handle[id].config, config, sizeof(struct I2C_CFG_T)); } // disable device i2c_handle[id].base->CTRL0 &= ~I2C_CTRL0_ENABLE_MSK; // disable all interrupt i2c_handle[id].base->INTR_EN = 0; i2c_handle[id].base->INTR_CLR = I2C_INTR_CLR_ALL_MSK; // reset device handle i2c_handle[id].tx_buff = NULL; i2c_handle[id].tx_callback = NULL; i2c_handle[id].tx_count = 0; i2c_handle[id].tx_size = 0; i2c_handle[id].rx_buff = NULL; i2c_handle[id].rx_callback = NULL; i2c_handle[id].rx_count = 0; i2c_handle[id].rx_size = 0; // speed i2c_clock_setup(id, clock_get_frequency(CLOCK_APB_CLK)); uint32_t val = 0; if (i2c_handle[id].config.mode == I2C_MASTER) { val = I2C_CTRL1_MASTER_MODE_MSK | (i2c_handle[id].config.speed_mode) | (i2c_handle[id].config.addr_mode ? I2C_CTRL1_10BITADDR_MASTER_MSK : 0) | I2C_CTRL1_RESTART_EN_MSK | I2C_CTRL1_SLAVE_DISABLE_MSK; // set target address i2c_handle[id].base->ADDR = i2c_handle[id].config.target_addr; } else { val = (i2c_handle[id].config.speed_mode) | (i2c_handle[id].config.addr_mode ? I2C_CTRL1_10BITADDR_SLAVE_MSK : 0); // set slave address i2c_handle[id].base->ADDR = I2C_ADDR_SLV_ADDR(i2c_handle[id].config.local_addr); } i2c_handle[id].base->CTRL1 = val; // FIFO trigger level i2c_handle[id].base->FIFO_TH = I2C_FIFO_TH_TX_TL(i2c_handle[id].config.tx_level) | I2C_FIFO_TH_RX_TL(i2c_handle[id].config.rx_level); // enable device i2c_handle[id].base->CTRL0 |= I2C_CTRL0_ENABLE_MSK; #if I2C_INT_MODE_EN // enable IRQ if (i2c_handle[id].config.int_tx || i2c_handle[id].config.int_rx) { NVIC_SetPriority(i2c_handle[id].irq, IRQ_PRIORITY_NORMAL); NVIC_ClearPendingIRQ(i2c_handle[id].irq); NVIC_EnableIRQ(i2c_handle[id].irq); } #endif i2c_handle[id].state = I2C_STATE_READY; return DRV_OK; } int i2c_close(enum I2C_DEV_T id) { if (id >= I2C_MAX_NUM) { return DRV_ERROR; } #if I2C_INT_MODE_EN if (i2c_handle[id].config.int_tx || i2c_handle[id].config.int_rx) { NVIC_DisableIRQ(i2c_handle[id].irq); NVIC_ClearPendingIRQ(i2c_handle[id].irq); } #endif // disable device i2c_handle[id].base->CTRL0 &= ~I2C_CTRL0_ENABLE_MSK; // disable all interrupt i2c_handle[id].base->INTR_EN &= ~I2C_INTR_ALL_MSK; i2c_handle[id].base->INTR_CLR = I2C_INTR_CLR_ALL_MSK; // disable I2C clock clock_disable(CLOCK_I2C); i2c_handle[id].state = I2C_STATE_RESET; return DRV_OK; } int i2c_master_send(enum I2C_DEV_T id, uint16_t dev_addr, uint8_t *tx_buf, uint32_t len, drv_callback_t callback) { if ((tx_buf == 0) || (len == 0)) { return DRV_ERROR; } // update state int ret = i2c_state_set(id, I2C_STATE_BUSY_TX); if (ret != DRV_OK) { return ret; } i2c_handle[id].tx_buff = tx_buf; i2c_handle[id].tx_count = 0; i2c_handle[id].tx_size = len; i2c_handle[id].tx_callback = callback; // update target address i2c_update_dev_addr(id, dev_addr); // check error i2c_check_error(id, (I2C_INTR_TX_ABRT_MSK | I2C_INTR_TX_OVER_MSK | I2C_INTR_RX_OVER_MSK | I2C_INTR_RX_UNDER_MSK)); if (i2c_handle[id].config.int_tx) { #if I2C_INT_MODE_EN i2c_handle[id].base->INTR_EN = I2C_INTR_TX_OVER_MSK | I2C_INTR_TX_EMPTY_MSK | I2C_INTR_TX_ABRT_MSK; #endif } else { #if I2C_POLL_MODE_EN // polling uint32_t restart = 0; uint32_t stop = 0; uint32_t err_code = 0; while (i2c_handle[id].tx_count < i2c_handle[id].tx_size) { if (i2c_handle[id].tx_count == 0) { restart = I2C_DATA_RESTART_MSK; } else { restart = 0; } if (i2c_handle[id].tx_count == i2c_handle[id].tx_size - 1) { stop = I2C_DATA_STOP_MSK; } else { stop = 0; } // wait for TFNF err_code = i2c_wait_status(id, I2C_STATUS1_TFNF_MSK, I2C_STATUS1_TFNF_MSK, I2C_WAIT_TFNF_CNT); if (err_code) { break; } i2c_handle[id].base->DATA = i2c_handle[id].tx_buff[i2c_handle[id].tx_count++] | restart | stop | I2C_DATA_CMD_WRITE; } // update state i2c_state_clear(id, I2C_STATE_BUSY_TX); if (i2c_handle[id].tx_callback) { i2c_handle[id].tx_callback(&id, err_code); } #endif } return ret; } int i2c_master_receive(enum I2C_DEV_T id, uint16_t dev_addr, uint8_t *rx_buf, uint32_t len, drv_callback_t callback) { if ((rx_buf == 0) || (len == 0)) { return DRV_ERROR; } // update state int ret = i2c_state_set(id, I2C_STATE_BUSY_RX); if (ret != DRV_OK) { return ret; } i2c_handle[id].rx_buff = rx_buf; i2c_handle[id].rx_count = 0; i2c_handle[id].rx_size = len; i2c_handle[id].rx_callback = callback; // send dummy i2c_handle[id].tx_count = 0; // update target address i2c_update_dev_addr(id, dev_addr); // clear FIFO uint32_t residual = GET_BIT_FIELD(i2c_handle[id].base->STATUS1, I2C_STATUS1_RXFL_MSK, I2C_STATUS1_RXFL_POS); if (residual) { for (uint32_t i = 0; i < residual; i++) { i2c_handle[id].base->DATA; } } // wait until txfifo empty i2c_wait_status(id, I2C_STATUS1_TFE_MSK, I2C_STATUS1_TFE_MSK, I2C_WAIT_TFE_CNT); // check error i2c_check_error(id, (I2C_INTR_TX_ABRT_MSK | I2C_INTR_TX_OVER_MSK | I2C_INTR_RX_OVER_MSK | I2C_INTR_RX_UNDER_MSK)); if (i2c_handle[id].config.int_rx) { #if I2C_INT_MODE_EN // set rx fifo threshold if necessary if (i2c_handle[id].rx_size <= i2c_handle[id].config.rx_level) { i2c_handle[id].base->FIFO_TH = SET_BIT_FIELD(i2c_handle[id].base->FIFO_TH, I2C_FIFO_TH_RX_TL_MSK, I2C_FIFO_TH_RX_TL_POS, (i2c_handle[id].rx_size - 1)); } // enable interrupts i2c_handle[id].base->INTR_EN = I2C_INTR_RX_UNDER_MSK | I2C_INTR_RX_OVER_MSK | I2C_INTR_RX_FULL_MSK | I2C_INTR_TX_OVER_MSK | I2C_INTR_TX_EMPTY_MSK | I2C_INTR_RD_REQ_MSK | I2C_INTR_TX_ABRT_MSK | I2C_INTR_RX_DONE_MSK | I2C_INTR_GEN_CALL_MSK; #endif } else { #if I2C_POLL_MODE_EN // polling uint32_t restart = 0; uint32_t stop = 0; uint32_t err_code = 0; while (i2c_handle[id].rx_count < i2c_handle[id].rx_size) { while ((i2c_handle[id].tx_count < i2c_handle[id].rx_size) && (i2c_handle[id].base->STATUS1 & I2C_STATUS1_TFNF_MSK)) { if (i2c_handle[id].tx_count == 0) { restart = I2C_DATA_RESTART_MSK; } else { restart = 0; } if (i2c_handle[id].tx_count == i2c_handle[id].rx_size - 1) { stop = I2C_DATA_STOP_MSK; } else { stop = 0; } i2c_handle[id].base->DATA = restart | stop | I2C_DATA_CMD_READ; i2c_handle[id].tx_count++; } // wait for RFNE err_code = i2c_wait_status(id, I2C_STATUS1_RFNE_MSK, I2C_STATUS1_RFNE_MSK, I2C_WAIT_RFNE_CNT); if (err_code) { break; } i2c_handle[id].rx_buff[i2c_handle[id].rx_count++] = (uint8_t)i2c_handle[id].base->DATA; } // update state i2c_state_clear(id, I2C_STATE_BUSY_RX); if (i2c_handle[id].rx_callback) { i2c_handle[id].rx_callback(&id, err_code); } #endif } return ret; } // limitation: int_tx == int_rx int i2c_master_transfer(enum I2C_DEV_T id, uint16_t dev_addr, uint8_t *tx_buf, uint32_t tx_len, uint8_t *rx_buf, uint32_t rx_len, drv_callback_t callback) { uint32_t lock; if ((tx_buf == 0) || (tx_len == 0) || (rx_buf == 0) || (rx_len == 0)) { return DRV_ERROR; } if (i2c_handle[id].config.int_rx != i2c_handle[id].config.int_tx) { return DRV_ERROR; } // update state int ret = i2c_state_set(id, I2C_STATE_BUSY_TX_RX); if (ret != DRV_OK) { return ret; } i2c_handle[id].tx_buff = tx_buf; i2c_handle[id].tx_count = 0; i2c_handle[id].tx_size = tx_len; i2c_handle[id].tx_callback = NULL; i2c_handle[id].rx_buff = rx_buf; i2c_handle[id].rx_count = 0; i2c_handle[id].rx_size = rx_len; i2c_handle[id].rx_callback = callback; // update target address i2c_update_dev_addr(id, dev_addr); // check error i2c_check_error(id, (I2C_INTR_TX_ABRT_MSK | I2C_INTR_TX_OVER_MSK | I2C_INTR_RX_OVER_MSK | I2C_INTR_RX_UNDER_MSK)); if (i2c_handle[id].config.int_tx) { #if I2C_INT_MODE_EN // set rx fifo threshold if necessary if (i2c_handle[id].rx_size <= i2c_handle[id].config.rx_level) { i2c_handle[id].base->FIFO_TH = SET_BIT_FIELD(i2c_handle[id].base->FIFO_TH, I2C_FIFO_TH_RX_TL_MSK, I2C_FIFO_TH_RX_TL_POS, (i2c_handle[id].rx_size - 1)); } i2c_handle[id].base->INTR_EN = I2C_INTR_RX_UNDER_MSK | I2C_INTR_RX_OVER_MSK | I2C_INTR_RX_FULL_MSK | I2C_INTR_TX_OVER_MSK | I2C_INTR_TX_EMPTY_MSK | I2C_INTR_RD_REQ_MSK | I2C_INTR_TX_ABRT_MSK | I2C_INTR_RX_DONE_MSK | I2C_INTR_GEN_CALL_MSK; #endif } else { #if I2C_POLL_MODE_EN // polling uint32_t restart = 0; uint32_t stop = 0; uint32_t err_code = 0; while (i2c_handle[id].tx_count < i2c_handle[id].tx_size) { if (i2c_handle[id].tx_count == 0) { restart = I2C_DATA_RESTART_MSK; } else { restart = 0; } stop = 0; // wait for TFNF err_code = i2c_wait_status(id, I2C_STATUS1_TFNF_MSK, I2C_STATUS1_TFNF_MSK, I2C_WAIT_TFNF_CNT); if (err_code) { goto _i2c_exit; } i2c_handle[id].base->DATA = i2c_handle[id].tx_buff[i2c_handle[id].tx_count++] | restart | stop | I2C_DATA_CMD_WRITE; } lock = int_lock(); // update state i2c_handle[id].state = I2C_STATE_BUSY_RX; i2c_handle[id].tx_count = 0; int_unlock(lock); while (i2c_handle[id].rx_count < i2c_handle[id].rx_size) { while ((i2c_handle[id].tx_count < i2c_handle[id].rx_size) && (i2c_handle[id].base->STATUS1 & I2C_STATUS1_TFNF_MSK)) { if (i2c_handle[id].tx_count == 0) { restart = I2C_DATA_RESTART_MSK; } else { restart = 0; } if (i2c_handle[id].tx_count == i2c_handle[id].rx_size - 1) { stop = I2C_DATA_STOP_MSK; } else { stop = 0; } i2c_handle[id].base->DATA = restart | stop | I2C_DATA_CMD_READ; i2c_handle[id].tx_count++; } // wait for RFNE err_code = i2c_wait_status(id, I2C_STATUS1_RFNE_MSK, I2C_STATUS1_RFNE_MSK, I2C_WAIT_RFNE_CNT); if (err_code) { break; } i2c_handle[id].rx_buff[i2c_handle[id].rx_count++] = (uint8_t)i2c_handle[id].base->DATA; } _i2c_exit: // update state i2c_handle[id].state = I2C_STATE_READY; if (i2c_handle[id].rx_callback) { i2c_handle[id].rx_callback(&id, err_code); } #endif } return ret; } int i2c_slave_send(enum I2C_DEV_T id, uint8_t *tx_buf, uint32_t len, drv_callback_t callback) { if ((tx_buf == 0) || (len == 0)) { return DRV_ERROR; } // update state int ret = i2c_state_set(id, I2C_STATE_BUSY_TX); if (ret != DRV_OK) { return ret; } i2c_handle[id].tx_buff = tx_buf; i2c_handle[id].tx_count = 0; i2c_handle[id].tx_size = len; i2c_handle[id].tx_callback = callback; // check error i2c_check_error(id, (I2C_INTR_TX_ABRT_MSK | I2C_INTR_TX_OVER_MSK | I2C_INTR_RX_OVER_MSK | I2C_INTR_RX_UNDER_MSK)); if (i2c_handle[id].config.int_tx) { #if I2C_INT_MODE_EN // enable interrupts i2c_handle[id].base->INTR_EN = I2C_INTR_RX_UNDER_MSK | I2C_INTR_RX_OVER_MSK | I2C_INTR_RX_FULL_MSK | I2C_INTR_TX_OVER_MSK | I2C_INTR_RD_REQ_MSK | I2C_INTR_RX_DONE_MSK | I2C_INTR_GEN_CALL_MSK; // Note: tx_empty is not enabled here as rd_req is the signal used to write the next byte of data to the tx fifo. #endif } else { #if I2C_POLL_MODE_EN // polling uint32_t err_code = 0; while (i2c_handle[id].tx_count < i2c_handle[id].tx_size) { // wait for read request while ((i2c_handle[id].base->STATUS0 & I2C_INTR_RD_REQ_MSK) == 0) { } i2c_handle[id].base->DATA = i2c_handle[id].tx_buff[i2c_handle[id].tx_count++]; i2c_handle[id].base->INTR_CLR = I2C_INTR_CLR_RD_REQ_MSK; } // wait RX done while ((i2c_handle[id].base->STATUS0 & I2C_INTR_RX_DONE_MSK) == 0) { } i2c_handle[id].base->INTR_CLR = I2C_INTR_RX_DONE_MSK; // update state i2c_state_clear(id, I2C_STATE_BUSY_TX); if (i2c_handle[id].tx_callback) { i2c_handle[id].tx_callback(&id, err_code); } #endif } return ret; } int i2c_slave_receive(enum I2C_DEV_T id, uint8_t *rx_buf, uint32_t len, drv_callback_t callback) { if ((rx_buf == 0) || (len == 0)) { return DRV_ERROR; } // update state int ret = i2c_state_set(id, I2C_STATE_BUSY_RX); if (ret != DRV_OK) { return ret; } i2c_handle[id].rx_buff = rx_buf; i2c_handle[id].rx_count = 0; i2c_handle[id].rx_size = len; i2c_handle[id].rx_callback = callback; // check error i2c_check_error(id, (I2C_INTR_TX_ABRT_MSK | I2C_INTR_TX_OVER_MSK | I2C_INTR_RX_OVER_MSK | I2C_INTR_RX_UNDER_MSK)); if (i2c_handle[id].config.int_rx) { #if I2C_INT_MODE_EN // set rx fifo threshold if necessary if (i2c_handle[id].rx_size <= i2c_handle[id].config.rx_level) { i2c_handle[id].base->FIFO_TH = SET_BIT_FIELD(i2c_handle[id].base->FIFO_TH, I2C_FIFO_TH_RX_TL_MSK, I2C_FIFO_TH_RX_TL_POS, (i2c_handle[id].rx_size - 1)); } // enable interrupts i2c_handle[id].base->INTR_EN = I2C_INTR_RX_UNDER_MSK | I2C_INTR_RX_OVER_MSK | I2C_INTR_RX_FULL_MSK | I2C_INTR_TX_OVER_MSK | I2C_INTR_RD_REQ_MSK | I2C_INTR_RX_DONE_MSK | I2C_INTR_GEN_CALL_MSK; #endif } else { #if I2C_POLL_MODE_EN // polling uint32_t err_code = 0; while (i2c_handle[id].rx_count < i2c_handle[id].rx_size) { // wait for RFNE if (i2c_handle[id].base->STATUS1 & I2C_STATUS1_RFNE_MSK) { i2c_handle[id].rx_buff[i2c_handle[id].rx_count++] = (uint8_t)i2c_handle[id].base->DATA; } else { // transmission is done err_code = i2c_handle[id].base->STATUS0 & (I2C_INTR_STOP_DET_MSK | I2C_INTR_TX_ABRT_MSK); if (err_code) { break; } } } // update state i2c_state_clear(id, I2C_STATE_BUSY_RX); if (i2c_handle[id].rx_callback) { i2c_handle[id].rx_callback(&id, err_code); } #endif } return ret; } #if I2C_INT_MODE_EN static void i2c_irq_handler(enum I2C_DEV_T id) { drv_callback_t usr_callback = NULL; // what caused the interrupt? uint32_t int_stat = i2c_handle[id].base->INTR_STATUS; if (int_stat & (I2C_INTR_TX_ABRT_MSK | I2C_INTR_TX_OVER_MSK | I2C_INTR_RX_OVER_MSK | I2C_INTR_RX_UNDER_MSK)) { // mask tx empty interrupt i2c_handle[id].base->INTR_EN &= ~I2C_INTR_TX_EMPTY_MSK; if (int_stat & I2C_INTR_TX_ABRT_MSK) { i2c_handle[id].base->INTR_CLR = I2C_INTR_CLR_TX_ABRT_MSK; } else if (int_stat & I2C_INTR_TX_OVER_MSK) { i2c_handle[id].base->INTR_CLR = I2C_INTR_CLR_TX_OVER_MSK; } else if (int_stat & I2C_INTR_RX_OVER_MSK) { i2c_handle[id].base->INTR_CLR = I2C_INTR_CLR_RX_OVER_MSK; } else if (int_stat & I2C_INTR_RX_UNDER_MSK) { i2c_handle[id].base->INTR_CLR = I2C_INTR_CLR_RX_UNDER_MSK; } // TODO: user handle error exception if (i2c_handle[id].state & I2C_STATE_BUSY_TX) { usr_callback = i2c_handle[id].tx_callback; } else if (i2c_handle[id].state & I2C_STATE_BUSY_RX) { usr_callback = i2c_handle[id].rx_callback; } // update state - an error has occurred i2c_handle[id].state = I2C_STATE_READY; } else if (int_stat & I2C_INTR_GEN_CALL_MSK) { i2c_handle[id].base->INTR_CLR = I2C_INTR_CLR_GEN_CALL_MSK; } else if (int_stat & I2C_INTR_RX_FULL_MSK) { // master-rx or slave-rx read FIFO if (i2c_handle[id].state & I2C_STATE_BUSY_RX) { uint8_t rx_len = GET_BIT_FIELD(i2c_handle[id].base->STATUS1, I2C_STATUS1_RXFL_MSK, I2C_STATUS1_RXFL_POS); while ((rx_len--) && (i2c_handle[id].rx_count < i2c_handle[id].rx_size)) { i2c_handle[id].rx_buff[i2c_handle[id].rx_count++] = (uint8_t)i2c_handle[id].base->DATA; } uint8_t rx_level = GET_BIT_FIELD(i2c_handle[id].base->FIFO_TH, I2C_FIFO_TH_RX_TL_MSK, I2C_FIFO_TH_RX_TL_POS); if (i2c_handle[id].rx_count == i2c_handle[id].rx_size) { // RX done usr_callback = i2c_handle[id].rx_callback; // restore rx threshold i2c_handle[id].base->FIFO_TH = SET_BIT_FIELD(i2c_handle[id].base->FIFO_TH, I2C_FIFO_TH_RX_TL_MSK, I2C_FIFO_TH_RX_TL_POS, i2c_handle[id].config.rx_level); // update state i2c_handle[id].state = I2C_STATE_READY; i2c_handle[id].rx_buff = NULL; i2c_handle[id].rx_callback = NULL; i2c_handle[id].rx_count = 0; i2c_handle[id].rx_size = 0; int_stat = 0; } else if ((i2c_handle[id].rx_size - i2c_handle[id].rx_count) <= rx_level) { // set rx fifo threshold if necessary i2c_handle[id].base->FIFO_TH = SET_BIT_FIELD(i2c_handle[id].base->FIFO_TH, I2C_FIFO_TH_RX_TL_MSK, I2C_FIFO_TH_RX_TL_POS, (i2c_handle[id].rx_size - i2c_handle[id].rx_count - 1)); } } } else if (int_stat & I2C_INTR_RX_DONE_MSK) { // slave-tx done if (i2c_handle[id].state & I2C_STATE_BUSY_TX) { usr_callback = i2c_handle[id].tx_callback; // update state i2c_handle[id].state = I2C_STATE_READY; i2c_handle[id].tx_buff = NULL; i2c_handle[id].tx_callback = NULL; int_stat = 0; } i2c_handle[id].base->INTR_CLR = I2C_INTR_CLR_RX_DONE_MSK; } else if (int_stat & I2C_INTR_RD_REQ_MSK) { // slave-tx write FIFO if (i2c_handle[id].state & I2C_STATE_BUSY_TX) { // tx buffer has all been sent in bulk mode yet the // master is still requesting more data. if (i2c_handle[id].tx_count == i2c_handle[id].tx_size) { // TX done usr_callback = i2c_handle[id].tx_callback; // update state i2c_handle[id].state = I2C_STATE_READY; i2c_handle[id].tx_buff = NULL; i2c_handle[id].tx_callback = NULL; i2c_handle[id].tx_count = 0; i2c_handle[id].tx_size = 0; int_stat = 0; } else { // TX continue // write data to FIFO while ((i2c_handle[id].tx_count < i2c_handle[id].tx_size) && (i2c_handle[id].base->STATUS1 & I2C_STATUS1_TFNF_MSK)) { i2c_handle[id].base->DATA = i2c_handle[id].tx_buff[i2c_handle[id].tx_count++]; } } } i2c_handle[id].base->INTR_CLR = I2C_INTR_CLR_RD_REQ_MSK; } else if (int_stat & I2C_INTR_TX_EMPTY_MSK) { // master-tx write FIFO if (i2c_handle[id].state & I2C_STATE_BUSY_TX) { if (i2c_handle[id].tx_count == i2c_handle[id].tx_size) { // TX done usr_callback = i2c_handle[id].tx_callback; // update state if (i2c_handle[id].state & I2C_STATE_BUSY_RX) { i2c_handle[id].state = I2C_STATE_BUSY_RX; } else { // mask tx empty interrupt i2c_handle[id].base->INTR_EN &= ~I2C_INTR_TX_EMPTY_MSK; i2c_handle[id].state = I2C_STATE_READY; } i2c_handle[id].tx_buff = NULL; i2c_handle[id].tx_callback = NULL; i2c_handle[id].tx_count = 0; i2c_handle[id].tx_size = 0; int_stat = 0; } else { // TX continue uint32_t restart = 0; uint32_t stop = 0; // write data to FIFO while ((i2c_handle[id].tx_count < i2c_handle[id].tx_size) && (i2c_handle[id].base->STATUS1 & I2C_STATUS1_TFNF_MSK)) { if (i2c_handle[id].tx_count == 0) { restart = I2C_DATA_RESTART_MSK; } else { restart = 0; } // trx mode will skip stop phase if ((i2c_handle[id].state == I2C_STATE_BUSY_TX) && (i2c_handle[id].tx_count == i2c_handle[id].tx_size - 1)) { stop = I2C_DATA_STOP_MSK; } else { stop = 0; } i2c_handle[id].base->DATA = i2c_handle[id].tx_buff[i2c_handle[id].tx_count++] | restart | stop | I2C_DATA_CMD_WRITE; } } } // master-rx write FIFO else if (i2c_handle[id].state & I2C_STATE_BUSY_RX) { if (i2c_handle[id].tx_count == i2c_handle[id].rx_size) { // TX done // mask tx empty interrupt i2c_handle[id].base->INTR_EN &= ~I2C_INTR_TX_EMPTY_MSK; } else { // TX continue uint32_t restart = 0; uint32_t stop = 0; // write data to FIFO while ((i2c_handle[id].tx_count < i2c_handle[id].rx_size) && (i2c_handle[id].base->STATUS1 & I2C_STATUS1_TFNF_MSK)) { if (i2c_handle[id].tx_count == 0) { restart = I2C_DATA_RESTART_MSK; } else { restart = 0; } if (i2c_handle[id].tx_count == i2c_handle[id].rx_size - 1) { stop = I2C_DATA_STOP_MSK; } else { stop = 0; } i2c_handle[id].base->DATA = restart | stop | I2C_DATA_CMD_READ; i2c_handle[id].tx_count++; } if (i2c_handle[id].tx_count == i2c_handle[id].rx_size) { // TX done // mask tx empty interrupt i2c_handle[id].base->INTR_EN &= ~I2C_INTR_TX_EMPTY_MSK; } } } } else if (int_stat & I2C_INTR_START_DET_MSK) { i2c_handle[id].base->INTR_CLR = I2C_INTR_CLR_START_DET_MSK; } else if (int_stat & I2C_INTR_STOP_DET_MSK) { i2c_handle[id].base->INTR_CLR = I2C_INTR_CLR_STOP_DET_MSK; } else if (int_stat & I2C_INTR_ACTIVITY_MSK) { i2c_handle[id].base->INTR_CLR = I2C_INTR_CLR_ACTIVITY_MSK; } else { // it may reach here since FIFO was read empty by last interrupt } if (usr_callback) { usr_callback(&id, int_stat); } } #endif void I2C0_IRQHandler(void) { #if I2C_INT_MODE_EN i2c_irq_handler(I2C_ID0); #endif }