]>
Commit | Line | Data |
---|---|---|
affae2bf | 1 | /* |
4ba31ab3 LCM |
2 | * (C) Copyright 2009 Industrie Dial Face S.p.A. |
3 | * Luigi 'Comio' Mantellini <luigi.mantellini@idf-hit.com> | |
4 | * | |
affae2bf WD |
5 | * (C) Copyright 2001 |
6 | * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com. | |
7 | * | |
1a459660 | 8 | * SPDX-License-Identifier: GPL-2.0+ |
affae2bf WD |
9 | */ |
10 | ||
11 | /* | |
12 | * This provides a bit-banged interface to the ethernet MII management | |
13 | * channel. | |
14 | */ | |
15 | ||
16 | #include <common.h> | |
17 | #include <ioports.h> | |
18 | #include <ppc_asm.tmpl> | |
4ba31ab3 LCM |
19 | #include <miiphy.h> |
20 | ||
21 | #define BB_MII_RELOCATE(v,off) (v += (v?off:0)) | |
22 | ||
23 | DECLARE_GLOBAL_DATA_PTR; | |
24 | ||
25 | #ifndef CONFIG_BITBANGMII_MULTI | |
26 | ||
27 | /* | |
28 | * If CONFIG_BITBANGMII_MULTI is not defined we use a | |
29 | * compatibility layer with the previous miiphybb implementation | |
30 | * based on macros usage. | |
31 | * | |
32 | */ | |
33 | static int bb_mii_init_wrap(struct bb_miiphy_bus *bus) | |
34 | { | |
35 | #ifdef MII_INIT | |
36 | MII_INIT; | |
37 | #endif | |
38 | return 0; | |
39 | } | |
40 | ||
41 | static int bb_mdio_active_wrap(struct bb_miiphy_bus *bus) | |
42 | { | |
43 | #ifdef MDIO_DECLARE | |
44 | MDIO_DECLARE; | |
45 | #endif | |
46 | MDIO_ACTIVE; | |
47 | return 0; | |
48 | } | |
49 | ||
50 | static int bb_mdio_tristate_wrap(struct bb_miiphy_bus *bus) | |
51 | { | |
52 | #ifdef MDIO_DECLARE | |
53 | MDIO_DECLARE; | |
54 | #endif | |
55 | MDIO_TRISTATE; | |
56 | return 0; | |
57 | } | |
58 | ||
59 | static int bb_set_mdio_wrap(struct bb_miiphy_bus *bus, int v) | |
60 | { | |
61 | #ifdef MDIO_DECLARE | |
62 | MDIO_DECLARE; | |
63 | #endif | |
64 | MDIO(v); | |
65 | return 0; | |
66 | } | |
67 | ||
68 | static int bb_get_mdio_wrap(struct bb_miiphy_bus *bus, int *v) | |
69 | { | |
70 | #ifdef MDIO_DECLARE | |
71 | MDIO_DECLARE; | |
72 | #endif | |
73 | *v = MDIO_READ; | |
74 | return 0; | |
75 | } | |
76 | ||
77 | static int bb_set_mdc_wrap(struct bb_miiphy_bus *bus, int v) | |
78 | { | |
79 | #ifdef MDC_DECLARE | |
80 | MDC_DECLARE; | |
81 | #endif | |
82 | MDC(v); | |
83 | return 0; | |
84 | } | |
85 | ||
86 | static int bb_delay_wrap(struct bb_miiphy_bus *bus) | |
87 | { | |
88 | MIIDELAY; | |
89 | return 0; | |
90 | } | |
91 | ||
92 | struct bb_miiphy_bus bb_miiphy_buses[] = { | |
93 | { | |
94 | .name = BB_MII_DEVNAME, | |
95 | .init = bb_mii_init_wrap, | |
96 | .mdio_active = bb_mdio_active_wrap, | |
97 | .mdio_tristate = bb_mdio_tristate_wrap, | |
98 | .set_mdio = bb_set_mdio_wrap, | |
99 | .get_mdio = bb_get_mdio_wrap, | |
100 | .set_mdc = bb_set_mdc_wrap, | |
101 | .delay = bb_delay_wrap, | |
102 | } | |
103 | }; | |
104 | ||
105 | int bb_miiphy_buses_num = sizeof(bb_miiphy_buses) / | |
4946775c | 106 | sizeof(bb_miiphy_buses[0]); |
4ba31ab3 LCM |
107 | #endif |
108 | ||
109 | void bb_miiphy_init(void) | |
110 | { | |
111 | int i; | |
112 | ||
113 | for (i = 0; i < bb_miiphy_buses_num; i++) { | |
2e5167cc | 114 | #if defined(CONFIG_NEEDS_MANUAL_RELOC) |
4ba31ab3 LCM |
115 | /* Relocate the hook pointers*/ |
116 | BB_MII_RELOCATE(bb_miiphy_buses[i].init, gd->reloc_off); | |
117 | BB_MII_RELOCATE(bb_miiphy_buses[i].mdio_active, gd->reloc_off); | |
118 | BB_MII_RELOCATE(bb_miiphy_buses[i].mdio_tristate, gd->reloc_off); | |
119 | BB_MII_RELOCATE(bb_miiphy_buses[i].set_mdio, gd->reloc_off); | |
120 | BB_MII_RELOCATE(bb_miiphy_buses[i].get_mdio, gd->reloc_off); | |
121 | BB_MII_RELOCATE(bb_miiphy_buses[i].set_mdc, gd->reloc_off); | |
122 | BB_MII_RELOCATE(bb_miiphy_buses[i].delay, gd->reloc_off); | |
123 | #endif | |
124 | if (bb_miiphy_buses[i].init != NULL) { | |
125 | bb_miiphy_buses[i].init(&bb_miiphy_buses[i]); | |
126 | } | |
127 | } | |
128 | } | |
129 | ||
d7fb9bcf | 130 | static inline struct bb_miiphy_bus *bb_miiphy_getbus(const char *devname) |
4ba31ab3 LCM |
131 | { |
132 | #ifdef CONFIG_BITBANGMII_MULTI | |
133 | int i; | |
134 | ||
135 | /* Search the correct bus */ | |
136 | for (i = 0; i < bb_miiphy_buses_num; i++) { | |
137 | if (!strcmp(bb_miiphy_buses[i].name, devname)) { | |
138 | return &bb_miiphy_buses[i]; | |
139 | } | |
140 | } | |
141 | return NULL; | |
142 | #else | |
143 | /* We have just one bitbanging bus */ | |
144 | return &bb_miiphy_buses[0]; | |
145 | #endif | |
146 | } | |
affae2bf | 147 | |
affae2bf WD |
148 | /***************************************************************************** |
149 | * | |
150 | * Utility to send the preamble, address, and register (common to read | |
151 | * and write). | |
152 | */ | |
4ba31ab3 | 153 | static void miiphy_pre(struct bb_miiphy_bus *bus, char read, |
4946775c | 154 | unsigned char addr, unsigned char reg) |
affae2bf | 155 | { |
4ba31ab3 | 156 | int j; |
2afbe4ed WD |
157 | |
158 | /* | |
159 | * Send a 32 bit preamble ('1's) with an extra '1' bit for good measure. | |
160 | * The IEEE spec says this is a PHY optional requirement. The AMD | |
161 | * 79C874 requires one after power up and one after a MII communications | |
162 | * error. This means that we are doing more preambles than we need, | |
163 | * but it is safer and will be much more robust. | |
164 | */ | |
165 | ||
4ba31ab3 LCM |
166 | bus->mdio_active(bus); |
167 | bus->set_mdio(bus, 1); | |
2afbe4ed | 168 | for (j = 0; j < 32; j++) { |
4ba31ab3 LCM |
169 | bus->set_mdc(bus, 0); |
170 | bus->delay(bus); | |
171 | bus->set_mdc(bus, 1); | |
172 | bus->delay(bus); | |
2afbe4ed WD |
173 | } |
174 | ||
175 | /* send the start bit (01) and the read opcode (10) or write (10) */ | |
4ba31ab3 LCM |
176 | bus->set_mdc(bus, 0); |
177 | bus->set_mdio(bus, 0); | |
178 | bus->delay(bus); | |
179 | bus->set_mdc(bus, 1); | |
180 | bus->delay(bus); | |
181 | bus->set_mdc(bus, 0); | |
182 | bus->set_mdio(bus, 1); | |
183 | bus->delay(bus); | |
184 | bus->set_mdc(bus, 1); | |
185 | bus->delay(bus); | |
186 | bus->set_mdc(bus, 0); | |
187 | bus->set_mdio(bus, read); | |
188 | bus->delay(bus); | |
189 | bus->set_mdc(bus, 1); | |
190 | bus->delay(bus); | |
191 | bus->set_mdc(bus, 0); | |
192 | bus->set_mdio(bus, !read); | |
193 | bus->delay(bus); | |
194 | bus->set_mdc(bus, 1); | |
195 | bus->delay(bus); | |
2afbe4ed WD |
196 | |
197 | /* send the PHY address */ | |
198 | for (j = 0; j < 5; j++) { | |
4ba31ab3 | 199 | bus->set_mdc(bus, 0); |
2afbe4ed | 200 | if ((addr & 0x10) == 0) { |
4ba31ab3 | 201 | bus->set_mdio(bus, 0); |
2afbe4ed | 202 | } else { |
4ba31ab3 | 203 | bus->set_mdio(bus, 1); |
2afbe4ed | 204 | } |
4ba31ab3 LCM |
205 | bus->delay(bus); |
206 | bus->set_mdc(bus, 1); | |
207 | bus->delay(bus); | |
2afbe4ed WD |
208 | addr <<= 1; |
209 | } | |
210 | ||
211 | /* send the register address */ | |
212 | for (j = 0; j < 5; j++) { | |
4ba31ab3 | 213 | bus->set_mdc(bus, 0); |
2afbe4ed | 214 | if ((reg & 0x10) == 0) { |
4ba31ab3 | 215 | bus->set_mdio(bus, 0); |
2afbe4ed | 216 | } else { |
4ba31ab3 | 217 | bus->set_mdio(bus, 1); |
2afbe4ed | 218 | } |
4ba31ab3 LCM |
219 | bus->delay(bus); |
220 | bus->set_mdc(bus, 1); | |
221 | bus->delay(bus); | |
2afbe4ed WD |
222 | reg <<= 1; |
223 | } | |
affae2bf WD |
224 | } |
225 | ||
affae2bf WD |
226 | /***************************************************************************** |
227 | * | |
228 | * Read a MII PHY register. | |
229 | * | |
230 | * Returns: | |
231 | * 0 on success | |
232 | */ | |
d7fb9bcf | 233 | int bb_miiphy_read(const char *devname, unsigned char addr, |
4946775c | 234 | unsigned char reg, unsigned short *value) |
affae2bf | 235 | { |
4ba31ab3 LCM |
236 | short rdreg; /* register working value */ |
237 | int v; | |
238 | int j; /* counter */ | |
239 | struct bb_miiphy_bus *bus; | |
240 | ||
241 | bus = bb_miiphy_getbus(devname); | |
242 | if (bus == NULL) { | |
243 | return -1; | |
244 | } | |
2afbe4ed | 245 | |
3f6b18ff RR |
246 | if (value == NULL) { |
247 | puts("NULL value pointer\n"); | |
4ba31ab3 | 248 | return -1; |
3f6b18ff RR |
249 | } |
250 | ||
4ba31ab3 | 251 | miiphy_pre (bus, 1, addr, reg); |
2afbe4ed WD |
252 | |
253 | /* tri-state our MDIO I/O pin so we can read */ | |
4ba31ab3 LCM |
254 | bus->set_mdc(bus, 0); |
255 | bus->mdio_tristate(bus); | |
256 | bus->delay(bus); | |
257 | bus->set_mdc(bus, 1); | |
258 | bus->delay(bus); | |
2afbe4ed WD |
259 | |
260 | /* check the turnaround bit: the PHY should be driving it to zero */ | |
4ba31ab3 LCM |
261 | bus->get_mdio(bus, &v); |
262 | if (v != 0) { | |
2afbe4ed WD |
263 | /* puts ("PHY didn't drive TA low\n"); */ |
264 | for (j = 0; j < 32; j++) { | |
4ba31ab3 LCM |
265 | bus->set_mdc(bus, 0); |
266 | bus->delay(bus); | |
267 | bus->set_mdc(bus, 1); | |
268 | bus->delay(bus); | |
2afbe4ed | 269 | } |
3f6b18ff RR |
270 | /* There is no PHY, set value to 0xFFFF and return */ |
271 | *value = 0xFFFF; | |
4ba31ab3 | 272 | return -1; |
2afbe4ed WD |
273 | } |
274 | ||
4ba31ab3 LCM |
275 | bus->set_mdc(bus, 0); |
276 | bus->delay(bus); | |
2afbe4ed WD |
277 | |
278 | /* read 16 bits of register data, MSB first */ | |
279 | rdreg = 0; | |
280 | for (j = 0; j < 16; j++) { | |
4ba31ab3 LCM |
281 | bus->set_mdc(bus, 1); |
282 | bus->delay(bus); | |
2afbe4ed | 283 | rdreg <<= 1; |
4ba31ab3 LCM |
284 | bus->get_mdio(bus, &v); |
285 | rdreg |= (v & 0x1); | |
286 | bus->set_mdc(bus, 0); | |
287 | bus->delay(bus); | |
2afbe4ed WD |
288 | } |
289 | ||
4ba31ab3 LCM |
290 | bus->set_mdc(bus, 1); |
291 | bus->delay(bus); | |
292 | bus->set_mdc(bus, 0); | |
293 | bus->delay(bus); | |
294 | bus->set_mdc(bus, 1); | |
295 | bus->delay(bus); | |
2afbe4ed WD |
296 | |
297 | *value = rdreg; | |
affae2bf WD |
298 | |
299 | #ifdef DEBUG | |
2afbe4ed | 300 | printf ("miiphy_read(0x%x) @ 0x%x = 0x%04x\n", reg, addr, *value); |
affae2bf WD |
301 | #endif |
302 | ||
2afbe4ed | 303 | return 0; |
affae2bf WD |
304 | } |
305 | ||
306 | ||
307 | /***************************************************************************** | |
308 | * | |
309 | * Write a MII PHY register. | |
310 | * | |
311 | * Returns: | |
312 | * 0 on success | |
313 | */ | |
d7fb9bcf | 314 | int bb_miiphy_write (const char *devname, unsigned char addr, |
4946775c | 315 | unsigned char reg, unsigned short value) |
affae2bf | 316 | { |
4ba31ab3 | 317 | struct bb_miiphy_bus *bus; |
2afbe4ed | 318 | int j; /* counter */ |
2afbe4ed | 319 | |
4ba31ab3 LCM |
320 | bus = bb_miiphy_getbus(devname); |
321 | if (bus == NULL) { | |
322 | /* Bus not found! */ | |
323 | return -1; | |
324 | } | |
325 | ||
326 | miiphy_pre (bus, 0, addr, reg); | |
2afbe4ed WD |
327 | |
328 | /* send the turnaround (10) */ | |
4ba31ab3 LCM |
329 | bus->set_mdc(bus, 0); |
330 | bus->set_mdio(bus, 1); | |
331 | bus->delay(bus); | |
332 | bus->set_mdc(bus, 1); | |
333 | bus->delay(bus); | |
334 | bus->set_mdc(bus, 0); | |
335 | bus->set_mdio(bus, 0); | |
336 | bus->delay(bus); | |
337 | bus->set_mdc(bus, 1); | |
338 | bus->delay(bus); | |
2afbe4ed WD |
339 | |
340 | /* write 16 bits of register data, MSB first */ | |
341 | for (j = 0; j < 16; j++) { | |
4ba31ab3 | 342 | bus->set_mdc(bus, 0); |
2afbe4ed | 343 | if ((value & 0x00008000) == 0) { |
4ba31ab3 | 344 | bus->set_mdio(bus, 0); |
2afbe4ed | 345 | } else { |
4ba31ab3 | 346 | bus->set_mdio(bus, 1); |
2afbe4ed | 347 | } |
4ba31ab3 LCM |
348 | bus->delay(bus); |
349 | bus->set_mdc(bus, 1); | |
350 | bus->delay(bus); | |
2afbe4ed WD |
351 | value <<= 1; |
352 | } | |
353 | ||
354 | /* | |
355 | * Tri-state the MDIO line. | |
356 | */ | |
4ba31ab3 LCM |
357 | bus->mdio_tristate(bus); |
358 | bus->set_mdc(bus, 0); | |
359 | bus->delay(bus); | |
360 | bus->set_mdc(bus, 1); | |
361 | bus->delay(bus); | |
2afbe4ed WD |
362 | |
363 | return 0; | |
16d1c107 | 364 | } |