Skip to content
Snippets Groups Projects
omap24xx_i2c.c 7.55 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * Basic I2C functions
     *
     * Copyright (c) 2004 Texas Instruments
     *
     * This package is free software;  you can redistribute it and/or
     * modify it under the terms of the license found in the file
     * named COPYING that should have accompanied this file.
     *
     * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
     * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
     * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
     *
     * Author: Jian Zhang jzhang@ti.com, Texas Instruments
     *
     * Copyright (c) 2003 Wolfgang Denk, wd@denx.de
     * Rewritten to fit into the current U-Boot framework
     *
     * Adapted for OMAP2420 I2C, r-woodruff2@ti.com
     *
     */
    
    #include <common.h>
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    #include <asm/arch/i2c.h>
    #include <asm/io.h>
    
    static void wait_for_bb (void);
    static u16 wait_for_pin (void);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    static void flush_fifo(void);
    
    
    void i2c_init (int speed, int slaveadd)
    {
    	u16 scl;
    
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew(0x2, I2C_SYSC); /* for ES2 after soft reset */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	udelay(1000);
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew(0x0, I2C_SYSC); /* will probably self clear but */
    
    Dirk Behme's avatar
    Dirk Behme committed
    	if (readw (I2C_CON) & I2C_CON_EN) {
    		writew (0, I2C_CON);
    
    	/* 12MHz I2C module clock */
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew (0, I2C_PSC);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	speed = speed/1000;		    /* 100 or 400 */
    	scl = ((12000/(speed*2)) - 7);	/* use 7 when PSC = 0 */
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew (scl, I2C_SCLL);
    	writew (scl, I2C_SCLH);
    
    	/* own address */
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew (slaveadd, I2C_OA);
    	writew (I2C_CON_EN, I2C_CON);
    
    	/* have to enable intrrupts or OMAP i2c module doesn't work */
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew (I2C_IE_XRDY_IE | I2C_IE_RRDY_IE | I2C_IE_ARDY_IE |
    		I2C_IE_NACK_IE | I2C_IE_AL_IE, I2C_IE);
    
    	udelay (1000);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	flush_fifo();
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew (0xFFFF, I2C_STAT);
    	writew (0, I2C_CNT);
    
    }
    
    static int i2c_read_byte (u8 devaddr, u8 regoffset, u8 * value)
    {
    	int i2c_error = 0;
    	u16 status;
    
    	/* wait until bus not busy */
    	wait_for_bb ();
    
    	/* one byte only */
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew (1, I2C_CNT);
    
    	/* set slave address */
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew (devaddr, I2C_SA);
    
    	/* no stop bit needed here */
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX, I2C_CON);
    
    
    	status = wait_for_pin ();
    
    	if (status & I2C_STAT_XRDY) {
    		/* Important: have to use byte access */
    
    Dirk Behme's avatar
    Dirk Behme committed
    		writeb (regoffset, I2C_DATA);
    
    		udelay (20000);
    
    Dirk Behme's avatar
    Dirk Behme committed
    		if (readw (I2C_STAT) & I2C_STAT_NACK) {
    
    			i2c_error = 1;
    		}
    	} else {
    		i2c_error = 1;
    	}
    
    	if (!i2c_error) {
    		/* free bus, otherwise we can't use a combined transction */
    
    Dirk Behme's avatar
    Dirk Behme committed
    		writew (0, I2C_CON);
    		while (readw (I2C_STAT) || (readw (I2C_CON) & I2C_CON_MST)) {
    
    			udelay (10000);
    			/* Have to clear pending interrupt to clear I2C_STAT */
    
    Dirk Behme's avatar
    Dirk Behme committed
    			writew (0xFFFF, I2C_STAT);
    
    		}
    
    		wait_for_bb ();
    		/* set slave address */
    
    Dirk Behme's avatar
    Dirk Behme committed
    		writew (devaddr, I2C_SA);
    
    		/* read one byte from slave */
    
    Dirk Behme's avatar
    Dirk Behme committed
    		writew (1, I2C_CNT);
    
    		/* need stop bit here */
    
    Dirk Behme's avatar
    Dirk Behme committed
    		writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP,
    			I2C_CON);
    
    
    		status = wait_for_pin ();
    		if (status & I2C_STAT_RRDY) {
    
    Dirk Behme's avatar
    Dirk Behme committed
    #if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
    			*value = readb (I2C_DATA);
    #else
    
    Dirk Behme's avatar
    Dirk Behme committed
    			*value = readw (I2C_DATA);
    
    Dirk Behme's avatar
    Dirk Behme committed
    #endif
    
    			udelay (20000);
    		} else {
    			i2c_error = 1;
    		}
    
    		if (!i2c_error) {
    
    Dirk Behme's avatar
    Dirk Behme committed
    			writew (I2C_CON_EN, I2C_CON);
    			while (readw (I2C_STAT)
    			       || (readw (I2C_CON) & I2C_CON_MST)) {
    
    				udelay (10000);
    
    Dirk Behme's avatar
    Dirk Behme committed
    				writew (0xFFFF, I2C_STAT);
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew (0xFFFF, I2C_STAT);
    	writew (0, I2C_CNT);
    
    	return i2c_error;
    }
    
    static int i2c_write_byte (u8 devaddr, u8 regoffset, u8 value)
    {
    	int i2c_error = 0;
    	u16 status, stat;
    
    	/* wait until bus not busy */
    	wait_for_bb ();
    
    	/* two bytes */
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew (2, I2C_CNT);
    
    	/* set slave address */
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew (devaddr, I2C_SA);
    
    	/* stop bit needed here */
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX |
    		I2C_CON_STP, I2C_CON);
    
    
    	/* wait until state change */
    	status = wait_for_pin ();
    
    	if (status & I2C_STAT_XRDY) {
    
    Dirk Behme's avatar
    Dirk Behme committed
    #if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
    		/* send out 1 byte */
    		writeb (regoffset, I2C_DATA);
    		writew (I2C_STAT_XRDY, I2C_STAT);
    
    		status = wait_for_pin ();
    		if ((status & I2C_STAT_XRDY)) {
    			/* send out next 1 byte */
    			writeb (value, I2C_DATA);
    			writew (I2C_STAT_XRDY, I2C_STAT);
    		} else {
    			i2c_error = 1;
    		}
    #else
    
    		/* send out two bytes */
    
    Dirk Behme's avatar
    Dirk Behme committed
    		writew ((value << 8) + regoffset, I2C_DATA);
    
    Dirk Behme's avatar
    Dirk Behme committed
    #endif
    
    		/* must have enough delay to allow BB bit to go low */
    		udelay (50000);
    
    Dirk Behme's avatar
    Dirk Behme committed
    		if (readw (I2C_STAT) & I2C_STAT_NACK) {
    
    			i2c_error = 1;
    		}
    	} else {
    		i2c_error = 1;
    	}
    
    	if (!i2c_error) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		int eout = 200;
    
    
    Dirk Behme's avatar
    Dirk Behme committed
    		writew (I2C_CON_EN, I2C_CON);
    		while ((stat = readw (I2C_STAT)) || (readw (I2C_CON) & I2C_CON_MST)) {
    
    			udelay (1000);
    			/* have to read to clear intrrupt */
    
    Dirk Behme's avatar
    Dirk Behme committed
    			writew (0xFFFF, I2C_STAT);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			if(--eout == 0) /* better leave with error than hang */
    				break;
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew (0xFFFF, I2C_STAT);
    	writew (0, I2C_CNT);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    static void flush_fifo(void)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	/* note: if you try and read data when its not there or ready
    	 * you get a bus error
    	 */
    
    Dirk Behme's avatar
    Dirk Behme committed
    		stat = readw(I2C_STAT);
    
    		if(stat == I2C_STAT_RRDY){
    
    Dirk Behme's avatar
    Dirk Behme committed
    #if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX)
    			readb(I2C_DATA);
    #else
    
    Dirk Behme's avatar
    Dirk Behme committed
    			readw(I2C_DATA);
    
    Dirk Behme's avatar
    Dirk Behme committed
    #endif
    
    Dirk Behme's avatar
    Dirk Behme committed
    			writew(I2C_STAT_RRDY,I2C_STAT);
    
    			udelay(1000);
    		}else
    			break;
    	}
    }
    
    int i2c_probe (uchar chip)
    {
    	int res = 1; /* default = fail */
    
    
    Dirk Behme's avatar
    Dirk Behme committed
    	if (chip == readw (I2C_OA)) {
    
    		return res;
    	}
    
    	/* wait until bus not busy */
    	wait_for_bb ();
    
    	/* try to read one byte */
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew (1, I2C_CNT);
    
    	/* set slave address */
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew (chip, I2C_SA);
    
    	/* stop bit needed here */
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP, I2C_CON);
    
    	/* enough delay for the NACK bit set */
    	udelay (50000);
    
    
    Dirk Behme's avatar
    Dirk Behme committed
    	if (!(readw (I2C_STAT) & I2C_STAT_NACK)) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		res = 0;      /* success case */
    
    Dirk Behme's avatar
    Dirk Behme committed
    		writew(0xFFFF, I2C_STAT);
    
    Dirk Behme's avatar
    Dirk Behme committed
    		writew(0xFFFF, I2C_STAT);	 /* failue, clear sources*/
    		writew (readw (I2C_CON) | I2C_CON_STP, I2C_CON); /* finish up xfer */
    
    		udelay(20000);
    		wait_for_bb ();
    	}
    	flush_fifo();
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew (0, I2C_CNT); /* don't allow any more data in...we don't want it.*/
    	writew(0xFFFF, I2C_STAT);
    
    	return res;
    }
    
    int i2c_read (uchar chip, uint addr, int alen, uchar * buffer, int len)
    {
    	int i;
    
    	if (alen > 1) {
    		printf ("I2C read: addr len %d not supported\n", alen);
    		return 1;
    	}
    
    	if (addr + len > 256) {
    		printf ("I2C read: address out of range\n");
    		return 1;
    	}
    
    	for (i = 0; i < len; i++) {
    		if (i2c_read_byte (chip, addr + i, &buffer[i])) {
    			printf ("I2C read: I/O error\n");
    
    			i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
    
    			return 1;
    		}
    	}
    
    	return 0;
    }
    
    int i2c_write (uchar chip, uint addr, int alen, uchar * buffer, int len)
    {
    	int i;
    
    	if (alen > 1) {
    		printf ("I2C read: addr len %d not supported\n", alen);
    		return 1;
    	}
    
    	if (addr + len > 256) {
    		printf ("I2C read: address out of range\n");
    		return 1;
    	}
    
    	for (i = 0; i < len; i++) {
    		if (i2c_write_byte (chip, addr + i, buffer[i])) {
    			printf ("I2C read: I/O error\n");
    
    			i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
    
    			return 1;
    		}
    	}
    
    	return 0;
    }
    
    static void wait_for_bb (void)
    {
    	int timeout = 10;
    	u16 stat;
    
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew(0xFFFF, I2C_STAT);	 /* clear current interruts...*/
    	while ((stat = readw (I2C_STAT) & I2C_STAT_BB) && timeout--) {
    		writew (stat, I2C_STAT);
    
    		udelay (50000);
    	}
    
    	if (timeout <= 0) {
    		printf ("timed out in wait_for_bb: I2C_STAT=%x\n",
    
    Dirk Behme's avatar
    Dirk Behme committed
    			readw (I2C_STAT));
    
    Dirk Behme's avatar
    Dirk Behme committed
    	writew(0xFFFF, I2C_STAT);	 /* clear delayed stuff*/
    
    }
    
    static u16 wait_for_pin (void)
    {
    	u16 status;
    	int timeout = 10;
    
    	do {
    		udelay (1000);
    
    Dirk Behme's avatar
    Dirk Behme committed
    		status = readw (I2C_STAT);
    
    	} while (  !(status &
    		   (I2C_STAT_ROVR | I2C_STAT_XUDF | I2C_STAT_XRDY |
    		    I2C_STAT_RRDY | I2C_STAT_ARDY | I2C_STAT_NACK |
    		    I2C_STAT_AL)) && timeout--);
    
    	if (timeout <= 0) {
    		printf ("timed out in wait_for_pin: I2C_STAT=%x\n",
    
    Dirk Behme's avatar
    Dirk Behme committed
    			readw (I2C_STAT));
    			writew(0xFFFF, I2C_STAT);