Hi
Please check the following code snippet to read and write on i2c, i2c read gives wrong data. I am trying to reads the chip id of SGTL5000's chip id on i.mx51 EVK board it should return 0xA000 but the code returns 0xff. Looking for quick solutions,
void mx6q_i2c_reset(void){
// Reset I2C registers
out16(dev_r->regbase + I2C_AR, 0);
out16(dev_r->regbase + I2C_IFDR, 0);
out16(dev_r->regbase + I2C_I2CR, 0);
out16(dev_r->regbase + I2C_I2SR, 0);
}
void i2c_init(void){
// I2C clock rate
out16(dev_r->regbase + I2C_IFDR, dev_r->div);
}
static int wait_op_done(int is_tx)
{
int i = 0;
volatile unsigned char stats;
i = WAIT_RXAK_LOOPS;
while ((((stats = in16(dev_r->regbase + I2C_I2SR)) & I2C_I2SR_ICF) == 0 )&& --i > 0){
//while ((((stats = in16(dev_r->regbase + I2C_I2SR)) & I2C_I2SR_IIF) == 0 ||
// (stats & I2C_I2SR_ICF) == 0)) {
if (stats & I2C_I2SR_IAL) {
printf("Arbitration lost\n");
return -1;
}
}
if (i <= 0) {
printf("wait op done timeout unexpected\n");
return -1;
}
if (is_tx) {
if (stats & I2C_I2SR_IAL) {
printf("Arbitration lost\n");
return -1;
}
if (stats & I2C_I2SR_RXAK) {
printf("No ack received\n");
return -1;
}
}
return 0;
}
static inline int wait_till_busy()
{
int i = I2C_WAIT_CNT;
while (((in16(dev_r->regbase + I2C_I2SR) & I2C_I2SR_IBB) == 0) && (--i > 0)) {
if (in16(dev_r->regbase + I2C_I2SR) & I2C_I2SR_IAL) {
printf("arbitration lost!\n");
return -1;
}
}
if (i <= 0) {
printf(" timeout unexpected\n");
return -1;
}
return 0;
}
static uint16_t tx_byte(uint16_t *data){
out16(dev_r->regbase + I2C_I2SR, 0);
printf("tx byte data 0x%x\n",*data);
out16(dev_r->regbase + I2C_I2DR, *data);
if (wait_op_done(1) != 0)
return -1;
return 0;
}
static uint16_t rx_byte(uint16_t *data)
{
unsigned short i2cr;
if (wait_op_done(0) != 0)
return -1;
out16(dev_r->regbase + I2C_I2SR, 0);
i2cr = in16(dev_r->regbase + I2C_I2CR);
out16(dev_r->regbase + I2C_I2CR, (i2cr & ~( I2C_I2CR_MSTA | I2C_I2CR_MTX)));
*data = in16(dev_r->regbase + I2C_I2DR);
printf("rx byte data 0x%x\n",*data); //when data is read from SGTL5000 chip id register gives wrong data.
return 0;
}
static uint16_t i2c_transaction(uint16_t reg, uint16_t * val, int dir){
volatile int i,i2cr=0;
uint16_t data;
int ret = 0;
i2cr |= I2C_I2CR_IEN;
out16(dev_r->regbase + I2C_I2CR,i2cr);
for (i = 0; i < 100; i++) ;
/* Check if bus is free */
if ((in16(dev_r->regbase + I2C_I2SR) & I2C_I2SR_IBB) != 0) {
printf(" Bus is not free\n");
return -1;
}
/* Set Device address to I2C Address Register*/
data = ((dev_r->slave << 1) | I2C_WRITE) & 0xFF;
out16(dev_r->regbase + I2C_AR , data);
/* Start I2C Transaction */
i2cr |= I2C_I2CR_MSTA | I2C_I2CR_MTX;
out16(dev_r->regbase + I2C_I2CR, i2cr);
/*Write Device Address to Data Register*/
if (tx_byte(&data) != 0) {
printf("Failed to transmit slave address\n");
return -1;
}
// make sure bus is busy after the START signal
if (wait_till_busy() != 0) {
printf("Bus is not busy\n");
return -1;
}
// send I2C device register address
data = reg & 0xFF;
if (tx_byte(&data) != 0) {
printf(" Failed to transmit device register address\n");
return -1;
}
if (dir == I2C_WRITE) {
data = *val & 0xFFFF;
if((ret = tx_byte(&data)) != 0) {
printf(" Failed to transmit data while write\n");
}
out16(dev_r->regbase + I2C_I2CR, (I2C_I2CR_IEN | I2C_I2CR_MTX));
}
else {
// do repeat-start
i2cr = in16(dev_r->regbase + I2C_I2CR);
out16(dev_r->regbase + I2C_I2CR, (i2cr | I2C_I2CR_RSTA));
// send slave address again, but indicate read operation
data = ((dev_r->slave << 1) | I2C_READ) & 0xFF;
if (tx_byte(&data) != 0) {
printf(" Failed to transmit slave address in read\n");
return -1;
}
// change to receive mode
i2cr = in16(dev_r->regbase + I2C_I2CR);
// read only one byte, make sure don't send ack
//i2cr |= I2C_I2CR_TXAK;
i2cr &= ~(I2C_I2CR_MTX);
out16(dev_r->regbase + I2C_I2CR, i2cr);
// dummy read
in16(dev_r->regbase + I2C_I2DR);
// now reading
if (rx_byte(&data) != 0) {
printf(" Failed to receive data\n");
return -1;
}
*val = data;
printf("data from i2c 0x%x\n",*val);
}
return ret;
}
/**** from read file operations sending the reg address to be read ***/
dev_r->reg = 0x0000;
/**** from write file operations sending the reg address to be read ***/
dev_r->reg = 0x0000;
dev_r->val = 0x0010;
Other initializations like slave address and setting up frequency divider value
dev->div = 0x16;
dev->slave = 0x0A;
Please provide suggestions where logics are going wrong.
Thanks
Could atleast help me what are the necessary mux and pad configurations to be enabled for SGTL5000 to respond.
According to the schematic of i.MX51EVK, the ctrl_data and ctrl_clk of SGTL5000 is connect to the I2C2_SDA and I2C2_SCL. And these two I2C2 pins are the KEY_COL4 and KEY_COL5. So, please check the IOMUX and PAD setting for these two pins are proper.
For the IOMUX example, you can take the Linux BSP for reference.
This is the iomux-mx51.h:
linux-2.6-imx.git - Freescale i.MX Linux Tree
The iomux-mx51.h defined all the pin mux and pad setting. You can find below settings for I2C2 in this file.
#define MX51_I2C_PAD_CTRL (PAD_CTL_SRE_FAST | PAD_CTL_ODE | \ PAD_CTL_DSE_HIGH | PAD_CTL_PUS_100K_UP | \ PAD_CTL_HYS)
#define _MX51_PAD_KEY_COL4__I2C2_SCL IOMUX_PAD(0x65c, 0x26c, 0x13, 0x09b8, 1, 0)
#define _MX51_PAD_KEY_COL5__I2C2_SDA IOMUX_PAD(0x660, 0x270, 0x13, 0x09bc, 1, 0)
#define MX51_PAD_KEY_COL4__I2C2_SCL (_MX51_PAD_KEY_COL4__I2C2_SCL | MUX_PAD_CTRL(MX51_I2C_PAD_CTRL))
#define MX51_PAD_KEY_COL5__I2C2_SDA (_MX51_PAD_KEY_COL5__I2C2_SDA | MUX_PAD_CTRL(MX51_I2C_PAD_CTRL))
let take the _MX51_PAD_KEY_COL4__I2C2_SCL for example:
(0x65C is the PAD setting offset address,
0x26C is the IOMUX setting offset address,
0x13 is SION=1, ALT3
0x09b8 is the offset address of daisy chain input select
1 is the "input select" for KEY_COL4 for Mode:ALT3.)
For more details, please read the "IOMUXC" in i.MX51 Reference Manual.
Then for i.MX51EVK, the specific pin mux will be defined in board-mx51_babbage.c "mx51babbage_pads[]":
Thanks for the reply. could you identify what actual mux and pad configurations are required. Since I am working for QNX driver and trying to understand what configurations is missed. In the link shared I found the following function,
io_cfg_i2c(base);
and its implementation in hardware.c
But what actual configuration is been missed in the above shared snippet. Looking for suggestions.
Thanks