|  | /* | 
|  | * 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> | 
|  |  | 
|  | #include <asm/arch/i2c.h> | 
|  | #include <asm/io.h> | 
|  |  | 
|  | static void wait_for_bb (void); | 
|  | static u16 wait_for_pin (void); | 
|  | static void flush_fifo(void); | 
|  |  | 
|  | void i2c_init (int speed, int slaveadd) | 
|  | { | 
|  | u16 scl; | 
|  |  | 
|  | writew(0x2, I2C_SYSC); /* for ES2 after soft reset */ | 
|  | udelay(1000); | 
|  | writew(0x0, I2C_SYSC); /* will probably self clear but */ | 
|  |  | 
|  | if (readw (I2C_CON) & I2C_CON_EN) { | 
|  | writew (0, I2C_CON); | 
|  | udelay (50000); | 
|  | } | 
|  |  | 
|  | /* 12MHz I2C module clock */ | 
|  | writew (0, I2C_PSC); | 
|  | speed = speed/1000;		    /* 100 or 400 */ | 
|  | scl = ((12000/(speed*2)) - 7);	/* use 7 when PSC = 0 */ | 
|  | writew (scl, I2C_SCLL); | 
|  | writew (scl, I2C_SCLH); | 
|  | /* own address */ | 
|  | writew (slaveadd, I2C_OA); | 
|  | writew (I2C_CON_EN, I2C_CON); | 
|  |  | 
|  | /* have to enable intrrupts or OMAP i2c module doesn't work */ | 
|  | 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); | 
|  | flush_fifo(); | 
|  | 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 */ | 
|  | writew (1, I2C_CNT); | 
|  | /* set slave address */ | 
|  | writew (devaddr, I2C_SA); | 
|  | /* no stop bit needed here */ | 
|  | 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 */ | 
|  | writeb (regoffset, I2C_DATA); | 
|  | udelay (20000); | 
|  | 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 */ | 
|  | 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 */ | 
|  | writew (0xFFFF, I2C_STAT); | 
|  | } | 
|  |  | 
|  | wait_for_bb (); | 
|  | /* set slave address */ | 
|  | writew (devaddr, I2C_SA); | 
|  | /* read one byte from slave */ | 
|  | writew (1, I2C_CNT); | 
|  | /* need stop bit here */ | 
|  | writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP, | 
|  | I2C_CON); | 
|  |  | 
|  | status = wait_for_pin (); | 
|  | if (status & I2C_STAT_RRDY) { | 
|  | #if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) | 
|  | *value = readb (I2C_DATA); | 
|  | #else | 
|  | *value = readw (I2C_DATA); | 
|  | #endif | 
|  | udelay (20000); | 
|  | } else { | 
|  | i2c_error = 1; | 
|  | } | 
|  |  | 
|  | if (!i2c_error) { | 
|  | writew (I2C_CON_EN, I2C_CON); | 
|  | while (readw (I2C_STAT) | 
|  | || (readw (I2C_CON) & I2C_CON_MST)) { | 
|  | udelay (10000); | 
|  | writew (0xFFFF, I2C_STAT); | 
|  | } | 
|  | } | 
|  | } | 
|  | flush_fifo(); | 
|  | 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 */ | 
|  | writew (2, I2C_CNT); | 
|  | /* set slave address */ | 
|  | writew (devaddr, I2C_SA); | 
|  | /* stop bit needed here */ | 
|  | 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) { | 
|  | #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 */ | 
|  | writew ((value << 8) + regoffset, I2C_DATA); | 
|  | #endif | 
|  | /* must have enough delay to allow BB bit to go low */ | 
|  | udelay (50000); | 
|  | if (readw (I2C_STAT) & I2C_STAT_NACK) { | 
|  | i2c_error = 1; | 
|  | } | 
|  | } else { | 
|  | i2c_error = 1; | 
|  | } | 
|  |  | 
|  | if (!i2c_error) { | 
|  | int eout = 200; | 
|  |  | 
|  | 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 */ | 
|  | writew (0xFFFF, I2C_STAT); | 
|  | if(--eout == 0) /* better leave with error than hang */ | 
|  | break; | 
|  | } | 
|  | } | 
|  | flush_fifo(); | 
|  | writew (0xFFFF, I2C_STAT); | 
|  | writew (0, I2C_CNT); | 
|  | return i2c_error; | 
|  | } | 
|  |  | 
|  | static void flush_fifo(void) | 
|  | {	u16 stat; | 
|  |  | 
|  | /* note: if you try and read data when its not there or ready | 
|  | * you get a bus error | 
|  | */ | 
|  | while(1){ | 
|  | stat = readw(I2C_STAT); | 
|  | if(stat == I2C_STAT_RRDY){ | 
|  | #if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) | 
|  | readb(I2C_DATA); | 
|  | #else | 
|  | readw(I2C_DATA); | 
|  | #endif | 
|  | writew(I2C_STAT_RRDY,I2C_STAT); | 
|  | udelay(1000); | 
|  | }else | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | int i2c_probe (uchar chip) | 
|  | { | 
|  | int res = 1; /* default = fail */ | 
|  |  | 
|  | if (chip == readw (I2C_OA)) { | 
|  | return res; | 
|  | } | 
|  |  | 
|  | /* wait until bus not busy */ | 
|  | wait_for_bb (); | 
|  |  | 
|  | /* try to read one byte */ | 
|  | writew (1, I2C_CNT); | 
|  | /* set slave address */ | 
|  | writew (chip, I2C_SA); | 
|  | /* stop bit needed here */ | 
|  | writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP, I2C_CON); | 
|  | /* enough delay for the NACK bit set */ | 
|  | udelay (50000); | 
|  |  | 
|  | if (!(readw (I2C_STAT) & I2C_STAT_NACK)) { | 
|  | res = 0;      /* success case */ | 
|  | flush_fifo(); | 
|  | writew(0xFFFF, I2C_STAT); | 
|  | } else { | 
|  | 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(); | 
|  | 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; | 
|  |  | 
|  | 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", | 
|  | readw (I2C_STAT)); | 
|  | } | 
|  | writew(0xFFFF, I2C_STAT);	 /* clear delayed stuff*/ | 
|  | } | 
|  |  | 
|  | static u16 wait_for_pin (void) | 
|  | { | 
|  | u16 status; | 
|  | int timeout = 10; | 
|  |  | 
|  | do { | 
|  | udelay (1000); | 
|  | 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", | 
|  | readw (I2C_STAT)); | 
|  | writew(0xFFFF, I2C_STAT); | 
|  | } | 
|  | return status; | 
|  | } |