lock_lfvx/bk_idk/middleware/driver/i2c/sim_i2c_driver.c

578 lines
11 KiB
C
Raw Normal View History

2025-10-10 16:07:00 +08:00
/*****************************************************************************
*
* General Purpose I2C Bus reference driver.
* To use this file, you must implement the first 9 functions below according
* To the specific platform IO implementation.
* To increase the speed, it is recommended to change the functions
* To inline functions or to macros.
*****************************************************************************/
#include <driver/gpio.h>
#include <driver/int.h>
#include <driver/i2c.h>
#include "clock_driver.h"
#include "gpio_driver.h"
#include "icu_driver.h"
#include <os/mem.h>
#include "power_driver.h"
#include <os/os.h>
#include "i2c_hw.h"
#include "i2c_driver.h"
#include "i2c_hal.h"
#include "i2c_statis.h"
#include "sys_driver.h"
#define TRUE 1
#define FALSE 0
#define SUPPORT_100K
/* Disable this macro by default, as gpio api way would cost more time to switch output level */
//#define SIM_I2C_GPIO_API_EN
#define HWD_GPIO_I2C_SDA GPIO_1
#define HWD_GPIO_I2C_SCL GPIO_0
/*****************************************************
* These Macros could be used for RISCV 120Mhz.
*****************************************************/
#define CLK_DELAY_100K 25
#define CLK_DELAY_400K 1
#define I2C_MIN_DELAY 1
#ifdef SUPPORT_100K
#define SCL_DELAY CLK_DELAY_100K
#else
#define SCL_DELAY CLK_DELAY_400K
#endif
#define SCL_HALF_DELAY ((SCL_DELAY + 1) / 2)
#define GPIO_OUTPUT_HIGH 0x2
#define GPIO_OUTPUT_LOW 0x0
static inline void I2cSetSdaInput(void)
{
bk_gpio_disable_output(HWD_GPIO_I2C_SDA);
bk_gpio_enable_input(HWD_GPIO_I2C_SDA);
}
static inline void I2cSetSdaOutput(void)
{
/* Disable this macro by default, as gpio api way would cost more time to switch output level */
#ifdef SIM_I2C_GPIO_API_EN
bk_gpio_disable_input(HWD_GPIO_I2C_SDA);
bk_gpio_enable_output(HWD_GPIO_I2C_SDA);
#else
*((volatile unsigned long *) (SOC_AON_GPIO_REG_BASE+(HWD_GPIO_I2C_SDA)*4)) = GPIO_OUTPUT_HIGH;
#endif
}
static inline void I2cSetSclInput(void) // Is it needed when this is the master? SCL line INPUT?
{
bk_gpio_disable_output(HWD_GPIO_I2C_SCL);
bk_gpio_enable_input(HWD_GPIO_I2C_SCL);
}
static inline void I2cSetSclOutput(void)
{
#ifdef SIM_I2C_GPIO_API_EN
bk_gpio_disable_input(HWD_GPIO_I2C_SCL);
bk_gpio_enable_output(HWD_GPIO_I2C_SCL);
#else
*((volatile unsigned long *) (SOC_AON_GPIO_REG_BASE+(HWD_GPIO_I2C_SCL)*4)) = GPIO_OUTPUT_HIGH;
#endif
}
static inline void I2cSetSdaHigh(void)
{
#ifdef SIM_I2C_GPIO_API_EN
bk_gpio_disable_input(HWD_GPIO_I2C_SDA);
bk_gpio_enable_output(HWD_GPIO_I2C_SDA);
bk_gpio_set_output_high(HWD_GPIO_I2C_SDA);
#else
*((volatile unsigned long *) (SOC_AON_GPIO_REG_BASE+(HWD_GPIO_I2C_SDA)*4)) = GPIO_OUTPUT_HIGH;
#endif
}
static inline void I2cSetSdaLow(void)
{
#ifdef SIM_I2C_GPIO_API_EN
bk_gpio_disable_input(HWD_GPIO_I2C_SDA);
bk_gpio_enable_output(HWD_GPIO_I2C_SDA);
bk_gpio_set_output_low(HWD_GPIO_I2C_SDA);
#else
*((volatile unsigned long *) (SOC_AON_GPIO_REG_BASE+(HWD_GPIO_I2C_SDA)*4)) = GPIO_OUTPUT_LOW;
#endif
}
static inline void I2cSetSclHigh(void)
{
#ifdef SIM_I2C_GPIO_API_EN
bk_gpio_disable_input(HWD_GPIO_I2C_SCL);
bk_gpio_enable_output(HWD_GPIO_I2C_SCL);
bk_gpio_set_output_high(HWD_GPIO_I2C_SCL);
#else
*((volatile unsigned long *) (SOC_AON_GPIO_REG_BASE+(HWD_GPIO_I2C_SCL)*4)) = GPIO_OUTPUT_HIGH;
#endif
}
static inline void I2cSetSclLow(void)
{
#ifdef SIM_I2C_GPIO_API_EN
bk_gpio_disable_input(HWD_GPIO_I2C_SCL);
bk_gpio_enable_output(HWD_GPIO_I2C_SCL);
bk_gpio_set_output_low(HWD_GPIO_I2C_SCL);
#else
*((volatile unsigned long *) (SOC_AON_GPIO_REG_BASE+(HWD_GPIO_I2C_SCL)*4)) = GPIO_OUTPUT_LOW;
#endif
}
//This function should return 1 if the SDA is in high state, or 0 if it is in low state
static inline uint8_t I2cReadSda(void)
{
return (uint8_t)(bk_gpio_get_input(HWD_GPIO_I2C_SDA));
}
void I2cDelay(uint32_t count)
{
volatile uint32_t i;
for(i = 0; i < count; i++)
{
}
}
/************************************************************************************
*
* Basic I2C Bus signal.
* START, STOP, ACK, No ACK.
* Pull down SCL line before return from any routines here.
************************************************************************************/
#define I2C_PIN_DELAY() I2cDelay(0)
static inline void I2cSclPulse()
{
I2cSetSclHigh();
I2cDelay(SCL_DELAY);
I2cSetSclLow();
I2cDelay(SCL_DELAY);
}
void I2cSclPulse_ack()
{
I2cSetSclHigh();
I2cDelay(SCL_DELAY);
I2cSetSclLow();
I2cSetSdaInput();
I2cDelay(SCL_DELAY);
}
void I2cStart(void)
{
// Just to ensure SCL is in LOW state.
// I2cSetSclLow();
// I2cDelay(SCL_HALF_DELAY);
I2cSetSdaOutput();
I2C_PIN_DELAY();
// SDA SCL in HIGH state
I2cSetSdaHigh();
I2C_PIN_DELAY();
I2cSetSclHigh();
I2cDelay(SCL_DELAY);
// A HIGH to LOW transition on the SDA line while SCL is HIGH
I2cSetSdaLow();
I2cDelay(MAX(SCL_HALF_DELAY, I2C_MIN_DELAY));
// to default state.
I2cSetSclLow();
I2cDelay(SCL_DELAY);
}
void I2cStop(void)
{
// Just to ensure SCL is in LOW state.
I2cSetSclLow();
I2cDelay(SCL_HALF_DELAY);
// SDA in LOW state, SCL in HIGH state
I2cSetSdaLow();
I2C_PIN_DELAY();
I2cSetSclHigh();
I2cDelay(SCL_DELAY);
// A LOW to HIGH transition on the SDA line while SCL is HIGH
I2cSetSdaHigh();
I2cDelay(SCL_DELAY);
// to default state.
/*
I2cSetSclLow();
I2cDelay(SCL_DELAY);
*/ // should discard these codes for multi-master I2C bus.( or new devices of latest protocol. )
}
static inline void I2cAck(void)
{
// Pull down SDA line.
I2cSetSdaLow();
I2C_PIN_DELAY();
I2cSclPulse();
}
static inline void I2cNoAck(void)
{
// let SDA line in HIGH state during ACK clock cycle.
I2cSetSdaHigh();
I2C_PIN_DELAY();
I2cSclPulse();
}
/****************************************************************
*
* I2C Bus Driver routines.
*
****************************************************************/
#if (CONFIG_TP_HY4633)
#define I2C_SPECIAL_DELAY() I2cDelay(550) // special timing requirements, the delay between bytes and bytes needs to be more than 25us.
#else
#define I2C_SPECIAL_DELAY()
#endif
void I2cInit( void )
{
I2cSetSdaOutput();
I2cSetSclOutput();
//I2cSetSclLow();
I2cStop();
}
bool I2cWaitForAck(void)
{
bool Ack;
I2cSetSdaInput();
I2C_PIN_DELAY();
I2cSetSclHigh();
I2cDelay(SCL_DELAY);
Ack = (I2cReadSda() == 0);
I2C_PIN_DELAY();
I2cSetSclLow();
I2C_PIN_DELAY();
I2cDelay(SCL_DELAY);
//Please note - the following call is required because
//the I2cSetSdaHigh and I2cSetSdaLow functions do not set the port to output state
//I2cSetSdaOutput();
I2C_SPECIAL_DELAY();
return Ack;
}
bool I2cSend(uint8_t I2cData)
{
uint8_t Mask;
I2cSetSdaOutput();
for(Mask = 128; Mask; Mask >>= 1)
{
if(I2cData & Mask)
{
I2cSetSdaHigh();
}
else
{
I2cSetSdaLow();
}
I2C_PIN_DELAY();
if(Mask == 1)
{
//The 9th bit of ack would exchange the output/input direction
//Change SDA PIN as input mode in time to improve wave not normative issue
I2cSclPulse_ack();
} else {
I2cSclPulse();
}
}
return I2cWaitForAck();
}
uint8_t I2cReceive(bool LastOne)
{
uint8_t Mask;
uint8_t I2cData = 0;
I2cSetSdaInput();
I2C_PIN_DELAY();
for(Mask = 128; Mask; Mask >>= 1)
{
I2cSetSclHigh();
I2cDelay(SCL_DELAY);
if(I2cReadSda())
{
I2cData |= Mask;
}
I2C_PIN_DELAY();
I2cSetSclLow();
I2cDelay(SCL_DELAY);
}
//Please note - the following call is required because
//the I2cSetSdaHigh and I2cSetSdaLow functions do not return the port to output state
I2cSetSdaOutput();
I2C_PIN_DELAY();
if(LastOne)
{
I2cNoAck();
}
else
{
I2cAck();
}
I2C_SPECIAL_DELAY();
return I2cData;
}
bool I2cWrite(uint8_t Addr, const uint8_t * pBuff, uint32_t len)
{
Addr <<= 1;
I2cStart();
if(I2cSend(Addr) == FALSE)
{
I2cStop();
return FALSE;
}
while(len-- > 0)
{
if(I2cSend(*pBuff++) == FALSE)
{
I2cStop();
return FALSE;
}
}
I2cStop();
return TRUE;
}
bool I2cRead(uint8_t Addr, uint8_t * pBuff, uint32_t len)
{
if(len == 0)
return FALSE;
Addr <<= 1;
Addr |= 0x01;
I2cStart();
if(I2cSend(Addr) == FALSE)
{
I2cStop();
return FALSE;
}
while(len > 0)
{
*pBuff++ = I2cReceive((--len) == 0);
}
I2cStop();
return TRUE;
}
bool I2cMemWrite(i2c_id_t id, const i2c_mem_param_t *mem_param)
{
uint32_t Addr;
uint32_t mem_addr;
uint8_t * pBuff;
uint32_t len;
uint32_t mem_addr_size;
Addr = mem_param->dev_addr;
mem_addr = mem_param->mem_addr;
pBuff = mem_param->data;
len = mem_param->data_size;
mem_addr_size = mem_param->mem_addr_size;
Addr <<= 1;
I2cStart();
if(I2cSend(Addr) == FALSE)
{
I2cStop();
return FALSE;
}
if (mem_addr_size == I2C_MEM_ADDR_SIZE_16BIT) {
if(I2cSend((mem_addr >> 8) & 0xff) == FALSE)
{
I2cStop();
return FALSE;
}
}
if(I2cSend((mem_addr & 0xff)) == FALSE)
{
I2cStop();
return FALSE;
}
while(len-- > 0)
{
if(I2cSend(*pBuff++) == FALSE)
{
I2cStop();
return FALSE;
}
}
I2cStop();
return TRUE;
}
bool I2cMemRead(i2c_id_t id, const i2c_mem_param_t *mem_param)
{
uint32_t Addr;
uint32_t mem_addr;
uint8_t * pBuff;
uint32_t len;
uint32_t mem_addr_size;
Addr = mem_param->dev_addr;
mem_addr = mem_param->mem_addr;
pBuff = mem_param->data;
len = mem_param->data_size;
mem_addr_size = mem_param->mem_addr_size;
if(len == 0)
return FALSE;
I2cStart();
Addr <<= 1;
if(I2cSend(Addr) == FALSE)
{
I2cStop();
return FALSE;
}
if (mem_addr_size == I2C_MEM_ADDR_SIZE_16BIT) {
if(I2cSend((mem_addr >> 8) & 0xff) == FALSE)
{
I2cStop();
return FALSE;
}
}
if(I2cSend((mem_addr & 0xff)) == FALSE)
{
I2cStop();
return FALSE;
}
I2cStart();
Addr |= 0x01;
if(I2cSend(Addr) == FALSE)
{
I2cStop();
return FALSE;
}
while(len > 0)
{
*pBuff++ = I2cReceive((--len) == 0);
}
I2cStop();
return TRUE;
}
bk_err_t bk_i2c_memory_write(i2c_id_t id, const i2c_mem_param_t *mem_param)
{
I2cMemWrite(id, mem_param);
return BK_OK;
}
bk_err_t bk_i2c_memory_read(i2c_id_t id, const i2c_mem_param_t *mem_param)
{
I2cMemRead(id, mem_param);
return BK_OK;
}
bk_err_t bk_i2c_driver_init(void)
{
return BK_OK;
}
bk_err_t bk_i2c_driver_deinit(void)
{
return BK_OK;
}
bk_err_t bk_i2c_init(i2c_id_t id, const i2c_config_t *cfg)
{
bk_gpio_pull_down(HWD_GPIO_I2C_SDA);
bk_gpio_pull_down(HWD_GPIO_I2C_SCL);
gpio_dev_unmap(HWD_GPIO_I2C_SDA);
gpio_dev_unmap(HWD_GPIO_I2C_SCL);
I2cInit();
return BK_OK;
}
bk_err_t bk_i2c_deinit(i2c_id_t id)
{
I2cSetSclLow();
I2cSetSdaLow();
return BK_OK;
}
bk_err_t bk_i2c_master_write(i2c_id_t id, uint32_t dev_addr, const uint8_t *data, uint32_t size, uint32_t timeout_ms)
{
I2cWrite(dev_addr, data, size);
return BK_OK;
}
bk_err_t bk_i2c_master_read(i2c_id_t id, uint32_t dev_addr, uint8_t *data, uint32_t size, uint32_t timeout_ms)
{
I2cRead(dev_addr, data, size);
return BK_OK;
}