i2c read and write issue on i.mx 51

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){
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 or any configuration is missed.
I have not implemented mux and pad configurations of i.mx51 in the RM, since those
configurations are taken care in BSP, should I re-initialize it again.
Thanks

Where is the rest of your test code? Ie your main()

You may not have obtained the right privileges / mapped the right memory etc. Hard to tell from what you posted here.

Also it’s nicer if you block code with [code]

[/code] blocks for formatting.

Tim

int i2c_io_read(resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb){

	printf("I2C MASTER READ\n");

	int         nleft;
	int         nbytes;
	int         nparts;
	int         status;

	if ((status = iofunc_read_verify (ctp, msg, ocb, NULL)) != EOK)
	        return (status);

	if ((msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE)
	        return (ENOSYS);

	nleft = ocb->attr->nbytes - ocb->offset;
	nbytes = min(msg->i.nbytes, nleft);

	if (nbytes > 0) {
	          /* set up the return data IOV */
			  dev_r->reg = 0x000E;
			  i2c_transaction(dev_r->reg, dev_r->val, I2C_READ);

			  _IO_SET_READ_NBYTES (ctp, 2);

			  resmgr_msgwrite(ctp, (dev_r->val),2, 0);

	} else {
	           _IO_SET_READ_NBYTES (ctp, 0);
               nparts = 0;
	}

	if (msg->i.nbytes > 0)
	        ocb->attr->flags |= IOFUNC_ATTR_ATIME;

    return (_RESMGR_NPARTS (nparts));
}

int i2c_io_write(resmgr_context_t *ctp, io_write_t *msg, RESMGR_OCB_T *ocb){

	printf("I2C MASTER WRITE\n");
	int     status;

	if ((status = iofunc_write_verify(ctp, msg, ocb, NULL)) != EOK)
	        return (status);

	if ((msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE)
	        return(ENOSYS);

	_IO_SET_WRITE_NBYTES (ctp, msg->i.nbytes);

	buffer =  malloc(msg->i.nbytes);
	if (buffer == NULL)
	        return(ENOMEM);

	resmgr_msgread(ctp, buffer, msg->i.nbytes, sizeof(msg->i));

	ocb->offset = 0;
	dev_r->attr.nbytes = msg->i.nbytes;

	if (msg->i.nbytes > 0)
		ocb->attr->flags |= IOFUNC_ATTR_MTIME | IOFUNC_ATTR_CTIME;

	dev_r->reg = ((uint16_t *)buffer)[0];
	dev_r->val = &(((uint16_t *)buffer)[1]);

	i2c_transaction(dev_r->reg, dev_r->val, I2C_WRITE);

	free(buffer);
	return (_RESMGR_NPARTS (0));
}

int i2c_io_open(resmgr_context_t *ctp, io_open_t *msg, RESMGR_HANDLE_T *handle, void *extra)
{
	printf("I2C Master driver  \n");

	mx6q_i2c_reset();
	i2c_init();

	return(iofunc_open_default( ctp, msg, handle, extra ));

}

int i2c_io_close( resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb )
{
	printf("I2C DRIVER CLOSED\n");
	mx6q_i2c_reset();
	
	return( EOK );
}
int mx6q_i2c_init()
{
	resmgr_attr_t  resmgr_attr;
	mx6x_i2c_dev_t *dev;

	if (-1 == ThreadCtl(_NTO_TCTL_IO, 0)) {
	        perror("ThreadCtl");
	        return NULL;
	}

	dev = malloc(sizeof(mx6x_i2c_dev_t));
	if (!dev){
			printf("allocation failed\n");
			goto fail;
	}

	dev->base	 = MX51_I2C2_BASE;
	dev->div	 = 0x14;
	dev->reglen	 = IMX_I2C_REGLEN;
	dev->slave 	 = I2C_SLAVE_ID;
	dev->intr 	 = MX35_I2C2_IRQ;
	dev->iid 	 = -1;
	dev->regbase = mmap_device_io(dev->reglen, dev->base);
	if (dev->regbase == (uintptr_t)MAP_FAILED) {
			perror("mmap_device_io");
			goto fail;
	}

	if(dev!=NULL){ /*work around to share the device information to other file operations*/
	    	 dev_r = dev;
	 }

	if((dev->i2c_dispatch = dispatch_create()) == NULL){
			fprintf( stderr, "Unable to alloc pool attr \n" );
			return EXIT_FAILURE;
	}

	memset(&resmgr_attr, 0, sizeof (resmgr_attr));
	resmgr_attr.nparts_max = 1;
	resmgr_attr.msg_max_size = 2048;

	iofunc_func_init (_RESMGR_CONNECT_NFUNCS,
						&dev->connect_funcs,
                        _RESMGR_IO_NFUNCS,
                        &dev->io_funcs);


    if((dev->attr.rdev = rsrcdbmgr_devno_attach("i2c2", -1, 0))!=-1){

    	dev->connect_funcs.open = i2c_io_open;
    	dev->io_funcs.close_ocb = i2c_io_close;
    	dev->io_funcs.read = i2c_io_read;
    	dev->io_funcs.write = i2c_io_write;
    	dev->io_funcs.devctl = i2c_io_devctl;
    	iofunc_attr_init (&dev->attr, 0666 | S_IFCHR, 0, 0);

    	if ((dev->resmgr_id=resmgr_attach (dev->i2c_dispatch,
    										&resmgr_attr,
    										"/dev/i2c2",
    										_FTYPE_ANY,
    											0,
    										&dev->connect_funcs,
    										&dev->io_funcs,
    										&dev->attr)) == -1) {
            printf("Unable to resmgr_attach \n");
            return EXIT_FAILURE;
    	}
    	else {
    		resmgr_devino( dev->resmgr_id, &dev->io_mount.dev, &dev->attr.inode );
    	}

   	rsrcdbmgr_devno_detach( dev->attr.rdev, 0 );

    	// allocate a context
    	dev->i2c_ctp = dispatch_context_alloc (dev->i2c_dispatch);

    	// wait here forever, handling messages
    	while (1) {
    		if ((dev->i2c_ctp = dispatch_block (dev->i2c_ctp)) == NULL) {
               printf ("Unable to dispatch_block\n");
               exit (EXIT_FAILURE);
        }

        dispatch_handler (dev->i2c_ctp);
     }
    }
    return (EXIT_SUCCESS);

fail:
     free(dev);
     return NULL;
}

int main( int argc, char *argv[] )
{
	if(mx6q_i2c_init())
		exit( EXIT_FAILURE );
	exit( EXIT_SUCCESS );

}

Should I reinitialize the mux and pad configurations which already done in BSP.

[code]Please find code below

int i2c_io_read(resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb){

printf("I2C MASTER READ\n");

int         nleft;
int         nbytes;
int         nparts;
int         status;

if ((status = iofunc_read_verify (ctp, msg, ocb, NULL)) != EOK)
        return (status);

if ((msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE)
        return (ENOSYS);

nleft = ocb->attr->nbytes - ocb->offset;
nbytes = min(msg->i.nbytes, nleft);

if (nbytes > 0) {
          /* set up the return data IOV */
		  dev_r->reg = 0x000E;
		  i2c_transaction(dev_r->reg, dev_r->val, I2C_READ);

		  _IO_SET_READ_NBYTES (ctp, 2);

		  resmgr_msgwrite(ctp, (dev_r->val),2, 0);

} else {
           _IO_SET_READ_NBYTES (ctp, 0);
           nparts = 0;
}

if (msg->i.nbytes > 0)
        ocb->attr->flags |= IOFUNC_ATTR_ATIME;

return (_RESMGR_NPARTS (nparts));

}

int i2c_io_write(resmgr_context_t *ctp, io_write_t *msg, RESMGR_OCB_T *ocb){

printf("I2C MASTER WRITE\n");
int     status;

if ((status = iofunc_write_verify(ctp, msg, ocb, NULL)) != EOK)
        return (status);

if ((msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE)
        return(ENOSYS);

_IO_SET_WRITE_NBYTES (ctp, msg->i.nbytes);

buffer =  malloc(msg->i.nbytes);
if (buffer == NULL)
        return(ENOMEM);

resmgr_msgread(ctp, buffer, msg->i.nbytes, sizeof(msg->i));

ocb->offset = 0;
dev_r->attr.nbytes = msg->i.nbytes;

if (msg->i.nbytes > 0)
	ocb->attr->flags |= IOFUNC_ATTR_MTIME | IOFUNC_ATTR_CTIME;

dev_r->reg = ((uint16_t *)buffer)[0];
dev_r->val = &(((uint16_t *)buffer)[1]);

i2c_transaction(dev_r->reg, dev_r->val, I2C_WRITE);

free(buffer);
return (_RESMGR_NPARTS (0));

}

int i2c_io_open(resmgr_context_t *ctp, io_open_t *msg, RESMGR_HANDLE_T *handle, void *extra)
{
printf(“I2C Master driver \n”);

imx_i2c_reset();
i2c_init();

return(iofunc_open_default( ctp, msg, handle, extra ));

}

int i2c_io_close( resmgr_context_t *ctp, void *reserved, RESMGR_OCB_T *ocb )
{
printf(“I2C DRIVER CLOSED\n”);
imx_i2c_reset();
InterruptUnmask(dev_r->intr, dev_r->iid);
return( EOK );
}

int mx6q_i2c_init()
{
resmgr_attr_t resmgr_attr;
mx6x_i2c_dev_t *dev;

if (-1 == ThreadCtl(_NTO_TCTL_IO, 0)) {
        perror("ThreadCtl");
        return NULL;
}

dev = malloc(sizeof(mx6x_i2c_dev_t));
if (!dev){
		printf("allocation failed\n");
		goto fail;
}

dev->base	 = MX51_I2C2_BASE;
dev->div	 = 0x14;
dev->reglen	 = IMX_I2C_REGLEN;
dev->slave 	 = I2C_SLAVE_ID;
dev->intr 	 = MX35_I2C2_IRQ;
dev->iid 	 = -1;
dev->regbase = mmap_device_io(dev->reglen, dev->base);
if (dev->regbase == (uintptr_t)MAP_FAILED) {
		perror("mmap_device_io");
		goto fail;
}

if(dev!=NULL){ /*work around to share the device information to other file operations*/
    	 dev_r = dev;
 }

if((dev->i2c_dispatch = dispatch_create()) == NULL){
		fprintf( stderr, "Unable to alloc pool attr \n" );
		return EXIT_FAILURE;
}

memset(&resmgr_attr, 0, sizeof (resmgr_attr));
resmgr_attr.nparts_max = 1;
resmgr_attr.msg_max_size = 2048;

iofunc_func_init (_RESMGR_CONNECT_NFUNCS,
					&dev->connect_funcs,
                    _RESMGR_IO_NFUNCS,
                    &dev->io_funcs);


if((dev->attr.rdev = rsrcdbmgr_devno_attach("i2c2", -1, 0))!=-1){

	dev->connect_funcs.open = i2c_io_open;
	dev->io_funcs.close_ocb = i2c_io_close;
	dev->io_funcs.read = i2c_io_read;
	dev->io_funcs.write = i2c_io_write;
	dev->io_funcs.devctl = i2c_io_devctl;
	iofunc_attr_init (&dev->attr, 0666 | S_IFCHR, 0, 0);

	if ((dev->resmgr_id=resmgr_attach (dev->i2c_dispatch,
										&resmgr_attr,
										"/dev/i2c2",
										_FTYPE_ANY,
											0,
										&dev->connect_funcs,
										&dev->io_funcs,
										&dev->attr)) == -1) {
        printf("Unable to resmgr_attach \n");
        return EXIT_FAILURE;
	}
	else {
		resmgr_devino( dev->resmgr_id, &dev->io_mount.dev, &dev->attr.inode );
	}

	rsrcdbmgr_devno_detach( dev->attr.rdev, 0 );

	// allocate a context
	dev->i2c_ctp = dispatch_context_alloc (dev->i2c_dispatch);

	// wait here forever, handling messages
	while (1) {
		if ((dev->i2c_ctp = dispatch_block (dev->i2c_ctp)) == NULL) {
           printf ("Unable to dispatch_block\n");
           exit (EXIT_FAILURE);
    }

    dispatch_handler (dev->i2c_ctp);
 }
}
return (EXIT_SUCCESS);

fail:
free(dev);
return NULL;
}

int main( int argc, char *argv[] )
{
if(i2c_init())
exit( EXIT_FAILURE );
exit( EXIT_SUCCESS );

}
[/code]

Already mux and pad configurations for I2C2 and SGTL5000 is done and part of BSP. Should I redo as part of Resource Manager

I don’t know if you have to reset the mux / pad configuration. You’d need to read the manual on the hardware to know for sure if it has to be reset more than one time.

That said you seem to have post 2 different pieces of code.

  1. The code in your initial post which is your Resource Manager
  2. The code in your second post which appears to be code from the manufacturer of the board.

I am guessing the code in your Resource Manager is running in another thread from the other code. I’m also guessing this is the routine you are having trouble with.

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

I suggest you simplify things. Right after you complete the mmap_device_io() call I suggest you attempt to read the register that is giving you the wrong data. In other words I suggest you paste in the lines above after this call

   dev->regbase = mmap_device_io(dev->reglen, dev->base);
   if (dev->regbase == (uintptr_t)MAP_FAILED) {
         perror("mmap_device_io");
         goto fail;
   }

   if(dev!=NULL){ /*work around to share the device information to other file operations*/
           dev_r = dev;
    }

At this point all you have done is map the device and are trying to read the register. If this is successful and you get the A000 you know things are working and you are reading the hardware. Then you can start to figure out why the other read in the other code block isn’t working.

Tim

Hi Tim,
Thanks for the reply. Yes rx_byte() is the bug. As you mentioned both RM and manufacturer code running in different threads is not true. I have posted only i2c transaction code alone initially and as you requested rest of the RM is posted. As earlier mentioned the mux and pad configurations are defined in the bsp (main.c), hope re-configuration is not necessary. The test data I write to RM from client program and test data from RM to client program is working properly.

If the test data below works:

   dev->regbase = mmap_device_io(dev->reglen, dev->base);
   if (dev->regbase == (uintptr_t)MAP_FAILED) {
         perror("mmap_device_io");
         goto fail;
   }

   if(dev!=NULL){ /*work around to share the device information to other file operations*/
           dev_r = dev;
    }

unsigned short i2cr;
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.

and you get A000 then it’s something else that’s messed up in your code.

At this point you have to get in there and debug things by commenting out / removing code in your i2c_transaction() function. All the calls you are making there to switch modes may be breaking something. Comment them out one at a time until rx_byte() works. Until that time there is nothing we can do to help you because we have no idea where/how it went wrong.

Tim

I tried doing that but prior to that I need to write i2c slavce address, so without those sequence it is impossible to read the data. Is there any method to export following functions(in bold) defined in BSP to resource manager.

chip_access(CCM_BASE, 0, 0, 0x80);
cosr = [b]chip_read32/b;
cosr &= 0xFFFF0000;
cosr |= MX51_CCM_CLKOSEL(MX51_CLKOSEL_AUDIOCLOCK) | MX51_CCM_CLKOEN | MX51_CCM_CLKO_DIV1(AUDIO_CLKO_DIV1) | MX51_CCM_CLKO_DIV(clko_div);
chip_write32(MX51_CCM_CCOSR, cosr);
[b]chip_done/b;

I feel some more configurations are missing to make slave respond. Looking for suggestions.

You are trying to solve too many problems at once. You have to simplify things down to a small test case to prove you are talking to your board correctly. Only then can you move on to the Resource Manager stuff.

You need a simple program like this

    int main( int argc, char *argv[] )
    {
        mx6x_i2c_dev_t *dev;

        // Get privileges needed
        if (-1 == ThreadCtl(_NTO_TCTL_IO, 0)) {
              perror("ThreadCtl");
              return NULL;
        }

        dev = malloc(sizeof(mx6x_i2c_dev_t));
        if (!dev){
             printf("allocation failed\n");
             exit(EXIT_FAILURE);
        }

        // Are these values right for your particular board???
        dev->base    = MX51_I2C2_BASE;
        dev->div    = 0x14;
        dev->reglen    = IMX_I2C_REGLEN;
        dev->slave     = I2C_SLAVE_ID;
        dev->intr     = MX35_I2C2_IRQ;
        dev->iid     = -1;
        dev->regbase = mmap_device_io(dev->reglen, dev->base);
        if (dev->regbase == (uintptr_t)MAP_FAILED) {
         perror("mmap_device_io");
         exit(EXIT_FAILURE);
       }

       // Set clock rate. Again make sure in i2c_init() you have right clock rate for your board
       i2c_init();

       // Do your simple read of the register looking for the A000 response
       // Where did the code in i2c_transaction() come from? Did you write it or did the board manufacturer?
       i2c_transaction();
    }

Once this successfully works you can move on to trying to add in your RM stuff.

Also what does the documentation for your board say? Did you get any sample code from them / their website? I see some references to clock timing. I2C is very timing dependent and you have to know what clock speed your board is running at in order to interface with it correctly. Do you know you are using the right timing for this board?

Tim