/**************************************************************************//** * @file DrvI2CH.c * @version V3.00 * @brief N9H20 series I2C driver source file * * SPDX-License-Identifier: Apache-2.0 * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved. *****************************************************************************/ #include <stdio.h> #include <string.h> #include "wblib.h" #include "DrvI2CH.h" volatile PFN_DRVI2CH_INT_CALLBACK g_pfnI2CHCallback = {0}; /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_ISR */ /* */ /* Parameters: */ /* None */ /* */ /* Returns: */ /* None */ /* */ /* Description: */ /* I2CH Interrupt Service Routine */ /* */ /*---------------------------------------------------------------------------------------------------------*/ void DrvI2CH_ISR(void) { if (g_pfnI2CHCallback != 0) { if (DrvI2CH_IsIntEnabled() == TRUE) { if (DrvI2CH_PollInt() == TRUE) g_pfnI2CHCallback(0,0); } } DrvI2CH_ClearInt(); } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_InstallCallBack */ /* */ /* Parameters: */ /* */ /* */ /* Returns: */ /* ERRCODE */ /* */ /* Description: */ /* Install I2CH interrupt Callback Function */ /* */ /*---------------------------------------------------------------------------------------------------------*/ ERRCODE DrvI2CH_InstallCallBack( PFN_DRVI2CH_INT_CALLBACK pfnCallback, PFN_DRVI2CH_INT_CALLBACK *pfnOldCallback ) { *pfnOldCallback = g_pfnI2CHCallback; // return previous installed callback function pointer g_pfnI2CHCallback = pfnCallback; // install current callback function sysInstallISR(IRQ_LEVEL_1, IRQ_I2C, (PVOID)DrvI2CH_ISR); sysEnableInterrupt(IRQ_I2C); return Successful; } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_InitSDASCK */ /* */ /* Parameters: None */ /* */ /* */ /* Returns: None */ /* */ /* Description: */ /* This function is used to initial SDA and SDK of I2C */ /*---------------------------------------------------------------------------------------------------------*/ void DrvI2CH_InitSDASCK(void) { outp32 (REG_I2C_CSR, inp32(REG_I2C_CSR)&(~I2C_EN)&(~TX_NUM) ); /* I2C core disable, Tx_NUM=0 */ outp32 (REG_I2C_CSR, inp32(REG_I2C_CSR)| CSR_IF | CSR_IE | I2C_EN ); /* I2C core enable */ } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_IsBusy */ /* */ /* Parameters: None */ /* */ /* */ /* Returns: */ /* Return TRUE if the I2C is in busy. */ /* */ /* Description: */ /* This function is used to check if I2C is in busy. */ /*---- -----------------------------------------------------------------------------------------------------*/ BOOL DrvI2CH_IsBusy(void) { return ((inp32(REG_I2C_CSR)&I2C_TIP)?TRUE:FALSE); } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_IsBusBusy */ /* */ /* Parameters: None */ /* */ /* */ /* Returns: */ /* Return TRUE if the I2C bus is in busy. */ /* */ /* Description: */ /* This function is used to check if I2C bus is in busy. The function will return TRUE if */ /* there is START signal on bus and it will return FALSE until STOP signal. */ /*---------------------------------------------------------------------------------------------------------*/ BOOL DrvI2CH_IsBusBusy(void) { return ((inp32(REG_I2C_CSR)&I2C_BUSY)?TRUE:FALSE); } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_IsArbitLost */ /* */ /* Parameters: None */ /* */ /* */ /* Returns: */ /* Return TRUE if arbitration error on I2C bus. */ /* */ /* Description: */ /* If the arbitration error is found on I2C bus, this function will return TRUE and send */ /* START and STOP command to the bus to try to recover the error. */ /* */ /*---------------------------------------------------------------------------------------------------------*/ BOOL DrvI2CH_IsArbitLost(void) { if( (inp32(REG_I2C_CSR)&I2C_AL) ==0x0) return FALSE; /* No arbitration lose */ else { /* Send clocks out when arbitration error to try to re-cover the device status */ outp32(REG_I2C_CMDR, 0x10); /* Send START signal to I2C bus */ outp32(REG_I2C_CMDR, 0x08); /* Send STOP signal to I2C bus */ return TRUE; /* Arbitration error */ } } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_SetBurstCnt */ /* */ /* Parameters: */ /* u8BurstCnt - [in], The sequential transfer number. It could be 1~4. */ /* */ /* Returns: */ /* E_SUCCESS */ /* E_DRVI2CH_WRONG_LENGTH Invalid sequential transfer number. */ /* */ /* Description: */ /* To set the burst transfer count. The I2C could be up to 4 transfers when trigger to */ /* send/get data */ /* */ /*---------------------------------------------------------------------------------------------------------*/ ERRCODE DrvI2CH_SetBurstCnt( UINT8 u8BurstCnt ) { if( (u8BurstCnt>0) && (u8BurstCnt<=4) ) { outp32(REG_I2C_CSR, inp32(REG_I2C_CSR)&(~TX_NUM) ); outp32(REG_I2C_CSR, inp32(REG_I2C_CSR)|((u8BurstCnt-1)<<4) ); } else return E_DRVI2CH_WRONG_LENGTH; return Successful; } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_SetTxData */ /* */ /* Parameters: */ /* u32TxData - [in], The data to send. */ /* */ /* Returns: */ /* None */ /* */ /* Description: */ /* Set the data to send */ /* */ /*---------------------------------------------------------------------------------------------------------*/ void DrvI2CH_SetTxData( UINT32 u32TxData ) { outp32(REG_I2C_TxR, u32TxData); } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_SendCmd */ /* */ /* Parameters: */ /* eCmd - [in], The I2C command. It could be eDRVI2CH_ACK, eDRVI2CH_WRITE, eDRVI2CH_READ, */ /* eDRVI2CH_STOP, eDRVI2CH_START. It is also able to send two commands at */ /* the same time by "OR",i.e eDRVI2CH_START | eDRVI2CH_READ | eDRVI2CH_ACK | */ /* eDRVI2CH_STOP. The sequences of the I2C actions are START => READ => ACK => */ /* STOP when these commands are send at the same time. */ /* */ /* */ /* Returns: */ /* None */ /* */ /* Description: */ /* Send the I2C command to the hardware to do the relative action. */ /* */ /*---------------------------------------------------------------------------------------------------------*/ void DrvI2CH_SendCmd( E_DRVI2CH_CMD eCmd ) { outp32 (REG_I2C_CMDR, eCmd); } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_IsACK */ /* */ /* Parameters: */ /* None */ /* */ /* Returns: */ /* Return TRUE if ACK received from slave, otherwize return FALSE */ /* */ /* Description: */ /* This function is used to check if ACK received from slave after sending data to slave */ /* device. */ /* */ /*---------------------------------------------------------------------------------------------------------*/ BOOL DrvI2CH_IsACK(void) { if( (inp32(REG_I2C_CSR)&I2C_RXACK)==0 ) return TRUE; else return FALSE; } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_GetRxData */ /* */ /* Parameters: */ /* None */ /* */ /* Returns: */ /* Return the last byte which is received from I2C bus */ /* */ /* Description: */ /* To read the last byte which is received from I2C bus */ /* */ /*---------------------------------------------------------------------------------------------------------*/ UINT8 DrvI2CH_GetRxData(void) { return ( inp32(REG_I2C_RxR)&0xff ); } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_EnableInt */ /* */ /* Parameters: */ /* bIsInterrupt - [in], TRUE = Enable the interrupt, FASE = Disable the I2C interrupt */ /* */ /* Returns: */ /* None */ /* */ /* Description: */ /* To enable the I2C interrupt. */ /* */ /*---------------------------------------------------------------------------------------------------------*/ void DrvI2CH_EnableInt(void) { outp32( REG_I2C_CSR, inp32(REG_I2C_CSR) | CSR_IE); } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_DisableInt */ /* */ /* Parameters: */ /* None */ /* */ /* Returns: */ /* None */ /* */ /* Description: */ /* To enable the I2C interrupt. */ /* */ /*---------------------------------------------------------------------------------------------------------*/ void DrvI2CH_DisableInt(void) { outp32( REG_I2C_CSR, inp32(REG_I2C_CSR) & ~CSR_IE); } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_IsIntEnabled */ /* */ /* Parameters: */ /* None */ /* */ /* Returns: */ /* TRUE = I2C Interrupt enabled. FALSE = I2C Interrupt disabled. */ /* */ /* Description: */ /* Get the interrupt enable or disable status. */ /* */ /*---------------------------------------------------------------------------------------------------------*/ BOOL DrvI2CH_IsIntEnabled(void) { return ((inp32(REG_I2C_CSR)&(CSR_IE)) ? TRUE:FALSE); } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_PollInt */ /* */ /* Parameters: */ /* None */ /* */ /* Returns: */ /* True/False */ /* */ /* Description: */ /* To return I2C interrupt flag. */ /* */ /*---------------------------------------------------------------------------------------------------------*/ BOOL DrvI2CH_PollInt(void) { return ((inp32(REG_I2C_CSR)&(CSR_IF)) ? TRUE:FALSE); } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_ClearInt */ /* */ /* Parameters: */ /* None */ /* */ /* Returns: */ /* None */ /* */ /* Description: */ /* To clear the I2C interrupt flag. */ /* */ /*---------------------------------------------------------------------------------------------------------*/ void DrvI2CH_ClearInt(void) { outp32( REG_I2C_CSR, inp32(REG_I2C_CSR) | CSR_IF); } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_Open */ /* */ /* Parameters: */ /* u32I2cClock - [in], To configure the I2C bus clock. its unit is Hz. */ /* */ /* Returns: */ /* E_SUCCESS */ /* */ /* Description: */ /* To open the I2C hardware and configure the I2C bus clock. */ /* */ /*---------------------------------------------------------------------------------------------------------*/ ERRCODE DrvI2CH_Open( UINT32 u32I2cClock ) { UINT32 u32ApbKHz; INT32 n32Divider; // enable I2C pin function (switch pin is removed to GPIO driver) // outp32(PINFUN1, inp32(PINFUN1) & ~(PF_ISDA|PF_ISCK)); // outp32(PINFUN1, inp32(PINFUN1) | (0x0F << 16)); // enable I2C engine clock outp32(REG_APBCLK, inp32(REG_APBCLK) | I2C_CKE); // reset I2C engine outp32(REG_APBIPRST, inp32(REG_APBIPRST) | I2CRST); outp32(REG_APBIPRST, inp32(REG_APBIPRST) & ~I2CRST); u32ApbKHz = sysGetAPBClock(); n32Divider = u32ApbKHz/(5*u32I2cClock)-1; if(n32Divider<=1) n32Divider = 1; outp32(REG_I2C_DIVIDER,n32Divider); outp32(REG_I2C_CSR, (inp32(REG_I2C_CSR)&(~TX_NUM)) | I2C_EN); return Successful; } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_Close */ /* */ /* Parameters: */ /* None */ /* */ /* Returns: */ /* None */ /* */ /* Description: */ /* To close the I2C hardware. */ /* */ /*---------------------------------------------------------------------------------------------------------*/ void DrvI2CH_Close(void) { outp32(REG_APBIPRST, inp32(REG_APBIPRST) | I2CRST); outp32(REG_APBIPRST, inp32(REG_APBIPRST) & ~I2CRST); outp32(REG_APBCLK, inp32(REG_APBCLK) & ~I2C_CKE); } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_Delay */ /* */ /* Parameters: */ /* nCount - [in], The delay count in micro-second. */ /* */ /* Returns: */ /* None */ /* */ /* Description: */ /* This is a delay function */ /* */ /*---------------------------------------------------------------------------------------------------------*/ static void DrvI2CH_Delay( UINT32 u32Count ) { UINT32 ii, jj; for(ii=0; ii<u32Count; ii++) for (jj=0; jj<10; jj++){} // DrvSYS_RoughDelay(nCount); } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_WaitReady */ /* */ /* Parameters: */ /* None */ /* */ /* Returns: */ /* E_SUCCESS Success */ /* E_DRVI2CH_ARBIT_LOSE Bus arbitration error */ /* E_DRVI2CH_TIME_OUT I2C time out */ /* */ /* Description: */ /* Wait for I2C hardware ready. */ /* */ /*---------------------------------------------------------------------------------------------------------*/ ERRCODE DrvI2CH_WaitReady(void) { UINT32 u32ErrCode=Successful; UINT32 u32DelayCount=0; if (DrvI2CH_IsArbitLost()==TRUE) return E_DRVI2CH_ARBIT_LOSE; while(DrvI2CH_IsBusy() && u32DelayCount!=500) { DrvI2CH_Delay(10); u32DelayCount++; } if(u32DelayCount==500) return E_DRVI2CH_TIME_OUT; return u32ErrCode; } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_SendStart */ /* */ /* Parameters: */ /* None */ /* */ /* Returns: */ /* E_SUCCESS Success */ /* E_DRVI2CH_ARBIT_LOSE Bus arbitration error */ /* E_DRVI2CH_TIME_OUT I2C time out */ /* */ /* Description: */ /* To send START signal to I2C bus. */ /* */ /*---------------------------------------------------------------------------------------------------------*/ ERRCODE DrvI2CH_SendStart(void) { UINT32 u32ErrCode; DrvI2CH_SendCmd(eDRVI2CH_START); u32ErrCode=DrvI2CH_WaitReady(); return u32ErrCode; } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_SendStop */ /* */ /* Parameters: */ /* None */ /* */ /* Returns: */ /* E_SUCCESS Success */ /* E_DRVI2CH_ARBIT_LOSE Bus arbitration error */ /* E_DRVI2CH_TIME_OUT I2C time out */ /* */ /* Description: */ /* To send STOP signal to I2C bus. */ /* */ /*---------------------------------------------------------------------------------------------------------*/ ERRCODE DrvI2CH_SendStop(void) { UINT32 u32ErrCode; DrvI2CH_SendCmd(eDRVI2CH_STOP); u32ErrCode=DrvI2CH_WaitReady(); return u32ErrCode; } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_WriteByte */ /* */ /* Parameters: */ /* bStart - [in], Enable to send START signal before send data. */ /* u8Data - [in], The byte to send through I2C bus. */ /* bCheckAck - [in], Enable to check ACK after send data. */ /* bStop - [in], Enable to send STOP signal at the end. */ /* */ /* Returns: */ /* E_SUCCESS Success */ /* E_DRVI2CH_ARBIT_LOSE Bus arbitration error */ /* E_DRVI2CH_TIME_OUT I2C time out */ /* E_DRVI2CH_NACK NACK received */ /* */ /* Description: */ /* Send a byte to I2C bus. */ /* */ /*---------------------------------------------------------------------------------------------------------*/ ERRCODE DrvI2CH_WriteByte( BOOL bStart, UINT8 u8Data, BOOL bCheckAck, BOOL bStop ) { UINT32 u32ErrCode; E_DRVI2CH_CMD eCmd; if (DrvI2CH_IsArbitLost()) return E_DRVI2CH_ARBIT_LOSE; DrvI2CH_SetBurstCnt(1); DrvI2CH_SetTxData(u8Data); eCmd = eDRVI2CH_WRITE; if(bStart) eCmd = (E_DRVI2CH_CMD)(eCmd | eDRVI2CH_START); if(bStop) eCmd = (E_DRVI2CH_CMD)(eCmd | eDRVI2CH_STOP); DrvI2CH_SendCmd(eCmd); u32ErrCode=DrvI2CH_WaitReady(); if (u32ErrCode==Successful) { if(bCheckAck) { if(DrvI2CH_IsACK() == FALSE) return E_DRVI2CH_NACK; } } return u32ErrCode; } /*---------------------------------------------------------------------------------------------------------*/ /* Function: DrvI2CH_ReadByte */ /* */ /* Parameters: */ /* bStart - [in], Enable to send START signal before send data. */ /* pu8ReadData - [in], The byte to read through I2C bus. */ /* bSendAck - [in], Enable to send ACK after read data. */ /* bStop - [in], Enable to send STOP signal at the end. */ /* */ /* Returns: */ /* E_SUCCESS Success */ /* E_DRVI2CH_ARBIT_LOSE Bus arbitration error */ /* E_DRVI2CH_TIME_OUT I2C time out */ /* */ /* Description: */ /* Read a byte from I2C bus. */ /* */ /*---------------------------------------------------------------------------------------------------------*/ ERRCODE DrvI2CH_ReadByte( BOOL bStart, PUINT8 pu8ReadData, BOOL bSendAck, BOOL bStop ) { UINT32 u32ErrCode = Successful; DrvI2CH_SendCmd((E_DRVI2CH_CMD)(((bStart)?eDRVI2CH_START:0) | eDRVI2CH_READ | ((bSendAck)?0:eDRVI2CH_ACK) | ((bStop)?eDRVI2CH_STOP:0))); u32ErrCode = DrvI2CH_WaitReady(); if(u32ErrCode == Successful) *pu8ReadData=DrvI2CH_GetRxData(); return u32ErrCode; }