blob: c5d4642b9ab51231c84f70533c7f174ed1eff6dc [file] [log] [blame]
Jon Loeligerdebb7352006-04-26 17:58:56 -05001/*
2 * (C) Copyright 2003,Motorola Inc.
3 * Xianghua Xiao <x.xiao@motorola.com>
4 * Adapted for Motorola 85xx chip.
5 *
6 * (C) Copyright 2003
7 * Gleb Natapov <gnatapov@mrv.com>
8 * Some bits are taken from linux driver writen by adrian@humboldt.co.uk
9 *
10 * Modified for MPC86xx by Jeff Brown (jeffrey@freescale.com)
11 *
12 * Hardware I2C driver for MPC107 PCI bridge.
13 *
14 * See file CREDITS for list of people who contributed to this
15 * project.
16 *
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License as
19 * published by the Free Software Foundation; either version 2 of
20 * the License, or (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
30 * MA 02111-1307 USA
31 */
32
33#include <common.h>
34#include <command.h>
35#include <asm/io.h>
36
37#ifdef CONFIG_HARD_I2C
38#include <i2c.h>
39
40#define TIMEOUT (CFG_HZ/4)
41
42#define I2C_Addr ((u8 *)(CFG_CCSRBAR + 0x3100))
43
44#define I2CADR &I2C_Addr[0]
45#define I2CFDR &I2C_Addr[4]
46#define I2CCCR &I2C_Addr[8]
47#define I2CCSR &I2C_Addr[12]
48#define I2CCDR &I2C_Addr[16]
49#define I2CDFSRR &I2C_Addr[20]
50
51#define I2C_READ 1
52#define I2C_WRITE 0
53
54void
55i2c_init(int speed, int slaveadd)
56{
57 /* stop I2C controller */
58 writeb(0x0, I2CCCR);
59
60 /* set clock */
61 writeb(0x3f, I2CFDR);
62
63 /* set default filter */
64 writeb(0x10,I2CDFSRR);
65
66 /* write slave address */
67 writeb(slaveadd, I2CADR);
68
69 /* clear status register */
70 writeb(0x0, I2CCSR);
71
72 /* start I2C controller */
73 writeb(MPC86xx_I2CCR_MEN, I2CCCR);
74}
75
76static __inline__ int
77i2c_wait4bus (void)
78{
79 ulong timeval = get_timer (0);
80
81 // debug("I2C: Wait for bus\n");
82 while (readb(I2CCSR) & MPC86xx_I2CSR_MBB) {
83 if (get_timer (timeval) > TIMEOUT) {
84 return -1;
85 }
86 }
87
88 return 0;
89}
90
91static __inline__ int
92i2c_wait (int write)
93{
94 u32 csr;
95 ulong timeval = get_timer (0);
96
97 do {
98 csr = readb(I2CCSR);
99
100 if (!(csr & MPC86xx_I2CSR_MIF))
101 continue;
102
103 writeb(0x0, I2CCSR);
104
105 if (csr & MPC86xx_I2CSR_MAL) {
106 debug("i2c_wait: MAL\n");
107 return -1;
108 }
109
110 if (!(csr & MPC86xx_I2CSR_MCF)) {
111 debug("i2c_wait: unfinished\n");
112 return -1;
113 }
114
115 if (write == I2C_WRITE && (csr & MPC86xx_I2CSR_RXAK)) {
116 debug("i2c_wait: No RXACK\n");
117 return -1;
118 }
119
120 return 0;
121 } while (get_timer (timeval) < TIMEOUT);
122
123 debug("i2c_wait: timed out\n");
124 return -1;
125}
126
127static __inline__ int
128i2c_write_addr (u8 dev, u8 dir, int rsta)
129{
130 // debug("I2C: Write Addr\n");
131 writeb(MPC86xx_I2CCR_MEN | MPC86xx_I2CCR_MSTA | MPC86xx_I2CCR_MTX |
132 (rsta?MPC86xx_I2CCR_RSTA:0),
133 I2CCCR);
134
135 writeb((dev << 1) | dir, I2CCDR);
136
137 if (i2c_wait (I2C_WRITE) < 0)
138 return 0;
139
140 return 1;
141}
142
143static __inline__ int
144__i2c_write (u8 *data, int length)
145{
146 int i;
147 // debug("I2C: __i2c_write\n");
148 writeb(MPC86xx_I2CCR_MEN | MPC86xx_I2CCR_MSTA | MPC86xx_I2CCR_MTX,
149 I2CCCR);
150
151 for (i=0; i < length; i++) {
152 writeb(data[i], I2CCDR);
153
154 if (i2c_wait (I2C_WRITE) < 0)
155 break;
156 }
157
158 return i;
159}
160
161static __inline__ int
162__i2c_read (u8 *data, int length)
163{
164 int i;
165
166 writeb(MPC86xx_I2CCR_MEN | MPC86xx_I2CCR_MSTA |
167 ((length == 1) ? MPC86xx_I2CCR_TXAK : 0),
168 I2CCCR);
169
170 /* dummy read */
171 readb(I2CCDR);
172 // debug("length = %d\n", length);
173
174 for (i=0; i < length; i++) {
175 if (i2c_wait (I2C_READ) < 0)
176 break;
177
178 /* Generate ack on last next to last byte */
179 if (i == length - 2)
180 writeb(MPC86xx_I2CCR_MEN | MPC86xx_I2CCR_MSTA |
181 MPC86xx_I2CCR_TXAK,
182 I2CCCR);
183
184 /* Generate stop on last byte */
185 if (i == length - 1)
186 writeb(MPC86xx_I2CCR_MEN | MPC86xx_I2CCR_TXAK, I2CCCR);
187
188 // debug("I2CCR = 0x%08x\n", readb(I2CCCR));
189 data[i] = readb(I2CCDR);
190 // debug("data[i] = 0x%08x\n", data[i]);
191 }
192 // debug("Returning i = %d\n", i);
193 return i;
194}
195
196int
197i2c_read (u8 dev, uint addr, int alen, u8 *data, int length)
198{
199 int i = 0;
200 u8 *a = (u8*)&addr;
201
202 if (i2c_wait4bus () < 0)
203 goto exit;
204
205 if (i2c_write_addr (dev, I2C_WRITE, 0) == 0)
206 goto exit;
207
208 if (__i2c_write (&a[4 - alen], alen) != alen)
209 goto exit;
210
211 if (i2c_write_addr (dev, I2C_READ, 1) == 0)
212 goto exit;
213
214 i = __i2c_read (data, length);
215
216 exit:
217 writeb(MPC86xx_I2CCR_MEN, I2CCCR);
218
219 return !(i == length);
220}
221
222int
223i2c_write (u8 dev, uint addr, int alen, u8 *data, int length)
224{
225 int i = 0;
226 u8 *a = (u8*)&addr;
227
228 if (i2c_wait4bus () < 0)
229 goto exit;
230
231 if (i2c_write_addr (dev, I2C_WRITE, 0) == 0)
232 goto exit;
233
234 if (__i2c_write (&a[4 - alen], alen) != alen)
235 goto exit;
236
237 i = __i2c_write (data, length);
238
239 exit:
240 writeb(MPC86xx_I2CCR_MEN, I2CCCR);
241
242 return !(i == length);
243}
244
245int i2c_probe (uchar chip)
246{
247 int tmp;
248
249 /*
250 * Try to read the first location of the chip. The underlying
251 * driver doesn't appear to support sending just the chip address
252 * and looking for an <ACK> back.
253 */
254 udelay(10000);
255
256 return i2c_read (chip, 0, 1, (char *)&tmp, 1);
257}
258
259uchar i2c_reg_read (uchar i2c_addr, uchar reg)
260{
261 char buf[1];
262
263 i2c_read (i2c_addr, reg, 1, buf, 1);
264
265 return (buf[0]);
266}
267
268void i2c_reg_write (uchar i2c_addr, uchar reg, uchar val)
269{
270 i2c_write (i2c_addr, reg, 1, &val, 1);
271}
272
273#endif /* CONFIG_HARD_I2C */