rt-thread/bsp/hc32/libraries/hc32_drivers/drv_i2c.c

588 lines
18 KiB
C

/*
* Copyright (C) 2022-2024, Xiaohua Semiconductor Co., Ltd.
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-04-28 CDT first version
*/
/*******************************************************************************
* Include files
******************************************************************************/
#include <rtthread.h>
#include <rthw.h>
#ifdef RT_USING_I2C
#if defined(BSP_USING_I2C1) || defined(BSP_USING_I2C2) || defined(BSP_USING_I2C3) || \
defined(BSP_USING_I2C4) || defined(BSP_USING_I2C5) || defined(BSP_USING_I2C6)
#include "drv_i2c.h"
/*******************************************************************************
* Local type definitions ('typedef')
******************************************************************************/
/*******************************************************************************
* Local pre-processor symbols/macros ('#define')
******************************************************************************/
#ifndef HC32_I2C_DEBUG
#define I2C_PRINT_DBG(fmt, args...)
#define I2C_PRINT_ERR(fmt, args...) rt_kprintf(fmt, ##args);
#else
#define I2C_PRINT_DBG(fmt, args...) rt_kprintf(fmt, ##args);
#define I2C_PRINT_ERR(fmt, args...) rt_kprintf(fmt, ##args);
#endif
#define I2C_TIMEOUT ((uint32_t)0x10000)
#define FCG_I2C_CLK FCG_Fcg1PeriphClockCmd
/*******************************************************************************
* Global variable definitions (declared in header file with 'extern')
******************************************************************************/
extern rt_err_t rt_hw_board_i2c_init(CM_I2C_TypeDef *I2Cx);
/*******************************************************************************
* Local function prototypes ('static')
******************************************************************************/
enum
{
#ifdef BSP_USING_I2C1
I2C1_INDEX,
#endif
#ifdef BSP_USING_I2C2
I2C2_INDEX,
#endif
#ifdef BSP_USING_I2C3
I2C3_INDEX,
#endif
#ifdef BSP_USING_I2C4
I2C4_INDEX,
#endif
#ifdef BSP_USING_I2C5
I2C5_INDEX,
#endif
#ifdef BSP_USING_I2C6
I2C6_INDEX,
#endif
};
static struct hc32_i2c_config i2c_config[] =
{
#ifdef BSP_USING_I2C1
I2C1_CONFIG,
#endif
#ifdef BSP_USING_I2C2
I2C2_CONFIG,
#endif
#ifdef BSP_USING_I2C3
I2C3_CONFIG,
#endif
#ifdef BSP_USING_I2C4
I2C4_CONFIG,
#endif
#ifdef BSP_USING_I2C5
I2C5_CONFIG,
#endif
#ifdef BSP_USING_I2C6
I2C6_CONFIG,
#endif
};
static void hc32_i2c_dma_configure(struct rt_i2c_bus_device *bus);
static struct hc32_i2c i2c_objs[sizeof(i2c_config) / sizeof(i2c_config[0])] = {0};
/*******************************************************************************
* Function implementation - global ('extern') and local ('static')
******************************************************************************/
static rt_err_t hc32_i2c_configure(struct rt_i2c_bus_device *bus)
{
int ret = -RT_ERROR;
stc_i2c_init_t i2c_init;
float32_t f32Error = 0.0F;
rt_uint32_t I2cSrcClk;
rt_uint32_t I2cClkDiv;
rt_uint32_t I2cClkDivReg;
RT_ASSERT(RT_NULL != bus);
struct hc32_i2c *i2c_obj = rt_container_of(bus, struct hc32_i2c, i2c_bus);
/* Enable I2C clock */
FCG_I2C_CLK(i2c_obj->config->clock, ENABLE);
if (RT_EOK != rt_hw_board_i2c_init(i2c_obj->config->Instance))
{
return -RT_ERROR;
}
I2C_DeInit(i2c_obj->config->Instance);
I2cSrcClk = I2C_SRC_CLK;
I2cClkDiv = I2cSrcClk / i2c_obj->config->baudrate / I2C_WIDTH_MAX_IMME;
for (I2cClkDivReg = I2C_CLK_DIV1; I2cClkDivReg <= I2C_CLK_DIV128; I2cClkDivReg++)
{
if (I2cClkDiv < (1UL << I2cClkDivReg))
{
break;
}
}
i2c_init.u32ClockDiv = I2cClkDivReg;
i2c_init.u32SclTime = 400UL * I2cSrcClk / (1UL << I2cClkDivReg) / 1000000000UL; /* SCL time is about 400nS in EVB board */
i2c_init.u32Baudrate = i2c_obj->config->baudrate;
ret = I2C_Init(i2c_obj->config->Instance, &i2c_init, &f32Error);
if (RT_EOK == ret)
{
I2C_BusWaitCmd(i2c_obj->config->Instance, ENABLE);
I2C_Cmd(i2c_obj->config->Instance, ENABLE);
}
if ((i2c_obj->i2c_dma_flag & I2C_USING_TX_DMA_FLAG) || (i2c_obj->i2c_dma_flag & I2C_USING_RX_DMA_FLAG))
{
hc32_i2c_dma_configure(bus);
}
return ret;
}
static void hc32_hw_i2c_reset(struct hc32_i2c *i2c_obj)
{
I2C_SWResetCmd(i2c_obj->config->Instance, ENABLE);
I2C_SWResetCmd(i2c_obj->config->Instance, DISABLE);
}
static int hc32_hw_i2c_start(struct hc32_i2c *i2c_obj)
{
if (LL_OK != I2C_Start(i2c_obj->config->Instance, i2c_obj->config->timeout))
{
return -RT_ERROR;
}
return RT_EOK;
}
static int hc32_hw_i2c_restart(struct hc32_i2c *i2c_obj)
{
if (LL_OK != I2C_Restart(i2c_obj->config->Instance, i2c_obj->config->timeout))
{
return -RT_ERROR;
}
return RT_EOK;
}
static int hc32_hw_i2c_send_addr(struct hc32_i2c *i2c_obj,
struct rt_i2c_msg *msg)
{
rt_uint8_t dir = ((msg->flags & RT_I2C_RD) == RT_I2C_RD) ? (I2C_DIR_RX) : (I2C_DIR_TX);
if (LL_OK != I2C_TransAddr(i2c_obj->config->Instance, msg->addr, dir, i2c_obj->config->timeout))
{
return -RT_ERROR;
}
return RT_EOK;
}
static int hc32_hw_i2c_stop(struct hc32_i2c *i2c_obj)
{
if (LL_OK != I2C_Stop(i2c_obj->config->Instance, i2c_obj->config->timeout))
{
return -RT_ERROR;
}
return RT_EOK;
}
static void hc32_i2c_get_dma_info(void)
{
#ifdef BSP_I2C1_TX_USING_DMA
static struct dma_config i2c1_tx_dma = I2C1_TX_DMA_CONFIG;
i2c_objs[I2C1_INDEX].i2c_dma_flag |= I2C_USING_TX_DMA_FLAG;
i2c_config[I2C1_INDEX].i2c_tx_dma = &i2c1_tx_dma;
#endif
#ifdef BSP_I2C1_RX_USING_DMA
static struct dma_config i2c1_rx_dma = I2C1_RX_DMA_CONFIG;
i2c_objs[I2C1_INDEX].i2c_dma_flag |= I2C_USING_RX_DMA_FLAG;
i2c_config[I2C1_INDEX].i2c_rx_dma = &i2c1_rx_dma;
#endif
#ifdef BSP_I2C2_TX_USING_DMA
static struct dma_config i2c2_tx_dma = I2C2_TX_DMA_CONFIG;
i2c_objs[I2C2_INDEX].i2c_dma_flag |= I2C_USING_TX_DMA_FLAG;
i2c_config[I2C2_INDEX].i2c_tx_dma = &i2c2_tx_dma;
#endif
#ifdef BSP_I2C2_RX_USING_DMA
static struct dma_config i2c2_rx_dma = I2C2_RX_DMA_CONFIG;
i2c_objs[I2C2_INDEX].i2c_dma_flag |= I2C_USING_RX_DMA_FLAG;
i2c_config[I2C2_INDEX].i2c_rx_dma = &i2c2_rx_dma;
#endif
#ifdef BSP_I2C3_TX_USING_DMA
static struct dma_config i2c3_tx_dma = I2C3_TX_DMA_CONFIG;
i2c_objs[I2C3_INDEX].i2c_dma_flag |= I2C_USING_TX_DMA_FLAG;
i2c_config[I2C3_INDEX].i2c_tx_dma = &i2c3_tx_dma;
#endif
#ifdef BSP_I2C3_RX_USING_DMA
static struct dma_config i2c3_rx_dma = I2C3_RX_DMA_CONFIG;
i2c_objs[I2C3_INDEX].i2c_dma_flag |= I2C_USING_RX_DMA_FLAG;
i2c_config[I2C3_INDEX].i2c_rx_dma = &i2c3_rx_dma;
#endif
#ifdef BSP_I2C4_TX_USING_DMA
static struct dma_config i2c4_tx_dma = I2C4_TX_DMA_CONFIG;
i2c_objs[I2C4_INDEX].i2c_dma_flag |= I2C_USING_TX_DMA_FLAG;
i2c_config[I2C4_INDEX].i2c_tx_dma = &i2c4_tx_dma;
#endif
#ifdef BSP_I2C4_RX_USING_DMA
static struct dma_config i2c4_rx_dma = I2C4_RX_DMA_CONFIG;
i2c_objs[I2C4_INDEX].i2c_dma_flag |= I2C_USING_RX_DMA_FLAG;
i2c_config[I2C4_INDEX].i2c_rx_dma = &i2c4_rx_dma;
#endif
#ifdef BSP_I2C5_TX_USING_DMA
static struct dma_config i2c5_tx_dma = I2C5_TX_DMA_CONFIG;
i2c_objs[I2C5_INDEX].i2c_dma_flag |= I2C_USING_TX_DMA_FLAG;
i2c_config[I2C5_INDEX].i2c_tx_dma = &i2c5_tx_dma;
#endif
#ifdef BSP_I2C5_RX_USING_DMA
static struct dma_config i2c5_rx_dma = I2C5_RX_DMA_CONFIG;
i2c_objs[I2C5_INDEX].i2c_dma_flag |= I2C_USING_RX_DMA_FLAG;
i2c_config[I2C5_INDEX].i2c_rx_dma = &i2c5_rx_dma;
#endif
#ifdef BSP_I2C6_TX_USING_DMA
static struct dma_config i2c6_tx_dma = I2C6_TX_DMA_CONFIG;
i2c_objs[I2C6_INDEX].i2c_dma_flag |= I2C_USING_TX_DMA_FLAG;
i2c_config[I2C6_INDEX].i2c_tx_dma = &i2c6_tx_dma;
#endif
#ifdef BSP_I2C6_RX_USING_DMA
static struct dma_config i2c6_rx_dma = I2C6_RX_DMA_CONFIG;
i2c_objs[I2C6_INDEX].i2c_dma_flag |= I2C_USING_RX_DMA_FLAG;
i2c_config[I2C6_INDEX].i2c_rx_dma = &i2c6_rx_dma;
#endif
}
static void hc32_i2c_dma_configure(struct rt_i2c_bus_device *bus)
{
stc_dma_init_t stcDmaInit;
struct hc32_i2c *i2c_obj = rt_container_of(bus, struct hc32_i2c, i2c_bus);
if (i2c_obj->i2c_dma_flag & I2C_USING_TX_DMA_FLAG)
{
/* DMA/AOS FCG enable */
FCG_Fcg0PeriphClockCmd(i2c_obj->config->i2c_tx_dma->clock, ENABLE);
(void)DMA_StructInit(&stcDmaInit);
stcDmaInit.u32BlockSize = 1UL;
stcDmaInit.u32TransCount = 0UL;
stcDmaInit.u32DataWidth = DMA_DATAWIDTH_8BIT;
/* Configure TX */
stcDmaInit.u32SrcAddrInc = DMA_SRC_ADDR_INC;
stcDmaInit.u32DestAddrInc = DMA_DEST_ADDR_FIX;
stcDmaInit.u32SrcAddr = (uint32_t)NULL;
stcDmaInit.u32DestAddr = (uint32_t)(&i2c_obj->config->Instance->DTR);
if (LL_OK != DMA_Init(i2c_obj->config->i2c_tx_dma->Instance, i2c_obj->config->i2c_tx_dma->channel, &stcDmaInit))
{
I2C_PRINT_ERR("[%s:%d]I2C TX DMA init error!\n", __func__, __LINE__);
}
AOS_SetTriggerEventSrc(i2c_obj->config->i2c_tx_dma->trigger_select, i2c_obj->config->i2c_tx_dma->trigger_event);
/* Enable DMA unit */
DMA_Cmd(i2c_obj->config->i2c_tx_dma->Instance, ENABLE);
}
if (i2c_obj->i2c_dma_flag & I2C_USING_RX_DMA_FLAG)
{
/* DMA/AOS FCG enable */
FCG_Fcg0PeriphClockCmd(i2c_obj->config->i2c_rx_dma->clock, ENABLE);
(void)DMA_StructInit(&stcDmaInit);
stcDmaInit.u32BlockSize = 1UL;
stcDmaInit.u32TransCount = 0UL;
stcDmaInit.u32DataWidth = DMA_DATAWIDTH_8BIT;
/* Configure RX */
stcDmaInit.u32SrcAddrInc = DMA_SRC_ADDR_FIX;
stcDmaInit.u32DestAddrInc = DMA_DEST_ADDR_INC;
stcDmaInit.u32SrcAddr = (uint32_t)(&i2c_obj->config->Instance->DRR);
stcDmaInit.u32DestAddr = (uint32_t)NULL;
if (LL_OK != DMA_Init(i2c_obj->config->i2c_rx_dma->Instance, i2c_obj->config->i2c_rx_dma->channel, &stcDmaInit))
{
I2C_PRINT_ERR("[%s:%d]I2C RX DMA init error!\n", __func__, __LINE__);
}
AOS_SetTriggerEventSrc(i2c_obj->config->i2c_rx_dma->trigger_select, i2c_obj->config->i2c_rx_dma->trigger_event);
/* Enable DMA unit */
DMA_Cmd(i2c_obj->config->i2c_rx_dma->Instance, ENABLE);
}
}
static int I2C_Master_Transmit_DMA(struct hc32_i2c *i2c_obj, struct rt_i2c_msg *msg)
{
rt_uint32_t timeCnt;
struct dma_config *i2c_tx_dma;
i2c_tx_dma = i2c_obj->config->i2c_tx_dma;
if (msg->len > 1U)
{
(void)DMA_SetTransCount(i2c_tx_dma->Instance, i2c_tx_dma->channel, msg->len - 1U);
(void)DMA_SetSrcAddr(i2c_tx_dma->Instance, i2c_tx_dma->channel, (uint32_t)(&msg->buf[1]));
(void)DMA_ChCmd(i2c_tx_dma->Instance, i2c_tx_dma->channel, ENABLE);
}
I2C_WriteData(i2c_obj->config->Instance, msg->buf[0]);
if (msg->len > 1U)
{
timeCnt = 0;
/* wait DMA transfer completed */
while (RESET == DMA_GetTransCompleteStatus(i2c_tx_dma->Instance, i2c_tx_dma->flag) && (timeCnt < i2c_obj->config->timeout))
{
rt_thread_mdelay(1);
timeCnt++;
}
if (timeCnt >= i2c_obj->config->timeout)
{
return -RT_ETIMEOUT;
}
}
/* wait last I2C data transfer completed */
timeCnt = 0;
while ((LL_OK != I2C_WaitStatus(i2c_obj->config->Instance, I2C_FLAG_TX_CPLT, SET, 1)) && (timeCnt < i2c_obj->config->timeout))
{
rt_thread_mdelay(1);
timeCnt++;
}
if (timeCnt >= i2c_obj->config->timeout)
{
return -RT_ETIMEOUT;
}
return RT_EOK;
}
static int I2C_Master_Receive_DMA(struct hc32_i2c *i2c_obj, struct rt_i2c_msg *msg)
{
rt_uint32_t timeCnt;
struct dma_config *i2c_rx_dma;
i2c_rx_dma = i2c_obj->config->i2c_rx_dma;
if (1UL == msg->len)
{
I2C_AckConfig(i2c_obj->config->Instance, I2C_NACK);
}
else if (msg->len > 2U)
{
(void)DMA_SetTransCount(i2c_rx_dma->Instance, i2c_rx_dma->channel, msg->len - 2U);
(void)DMA_SetDestAddr(i2c_rx_dma->Instance, i2c_rx_dma->channel, (uint32_t)(&msg->buf[0]));
(void)DMA_ChCmd(i2c_rx_dma->Instance, i2c_rx_dma->channel, ENABLE);
}
if (msg->len > 2U)
{
timeCnt = 0;
/* Wait DMA finish */
while ((RESET == DMA_GetTransCompleteStatus(i2c_rx_dma->Instance, i2c_rx_dma->flag)) && (timeCnt < i2c_obj->config->timeout))
{
/* Need add timeout release process here */
rt_thread_mdelay(1);
timeCnt++;
}
if (timeCnt >= i2c_obj->config->timeout)
{
return -RT_ETIMEOUT;
}
}
if (msg->len > 1U)
{
timeCnt = 0;
/* Wait data receive finish */
while ((LL_OK != I2C_WaitStatus(i2c_obj->config->Instance, I2C_FLAG_RX_FULL, SET, 1)) && (timeCnt < i2c_obj->config->timeout))
{
rt_thread_mdelay(1);
timeCnt++;
}
if (timeCnt >= i2c_obj->config->timeout)
{
return -RT_ETIMEOUT;
}
I2C_AckConfig(i2c_obj->config->Instance, I2C_NACK);
msg->buf[msg->len - 2U] = I2C_ReadData(i2c_obj->config->Instance);
}
timeCnt = 0;
/* Wait last data receive finish */
while ((LL_OK != I2C_WaitStatus(i2c_obj->config->Instance, I2C_FLAG_RX_FULL, SET, 1)) && (timeCnt < i2c_obj->config->timeout))
{
rt_thread_mdelay(1);
timeCnt++;
}
if (timeCnt >= i2c_obj->config->timeout)
{
return -RT_ETIMEOUT;
}
/* Stop before read last data */
I2C_ClearStatus(i2c_obj->config->Instance, I2C_FLAG_STOP);
I2C_GenerateStop(i2c_obj->config->Instance);
/* read data from register */
msg->buf[msg->len - 1U] = I2C_ReadData(i2c_obj->config->Instance);
timeCnt = 0;
while ((LL_OK != I2C_WaitStatus(i2c_obj->config->Instance, I2C_FLAG_STOP, SET, 1)) && (timeCnt < i2c_obj->config->timeout))
{
rt_thread_mdelay(1);
timeCnt++;
}
if (timeCnt >= i2c_obj->config->timeout)
{
return -RT_ETIMEOUT;
}
I2C_AckConfig(i2c_obj->config->Instance, I2C_ACK);
return RT_EOK;
}
static int I2C_Master_Transmit(struct hc32_i2c *i2c_obj,
struct rt_i2c_msg *msg)
{
return I2C_TransData(i2c_obj->config->Instance, msg->buf, msg->len, i2c_obj->config->timeout);
}
static int I2C_Master_Receive(struct hc32_i2c *i2c_obj,
struct rt_i2c_msg *msg)
{
if (msg->len == 1UL)
{
I2C_AckConfig(i2c_obj->config->Instance, I2C_NACK);
}
return I2C_MasterReceiveDataAndStop(i2c_obj->config->Instance, msg->buf, msg->len, i2c_obj->config->timeout);
}
static int hc32_i2c_write(struct hc32_i2c *i2c_obj,
struct rt_i2c_msg *msg)
{
int ret;
if (i2c_obj->i2c_dma_flag & I2C_USING_TX_DMA_FLAG)
{
ret = I2C_Master_Transmit_DMA(i2c_obj, msg);
}
else
{
ret = I2C_Master_Transmit(i2c_obj, msg);
}
return ret;
}
static int hc32_i2c_read(struct hc32_i2c *i2c_obj,
struct rt_i2c_msg *msg)
{
int ret;
if (i2c_obj->i2c_dma_flag & I2C_USING_RX_DMA_FLAG)
{
ret = I2C_Master_Receive_DMA(i2c_obj, msg);
}
else
{
ret = I2C_Master_Receive(i2c_obj, msg);
}
return ret;
}
static rt_ssize_t hc32_i2c_master_xfer(struct rt_i2c_bus_device *bus,
struct rt_i2c_msg msgs[],
rt_uint32_t num)
{
rt_int32_t i, ret;
rt_uint16_t ignore_nack;
struct rt_i2c_msg *msg = msgs;
struct hc32_i2c *i2c_obj;
RT_ASSERT((msgs != RT_NULL) && (bus != RT_NULL));
i2c_obj = rt_container_of(bus, struct hc32_i2c, i2c_bus);
if (num == 0)
{
return 0;
}
for (i = 0; i < num; i++)
{
msg = &msgs[i];
ignore_nack = msg->flags & RT_I2C_IGNORE_NACK;
if (!(msg->flags & RT_I2C_NO_START))
{
if (SET == I2C_GetStatus(i2c_obj->config->Instance, I2C_FLAG_BUSY))
{
hc32_hw_i2c_restart(i2c_obj);
}
else
{
hc32_hw_i2c_reset(i2c_obj);
hc32_hw_i2c_start(i2c_obj);
}
/* addr R or W */
ret = hc32_hw_i2c_send_addr(i2c_obj, msg);
if ((ret != RT_EOK) && !ignore_nack)
{
I2C_PRINT_DBG("receive NACK from device addr 0x%02x msg %d", msgs[i].addr, i);
goto out;
}
}
if (msg->flags & RT_I2C_RD)
{
ret = hc32_i2c_read(i2c_obj, msg);
if (ret != RT_EOK)
{
I2C_PRINT_ERR("[%s:%d]I2C Read error!\n", __func__, __LINE__);
goto out;
}
}
else
{
ret = hc32_i2c_write(i2c_obj, msg);
if (ret != RT_EOK)
{
I2C_PRINT_ERR("[%s:%d]I2C Write error!\n", __func__, __LINE__);
goto out;
}
}
}
ret = i;
out:
if (!(msg->flags & RT_I2C_NO_STOP))
{
hc32_hw_i2c_stop(i2c_obj);
I2C_PRINT_DBG("send stop condition\n");
}
return ret;
}
static const struct rt_i2c_bus_device_ops hc32_i2c_ops =
{
.master_xfer = hc32_i2c_master_xfer,
RT_NULL,
RT_NULL
};
int hc32_hw_i2c_init(void)
{
int ret = -RT_ERROR;
rt_size_t obj_num = sizeof(i2c_objs) / sizeof(struct hc32_i2c);
I2C_PRINT_DBG("%s start\n", __func__);
for (int i = 0; i < obj_num; i++)
{
i2c_objs[i].i2c_bus.ops = &hc32_i2c_ops;
i2c_objs[i].config = &i2c_config[i];
i2c_objs[i].i2c_bus.timeout = i2c_config[i].timeout;
hc32_i2c_get_dma_info();
if (i2c_objs[i].i2c_dma_flag & I2C_USING_TX_DMA_FLAG)
{
i2c_objs[i].config->i2c_tx_dma = i2c_config[i].i2c_tx_dma;
}
if ((i2c_objs[i].i2c_dma_flag & I2C_USING_RX_DMA_FLAG))
{
i2c_objs[i].config->i2c_rx_dma = i2c_config[i].i2c_rx_dma;
}
hc32_i2c_configure(&i2c_objs[i].i2c_bus);
ret = rt_i2c_bus_device_register(&i2c_objs[i].i2c_bus, i2c_objs[i].config->name);
RT_ASSERT(ret == RT_EOK);
}
I2C_PRINT_DBG("%s end\n", __func__);
return ret;
}
INIT_BOARD_EXPORT(hc32_hw_i2c_init);
#endif
#endif /* RT_USING_I2C */