www.pudn.com > I2c.zip > Dm644xI2c.c


//======================================================================== 
//   Copyright 2006 Mistral Software Pvt Ltd. 
// 
//   Use of this software is controlled by the terms and conditions found 
//   in the license agreement under which this software has been supplied. 
//======================================================================== 
 
//! \file Dm644xI2c.c 
//! \brief DM6446 I2C Init, Read, Write routine implementation 
//! This file implements the I2C initialization , read, write functionality 
//! \version  1.00 Created on May 16th 2006 
 
// Includes 
#include  
#include  
#include "Dm6446RegOffsets.h" 
#include "DM644xi2c.h" 
#include "Dm6446RegOverlay.h" 
 
/* File-level I2C Global Overlay Struct Pntr */ 
static PI2C_REGS f_pI2cRegs  = NULL; 
 
static int  f_I2cInputDivisor     = 0; 
static int  f_I2cClockLowDivisor  = 0; 
static int  f_I2cClockHighDivisor = 0; 
 
static volatile ULONG   f_I2cBusHandle = 0; 
static volatile BOOL    f_I2cBusAcquired = FALSE; 
static volatile USHORT  f_PrevI2cSlaveAddr = 0; 
 
//============================================================================== 
//!  \fn     BOOL Dm6446I2cAcquire( 
//!                     PULONG pHandle 
//!                     ) 
//!  \brief  Implements DM6446 I2c Acquire bus for further transcations. 
//!  \param  pHandle PULONG Pointer to return the handle  
//!  \return Booloean TRUE if Successfully acquired. False oterhwise 
//============================================================================== 
BOOL  
Dm644xI2cAcquire ( 
    PULONG pHandle 
    ) 
{ 
    BOOL RetValue = FALSE; 
     
    OALMSG(FALSE, (L"Dm644xI2cAcquire+ .\r\n")); 
      
    /* If the bus is already acquired, then return false */ 
    if (TRUE == f_I2cBusAcquired) 
        { 
        OALMSG(OAL_ERROR, (L"Dm644xI2cAcquire: Bus already acquired.\r\n")); 
        } 
    else 
        { 
        /* Bus is not already acquired. Now check if the passed handle is valid 
         * pointer if so, we cannot procced further. 
         */ 
        if (NULL == pHandle) 
            { 
            OALMSG( OAL_ERROR, (L"Dm644xI2cAcquire: Insufficient parameters.\r\n")); 
            } 
        else 
            { 
            /* Bus is not already acquired and we can actually grant the bus  
             * We require to have Unique Bus handles each time a I2c Client 
             * requires to perform Transactions on the I2c Bus.  
             * We will be utilizing the OALTimerGetCount() to return 
             * unique numbers [bus handles] 
             */ 
            f_I2cBusHandle = OALTimerGetCount (); 
            *pHandle = f_I2cBusHandle; 
            f_I2cBusAcquired = TRUE; 
            RetValue = TRUE; 
            } 
        } 
    OALMSG(FALSE, (L"Dm644xI2cAcquire- .\r\n"));     
    return (RetValue); 
} 
//============================================================================== 
//!  \fn     BOOL Dm6446I2cRelease( 
//!                     ULONG Handle 
//!                     ) 
//!  \brief  Implements DM6446 I2c Acquire bus for further transcations. 
//!  \param  pHandle PULONG Handle of the I2C bus returned during Acquire 
//!  \return Booloean TRUE if Successfully acquired. False oterhwise 
//============================================================================== 
BOOL  
Dm6446I2cRelease ( 
    ULONG Handle 
    ) 
{ 
    BOOL RetValue = FALSE; 
     
    OALMSG(FALSE, (L"Dm6446I2cRelease+ .\r\n")); 
     
    /* If the bus is already acquired, then return false */ 
    if (FALSE == f_I2cBusAcquired) 
        { 
        OALMSG(OAL_ERROR, (L"Dm6446I2cRelease: Bus already Free.\r\n")); 
        RetValue = TRUE; 
        } 
    else 
        { 
        /* Bus is not already acquired. Now check if the passed handle is valid 
         * pointer if so, we cannot procced further. 
         */ 
        if (f_I2cBusHandle != Handle) 
            { 
            OALMSG( OAL_ERROR, (L"Dm6446I2cRelease: Invalid parameters.\r\n")); 
            } 
        else 
            { 
            /* Bus is released for others to use */ 
            f_I2cBusHandle = 0; 
            f_I2cBusAcquired = FALSE; 
            f_PrevI2cSlaveAddr = 0; 
            RetValue = TRUE; 
            } 
        } 
    OALMSG(FALSE, (L"Dm6446I2cRelease- .\r\n"));     
    return (RetValue); 
} 
 
//============================================================================== 
//!  \fn     void Dm6446I2cRegDump (void) 
//!  \brief  Dumps the DM6446 I2c Controller Registers 
//!  \param  None 
//!  \return None 
//============================================================================== 
void 
Dm6446I2cRegDump( 
    void 
    ) 
{ 
    OALMSG(OAL_FUNC, (L"+Dm6446I2cRegDump\r\n")); 
 
    OALLogSerial(L"I2C OAR : 0x%04x I2C IMR : 0x%04x I2C STR : 0x%04x\r\n", 
                 f_pI2cRegs->m_Icoar, 
                 f_pI2cRegs->m_Icimr, 
                 f_pI2cRegs->m_Icstr ); 
 
    OALLogSerial(L"I2C CLKL : 0x%04x I2C CLKH : 0x%04x I2C MODE : 0x%04x\r\n", 
                 f_pI2cRegs->m_Icclkl, 
                 f_pI2cRegs->m_Icclkh, 
                 f_pI2cRegs->m_Icmdr ); 
 
    OALLogSerial(L"I2C ExMR : 0x%04x I2C PSC : 0x%04x I2C IVR : 0x%04x\r\n", 
                 f_pI2cRegs->m_Icemdr, 
                 f_pI2cRegs->m_Icpsc, 
                 f_pI2cRegs->m_Icivr ); 
 
    OALLogSerial(L"I2C PID1 : 0x%04x I2C PID2 : 0x%04x \r\n", 
                 f_pI2cRegs->m_Icpid1, 
                 f_pI2cRegs->m_Icpid2 ); 
 
    OALMSG(OAL_FUNC, (L"-Dm6446I2cRegDump\r\n")); 
    return; 
} 
//======================================================================== 
//!  \fn void DaVinciI2cPowerOn(void ) 
//!  \brief Power on the I2C module. 
//!  \param None. 
//!  \return None. 
//======================================================================== 
VOID 
DaVinciI2cPowerOn( 
    VOID 
    ) 
{ 
    PPSC_REGS pPSCRegs = (PPSC_REGS) OALPAtoUA(PSC_BASE); 
 
    if (( pPSCRegs->m_Mdstat[MOD_I2C] & 0x1F) == MOD_ENABLE) 
        { 
        OALMSG( OAL_INFO, (TEXT("I2C is already enabled.\r\n"))); 
        } 
    else 
        { 
         OALMSG( OAL_INFO, (TEXT("I2C will be enabled.\r\n"))); 
         
        /* Enable the power to the EMAC, MDIO and EMACWraper modules by setting 
         * appropriate BITS in the Module control registers 
         */ 
     
        pPSCRegs->m_Mdctl[MOD_I2C] = MOD_ENABLE;        /* I2C power enable */ 
     
        /* Set PTCMD.GO0 to 0x1 to initiate the state transtion for Modules in 
         * the ALWAYSON Power Domain 
         */ 
        pPSCRegs->m_Ptcmd = PTCMD_GO; 
     
        /* Wait for PTSTAT.GOSTAT0 to clear to 0x0 */ 
        while ( pPSCRegs->m_Ptstat & PTSTAT_GO ) 
            { 
            } 
        } 
         
    if (( pPSCRegs->m_Mdstat[MOD_I2C] & 0x1F) != MOD_ENABLE) 
        OALMSG( TRUE, (TEXT("I2C is not enabled.\r\n"))); 
 
} 
 
//============================================================================== 
//!  \fn     void Dm6446I2cStop (void) 
//!  \brief  DM6446 I2C close Function 
//!  \param  None 
//!  \return None 
//============================================================================== 
void 
Dm6446I2cStop( 
    void 
    ) 
{ 
    // Put I2C into reset 
    f_pI2cRegs->m_Icmdr &=  ~I2C_CONFIG_IRS; 
     
    return; 
} 
//======================================================================== 
//!  \fn static void DelayUSec(DWORD Us) 
//!  \brief Microsecond Delay Routine. 
//!  \param Us DWORD value in Us. 
//!  \return void. 
//======================================================================== 
static 
void 
DelayUSec( 
    DWORD Us 
    ) 
{ 
    DWORD TotalTime = 0; 
    unsigned int Current; 
    unsigned int Previous; 
 
    Previous = ((OALTimerGetCount())/27); 
 
    /* We desire the delay to be a minimum amount of time. */ 
    Us++; 
 
    do 
        { 
        Current = ((OALTimerGetCount())/27); 
        /* If GetMSecs() changed values, update totalTime */ 
        if ( Previous != Current ) 
            { 
            TotalTime++; 
            } 
        Previous = Current; 
        } while ( TotalTime < Us ); 
 
    return; 
} 
//============================================================================== 
//!  \fn      void Dm6446I2cInit(UINT32  InputClockRate) 
//!  \brief   DM6446 I2C initilization function. 
//!  \param   InputClockRate UINT32  - I2c Bus Clock frequency desired 
//!  \return  None 
//============================================================================== 
void 
Dm6446I2cInit( 
    UINT32  InputClockRate 
    ) 
{ 
    UINT32  InputDivisor = 1; 
    UINT32  I2cPrescale  = 0; 
    UINT32  i2cClkDivisor; 
    OALMSG(OAL_FUNC, (L"+Dm6446I2cInit\r\n")); 
 
    if ( f_pI2cRegs == NULL ) 
        { 
        f_pI2cRegs = (PI2C_REGS) OALPAtoUA (I2C_BASE); 
        } 
 
    /* Enable the power to the I2C controller */ 
    DaVinciI2cPowerOn (); 
 
    Dm6446I2cStop(); 
     
    DelayUSec(5000); 
 
    do 
        { 
        I2cPrescale = (DVEVM_OSC_FREQ / InputDivisor); 
 
        if ( (I2cPrescale >= I2C_PRESCALE_MIN_CLOCK_RATE) && 
             (I2cPrescale <  I2C_PRESCALE_MAX_CLOCK_RATE) ) 
            { 
             // Select the Divisor For the I2c Prescaled Module 
             // Frequency 
             f_I2cInputDivisor = (InputDivisor - 1); 
             break; 
            } 
         }while (InputDivisor++ < 255); 
 
    // Reset I2C 
    f_pI2cRegs->m_Icmdr = 0x00; 
    f_pI2cRegs->m_Icimr = 0x00; 
 
    // Section 2.1 of sprue27.pdf explains the 
    // I2c Clock Generation. 
    // Prescaler Module Clock Freq = 1Mhz 
    // On the Davinci EVM board, bypass clock is 27 Mhz 
 
    f_pI2cRegs->m_Icpsc = f_I2cInputDivisor; 
 
    // Based on the Prescale Divisor, the Value of D 
    // is fixed. 
    switch (f_I2cInputDivisor) 
        { 
        case 0: 
          InputDivisor = D_VAL_0; 
        break; 
        case 1: 
          InputDivisor = D_VAL_1; 
        break; 
        default: 
          InputDivisor = D_VAL_2; 
        } 
 
    /* I2cBusClk = (BypassClk / ICPSC) / ((ICCLKL + D_VAL) + (ICCLKH + D_VAL))*/ 
    i2cClkDivisor =  (( I2cPrescale / I2C_BUS_CLOCK_RATE) - ( 2 * InputDivisor)); 
 
    f_pI2cRegs->m_Icclkl = (i2cClkDivisor/2) + (i2cClkDivisor % 2); 
 
    f_pI2cRegs->m_Icclkh = (i2cClkDivisor/2); 
 
    // Release I2C from reset 
    f_pI2cRegs->m_Icmdr |=  I2C_CONFIG_IRS; 
     
    Dm6446I2cRegDump(); 
     
    OALMSG(OAL_FUNC, (L"-Dm6446I2cInit\r\n")); 
 
    return; 
} 
//============================================================================== 
//!  \fn     void Dm6446I2cReset (void) 
//!  \brief  DM6446 I2C reset Function 
//!  \param  None 
//!  \return Booloean 
//============================================================================== 
void 
Dm6446I2cReset( 
    void 
    ) 
{ 
    Dm6446I2cStop (); 
    Dm6446I2cInit (DVEVM_OSC_FREQ); 
 
    return; 
} 
//============================================================================== 
//!  \fn     USHORT Dm6446I2cWrite ( 
//!                     ULONG  Handle, 
//!                     USHORT slave_addr, 
//!                     UINT8* buffer, 
//!                     USHORT length 
//!                     ) 
//!  \brief  DM6446 I2C Write Function 
//!  \param  Handle ULONG:  Handle to the I2C bus returned by Acquire bus 
//!          slave_addrs : Address of the slave to which data is sent 
//!          buffer      : Contains data to be sent 
//!          length      : Length of the data to be sent 
//!  \return USHORT Number of bytes written 
//============================================================================== 
USHORT 
Dm6446I2cWrite( 
    ULONG   Handle, 
    USHORT i2caddr, 
    UINT8* pData, 
    USHORT len 
    ) 
{ 
    USHORT tempCntr; 
    volatile ULONG  i2cStatus; 
    USHORT BytesWritten = 0; 
    USHORT InterCharDelay = 0; 
    int timeout   = 0x20000; 
    int timecount = 0; 
 
 
    OALMSG(FALSE, (L"+Dm6446I2cWrite\r\n")); 
     
    /* kick back if the handle is not corresponding to the one we have */ 
    if ((Handle != f_I2cBusHandle) || 
        (f_I2cBusAcquired != TRUE)) 
        { 
        OALMSG( OAL_ERROR, (L"Dm644xI2cWrite: invalid access.\r\n")); 
        return (0); 
        } 
 
    /* Poll BB field on I2C Interrupt Status - Wait for bus to become free */ 
    for ( ; (f_pI2cRegs->m_Icstr & I2C_STAT_BB) ; timecount++ ) 
        { 
        if ( timecount >= timeout ) 
            { 
            OALMSG( OAL_ERROR, (L"Dm6446I2cWrite: Bus Busy.\r\n")); 
            return (BytesWritten); 
            } 
        if (f_PrevI2cSlaveAddr == i2caddr) 
            { 
            /* This is essentially the repeat start so let this proceed */ 
            break; 
            } 
        } 
         
    /* The I2C on DaVinci is seeming to have a issue while playing the role 
     * of Master Transmitter. It was observed that the DMSoC does not generate 
     * the proper stop condition and the SCD bit does not get set at all. Thus 
     * we are forced to introduce the delay permitting the data to be  
     * transmitted and cause the I2C reset. Failing this, the SCL line is always 
     * held low and BUS-Busy is always set. 
     *  
     * calculate the inter-char delay in micro-seconds that is time required 
     * to transmit one byte of info at the selected baud rate. This needs to 
     * include the 8 clock pulses for data and 1 clock pulse for ACK. That is 
     * total of 9 clock pulses. Add 1 clock pulse as buffer making the total of 
     * 10 clock pulses. */ 
    InterCharDelay = ((10000000) / I2C_BUS_CLOCK_RATE); 
 
    /* Clear any pending interrupts by reading the IVR */ 
    f_pI2cRegs->m_Icstr = f_pI2cRegs->m_Icstr; 
    tempCntr = f_pI2cRegs->m_Icivr; 
     
    /* Assign the Length and Slave Address */ 
    f_pI2cRegs->m_Iccnt = len; 
    f_pI2cRegs->m_Icsar = i2caddr; 
    f_PrevI2cSlaveAddr = i2caddr; 
 
    /* Configure the I2C Mode Register for Write master Mode */ 
    f_pI2cRegs->m_Icmdr = ( 
                          I2C_CONFIG_MST        // Master Mode 
                          | I2C_CONFIG_TRX      // Transmit 
                          | I2C_CONFIG_STT 
                          | I2C_CONFIG_IRS      // Enabled Mode 
                          | I2C_CONFIG_FREE 
                          | I2C_CONFIG_STP 
                          ); 
    DelayUSec(5); 
     
    timecount = 0; 
    while ( 1 ) 
        { 
        if ( timeout < timecount++) 
            { 
            OALMSG(OAL_ERROR, (L"Dm6446I2cWrite: Write Timed out.\r\n")); 
            break; 
            } 
 
        /* Read I2C Status register */ 
        i2cStatus = f_pI2cRegs->m_Icstr; 
         
        /* If we received the NACK then slave is not responding. so break */ 
        if (i2cStatus & I2C_STAT_NACK) 
            { 
            OALMSG(OAL_ERROR, (L"Dm6446I2cWrite: NACK received.\r\n")); 
            break; 
            } 
 
        /* Check if we lost the arbitration */ 
        if (i2cStatus & I2C_STAT_AL) 
            { 
            OALMSG(OAL_ERROR, (L"Dm6446I2cWrite: Arbitration Lost.\r\n")); 
            break; 
            } 
             
        /* Check if Stop condition was detected*/ 
        if (i2cStatus & I2C_STAT_SCD) 
            { 
            OALMSG(OAL_ERROR, (L"Dm6446I2cWrite: STOP condition detected.\r\n")); 
            break; 
            } 
         
        /* Check if we can copy the next data to transmit */ 
        if (i2cStatus & I2C_STAT_XRDY) 
            { 
            /* The Previous byte got written sucessfully */ 
             
            DelayUSec(InterCharDelay); 
             
            if ( 0 >= len ) 
                { 
                break; 
                } 
            else 
                { 
                /* restart the timeout count */ 
                timecount = 0; 
                 
                /* Send Data */ 
                f_pI2cRegs->m_Icdxr = *pData++; 
                 
                /* Increment the number of bytes written */ 
                BytesWritten++;                 
  
                /* Decrement Length */ 
                len--; 
                 
                /* Clear Transmit Ready field */ 
                f_pI2cRegs->m_Icstr |= (I2C_STAT_XRDY); 
                } 
            } 
        } 
    Dm6446I2cStop(); 
     
    OALMSG(FALSE, (L"-Dm6446I2cWrite: %x\r\n", BytesWritten)); 
     
    return (BytesWritten); 
} 
//============================================================================== 
//!  \fn     USHORT Dm6446I2cRead( 
//!                         ULONG   Handle 
//!                         USHORT Slave_addrs, 
//!                         UINT8* Buffer, 
//!                         USHORT Len, 
//!                         USHORT* pBytesWritten 
//!                         ) 
//!  \brief  Implements DM6446 I2c Read function 
//!  \param  Handle ULONG Handle to the I2C bus returned by BusAcauire  
//!          Slave_addrs Address of the slave from which data is to be read 
//!          Buffer     Buffer to hold the data read 
//!          length     Length of the data to be read from slave 
//!          pBytesWritten Pointer to Update Actual Number of Bytes Read 
//!  \return USHORT Number of Bytes read 
//============================================================================== 
USHORT 
Dm6446I2cRead( 
    ULONG   Handle, 
    USHORT i2caddr, 
    UINT8* pData, 
    USHORT len, 
    USHORT* pBytesWritten 
    ) 
{ 
    USHORT BytesRead = 0; 
    USHORT tempCntr; 
    ULONG  i2cStatus; 
    int timeout   = 0x20000; 
    int timecount = 0; 
 
     
    /* kick back if the handle is not corresponding to the one we have */ 
    if ((Handle != f_I2cBusHandle) || 
        (f_I2cBusAcquired != TRUE)) 
        { 
        OALMSG( OAL_ERROR, (L"Dm6446I2cRead: invalid access.\r\n")); 
        return (BytesRead); 
        } 
 
    /* Poll BB field on I2C Interrupt Status - Wait for bus to become free */ 
    for ( ; (f_pI2cRegs->m_Icstr & I2C_STAT_BB) ; timecount++ ) 
        { 
        /* MISTRAL TBD: NEED TO DEFINE BUS_BUSY_STATE #define */ 
         
        if ( timecount >= timeout ) 
            { 
            OALMSG( OAL_ERROR, (L"Dm6446I2cRead: Bus Busy.\r\n")); 
            return(BytesRead); 
            } 
        if (f_PrevI2cSlaveAddr == i2caddr) 
            { 
            /* This is essentially the repeat start so let this proceed */ 
            break; 
            } 
        } 
         
    /* We got the bus access */ 
    /* Assign the Length and Slave Address */ 
    f_pI2cRegs->m_Iccnt = len; 
    f_pI2cRegs->m_Icsar = i2caddr; 
 
 
    /* Clear any pending interrupts by reading the IVR */ 
    f_pI2cRegs->m_Icstr = f_pI2cRegs->m_Icstr; 
    tempCntr = f_pI2cRegs->m_Icivr; 
 
    /* Configure the I2C Mode Register for Read master Mode */ 
    f_pI2cRegs->m_Icmdr = ( I2C_CONFIG_MST 
                            | I2C_CONFIG_STT 
                            | I2C_CONFIG_IRS 
                            | I2C_CONFIG_FREE 
                            | I2C_CONFIG_STP 
                          ); 
    DelayUSec(5); 
     
    timecount = 0; 
    while ( 1 ) 
    { 
        if ( timeout < timecount++) 
            { 
            OALMSG(OAL_ERROR, (L"Dm6446I2cRead: Read Timed out.\r\n")); 
            break; 
            } 
 
        /* Read I2C Status register */ 
        i2cStatus = f_pI2cRegs->m_Icstr; 
         
        /* If we received the NACK then slave is not responding. so break */ 
        if (i2cStatus & I2C_STAT_NACK) 
            { 
            OALMSG(OAL_ERROR, (L"Dm6446I2cRead: NACK received.\r\n")); 
            break; 
            } 
         
        /* Check if Stop condition was detected*/ 
        if (i2cStatus & I2C_STAT_SCD) 
            { 
            OALMSG(FALSE, (L"Dm6446I2cRead: STOP condition detected.\r\n")); 
            break; 
            } 
         
        if (i2cStatus & I2C_STAT_RRDY) 
            { 
            /* Read Data */ 
            *pData++ = f_pI2cRegs->m_Icdrr; 
            BytesRead++; 
            len--; 
     
            /* Clear Receive Ready field */ 
            f_pI2cRegs->m_Icstr |= I2C_STAT_RRDY; 
            }     
    } 
     
    OALMSG(FALSE, (L"-Dm6446I2cRead: %x\r\n", BytesRead)); 
    Dm6446I2cStop(); 
     
    /* Update the Actual number of bytes read */ 
    if (pBytesWritten != NULL) 
        { 
        *pBytesWritten = BytesRead; 
        } 
     
    return (BytesRead); 
}