]>
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 | * | |
1a459660 | 8 | * SPDX-License-Identifier: GPL-2.0+ |
7205e407 WD |
9 | */ |
10 | ||
11 | #include <common.h> | |
12 | ||
13 | #undef I2CDBG | |
14 | ||
15 | #ifdef CONFIG_HARD_I2C | |
16 | #include <i2c.h> | |
17 | ||
6d0f6bcf | 18 | #define TIMEOUT (CONFIG_SYS_HZ/4) |
7205e407 | 19 | |
6d0f6bcf | 20 | #define I2C_Addr ((unsigned *)(CONFIG_SYS_EUMB_ADDR + 0x3000)) |
7205e407 WD |
21 | |
22 | #define I2CADR &I2C_Addr[0] | |
23 | #define I2CFDR &I2C_Addr[1] | |
24 | #define I2CCCR &I2C_Addr[2] | |
25 | #define I2CCSR &I2C_Addr[3] | |
26 | #define I2CCDR &I2C_Addr[4] | |
27 | ||
28 | #define MPC107_CCR_MEN 0x80 | |
29 | #define MPC107_CCR_MIEN 0x40 | |
30 | #define MPC107_CCR_MSTA 0x20 | |
31 | #define MPC107_CCR_MTX 0x10 | |
32 | #define MPC107_CCR_TXAK 0x08 | |
33 | #define MPC107_CCR_RSTA 0x04 | |
34 | ||
35 | #define MPC107_CSR_MCF 0x80 | |
36 | #define MPC107_CSR_MAAS 0x40 | |
37 | #define MPC107_CSR_MBB 0x20 | |
38 | #define MPC107_CSR_MAL 0x10 | |
39 | #define MPC107_CSR_SRW 0x04 | |
40 | #define MPC107_CSR_MIF 0x02 | |
41 | #define MPC107_CSR_RXAK 0x01 | |
42 | ||
43 | #define I2C_READ 1 | |
44 | #define I2C_WRITE 0 | |
45 | ||
46 | /* taken from linux include/asm-ppc/io.h */ | |
47 | inline unsigned in_le32 (volatile unsigned *addr) | |
48 | { | |
49 | unsigned ret; | |
50 | ||
51 | __asm__ __volatile__ ("lwbrx %0,0,%1;\n" | |
52 | "twi 0,%0,0;\n" | |
53 | "isync":"=r" (ret): "r" (addr), "m" (*addr)); | |
54 | return ret; | |
55 | } | |
56 | ||
57 | inline void out_le32 (volatile unsigned *addr, int val) | |
58 | { | |
59 | __asm__ __volatile__ ("stwbrx %1,0,%2; eieio":"=m" (*addr):"r" (val), | |
60 | "r" (addr)); | |
61 | } | |
62 | ||
63 | #define writel(val, addr) out_le32(addr, val) | |
64 | #define readl(addr) in_le32(addr) | |
65 | ||
66 | void i2c_init (int speed, int slaveadd) | |
67 | { | |
68 | /* stop I2C controller */ | |
69 | writel (0x0, I2CCCR); | |
70 | /* set clock */ | |
71 | writel (0x1020, I2CFDR); | |
72 | /* write slave address */ | |
73 | writel (slaveadd, I2CADR); | |
74 | /* clear status register */ | |
75 | writel (0x0, I2CCSR); | |
76 | /* start I2C controller */ | |
77 | writel (MPC107_CCR_MEN, I2CCCR); | |
78 | ||
79 | return; | |
80 | } | |
81 | ||
82 | static __inline__ int i2c_wait4bus (void) | |
83 | { | |
84 | ulong timeval = get_timer (0); | |
85 | ||
86 | while (readl (I2CCSR) & MPC107_CSR_MBB) | |
87 | if (get_timer (timeval) > TIMEOUT) | |
88 | return -1; | |
89 | ||
90 | return 0; | |
91 | } | |
92 | ||
93 | static __inline__ int i2c_wait (int write) | |
94 | { | |
95 | u32 csr; | |
96 | ulong timeval = get_timer (0); | |
97 | ||
98 | do { | |
99 | csr = readl (I2CCSR); | |
100 | ||
101 | if (!(csr & MPC107_CSR_MIF)) | |
102 | continue; | |
103 | ||
104 | writel (0x0, I2CCSR); | |
105 | ||
106 | if (csr & MPC107_CSR_MAL) { | |
107 | #ifdef I2CDBG | |
108 | printf ("i2c_wait: MAL\n"); | |
109 | #endif | |
110 | return -1; | |
111 | } | |
112 | ||
113 | if (!(csr & MPC107_CSR_MCF)) { | |
114 | #ifdef I2CDBG | |
115 | printf ("i2c_wait: unfinished\n"); | |
116 | #endif | |
117 | return -1; | |
118 | } | |
119 | ||
120 | if (write == I2C_WRITE && (csr & MPC107_CSR_RXAK)) { | |
121 | #ifdef I2CDBG | |
122 | printf ("i2c_wait: No RXACK\n"); | |
123 | #endif | |
124 | return -1; | |
125 | } | |
126 | ||
127 | return 0; | |
128 | } while (get_timer (timeval) < TIMEOUT); | |
129 | ||
130 | #ifdef I2CDBG | |
131 | printf ("i2c_wait: timed out\n"); | |
132 | #endif | |
133 | return -1; | |
134 | } | |
135 | ||
136 | static __inline__ int i2c_write_addr (u8 dev, u8 dir, int rsta) | |
137 | { | |
138 | writel (MPC107_CCR_MEN | MPC107_CCR_MSTA | MPC107_CCR_MTX | | |
139 | (rsta ? MPC107_CCR_RSTA : 0), I2CCCR); | |
140 | ||
141 | writel ((dev << 1) | dir, I2CCDR); | |
142 | ||
143 | if (i2c_wait (I2C_WRITE) < 0) | |
144 | return 0; | |
145 | ||
146 | return 1; | |
147 | } | |
148 | ||
149 | static __inline__ int __i2c_write (u8 * data, int length) | |
150 | { | |
151 | int i; | |
152 | ||
153 | writel (MPC107_CCR_MEN | MPC107_CCR_MSTA | MPC107_CCR_MTX, I2CCCR); | |
154 | ||
155 | for (i = 0; i < length; i++) { | |
156 | writel (data[i], I2CCDR); | |
157 | ||
158 | if (i2c_wait (I2C_WRITE) < 0) | |
159 | break; | |
160 | } | |
161 | ||
162 | return i; | |
163 | } | |
164 | ||
165 | static __inline__ int __i2c_read (u8 * data, int length) | |
166 | { | |
167 | int i; | |
168 | ||
169 | writel (MPC107_CCR_MEN | MPC107_CCR_MSTA | | |
170 | ((length == 1) ? MPC107_CCR_TXAK : 0), I2CCCR); | |
171 | ||
172 | /* dummy read */ | |
173 | readl (I2CCDR); | |
174 | ||
175 | for (i = 0; i < length; i++) { | |
176 | if (i2c_wait (I2C_READ) < 0) | |
177 | break; | |
178 | ||
179 | /* Generate ack on last next to last byte */ | |
180 | if (i == length - 2) | |
181 | writel (MPC107_CCR_MEN | MPC107_CCR_MSTA | | |
182 | MPC107_CCR_TXAK, I2CCCR); | |
183 | ||
184 | /* Generate stop on last byte */ | |
185 | if (i == length - 1) | |
186 | writel (MPC107_CCR_MEN | MPC107_CCR_TXAK, I2CCCR); | |
187 | ||
188 | data[i] = readl (I2CCDR); | |
189 | } | |
190 | ||
191 | return i; | |
192 | } | |
193 | ||
194 | int i2c_read (u8 dev, uint addr, int alen, u8 * data, int length) | |
195 | { | |
196 | int i = 0; | |
197 | u8 *a = (u8 *) & addr; | |
198 | ||
199 | if (i2c_wait4bus () < 0) | |
200 | goto exit; | |
201 | ||
202 | if (i2c_write_addr (dev, I2C_WRITE, 0) == 0) | |
203 | goto exit; | |
204 | ||
205 | if (__i2c_write (&a[4 - alen], alen) != alen) | |
206 | goto exit; | |
207 | ||
208 | if (i2c_write_addr (dev, I2C_READ, 1) == 0) | |
209 | goto exit; | |
210 | ||
211 | i = __i2c_read (data, length); | |
212 | ||
213 | exit: | |
214 | writel (MPC107_CCR_MEN, I2CCCR); | |
215 | ||
216 | return !(i == length); | |
217 | } | |
218 | ||
219 | int i2c_write (u8 dev, uint addr, int alen, u8 * data, int length) | |
220 | { | |
221 | int i = 0; | |
222 | u8 *a = (u8 *) & addr; | |
223 | ||
224 | if (i2c_wait4bus () < 0) | |
225 | goto exit; | |
226 | ||
227 | if (i2c_write_addr (dev, I2C_WRITE, 0) == 0) | |
228 | goto exit; | |
229 | ||
230 | if (__i2c_write (&a[4 - alen], alen) != alen) | |
231 | goto exit; | |
232 | ||
233 | i = __i2c_write (data, length); | |
234 | ||
235 | exit: | |
236 | writel (MPC107_CCR_MEN, I2CCCR); | |
237 | ||
238 | return !(i == length); | |
239 | } | |
240 | ||
241 | int i2c_probe (uchar chip) | |
242 | { | |
243 | int tmp; | |
244 | ||
245 | /* | |
246 | * Try to read the first location of the chip. The underlying | |
247 | * driver doesn't appear to support sending just the chip address | |
248 | * and looking for an <ACK> back. | |
249 | */ | |
250 | udelay (10000); | |
77ddac94 | 251 | return i2c_read (chip, 0, 1, (uchar *) &tmp, 1); |
7205e407 WD |
252 | } |
253 | ||
7205e407 | 254 | #endif /* CONFIG_HARD_I2C */ |