blob: 553af2c10329aeee1b6b7a5a6fcfdd2dcbd9458b [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>
Marek Vasuta392ff52025-02-22 21:33:23 +010017#include <malloc.h>
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
Marek Vasutb6c2c382025-02-22 21:33:28 +010021static inline struct bb_miiphy_bus *bb_miiphy_getbus(struct mii_dev *miidev)
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020022{
Marek Vasutb6c2c382025-02-22 21:33:28 +010023 return container_of(miidev, struct bb_miiphy_bus, mii);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020024}
wdenkaffae2b2002-08-17 09:36:01 +000025
Marek Vasuta392ff52025-02-22 21:33:23 +010026struct bb_miiphy_bus *bb_miiphy_alloc(void)
27{
28 struct bb_miiphy_bus *bus;
29
30 bus = malloc(sizeof(*bus));
31 if (!bus)
32 return bus;
33
34 mdio_init(&bus->mii);
35
36 return bus;
37}
38
39void bb_miiphy_free(struct bb_miiphy_bus *bus)
40{
41 free(bus);
42}
43
wdenkaffae2b2002-08-17 09:36:01 +000044/*****************************************************************************
45 *
46 * Utility to send the preamble, address, and register (common to read
47 * and write).
48 */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020049static void miiphy_pre(struct bb_miiphy_bus *bus, char read,
Wolfgang Denkd61fbcc2009-10-28 00:49:47 +010050 unsigned char addr, unsigned char reg)
wdenkaffae2b2002-08-17 09:36:01 +000051{
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020052 int j;
wdenkaffae2b2002-08-17 09:36:01 +000053
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020054 /*
55 * Send a 32 bit preamble ('1's) with an extra '1' bit for good measure.
56 * The IEEE spec says this is a PHY optional requirement. The AMD
57 * 79C874 requires one after power up and one after a MII communications
58 * error. This means that we are doing more preambles than we need,
59 * but it is safer and will be much more robust.
60 */
wdenkaffae2b2002-08-17 09:36:01 +000061
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020062 bus->mdio_active(bus);
63 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020064 for (j = 0; j < 32; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020065 bus->set_mdc(bus, 0);
66 bus->delay(bus);
67 bus->set_mdc(bus, 1);
68 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020069 }
wdenkaffae2b2002-08-17 09:36:01 +000070
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020071 /* send the start bit (01) and the read opcode (10) or write (10) */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020072 bus->set_mdc(bus, 0);
73 bus->set_mdio(bus, 0);
74 bus->delay(bus);
75 bus->set_mdc(bus, 1);
76 bus->delay(bus);
77 bus->set_mdc(bus, 0);
78 bus->set_mdio(bus, 1);
79 bus->delay(bus);
80 bus->set_mdc(bus, 1);
81 bus->delay(bus);
82 bus->set_mdc(bus, 0);
83 bus->set_mdio(bus, read);
84 bus->delay(bus);
85 bus->set_mdc(bus, 1);
86 bus->delay(bus);
87 bus->set_mdc(bus, 0);
88 bus->set_mdio(bus, !read);
89 bus->delay(bus);
90 bus->set_mdc(bus, 1);
91 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +000092
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020093 /* send the PHY address */
94 for (j = 0; j < 5; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020095 bus->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020096 if ((addr & 0x10) == 0) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020097 bus->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020098 } else {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020099 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200100 }
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200101 bus->delay(bus);
102 bus->set_mdc(bus, 1);
103 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200104 addr <<= 1;
105 }
wdenkaffae2b2002-08-17 09:36:01 +0000106
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200107 /* send the register address */
108 for (j = 0; j < 5; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200109 bus->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200110 if ((reg & 0x10) == 0) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200111 bus->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200112 } else {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200113 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200114 }
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200115 bus->delay(bus);
116 bus->set_mdc(bus, 1);
117 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200118 reg <<= 1;
119 }
wdenkaffae2b2002-08-17 09:36:01 +0000120}
121
wdenkaffae2b2002-08-17 09:36:01 +0000122/*****************************************************************************
123 *
124 * Read a MII PHY register.
125 *
126 * Returns:
127 * 0 on success
128 */
Joe Hershberger0c333192016-08-08 11:28:39 -0500129int bb_miiphy_read(struct mii_dev *miidev, int addr, int devad, int reg)
wdenkaffae2b2002-08-17 09:36:01 +0000130{
Chris Brandt7e4d4d12017-11-03 08:30:13 -0500131 unsigned short rdreg; /* register working value */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200132 int v;
133 int j; /* counter */
134 struct bb_miiphy_bus *bus;
135
Marek Vasutb6c2c382025-02-22 21:33:28 +0100136 bus = bb_miiphy_getbus(miidev);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200137 if (bus == NULL) {
138 return -1;
139 }
wdenkaffae2b2002-08-17 09:36:01 +0000140
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200141 miiphy_pre (bus, 1, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000142
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200143 /* tri-state our MDIO I/O pin so we can read */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200144 bus->set_mdc(bus, 0);
145 bus->mdio_tristate(bus);
146 bus->delay(bus);
147 bus->set_mdc(bus, 1);
148 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000149
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200150 /* check the turnaround bit: the PHY should be driving it to zero */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200151 bus->get_mdio(bus, &v);
152 if (v != 0) {
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200153 /* puts ("PHY didn't drive TA low\n"); */
154 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 }
Joe Hershberger0c333192016-08-08 11:28:39 -0500160 /* There is no PHY, return */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200161 return -1;
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200162 }
wdenkaffae2b2002-08-17 09:36:01 +0000163
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200164 bus->set_mdc(bus, 0);
165 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000166
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200167 /* read 16 bits of register data, MSB first */
168 rdreg = 0;
169 for (j = 0; j < 16; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200170 bus->set_mdc(bus, 1);
171 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200172 rdreg <<= 1;
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200173 bus->get_mdio(bus, &v);
174 rdreg |= (v & 0x1);
175 bus->set_mdc(bus, 0);
176 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200177 }
wdenkaffae2b2002-08-17 09:36:01 +0000178
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200179 bus->set_mdc(bus, 1);
180 bus->delay(bus);
181 bus->set_mdc(bus, 0);
182 bus->delay(bus);
183 bus->set_mdc(bus, 1);
184 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000185
Marek Vasut06effa22025-01-25 13:28:30 +0100186 debug("%s[%s](0x%x) @ 0x%x = 0x%04x\n", __func__, miidev->name, reg, addr, rdreg);
wdenkaffae2b2002-08-17 09:36:01 +0000187
Joe Hershberger0c333192016-08-08 11:28:39 -0500188 return rdreg;
wdenkaffae2b2002-08-17 09:36:01 +0000189}
190
wdenkaffae2b2002-08-17 09:36:01 +0000191/*****************************************************************************
192 *
193 * Write a MII PHY register.
194 *
195 * Returns:
196 * 0 on success
197 */
Joe Hershberger0c333192016-08-08 11:28:39 -0500198int bb_miiphy_write(struct mii_dev *miidev, int addr, int devad, int reg,
199 u16 value)
wdenkaffae2b2002-08-17 09:36:01 +0000200{
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200201 struct bb_miiphy_bus *bus;
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200202 int j; /* counter */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200203
Marek Vasutb6c2c382025-02-22 21:33:28 +0100204 bus = bb_miiphy_getbus(miidev);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200205 if (bus == NULL) {
206 /* Bus not found! */
207 return -1;
208 }
wdenkaffae2b2002-08-17 09:36:01 +0000209
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200210 miiphy_pre (bus, 0, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000211
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200212 /* send the turnaround (10) */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200213 bus->set_mdc(bus, 0);
214 bus->set_mdio(bus, 1);
215 bus->delay(bus);
216 bus->set_mdc(bus, 1);
217 bus->delay(bus);
218 bus->set_mdc(bus, 0);
219 bus->set_mdio(bus, 0);
220 bus->delay(bus);
221 bus->set_mdc(bus, 1);
222 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000223
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200224 /* write 16 bits of register data, MSB first */
225 for (j = 0; j < 16; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200226 bus->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200227 if ((value & 0x00008000) == 0) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200228 bus->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200229 } else {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200230 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200231 }
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200232 bus->delay(bus);
233 bus->set_mdc(bus, 1);
234 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200235 value <<= 1;
236 }
wdenkaffae2b2002-08-17 09:36:01 +0000237
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200238 /*
239 * Tri-state the MDIO line.
240 */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200241 bus->mdio_tristate(bus);
242 bus->set_mdc(bus, 0);
243 bus->delay(bus);
244 bus->set_mdc(bus, 1);
245 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000246
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200247 return 0;
Wolfgang Denk9235e0c2009-10-25 23:00:09 +0100248}