blob: 83ebd5c99bcfae6c376a4bd73579aa7a31b79b43 [file] [log] [blame]
/*
* (C) Copyright 2016, ZIXC Corporation.
*
*/
#include <common.h>
#include <command.h>
#include <malloc.h>
#include <asm/io.h>
#include <i2c.h>
#include <drvs_gpio.h>
#include <asm/arch/i2c.h>
#include <asm/arch/hardware.h>
#include <asm/arch/uart.h>
#include <asm/arch/lsp_crpm.h>
#include <config.h>
T_I2C_Regs *p_i2c_reg[2] = {NULL};
static uint retries = 3; /* ÖØ¸´²Ù×÷´ÎÊý */
#define REG32(x) (*(volatile u32*)(x))
/*******************************************************************************
* Function:
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others: 1. ÔÚ100KHZƵÂÊÏ£¬Ê¹Óà zDrvI2c_DevRead_ByteStream ¶Á 32 ×Ö½Úʱ£¬(ÔÚU207ÊÖ»úÉÏ)
Ô¼ÏûºÄ tempTimeOut = 600
2. ÓÉÓÚCPUƵÂʲ»¶¨£¬bsp_udelayº¯ÊýÑÓʱ²»Ì«Çå³þ£¬¹ÊʹÓôó²ÎÊý
tempTimeOut = 0x20000;
********************************************************************************/
static int i2c_WaitTransferOver( T_I2C_Regs *i2c_reg )
{
uint tempTimeOut = 0x20000;
uint tempStatus = 0x0;
while( tempTimeOut )
{
tempStatus = i2c_reg->I2C_STATUS;
if(tempStatus & 0x1)
{
break;
}
else
{
tempTimeOut--;
}
}
if( 0 == tempTimeOut )
{
i2c_reg->I2C_STATUS |= I2C_IRQ_ACK;
return DRV_I2C_ERROR_TIMEOUT; /* ³¬Ê±´íÎó */
}
if (tempStatus & I2C_IRQ_ERR_DEVICE)
{
i2c_reg->I2C_STATUS |= I2C_IRQ_ACK;
return DRV_I2C_ERROR_DEVICE; /* Æ÷¼þµØÖ·´«ÊäÆÚ¼äÓдíÎó·¢Éú */
}
if (tempStatus & I2C_IRQ_ERR_DATA)
{
i2c_reg->I2C_STATUS |= I2C_IRQ_ACK;
return DRV_I2C_ERROR_DATA; /* ×ÓµØÖ·´«Êä»òÊý¾Ý´«Êä¹ý³ÌÖÐÓдíÎó·¢Éú */
}
if (tempStatus & I2C_IRQ_TIME_OUT)
{
i2c_reg->I2C_STATUS |= I2C_IRQ_ACK;
return DRV_I2C_ERROR_TIMEOUTSELF; /* ³¬Ê±´íÎó */
}
if (tempStatus & I2C_IRQ_TRANS_DONE)
{
i2c_reg->I2C_STATUS |= I2C_IRQ_ACK;
return 0; /* Õý³£Íê³É */
}
return 0;
}
/*******************************************************************************
* Function:
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others: -----------------------------
********************************************************************************/
void i2c_Start( T_I2C_Regs *i2c_reg )
{
i2c_reg->CMD |= I2C_CMD_START; /* ²úÉú¿ªÊ¼Ìõ¼þ£¬¿ªÊ¼´«Êä */
}
/*******************************************************************************
* Function:
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others:
********************************************************************************/
static int i2c_ReadFifoData( T_I2C_Regs *i2c_reg, uchar *read_buf, uint read_len )
{
if( i2c_reg->RXF_STATUS & I2C_RX_FIFO_FULL )
{
do
{
*read_buf++ = i2c_reg->DATA;
} while( --read_len );
}
else
{
return DRV_I2C_RECV_NOT_COMPLETE;
}
return 0;
}
/*******************************************************************************
* Function:
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others: ÕâÀïÖ±½ÓÈ϶¨ дµÄ¾ßÌåÊý¾Ý×î´ó¸öÊýΪ 30 £¬ÆäʵÔÚ 8λ¼Ä´æÆ÷ģʽÏÂ×î¶à¿ÉÒÔ 31¸ö
********************************************************************************/
void i2c_WriteFifoData( T_I2C_Regs *i2c_reg, uchar *writeBuf, uint writerNum )
{
do
{
i2c_reg->DATA = *writeBuf++; /* дÊý¾Ý */
}while(--writerNum != 0);
}
/*******************************************************************************
* Function:
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others: ÔÚÐ´ÕæÕýµÄÊý¾ÝǰдÈë¼Ä´æÆ÷µØÖ·µ½ FIFO
1.8λ¼Ä´æÆ÷µØÖ·
2.16λ¼Ä´æÆ÷µØÖ·
********************************************************************************/
static void i2c_WriteFifoRegAddr( T_I2C_Regs *i2c_reg, ushort reg_addr, uint reg_len )
{
ushort tempRegAddr = 0;
if( reg_len == 16 ) // Èç¹ûλ16λ¼Ä´æÆ÷µØÖ·£¬ÏÈд--¸ß8λ--; ºóд -µÍ8λ--
{
tempRegAddr = reg_addr;
i2c_reg->DATA = tempRegAddr >> 8;
}
i2c_reg->DATA = reg_addr;
}
/*******************************************************************************
* Function:
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others: ÔÚ¼òµ¥Ð´Ä£Ê½ÏÂ( 8λ¼Ä´æÆ÷ºÍ16λ¼Ä´æÆ÷ )£¬ÉèÖà FifoDepth
1.¸´Î» ½ÓÊÕFIFO ºÍ ·¢ËÍFIFO
2.ÅжÏÊÇ·ñΪ16λ¼Ä´æÆ÷µØÖ·£¬ÈçÊÇ£¬Ôò writerNum +2;
²»È»Îª 8 λ¼Ä´æÆ÷µØÖ·£¬ writerNum +1;
3.дÈë·¢Ë͸öÊýµ½ÏàÓ¦µÄ¼Ä´æÆ÷¡£
********************************************************************************/
static void i2c_SetFifoDepthSimpleWrite( T_I2C_Regs *i2c_reg, uint write_len, uint reg_len )
{
uint tempWriterNum = write_len;
//¸´Î»WRITE(read)_FIFO_SOFT_RESET
i2c_reg->TXF_CTRL |= I2C_TX_FIFO_RST;
i2c_reg->RXF_CTRL |= I2C_RX_FIFO_RST;
if( reg_len == 16 ) //ʹÓà 16λ¼Ä´æÆ÷µØÖ·
{
tempWriterNum++; //writerNum = writerNum + 2 - 1; //ÕâÀï¼õÈ¥ÁËд¼Ä´æÆ÷ʱÐèÒª½«È¥µÄ 1
}
i2c_reg->TXF_CTRL = tempWriterNum;
i2c_reg->RXF_CTRL = 0;
}
/*******************************************************************************
* Function:
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others:
********************************************************************************/
static void i2c_SetFifoDepthCombineRead( T_I2C_Regs *i2c_reg, uint read_len, uint reg_len )
{
uint tempWriterNum = 0;
//¸´Î»WRITE(read)_FIFO_SOFT_RESET
i2c_reg->TXF_CTRL |= I2C_TX_FIFO_RST;
i2c_reg->RXF_CTRL |= I2C_RX_FIFO_RST;
if( reg_len == 16 ) //ʹÓà 16λ¼Ä´æÆ÷µØÖ·
{
tempWriterNum++; //tempWriterNum = tempWriterNum + 2 - 1;
//ÕâÀï¼õÈ¥ÁËд¼Ä´æÆ÷ʱÐèÒª½«È¥µÄ 1
}
i2c_reg->TXF_CTRL = tempWriterNum;
i2c_reg->RXF_CTRL = --read_len;
}
/*******************************************************************************
* Function: i2c_SetFifoDepthSimpleRead
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others:
********************************************************************************/
static void i2c_SetFifoDepthSimpleRead( T_I2C_Regs *pi2cReg, uint readNum )
{
//¸´Î»WRITE(read)_FIFO_SOFT_RESET
pi2cReg->TXF_CTRL |= I2C_TX_FIFO_RST;
pi2cReg->RXF_CTRL |= I2C_RX_FIFO_RST;
pi2cReg->TXF_CTRL = 0;
pi2cReg->RXF_CTRL = --readNum;
}
/*******************************************************************************
* Function: i2c_SetFifoDepthSimpleWriteWithoutReg
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others:
********************************************************************************/
static void i2c_SetFifoDepthSimpleWriteWithoutReg( T_I2C_Regs *pi2cReg, uint writerNum )
{
//¸´Î»WRITE(read)_FIFO_SOFT_RESET
pi2cReg->TXF_CTRL |= I2C_TX_FIFO_RST;
pi2cReg->RXF_CTRL |= I2C_RX_FIFO_RST;
pi2cReg->TXF_CTRL = writerNum - 1;
pi2cReg->RXF_CTRL = 0;
}
/*******************************************************************************
* Function: i2c_SetFreq
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others: ÉèÖÃi2c×ÜÏߵįµÂÊ£¬CLK = Fref_clk/( (CLK_DIV_FS+1)*4 )
Fref_clk = 26M
²ÎÊý: I2C_FREQ_ONE_HUNDRED_KILO_HZ 100kHz CLK_DIV_FS = 64
²ÎÊý: I2C_FREQ_THREE_HUNDRED_KILO_HZ 300kHz CLK_DIV_FS = 21
²ÎÊý: I2C_FREQ_FOUR_HUNDRED_KILO_HZ 400kHz CLK_DIV_FS = 16
********************************************************************************/
static void i2c_SetFreq( T_I2C_Regs *pi2cReg, T_ZDrvI2c_BusFreq devFreq )
{
pi2cReg->CMD &= (~I2C_CMD_SPEED_MODE); /* ʹÓÿìËÙģʽ */
switch( devFreq )
{
case I2C_FREQ_ONE_HUNDRED_KILO_HZ:
pi2cReg->CLK_DIV_FS = 64;
break;
case I2C_FREQ_THREE_HUNDRED_KILO_HZ:
pi2cReg->CLK_DIV_FS = 21;
break;
case I2C_FREQ_FOUR_HUNDRED_KILO_HZ:
pi2cReg->CLK_DIV_FS = 16;
break;
default:
pi2cReg->CLK_DIV_FS = 64;
break;
}
}
/*******************************************************************************
* Function:
* Description:
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others: ÉèÖÃI2C×ÜÏߵĶÁд·½Ê½£¬ÕâÀïĬÈÏΪÖ÷»úģʽ£¬²»Ê¹ÓÃÖжÏ
·ÖΪ ¼òµ¥Ð´¡¢¼òµ¥¶Á¡¢ ---×éºÏ¶Á--- ÈýÖÖģʽ
********************************************************************************/
static void i2c_SetMode( T_I2C_Regs *i2c_reg, T_I2C_Mode mode )
{
uint cmd_reg = 0x0;
cmd_reg |= I2C_IRQ_ACK; //Çå³ýÖжϼ°ÆäËû´íÎó¼°×´Ì¬
/* ÕâÀï·ÀÖ¹Çå³ýÉϴδ«Êä¿ÉÄܲúÉúµÄ´íÎó״̬ */
cmd_reg |= I2C_CMD_MS_SEL; //Ö÷»úģʽ
cmd_reg &= (~I2C_CMD_IRQ_EN); //ÖжÏÇëÇ󹨱Õ;
switch( mode )
{
case I2C_SIMPLE_WRITE: //²ÉÓüòµ¥Ð´Ä£Ê½
cmd_reg &= (~(I2C_CMD_CMB_RW | I2C_CMD_RW));
break;
case I2C_COMBINE_READ:
cmd_reg |= I2C_CMD_CMB_RW; //---²ÉÓÃ×éºÏ¶Á---
cmd_reg &= (~I2C_CMD_RW); //0:w 1:r
break;
default:
cmd_reg &= (~I2C_CMD_CMB_RW); //²ÉÓüòµ¥¶Á
cmd_reg |= I2C_CMD_RW;
break;
}
i2c_reg->CMD = cmd_reg;
}
/*******************************************************************************
* Function:
* Description: д´Ó»úµØÖ·
* Parameters:
* Input:
*
* Output:
*
* Returns:
*
*
* Others: дÈë´Ó»úµØÖ·£¬ÕâÀïµÄ´Ó»úµØÖ·Ö§³Ö --7λ--10λ--- ´Ó»úµØÖ·
1.7λ´Ó»úµØÖ·£¬²»Ê¹ÄÜ10λµØÖ·£¬Ö±½ÓдÈë´Ó»úµØÖ·¼Ä´æÆ÷¡£¸üе±Ç°×ÜÏߵĴӻúµØÖ·
2.10λ´Ó»úµØÖ·£¬Ê¹ÄÜ10λµØÖ·£¬·Ö±ðдÈëÏàÓ¦¼Ä´æÆ÷¡£ ¸üе±Ç°×ÜÏߵĴӻúµØÖ·
********************************************************************************/
static void i2c_SetSlaveAddress( T_I2C_Regs *i2c_reg, ushort slaveAddr )
{
if( (slaveAddr & 0xf700) == 0 ) /* 7λ´Ó»úµØÖ· */
{
i2c_reg->CMD &= (~I2C_CMD_ADDR_MODE);
i2c_reg->DEVICE_L = slaveAddr;
}
else
{
i2c_reg->CMD |= I2C_CMD_ADDR_MODE;
i2c_reg->DEVICE_L = slaveAddr;
i2c_reg->DEVICE_H = slaveAddr >> 7;
}
}
/* ================================================================================
* i2c_read:
*/
int i2c_read(uint i2c_bus, ushort slave_addr, ushort reg_addr, uint reg_len,
uchar *read_buf, uint read_len)
{
int ret = 0;
T_I2C_Regs *i2c_Reg = p_i2c_reg[i2c_bus];
do //¿ªÊ¼´«Êä
{
i2c_SetFreq(i2c_Reg, I2C_FREQ_ONE_HUNDRED_KILO_HZ); //ÉèÖÃ×ÜÏ߯µÂÊ
i2c_SetSlaveAddress(i2c_Reg, slave_addr); //д´Ó»úµØÖ·
i2c_SetMode(i2c_Reg, I2C_COMBINE_READ); //×éºÏ¶Áģʽ
i2c_SetFifoDepthCombineRead(i2c_Reg, read_len, reg_len); //ÉèÖÃFIFOµÄÉî¶È
i2c_WriteFifoRegAddr(i2c_Reg, reg_addr, reg_len); //ÍùFIFOÖÐдÈë¼Ä´æÆ÷µØÖ·
i2c_Start(i2c_Reg); //²úÉú¿ªÊ¼Ìõ¼þ£¬¿ªÊ¼´«Êä
ret = i2c_WaitTransferOver(i2c_Reg); //µÈ´ý´«Êä½áÊø
if( ret != 0 )
{
continue; //´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ­»·²¢ÖØ´«
}
ret = i2c_ReadFifoData(i2c_Reg, read_buf, read_len ); //¶ÁÈ¡Êý¾Ý
if( ret != 0 )
{
continue; //´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ­»·²¢ÖØ´«
}
break; //´«Êä³É¹¦£¬Ìø³öÑ­»·
} while( --retries != 0 );
retries = 3;
return ret;
}
/* ================================================================================
* i2c_write:
*/
int i2c_write(uint i2c_bus, ushort slave_addr, ushort reg_addr, uint reg_len,
uchar *write_buf, uint write_len)
{
int ret = 0;
T_I2C_Regs *i2c_Reg = p_i2c_reg[i2c_bus];
do //¿ªÊ¼´«Êä
{
i2c_SetFreq(i2c_Reg, I2C_FREQ_ONE_HUNDRED_KILO_HZ); //ÉèÖÃ×ÜÏ߯µÂÊ
i2c_SetSlaveAddress(i2c_Reg, slave_addr); //д´Ó»úµØÖ·
i2c_SetMode(i2c_Reg, I2C_SIMPLE_WRITE); //×éºÏ¶Áģʽ
i2c_SetFifoDepthSimpleWrite(i2c_Reg, write_len, reg_len); //ÉèÖÃFIFOµÄÉî¶È
i2c_WriteFifoRegAddr(i2c_Reg, reg_addr, reg_len); //ÍùFIFOÖÐдÈë¼Ä´æÆ÷µØÖ·
i2c_WriteFifoData(i2c_Reg, write_buf, write_len); //ÍùFIFOдÈëÊý¾Ý
i2c_Start(i2c_Reg); //²úÉú¿ªÊ¼Ìõ¼þ£¬¿ªÊ¼´«Êä
ret = i2c_WaitTransferOver(i2c_Reg); //µÈ´ý´«Êä½áÊø
if( ret != 0 )
{
continue; //´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ­»·²¢ÖØ´«
}
break; //´«Êä³É¹¦£¬Ìø³öÑ­»·
} while( --retries != 0 );
retries = 3;
return ret;
}
/* ================================================================================
* i2c_simple_read:
*/
int i2c_simple_read(uint i2c_bus, ushort slave_addr, uchar *read_buf, uint read_len)
{
int ret = 0;
T_I2C_Regs *i2c_Reg = p_i2c_reg[i2c_bus];
if ( (i2c_Reg == NULL) || (read_buf == NULL) || (read_len > 33))//×î¶àд 32 ¸ö×Ö½Ú
{
return -1;
}
do //¿ªÊ¼´«Êä
{
i2c_SetFreq(i2c_Reg, I2C_FREQ_FOUR_HUNDRED_KILO_HZ); //ÉèÖÃ×ÜÏ߯µÂÊ
i2c_SetSlaveAddress(i2c_Reg, slave_addr); //д´Ó»úµØÖ·
i2c_SetMode(i2c_Reg, I2C_SIMPLE_READ); //×éºÏ¶Áģʽ
i2c_SetFifoDepthSimpleRead(i2c_Reg, read_len); //ÉèÖÃFIFOµÄÉî¶È
i2c_Start(i2c_Reg); //²úÉú¿ªÊ¼Ìõ¼þ£¬¿ªÊ¼´«Êä
ret = i2c_WaitTransferOver(i2c_Reg); //µÈ´ý´«Êä½áÊø
if( ret != 0 )
{
continue; //´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ­»·²¢ÖØ´«
}
ret = i2c_ReadFifoData(i2c_Reg, read_buf, read_len ); //¶ÁÈ¡Êý¾Ý
if( ret != 0 )
{
continue; //´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ­»·²¢ÖØ´«
}
break; //´«Êä³É¹¦£¬Ìø³öÑ­»·
} while( --retries != 0 );
retries = 3;
return ret;
}
/* ================================================================================
* i2c_simple_write:
*/
int i2c_simple_write(uint i2c_bus, ushort slave_addr,uchar *write_buf, uint write_len)
{
int ret = 0;
T_I2C_Regs *i2c_Reg = p_i2c_reg[i2c_bus];
if ( (i2c_Reg == NULL) || (write_buf == NULL) || (write_len > 33))//×î¶àд 32 ¸ö×Ö½Ú
{
return -1;
}
do //¿ªÊ¼´«Êä
{
i2c_SetFreq(i2c_Reg, I2C_FREQ_FOUR_HUNDRED_KILO_HZ); //ÉèÖÃ×ÜÏ߯µÂÊ
i2c_SetSlaveAddress(i2c_Reg, slave_addr); //д´Ó»úµØÖ·
i2c_SetMode(i2c_Reg, I2C_SIMPLE_WRITE); //×éºÏ¶Áģʽ
i2c_SetFifoDepthSimpleWriteWithoutReg(i2c_Reg, write_len); //ÉèÖÃFIFOµÄÉî¶È
i2c_WriteFifoData(i2c_Reg, write_buf, write_len); //ÍùFIFOдÈëÊý¾Ý
i2c_Start(i2c_Reg); //²úÉú¿ªÊ¼Ìõ¼þ£¬¿ªÊ¼´«Êä
ret = i2c_WaitTransferOver(i2c_Reg); //µÈ´ý´«Êä½áÊø
if( ret != 0 )
{
continue; //´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ­»·²¢ÖØ´«
}
break; //´«Êä³É¹¦£¬Ìø³öÑ­»·
} while( --retries != 0 );
retries = 3;
return ret;
}
/* ================================================================================
* i2c_init:
*/
int i2c_init(void)
{
p_i2c_reg[0] = (T_I2C_Regs *)ARM_I2C0_BASE;
p_i2c_reg[1] = (T_I2C_Regs *)ARM_I2C1_BASE;
/* p_i2c_reg[2] = (T_I2C_Regs *)ARM_PMICI2C_BASE; */
/* gpio config */
//gpio_set_reuse(GPIO_I2C0_CLK, 0x0);
zDrvGpio_SetFunc(GPIO43, GPIO43_SCL0);
zDrvGpio_SetDirection(GPIO43, GPIO_OUT);
//gpio_set_reuse(GPIO_I2C0_DATA, 0x0);
zDrvGpio_SetFunc(GPIO44, GPIO44_SDA0);
zDrvGpio_SetDirection(GPIO44, GPIO_OUT);
//gpio_set_reuse(GPIO_I2C1_CLK, 0x0);
zDrvGpio_SetFunc(GPIO45, GPIO45_SCL1);
zDrvGpio_SetDirection(GPIO45, GPIO_OUT);
//gpio_set_reuse(GPIO_I2C1_DATA, 0x0);
zDrvGpio_SetFunc(GPIO46, GPIO46_SDA1);
zDrvGpio_SetDirection(GPIO46, GPIO_OUT);
/* i2c1 new clk config */
REG32(0x140002c) &= ~(0x1<<4);//i2c1 select 26M
REG32(0x140002c) &= ~(0x1<<8);//reset on i2c1 apb clk
REG32(0x140002c) |= (0x1<<8);//reset off i2c1 apb clk
REG32(0x140002c) &= ~(0x1<<9);//reset on i2c1 work clk
REG32(0x140002c) |= (0x1<<9);//reset off i2c1 work clk
REG32(0x140002c) |= (0x1<<0);//enable i2c1 apb clk
REG32(0x140002c) |= (0x1<<1);//enable i2c1 work clk
/* pmic i2c clk config */
REG32(0x13b03c) &= ~(0x1<<1);//pmici2c select 26M
REG32(0x13b074) &= ~(0x1<<8);//reset on pmici2c apb clk
REG32(0x13b074) |= (0x1<<8);//reset off pmici2c apb clk
REG32(0x13b074) &= ~(0x1<<9);//reset on pmici2c work clk
REG32(0x13b074) |= (0x1<<9);//reset off pmici2c work clk
REG32(0x13b054) |= (0x1<<8);//enable pmici2c apb clk
REG32(0x13b054) |= (0x1<<9);//enable pmici2c work clk
return 0;
}