/***************************************************************************** * * 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 #include #include #include "clock_driver.h" #include "gpio_driver.h" #include "icu_driver.h" #include #include "power_driver.h" #include #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; }