blob: 4427938ff3cd078030555082a43e98732ff00bfe [file] [log] [blame]
wdenk8ed96042005-01-09 23:16:25 +00001/*
2 * Basic I2C functions
3 *
4 * Copyright (c) 2004 Texas Instruments
5 *
6 * This package is free software; you can redistribute it and/or
7 * modify it under the terms of the license found in the file
8 * named COPYING that should have accompanied this file.
9 *
10 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
11 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
12 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13 *
14 * Author: Jian Zhang jzhang@ti.com, Texas Instruments
15 *
16 * Copyright (c) 2003 Wolfgang Denk, wd@denx.de
17 * Rewritten to fit into the current U-Boot framework
18 *
19 * Adapted for OMAP2420 I2C, r-woodruff2@ti.com
20 *
21 */
22
23#include <common.h>
wdenk289f9322005-01-12 00:15:14 +000024
wdenk8ed96042005-01-09 23:16:25 +000025#include <asm/arch/i2c.h>
26#include <asm/io.h>
27
wdenk8ed96042005-01-09 23:16:25 +000028static void wait_for_bb (void);
29static u16 wait_for_pin (void);
Wolfgang Denk49a75812005-09-25 18:41:04 +020030static void flush_fifo(void);
wdenk8ed96042005-01-09 23:16:25 +000031
32void i2c_init (int speed, int slaveadd)
33{
34 u16 scl;
35
Dirk Behmee23c7c92008-11-10 20:15:25 +010036 writew(0x2, I2C_SYSC); /* for ES2 after soft reset */
Wolfgang Denk49a75812005-09-25 18:41:04 +020037 udelay(1000);
Dirk Behmee23c7c92008-11-10 20:15:25 +010038 writew(0x0, I2C_SYSC); /* will probably self clear but */
Wolfgang Denk49a75812005-09-25 18:41:04 +020039
Dirk Behmee23c7c92008-11-10 20:15:25 +010040 if (readw (I2C_CON) & I2C_CON_EN) {
41 writew (0, I2C_CON);
wdenk8ed96042005-01-09 23:16:25 +000042 udelay (50000);
43 }
44
Wolfgang Denk8ed44d92008-10-19 02:35:50 +020045 /* 12MHz I2C module clock */
Dirk Behmee23c7c92008-11-10 20:15:25 +010046 writew (0, I2C_PSC);
wdenk082acfd2005-01-10 00:01:04 +000047 speed = speed/1000; /* 100 or 400 */
48 scl = ((12000/(speed*2)) - 7); /* use 7 when PSC = 0 */
Dirk Behmee23c7c92008-11-10 20:15:25 +010049 writew (scl, I2C_SCLL);
50 writew (scl, I2C_SCLH);
wdenk8ed96042005-01-09 23:16:25 +000051 /* own address */
Dirk Behmee23c7c92008-11-10 20:15:25 +010052 writew (slaveadd, I2C_OA);
53 writew (I2C_CON_EN, I2C_CON);
Wolfgang Denk49a75812005-09-25 18:41:04 +020054
wdenk8ed96042005-01-09 23:16:25 +000055 /* have to enable intrrupts or OMAP i2c module doesn't work */
Dirk Behmee23c7c92008-11-10 20:15:25 +010056 writew (I2C_IE_XRDY_IE | I2C_IE_RRDY_IE | I2C_IE_ARDY_IE |
57 I2C_IE_NACK_IE | I2C_IE_AL_IE, I2C_IE);
wdenk8ed96042005-01-09 23:16:25 +000058 udelay (1000);
Wolfgang Denk49a75812005-09-25 18:41:04 +020059 flush_fifo();
Dirk Behmee23c7c92008-11-10 20:15:25 +010060 writew (0xFFFF, I2C_STAT);
61 writew (0, I2C_CNT);
wdenk8ed96042005-01-09 23:16:25 +000062}
63
64static int i2c_read_byte (u8 devaddr, u8 regoffset, u8 * value)
65{
66 int i2c_error = 0;
67 u16 status;
68
69 /* wait until bus not busy */
70 wait_for_bb ();
71
72 /* one byte only */
Dirk Behmee23c7c92008-11-10 20:15:25 +010073 writew (1, I2C_CNT);
wdenk8ed96042005-01-09 23:16:25 +000074 /* set slave address */
Dirk Behmee23c7c92008-11-10 20:15:25 +010075 writew (devaddr, I2C_SA);
wdenk8ed96042005-01-09 23:16:25 +000076 /* no stop bit needed here */
Dirk Behmee23c7c92008-11-10 20:15:25 +010077 writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX, I2C_CON);
wdenk8ed96042005-01-09 23:16:25 +000078
79 status = wait_for_pin ();
80
81 if (status & I2C_STAT_XRDY) {
82 /* Important: have to use byte access */
Dirk Behmee23c7c92008-11-10 20:15:25 +010083 writeb (regoffset, I2C_DATA);
wdenk8ed96042005-01-09 23:16:25 +000084 udelay (20000);
Dirk Behmee23c7c92008-11-10 20:15:25 +010085 if (readw (I2C_STAT) & I2C_STAT_NACK) {
wdenk8ed96042005-01-09 23:16:25 +000086 i2c_error = 1;
87 }
88 } else {
89 i2c_error = 1;
90 }
91
92 if (!i2c_error) {
93 /* free bus, otherwise we can't use a combined transction */
Dirk Behmee23c7c92008-11-10 20:15:25 +010094 writew (0, I2C_CON);
95 while (readw (I2C_STAT) || (readw (I2C_CON) & I2C_CON_MST)) {
wdenk8ed96042005-01-09 23:16:25 +000096 udelay (10000);
97 /* Have to clear pending interrupt to clear I2C_STAT */
Dirk Behmee23c7c92008-11-10 20:15:25 +010098 writew (0xFFFF, I2C_STAT);
wdenk8ed96042005-01-09 23:16:25 +000099 }
100
101 wait_for_bb ();
102 /* set slave address */
Dirk Behmee23c7c92008-11-10 20:15:25 +0100103 writew (devaddr, I2C_SA);
wdenk8ed96042005-01-09 23:16:25 +0000104 /* read one byte from slave */
Dirk Behmee23c7c92008-11-10 20:15:25 +0100105 writew (1, I2C_CNT);
wdenk8ed96042005-01-09 23:16:25 +0000106 /* need stop bit here */
Dirk Behmee23c7c92008-11-10 20:15:25 +0100107 writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP,
108 I2C_CON);
wdenk8ed96042005-01-09 23:16:25 +0000109
110 status = wait_for_pin ();
111 if (status & I2C_STAT_RRDY) {
Dirk Behmee23c7c92008-11-10 20:15:25 +0100112 *value = readw (I2C_DATA);
wdenk8ed96042005-01-09 23:16:25 +0000113 udelay (20000);
114 } else {
115 i2c_error = 1;
116 }
117
118 if (!i2c_error) {
Dirk Behmee23c7c92008-11-10 20:15:25 +0100119 writew (I2C_CON_EN, I2C_CON);
120 while (readw (I2C_STAT)
121 || (readw (I2C_CON) & I2C_CON_MST)) {
wdenk8ed96042005-01-09 23:16:25 +0000122 udelay (10000);
Dirk Behmee23c7c92008-11-10 20:15:25 +0100123 writew (0xFFFF, I2C_STAT);
wdenk8ed96042005-01-09 23:16:25 +0000124 }
125 }
126 }
127 flush_fifo();
Dirk Behmee23c7c92008-11-10 20:15:25 +0100128 writew (0xFFFF, I2C_STAT);
129 writew (0, I2C_CNT);
wdenk8ed96042005-01-09 23:16:25 +0000130 return i2c_error;
131}
132
133static int i2c_write_byte (u8 devaddr, u8 regoffset, u8 value)
134{
135 int i2c_error = 0;
136 u16 status, stat;
137
138 /* wait until bus not busy */
139 wait_for_bb ();
140
141 /* two bytes */
Dirk Behmee23c7c92008-11-10 20:15:25 +0100142 writew (2, I2C_CNT);
wdenk8ed96042005-01-09 23:16:25 +0000143 /* set slave address */
Dirk Behmee23c7c92008-11-10 20:15:25 +0100144 writew (devaddr, I2C_SA);
wdenk8ed96042005-01-09 23:16:25 +0000145 /* stop bit needed here */
Dirk Behmee23c7c92008-11-10 20:15:25 +0100146 writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX |
147 I2C_CON_STP, I2C_CON);
wdenk8ed96042005-01-09 23:16:25 +0000148
149 /* wait until state change */
150 status = wait_for_pin ();
151
152 if (status & I2C_STAT_XRDY) {
153 /* send out two bytes */
Dirk Behmee23c7c92008-11-10 20:15:25 +0100154 writew ((value << 8) + regoffset, I2C_DATA);
wdenk8ed96042005-01-09 23:16:25 +0000155 /* must have enough delay to allow BB bit to go low */
156 udelay (50000);
Dirk Behmee23c7c92008-11-10 20:15:25 +0100157 if (readw (I2C_STAT) & I2C_STAT_NACK) {
wdenk8ed96042005-01-09 23:16:25 +0000158 i2c_error = 1;
159 }
160 } else {
161 i2c_error = 1;
162 }
163
164 if (!i2c_error) {
Wolfgang Denk49a75812005-09-25 18:41:04 +0200165 int eout = 200;
166
Dirk Behmee23c7c92008-11-10 20:15:25 +0100167 writew (I2C_CON_EN, I2C_CON);
168 while ((stat = readw (I2C_STAT)) || (readw (I2C_CON) & I2C_CON_MST)) {
wdenk8ed96042005-01-09 23:16:25 +0000169 udelay (1000);
170 /* have to read to clear intrrupt */
Dirk Behmee23c7c92008-11-10 20:15:25 +0100171 writew (0xFFFF, I2C_STAT);
Wolfgang Denk49a75812005-09-25 18:41:04 +0200172 if(--eout == 0) /* better leave with error than hang */
173 break;
wdenk8ed96042005-01-09 23:16:25 +0000174 }
175 }
176 flush_fifo();
Dirk Behmee23c7c92008-11-10 20:15:25 +0100177 writew (0xFFFF, I2C_STAT);
178 writew (0, I2C_CNT);
wdenk8ed96042005-01-09 23:16:25 +0000179 return i2c_error;
180}
181
Wolfgang Denk49a75812005-09-25 18:41:04 +0200182static void flush_fifo(void)
wdenk8ed96042005-01-09 23:16:25 +0000183{ u16 stat;
wdenk082acfd2005-01-10 00:01:04 +0000184
185 /* note: if you try and read data when its not there or ready
186 * you get a bus error
187 */
wdenk8ed96042005-01-09 23:16:25 +0000188 while(1){
Dirk Behmee23c7c92008-11-10 20:15:25 +0100189 stat = readw(I2C_STAT);
wdenk8ed96042005-01-09 23:16:25 +0000190 if(stat == I2C_STAT_RRDY){
Dirk Behmee23c7c92008-11-10 20:15:25 +0100191 readw(I2C_DATA);
192 writew(I2C_STAT_RRDY,I2C_STAT);
wdenk8ed96042005-01-09 23:16:25 +0000193 udelay(1000);
194 }else
195 break;
196 }
197}
198
199int i2c_probe (uchar chip)
200{
201 int res = 1; /* default = fail */
202
Dirk Behmee23c7c92008-11-10 20:15:25 +0100203 if (chip == readw (I2C_OA)) {
wdenk8ed96042005-01-09 23:16:25 +0000204 return res;
205 }
206
207 /* wait until bus not busy */
208 wait_for_bb ();
209
210 /* try to read one byte */
Dirk Behmee23c7c92008-11-10 20:15:25 +0100211 writew (1, I2C_CNT);
wdenk8ed96042005-01-09 23:16:25 +0000212 /* set slave address */
Dirk Behmee23c7c92008-11-10 20:15:25 +0100213 writew (chip, I2C_SA);
wdenk8ed96042005-01-09 23:16:25 +0000214 /* stop bit needed here */
Dirk Behmee23c7c92008-11-10 20:15:25 +0100215 writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP, I2C_CON);
wdenk8ed96042005-01-09 23:16:25 +0000216 /* enough delay for the NACK bit set */
217 udelay (50000);
218
Dirk Behmee23c7c92008-11-10 20:15:25 +0100219 if (!(readw (I2C_STAT) & I2C_STAT_NACK)) {
wdenk082acfd2005-01-10 00:01:04 +0000220 res = 0; /* success case */
wdenk8ed96042005-01-09 23:16:25 +0000221 flush_fifo();
Dirk Behmee23c7c92008-11-10 20:15:25 +0100222 writew(0xFFFF, I2C_STAT);
wdenk8ed96042005-01-09 23:16:25 +0000223 } else {
Dirk Behmee23c7c92008-11-10 20:15:25 +0100224 writew(0xFFFF, I2C_STAT); /* failue, clear sources*/
225 writew (readw (I2C_CON) | I2C_CON_STP, I2C_CON); /* finish up xfer */
wdenk8ed96042005-01-09 23:16:25 +0000226 udelay(20000);
227 wait_for_bb ();
228 }
229 flush_fifo();
Dirk Behmee23c7c92008-11-10 20:15:25 +0100230 writew (0, I2C_CNT); /* don't allow any more data in...we don't want it.*/
231 writew(0xFFFF, I2C_STAT);
wdenk8ed96042005-01-09 23:16:25 +0000232 return res;
233}
234
235int i2c_read (uchar chip, uint addr, int alen, uchar * buffer, int len)
236{
237 int i;
238
239 if (alen > 1) {
240 printf ("I2C read: addr len %d not supported\n", alen);
241 return 1;
242 }
243
244 if (addr + len > 256) {
245 printf ("I2C read: address out of range\n");
246 return 1;
247 }
248
249 for (i = 0; i < len; i++) {
250 if (i2c_read_byte (chip, addr + i, &buffer[i])) {
251 printf ("I2C read: I/O error\n");
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200252 i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
wdenk8ed96042005-01-09 23:16:25 +0000253 return 1;
254 }
255 }
256
257 return 0;
258}
259
260int i2c_write (uchar chip, uint addr, int alen, uchar * buffer, int len)
261{
262 int i;
263
264 if (alen > 1) {
265 printf ("I2C read: addr len %d not supported\n", alen);
266 return 1;
267 }
268
269 if (addr + len > 256) {
270 printf ("I2C read: address out of range\n");
271 return 1;
272 }
273
274 for (i = 0; i < len; i++) {
275 if (i2c_write_byte (chip, addr + i, buffer[i])) {
276 printf ("I2C read: I/O error\n");
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200277 i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
wdenk8ed96042005-01-09 23:16:25 +0000278 return 1;
279 }
280 }
281
282 return 0;
283}
284
285static void wait_for_bb (void)
286{
287 int timeout = 10;
288 u16 stat;
289
Dirk Behmee23c7c92008-11-10 20:15:25 +0100290 writew(0xFFFF, I2C_STAT); /* clear current interruts...*/
291 while ((stat = readw (I2C_STAT) & I2C_STAT_BB) && timeout--) {
292 writew (stat, I2C_STAT);
wdenk8ed96042005-01-09 23:16:25 +0000293 udelay (50000);
294 }
295
296 if (timeout <= 0) {
297 printf ("timed out in wait_for_bb: I2C_STAT=%x\n",
Dirk Behmee23c7c92008-11-10 20:15:25 +0100298 readw (I2C_STAT));
wdenk8ed96042005-01-09 23:16:25 +0000299 }
Dirk Behmee23c7c92008-11-10 20:15:25 +0100300 writew(0xFFFF, I2C_STAT); /* clear delayed stuff*/
wdenk8ed96042005-01-09 23:16:25 +0000301}
302
303static u16 wait_for_pin (void)
304{
305 u16 status;
306 int timeout = 10;
307
308 do {
309 udelay (1000);
Dirk Behmee23c7c92008-11-10 20:15:25 +0100310 status = readw (I2C_STAT);
wdenk8ed96042005-01-09 23:16:25 +0000311 } while ( !(status &
312 (I2C_STAT_ROVR | I2C_STAT_XUDF | I2C_STAT_XRDY |
313 I2C_STAT_RRDY | I2C_STAT_ARDY | I2C_STAT_NACK |
314 I2C_STAT_AL)) && timeout--);
315
316 if (timeout <= 0) {
317 printf ("timed out in wait_for_pin: I2C_STAT=%x\n",
Dirk Behmee23c7c92008-11-10 20:15:25 +0100318 readw (I2C_STAT));
319 writew(0xFFFF, I2C_STAT);
wdenk8ed96042005-01-09 23:16:25 +0000320}
321 return status;
322}