blob: cf71f7d4e7e5a3526c1e97e11ded132575dd7b9f [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
Tom Riniabb9a042024-05-18 20:20:43 -060015#include <common.h>
wdenkaffae2b2002-08-17 09:36:01 +000016#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
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020021#ifndef CONFIG_BITBANGMII_MULTI
22
23/*
24 * If CONFIG_BITBANGMII_MULTI is not defined we use a
25 * compatibility layer with the previous miiphybb implementation
26 * based on macros usage.
27 *
28 */
29static int bb_mii_init_wrap(struct bb_miiphy_bus *bus)
30{
31#ifdef MII_INIT
32 MII_INIT;
33#endif
34 return 0;
35}
36
37static int bb_mdio_active_wrap(struct bb_miiphy_bus *bus)
38{
39#ifdef MDIO_DECLARE
40 MDIO_DECLARE;
41#endif
42 MDIO_ACTIVE;
43 return 0;
44}
45
46static int bb_mdio_tristate_wrap(struct bb_miiphy_bus *bus)
47{
48#ifdef MDIO_DECLARE
49 MDIO_DECLARE;
50#endif
51 MDIO_TRISTATE;
52 return 0;
53}
54
55static int bb_set_mdio_wrap(struct bb_miiphy_bus *bus, int v)
56{
57#ifdef MDIO_DECLARE
58 MDIO_DECLARE;
59#endif
60 MDIO(v);
61 return 0;
62}
63
64static int bb_get_mdio_wrap(struct bb_miiphy_bus *bus, int *v)
65{
66#ifdef MDIO_DECLARE
67 MDIO_DECLARE;
68#endif
69 *v = MDIO_READ;
70 return 0;
71}
72
73static int bb_set_mdc_wrap(struct bb_miiphy_bus *bus, int v)
74{
75#ifdef MDC_DECLARE
76 MDC_DECLARE;
77#endif
78 MDC(v);
79 return 0;
80}
81
82static int bb_delay_wrap(struct bb_miiphy_bus *bus)
83{
84 MIIDELAY;
85 return 0;
86}
87
88struct bb_miiphy_bus bb_miiphy_buses[] = {
89 {
90 .name = BB_MII_DEVNAME,
91 .init = bb_mii_init_wrap,
92 .mdio_active = bb_mdio_active_wrap,
93 .mdio_tristate = bb_mdio_tristate_wrap,
94 .set_mdio = bb_set_mdio_wrap,
95 .get_mdio = bb_get_mdio_wrap,
96 .set_mdc = bb_set_mdc_wrap,
97 .delay = bb_delay_wrap,
98 }
99};
100
101int bb_miiphy_buses_num = sizeof(bb_miiphy_buses) /
Wolfgang Denkd61fbcc2009-10-28 00:49:47 +0100102 sizeof(bb_miiphy_buses[0]);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200103#endif
104
Ovidiu Panait69d1ddb2020-11-28 10:43:17 +0200105int bb_miiphy_init(void)
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200106{
107 int i;
108
Marek Vasutab7c4be2023-09-06 23:30:08 +0200109 for (i = 0; i < bb_miiphy_buses_num; i++)
110 if (bb_miiphy_buses[i].init != NULL)
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200111 bb_miiphy_buses[i].init(&bb_miiphy_buses[i]);
Ovidiu Panait69d1ddb2020-11-28 10:43:17 +0200112
113 return 0;
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200114}
115
Ben Warren97824d62010-07-29 12:56:11 -0700116static inline struct bb_miiphy_bus *bb_miiphy_getbus(const char *devname)
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200117{
118#ifdef CONFIG_BITBANGMII_MULTI
119 int i;
120
121 /* Search the correct bus */
122 for (i = 0; i < bb_miiphy_buses_num; i++) {
123 if (!strcmp(bb_miiphy_buses[i].name, devname)) {
124 return &bb_miiphy_buses[i];
125 }
126 }
127 return NULL;
128#else
129 /* We have just one bitbanging bus */
130 return &bb_miiphy_buses[0];
131#endif
132}
wdenkaffae2b2002-08-17 09:36:01 +0000133
wdenkaffae2b2002-08-17 09:36:01 +0000134/*****************************************************************************
135 *
136 * Utility to send the preamble, address, and register (common to read
137 * and write).
138 */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200139static void miiphy_pre(struct bb_miiphy_bus *bus, char read,
Wolfgang Denkd61fbcc2009-10-28 00:49:47 +0100140 unsigned char addr, unsigned char reg)
wdenkaffae2b2002-08-17 09:36:01 +0000141{
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200142 int j;
wdenkaffae2b2002-08-17 09:36:01 +0000143
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200144 /*
145 * Send a 32 bit preamble ('1's) with an extra '1' bit for good measure.
146 * The IEEE spec says this is a PHY optional requirement. The AMD
147 * 79C874 requires one after power up and one after a MII communications
148 * error. This means that we are doing more preambles than we need,
149 * but it is safer and will be much more robust.
150 */
wdenkaffae2b2002-08-17 09:36:01 +0000151
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200152 bus->mdio_active(bus);
153 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200154 for (j = 0; j < 32; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200155 bus->set_mdc(bus, 0);
156 bus->delay(bus);
157 bus->set_mdc(bus, 1);
158 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200159 }
wdenkaffae2b2002-08-17 09:36:01 +0000160
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200161 /* send the start bit (01) and the read opcode (10) or write (10) */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200162 bus->set_mdc(bus, 0);
163 bus->set_mdio(bus, 0);
164 bus->delay(bus);
165 bus->set_mdc(bus, 1);
166 bus->delay(bus);
167 bus->set_mdc(bus, 0);
168 bus->set_mdio(bus, 1);
169 bus->delay(bus);
170 bus->set_mdc(bus, 1);
171 bus->delay(bus);
172 bus->set_mdc(bus, 0);
173 bus->set_mdio(bus, read);
174 bus->delay(bus);
175 bus->set_mdc(bus, 1);
176 bus->delay(bus);
177 bus->set_mdc(bus, 0);
178 bus->set_mdio(bus, !read);
179 bus->delay(bus);
180 bus->set_mdc(bus, 1);
181 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000182
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200183 /* send the PHY address */
184 for (j = 0; j < 5; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200185 bus->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200186 if ((addr & 0x10) == 0) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200187 bus->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200188 } else {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200189 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200190 }
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200191 bus->delay(bus);
192 bus->set_mdc(bus, 1);
193 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200194 addr <<= 1;
195 }
wdenkaffae2b2002-08-17 09:36:01 +0000196
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200197 /* send the register address */
198 for (j = 0; j < 5; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200199 bus->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200200 if ((reg & 0x10) == 0) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200201 bus->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200202 } else {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200203 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200204 }
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200205 bus->delay(bus);
206 bus->set_mdc(bus, 1);
207 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200208 reg <<= 1;
209 }
wdenkaffae2b2002-08-17 09:36:01 +0000210}
211
wdenkaffae2b2002-08-17 09:36:01 +0000212/*****************************************************************************
213 *
214 * Read a MII PHY register.
215 *
216 * Returns:
217 * 0 on success
218 */
Joe Hershberger0c333192016-08-08 11:28:39 -0500219int bb_miiphy_read(struct mii_dev *miidev, int addr, int devad, int reg)
wdenkaffae2b2002-08-17 09:36:01 +0000220{
Chris Brandt7e4d4d12017-11-03 08:30:13 -0500221 unsigned short rdreg; /* register working value */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200222 int v;
223 int j; /* counter */
224 struct bb_miiphy_bus *bus;
225
Joe Hershberger0c333192016-08-08 11:28:39 -0500226 bus = bb_miiphy_getbus(miidev->name);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200227 if (bus == NULL) {
228 return -1;
229 }
wdenkaffae2b2002-08-17 09:36:01 +0000230
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200231 miiphy_pre (bus, 1, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000232
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200233 /* tri-state our MDIO I/O pin so we can read */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200234 bus->set_mdc(bus, 0);
235 bus->mdio_tristate(bus);
236 bus->delay(bus);
237 bus->set_mdc(bus, 1);
238 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000239
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200240 /* check the turnaround bit: the PHY should be driving it to zero */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200241 bus->get_mdio(bus, &v);
242 if (v != 0) {
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200243 /* puts ("PHY didn't drive TA low\n"); */
244 for (j = 0; j < 32; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200245 bus->set_mdc(bus, 0);
246 bus->delay(bus);
247 bus->set_mdc(bus, 1);
248 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200249 }
Joe Hershberger0c333192016-08-08 11:28:39 -0500250 /* There is no PHY, return */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200251 return -1;
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200252 }
wdenkaffae2b2002-08-17 09:36:01 +0000253
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200254 bus->set_mdc(bus, 0);
255 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000256
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200257 /* read 16 bits of register data, MSB first */
258 rdreg = 0;
259 for (j = 0; j < 16; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200260 bus->set_mdc(bus, 1);
261 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200262 rdreg <<= 1;
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200263 bus->get_mdio(bus, &v);
264 rdreg |= (v & 0x1);
265 bus->set_mdc(bus, 0);
266 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200267 }
wdenkaffae2b2002-08-17 09:36:01 +0000268
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200269 bus->set_mdc(bus, 1);
270 bus->delay(bus);
271 bus->set_mdc(bus, 0);
272 bus->delay(bus);
273 bus->set_mdc(bus, 1);
274 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000275
wdenkaffae2b2002-08-17 09:36:01 +0000276#ifdef DEBUG
Joe Hershberger0c333192016-08-08 11:28:39 -0500277 printf("miiphy_read(0x%x) @ 0x%x = 0x%04x\n", reg, addr, rdreg);
wdenkaffae2b2002-08-17 09:36:01 +0000278#endif
279
Joe Hershberger0c333192016-08-08 11:28:39 -0500280 return rdreg;
wdenkaffae2b2002-08-17 09:36:01 +0000281}
282
283
284/*****************************************************************************
285 *
286 * Write a MII PHY register.
287 *
288 * Returns:
289 * 0 on success
290 */
Joe Hershberger0c333192016-08-08 11:28:39 -0500291int bb_miiphy_write(struct mii_dev *miidev, int addr, int devad, int reg,
292 u16 value)
wdenkaffae2b2002-08-17 09:36:01 +0000293{
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200294 struct bb_miiphy_bus *bus;
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200295 int j; /* counter */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200296
Joe Hershberger0c333192016-08-08 11:28:39 -0500297 bus = bb_miiphy_getbus(miidev->name);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200298 if (bus == NULL) {
299 /* Bus not found! */
300 return -1;
301 }
wdenkaffae2b2002-08-17 09:36:01 +0000302
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200303 miiphy_pre (bus, 0, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000304
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200305 /* send the turnaround (10) */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200306 bus->set_mdc(bus, 0);
307 bus->set_mdio(bus, 1);
308 bus->delay(bus);
309 bus->set_mdc(bus, 1);
310 bus->delay(bus);
311 bus->set_mdc(bus, 0);
312 bus->set_mdio(bus, 0);
313 bus->delay(bus);
314 bus->set_mdc(bus, 1);
315 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000316
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200317 /* write 16 bits of register data, MSB first */
318 for (j = 0; j < 16; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200319 bus->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200320 if ((value & 0x00008000) == 0) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200321 bus->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200322 } else {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200323 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200324 }
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200325 bus->delay(bus);
326 bus->set_mdc(bus, 1);
327 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200328 value <<= 1;
329 }
wdenkaffae2b2002-08-17 09:36:01 +0000330
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200331 /*
332 * Tri-state the MDIO line.
333 */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200334 bus->mdio_tristate(bus);
335 bus->set_mdc(bus, 0);
336 bus->delay(bus);
337 bus->set_mdc(bus, 1);
338 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000339
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200340 return 0;
Wolfgang Denk9235e0c2009-10-25 23:00:09 +0100341}