blob: 24d617553e71d160ec3dcf5c31118e05de54aa8f [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
wdenkaffae2b2002-08-17 09:36:01 +00002/*
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +02003 * (C) Copyright 2009 Industrie Dial Face S.p.A.
4 * Luigi 'Comio' Mantellini <luigi.mantellini@idf-hit.com>
5 *
wdenkaffae2b2002-08-17 09:36:01 +00006 * (C) Copyright 2001
7 * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com.
wdenkaffae2b2002-08-17 09:36:01 +00008 */
9
10/*
11 * This provides a bit-banged interface to the ethernet MII management
12 * channel.
13 */
14
15#include <common.h>
16#include <ioports.h>
17#include <ppc_asm.tmpl>
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020018#include <miiphy.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060019#include <asm/global_data.h>
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020020
21#define BB_MII_RELOCATE(v,off) (v += (v?off:0))
22
23DECLARE_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 */
33static 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
41static 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
50static 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
59static 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
68static 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
77static 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
86static int bb_delay_wrap(struct bb_miiphy_bus *bus)
87{
88 MIIDELAY;
89 return 0;
90}
91
92struct 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
105int bb_miiphy_buses_num = sizeof(bb_miiphy_buses) /
Wolfgang Denkd61fbcc2009-10-28 00:49:47 +0100106 sizeof(bb_miiphy_buses[0]);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200107#endif
108
Ovidiu Panait69d1ddb2020-11-28 10:43:17 +0200109int bb_miiphy_init(void)
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200110{
111 int i;
112
113 for (i = 0; i < bb_miiphy_buses_num; i++) {
Wolfgang Denkd0813e52010-10-28 20:00:11 +0200114#if defined(CONFIG_NEEDS_MANUAL_RELOC)
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200115 /* 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 }
Ovidiu Panait69d1ddb2020-11-28 10:43:17 +0200128
129 return 0;
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200130}
131
Ben Warren97824d62010-07-29 12:56:11 -0700132static inline struct bb_miiphy_bus *bb_miiphy_getbus(const char *devname)
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200133{
134#ifdef CONFIG_BITBANGMII_MULTI
135 int i;
136
137 /* Search the correct bus */
138 for (i = 0; i < bb_miiphy_buses_num; i++) {
139 if (!strcmp(bb_miiphy_buses[i].name, devname)) {
140 return &bb_miiphy_buses[i];
141 }
142 }
143 return NULL;
144#else
145 /* We have just one bitbanging bus */
146 return &bb_miiphy_buses[0];
147#endif
148}
wdenkaffae2b2002-08-17 09:36:01 +0000149
wdenkaffae2b2002-08-17 09:36:01 +0000150/*****************************************************************************
151 *
152 * Utility to send the preamble, address, and register (common to read
153 * and write).
154 */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200155static void miiphy_pre(struct bb_miiphy_bus *bus, char read,
Wolfgang Denkd61fbcc2009-10-28 00:49:47 +0100156 unsigned char addr, unsigned char reg)
wdenkaffae2b2002-08-17 09:36:01 +0000157{
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200158 int j;
wdenkaffae2b2002-08-17 09:36:01 +0000159
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200160 /*
161 * Send a 32 bit preamble ('1's) with an extra '1' bit for good measure.
162 * The IEEE spec says this is a PHY optional requirement. The AMD
163 * 79C874 requires one after power up and one after a MII communications
164 * error. This means that we are doing more preambles than we need,
165 * but it is safer and will be much more robust.
166 */
wdenkaffae2b2002-08-17 09:36:01 +0000167
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200168 bus->mdio_active(bus);
169 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200170 for (j = 0; j < 32; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200171 bus->set_mdc(bus, 0);
172 bus->delay(bus);
173 bus->set_mdc(bus, 1);
174 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200175 }
wdenkaffae2b2002-08-17 09:36:01 +0000176
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200177 /* send the start bit (01) and the read opcode (10) or write (10) */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200178 bus->set_mdc(bus, 0);
179 bus->set_mdio(bus, 0);
180 bus->delay(bus);
181 bus->set_mdc(bus, 1);
182 bus->delay(bus);
183 bus->set_mdc(bus, 0);
184 bus->set_mdio(bus, 1);
185 bus->delay(bus);
186 bus->set_mdc(bus, 1);
187 bus->delay(bus);
188 bus->set_mdc(bus, 0);
189 bus->set_mdio(bus, read);
190 bus->delay(bus);
191 bus->set_mdc(bus, 1);
192 bus->delay(bus);
193 bus->set_mdc(bus, 0);
194 bus->set_mdio(bus, !read);
195 bus->delay(bus);
196 bus->set_mdc(bus, 1);
197 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000198
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200199 /* send the PHY address */
200 for (j = 0; j < 5; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200201 bus->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200202 if ((addr & 0x10) == 0) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200203 bus->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200204 } else {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200205 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200206 }
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200207 bus->delay(bus);
208 bus->set_mdc(bus, 1);
209 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200210 addr <<= 1;
211 }
wdenkaffae2b2002-08-17 09:36:01 +0000212
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200213 /* send the register address */
214 for (j = 0; j < 5; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200215 bus->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200216 if ((reg & 0x10) == 0) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200217 bus->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200218 } else {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200219 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200220 }
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200221 bus->delay(bus);
222 bus->set_mdc(bus, 1);
223 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200224 reg <<= 1;
225 }
wdenkaffae2b2002-08-17 09:36:01 +0000226}
227
wdenkaffae2b2002-08-17 09:36:01 +0000228/*****************************************************************************
229 *
230 * Read a MII PHY register.
231 *
232 * Returns:
233 * 0 on success
234 */
Joe Hershberger0c333192016-08-08 11:28:39 -0500235int bb_miiphy_read(struct mii_dev *miidev, int addr, int devad, int reg)
wdenkaffae2b2002-08-17 09:36:01 +0000236{
Chris Brandt7e4d4d12017-11-03 08:30:13 -0500237 unsigned short rdreg; /* register working value */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200238 int v;
239 int j; /* counter */
240 struct bb_miiphy_bus *bus;
241
Joe Hershberger0c333192016-08-08 11:28:39 -0500242 bus = bb_miiphy_getbus(miidev->name);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200243 if (bus == NULL) {
244 return -1;
245 }
wdenkaffae2b2002-08-17 09:36:01 +0000246
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200247 miiphy_pre (bus, 1, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000248
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200249 /* tri-state our MDIO I/O pin so we can read */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200250 bus->set_mdc(bus, 0);
251 bus->mdio_tristate(bus);
252 bus->delay(bus);
253 bus->set_mdc(bus, 1);
254 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000255
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200256 /* check the turnaround bit: the PHY should be driving it to zero */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200257 bus->get_mdio(bus, &v);
258 if (v != 0) {
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200259 /* puts ("PHY didn't drive TA low\n"); */
260 for (j = 0; j < 32; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200261 bus->set_mdc(bus, 0);
262 bus->delay(bus);
263 bus->set_mdc(bus, 1);
264 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200265 }
Joe Hershberger0c333192016-08-08 11:28:39 -0500266 /* There is no PHY, return */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200267 return -1;
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200268 }
wdenkaffae2b2002-08-17 09:36:01 +0000269
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200270 bus->set_mdc(bus, 0);
271 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000272
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200273 /* read 16 bits of register data, MSB first */
274 rdreg = 0;
275 for (j = 0; j < 16; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200276 bus->set_mdc(bus, 1);
277 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200278 rdreg <<= 1;
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200279 bus->get_mdio(bus, &v);
280 rdreg |= (v & 0x1);
281 bus->set_mdc(bus, 0);
282 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200283 }
wdenkaffae2b2002-08-17 09:36:01 +0000284
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200285 bus->set_mdc(bus, 1);
286 bus->delay(bus);
287 bus->set_mdc(bus, 0);
288 bus->delay(bus);
289 bus->set_mdc(bus, 1);
290 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000291
wdenkaffae2b2002-08-17 09:36:01 +0000292#ifdef DEBUG
Joe Hershberger0c333192016-08-08 11:28:39 -0500293 printf("miiphy_read(0x%x) @ 0x%x = 0x%04x\n", reg, addr, rdreg);
wdenkaffae2b2002-08-17 09:36:01 +0000294#endif
295
Joe Hershberger0c333192016-08-08 11:28:39 -0500296 return rdreg;
wdenkaffae2b2002-08-17 09:36:01 +0000297}
298
299
300/*****************************************************************************
301 *
302 * Write a MII PHY register.
303 *
304 * Returns:
305 * 0 on success
306 */
Joe Hershberger0c333192016-08-08 11:28:39 -0500307int bb_miiphy_write(struct mii_dev *miidev, int addr, int devad, int reg,
308 u16 value)
wdenkaffae2b2002-08-17 09:36:01 +0000309{
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200310 struct bb_miiphy_bus *bus;
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200311 int j; /* counter */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200312
Joe Hershberger0c333192016-08-08 11:28:39 -0500313 bus = bb_miiphy_getbus(miidev->name);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200314 if (bus == NULL) {
315 /* Bus not found! */
316 return -1;
317 }
wdenkaffae2b2002-08-17 09:36:01 +0000318
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200319 miiphy_pre (bus, 0, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000320
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200321 /* send the turnaround (10) */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200322 bus->set_mdc(bus, 0);
323 bus->set_mdio(bus, 1);
324 bus->delay(bus);
325 bus->set_mdc(bus, 1);
326 bus->delay(bus);
327 bus->set_mdc(bus, 0);
328 bus->set_mdio(bus, 0);
329 bus->delay(bus);
330 bus->set_mdc(bus, 1);
331 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000332
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200333 /* write 16 bits of register data, MSB first */
334 for (j = 0; j < 16; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200335 bus->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200336 if ((value & 0x00008000) == 0) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200337 bus->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200338 } else {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200339 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200340 }
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200341 bus->delay(bus);
342 bus->set_mdc(bus, 1);
343 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200344 value <<= 1;
345 }
wdenkaffae2b2002-08-17 09:36:01 +0000346
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200347 /*
348 * Tri-state the MDIO line.
349 */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200350 bus->mdio_tristate(bus);
351 bus->set_mdc(bus, 0);
352 bus->delay(bus);
353 bus->set_mdc(bus, 1);
354 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000355
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200356 return 0;
Wolfgang Denk9235e0c2009-10-25 23:00:09 +0100357}