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