/*
|
* 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
|
}
|