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); }