blob: b143137893f739d0c6611b9c76b17d118622259a [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
wdenkaffae2b2002-08-17 09:36:01 +000015#include <ioports.h>
16#include <ppc_asm.tmpl>
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020017#include <miiphy.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060018#include <asm/global_data.h>
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020019
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020020#ifndef CONFIG_BITBANGMII_MULTI
21
22/*
23 * If CONFIG_BITBANGMII_MULTI is not defined we use a
24 * compatibility layer with the previous miiphybb implementation
25 * based on macros usage.
26 *
27 */
28static int bb_mii_init_wrap(struct bb_miiphy_bus *bus)
29{
30#ifdef MII_INIT
31 MII_INIT;
32#endif
33 return 0;
34}
35
36static int bb_mdio_active_wrap(struct bb_miiphy_bus *bus)
37{
38#ifdef MDIO_DECLARE
39 MDIO_DECLARE;
40#endif
41 MDIO_ACTIVE;
42 return 0;
43}
44
45static int bb_mdio_tristate_wrap(struct bb_miiphy_bus *bus)
46{
47#ifdef MDIO_DECLARE
48 MDIO_DECLARE;
49#endif
50 MDIO_TRISTATE;
51 return 0;
52}
53
54static int bb_set_mdio_wrap(struct bb_miiphy_bus *bus, int v)
55{
56#ifdef MDIO_DECLARE
57 MDIO_DECLARE;
58#endif
59 MDIO(v);
60 return 0;
61}
62
63static int bb_get_mdio_wrap(struct bb_miiphy_bus *bus, int *v)
64{
65#ifdef MDIO_DECLARE
66 MDIO_DECLARE;
67#endif
68 *v = MDIO_READ;
69 return 0;
70}
71
72static int bb_set_mdc_wrap(struct bb_miiphy_bus *bus, int v)
73{
74#ifdef MDC_DECLARE
75 MDC_DECLARE;
76#endif
77 MDC(v);
78 return 0;
79}
80
81static int bb_delay_wrap(struct bb_miiphy_bus *bus)
82{
83 MIIDELAY;
84 return 0;
85}
86
87struct bb_miiphy_bus bb_miiphy_buses[] = {
88 {
89 .name = BB_MII_DEVNAME,
90 .init = bb_mii_init_wrap,
91 .mdio_active = bb_mdio_active_wrap,
92 .mdio_tristate = bb_mdio_tristate_wrap,
93 .set_mdio = bb_set_mdio_wrap,
94 .get_mdio = bb_get_mdio_wrap,
95 .set_mdc = bb_set_mdc_wrap,
96 .delay = bb_delay_wrap,
97 }
98};
99
100int bb_miiphy_buses_num = sizeof(bb_miiphy_buses) /
Wolfgang Denkd61fbcc2009-10-28 00:49:47 +0100101 sizeof(bb_miiphy_buses[0]);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200102#endif
103
Ovidiu Panait69d1ddb2020-11-28 10:43:17 +0200104int bb_miiphy_init(void)
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200105{
106 int i;
107
Marek Vasutab7c4be2023-09-06 23:30:08 +0200108 for (i = 0; i < bb_miiphy_buses_num; i++)
109 if (bb_miiphy_buses[i].init != NULL)
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200110 bb_miiphy_buses[i].init(&bb_miiphy_buses[i]);
Ovidiu Panait69d1ddb2020-11-28 10:43:17 +0200111
112 return 0;
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200113}
114
Ben Warren97824d62010-07-29 12:56:11 -0700115static inline struct bb_miiphy_bus *bb_miiphy_getbus(const char *devname)
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200116{
117#ifdef CONFIG_BITBANGMII_MULTI
118 int i;
119
120 /* Search the correct bus */
121 for (i = 0; i < bb_miiphy_buses_num; i++) {
122 if (!strcmp(bb_miiphy_buses[i].name, devname)) {
123 return &bb_miiphy_buses[i];
124 }
125 }
126 return NULL;
127#else
128 /* We have just one bitbanging bus */
129 return &bb_miiphy_buses[0];
130#endif
131}
wdenkaffae2b2002-08-17 09:36:01 +0000132
wdenkaffae2b2002-08-17 09:36:01 +0000133/*****************************************************************************
134 *
135 * Utility to send the preamble, address, and register (common to read
136 * and write).
137 */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200138static void miiphy_pre(struct bb_miiphy_bus *bus, char read,
Wolfgang Denkd61fbcc2009-10-28 00:49:47 +0100139 unsigned char addr, unsigned char reg)
wdenkaffae2b2002-08-17 09:36:01 +0000140{
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200141 int j;
wdenkaffae2b2002-08-17 09:36:01 +0000142
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200143 /*
144 * Send a 32 bit preamble ('1's) with an extra '1' bit for good measure.
145 * The IEEE spec says this is a PHY optional requirement. The AMD
146 * 79C874 requires one after power up and one after a MII communications
147 * error. This means that we are doing more preambles than we need,
148 * but it is safer and will be much more robust.
149 */
wdenkaffae2b2002-08-17 09:36:01 +0000150
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200151 bus->mdio_active(bus);
152 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200153 for (j = 0; j < 32; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200154 bus->set_mdc(bus, 0);
155 bus->delay(bus);
156 bus->set_mdc(bus, 1);
157 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200158 }
wdenkaffae2b2002-08-17 09:36:01 +0000159
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200160 /* send the start bit (01) and the read opcode (10) or write (10) */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200161 bus->set_mdc(bus, 0);
162 bus->set_mdio(bus, 0);
163 bus->delay(bus);
164 bus->set_mdc(bus, 1);
165 bus->delay(bus);
166 bus->set_mdc(bus, 0);
167 bus->set_mdio(bus, 1);
168 bus->delay(bus);
169 bus->set_mdc(bus, 1);
170 bus->delay(bus);
171 bus->set_mdc(bus, 0);
172 bus->set_mdio(bus, read);
173 bus->delay(bus);
174 bus->set_mdc(bus, 1);
175 bus->delay(bus);
176 bus->set_mdc(bus, 0);
177 bus->set_mdio(bus, !read);
178 bus->delay(bus);
179 bus->set_mdc(bus, 1);
180 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000181
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200182 /* send the PHY address */
183 for (j = 0; j < 5; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200184 bus->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200185 if ((addr & 0x10) == 0) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200186 bus->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200187 } else {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200188 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200189 }
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200190 bus->delay(bus);
191 bus->set_mdc(bus, 1);
192 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200193 addr <<= 1;
194 }
wdenkaffae2b2002-08-17 09:36:01 +0000195
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200196 /* send the register address */
197 for (j = 0; j < 5; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200198 bus->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200199 if ((reg & 0x10) == 0) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200200 bus->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200201 } else {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200202 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200203 }
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200204 bus->delay(bus);
205 bus->set_mdc(bus, 1);
206 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200207 reg <<= 1;
208 }
wdenkaffae2b2002-08-17 09:36:01 +0000209}
210
wdenkaffae2b2002-08-17 09:36:01 +0000211/*****************************************************************************
212 *
213 * Read a MII PHY register.
214 *
215 * Returns:
216 * 0 on success
217 */
Joe Hershberger0c333192016-08-08 11:28:39 -0500218int bb_miiphy_read(struct mii_dev *miidev, int addr, int devad, int reg)
wdenkaffae2b2002-08-17 09:36:01 +0000219{
Chris Brandt7e4d4d12017-11-03 08:30:13 -0500220 unsigned short rdreg; /* register working value */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200221 int v;
222 int j; /* counter */
223 struct bb_miiphy_bus *bus;
224
Joe Hershberger0c333192016-08-08 11:28:39 -0500225 bus = bb_miiphy_getbus(miidev->name);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200226 if (bus == NULL) {
227 return -1;
228 }
wdenkaffae2b2002-08-17 09:36:01 +0000229
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200230 miiphy_pre (bus, 1, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000231
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200232 /* tri-state our MDIO I/O pin so we can read */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200233 bus->set_mdc(bus, 0);
234 bus->mdio_tristate(bus);
235 bus->delay(bus);
236 bus->set_mdc(bus, 1);
237 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000238
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200239 /* check the turnaround bit: the PHY should be driving it to zero */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200240 bus->get_mdio(bus, &v);
241 if (v != 0) {
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200242 /* puts ("PHY didn't drive TA low\n"); */
243 for (j = 0; j < 32; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200244 bus->set_mdc(bus, 0);
245 bus->delay(bus);
246 bus->set_mdc(bus, 1);
247 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200248 }
Joe Hershberger0c333192016-08-08 11:28:39 -0500249 /* There is no PHY, return */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200250 return -1;
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200251 }
wdenkaffae2b2002-08-17 09:36:01 +0000252
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200253 bus->set_mdc(bus, 0);
254 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000255
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200256 /* read 16 bits of register data, MSB first */
257 rdreg = 0;
258 for (j = 0; j < 16; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200259 bus->set_mdc(bus, 1);
260 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200261 rdreg <<= 1;
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200262 bus->get_mdio(bus, &v);
263 rdreg |= (v & 0x1);
264 bus->set_mdc(bus, 0);
265 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200266 }
wdenkaffae2b2002-08-17 09:36:01 +0000267
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200268 bus->set_mdc(bus, 1);
269 bus->delay(bus);
270 bus->set_mdc(bus, 0);
271 bus->delay(bus);
272 bus->set_mdc(bus, 1);
273 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000274
wdenkaffae2b2002-08-17 09:36:01 +0000275#ifdef DEBUG
Joe Hershberger0c333192016-08-08 11:28:39 -0500276 printf("miiphy_read(0x%x) @ 0x%x = 0x%04x\n", reg, addr, rdreg);
wdenkaffae2b2002-08-17 09:36:01 +0000277#endif
278
Joe Hershberger0c333192016-08-08 11:28:39 -0500279 return rdreg;
wdenkaffae2b2002-08-17 09:36:01 +0000280}
281
wdenkaffae2b2002-08-17 09:36:01 +0000282/*****************************************************************************
283 *
284 * Write a MII PHY register.
285 *
286 * Returns:
287 * 0 on success
288 */
Joe Hershberger0c333192016-08-08 11:28:39 -0500289int bb_miiphy_write(struct mii_dev *miidev, int addr, int devad, int reg,
290 u16 value)
wdenkaffae2b2002-08-17 09:36:01 +0000291{
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200292 struct bb_miiphy_bus *bus;
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200293 int j; /* counter */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200294
Joe Hershberger0c333192016-08-08 11:28:39 -0500295 bus = bb_miiphy_getbus(miidev->name);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200296 if (bus == NULL) {
297 /* Bus not found! */
298 return -1;
299 }
wdenkaffae2b2002-08-17 09:36:01 +0000300
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200301 miiphy_pre (bus, 0, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000302
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200303 /* send the turnaround (10) */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200304 bus->set_mdc(bus, 0);
305 bus->set_mdio(bus, 1);
306 bus->delay(bus);
307 bus->set_mdc(bus, 1);
308 bus->delay(bus);
309 bus->set_mdc(bus, 0);
310 bus->set_mdio(bus, 0);
311 bus->delay(bus);
312 bus->set_mdc(bus, 1);
313 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000314
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200315 /* write 16 bits of register data, MSB first */
316 for (j = 0; j < 16; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200317 bus->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200318 if ((value & 0x00008000) == 0) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200319 bus->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200320 } else {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200321 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200322 }
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200323 bus->delay(bus);
324 bus->set_mdc(bus, 1);
325 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200326 value <<= 1;
327 }
wdenkaffae2b2002-08-17 09:36:01 +0000328
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200329 /*
330 * Tri-state the MDIO line.
331 */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200332 bus->mdio_tristate(bus);
333 bus->set_mdc(bus, 0);
334 bus->delay(bus);
335 bus->set_mdc(bus, 1);
336 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000337
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200338 return 0;
Wolfgang Denk9235e0c2009-10-25 23:00:09 +0100339}