blob: e6106341eb3bceda5fea670073188720041a3a11 [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 */
Marek Vasut3d5149c2025-03-02 02:24:42 +010049static void miiphy_pre(struct bb_miiphy_bus *bus, const struct bb_miiphy_bus_ops *ops,
50 char read, 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
Marek Vasut3d5149c2025-03-02 02:24:42 +010062 ops->mdio_active(bus);
63 ops->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020064 for (j = 0; j < 32; j++) {
Marek Vasut3d5149c2025-03-02 02:24:42 +010065 ops->set_mdc(bus, 0);
66 ops->delay(bus);
67 ops->set_mdc(bus, 1);
68 ops->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) */
Marek Vasut3d5149c2025-03-02 02:24:42 +010072 ops->set_mdc(bus, 0);
73 ops->set_mdio(bus, 0);
74 ops->delay(bus);
75 ops->set_mdc(bus, 1);
76 ops->delay(bus);
77 ops->set_mdc(bus, 0);
78 ops->set_mdio(bus, 1);
79 ops->delay(bus);
80 ops->set_mdc(bus, 1);
81 ops->delay(bus);
82 ops->set_mdc(bus, 0);
83 ops->set_mdio(bus, read);
84 ops->delay(bus);
85 ops->set_mdc(bus, 1);
86 ops->delay(bus);
87 ops->set_mdc(bus, 0);
88 ops->set_mdio(bus, !read);
89 ops->delay(bus);
90 ops->set_mdc(bus, 1);
91 ops->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++) {
Marek Vasut3d5149c2025-03-02 02:24:42 +010095 ops->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020096 if ((addr & 0x10) == 0) {
Marek Vasut3d5149c2025-03-02 02:24:42 +010097 ops->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020098 } else {
Marek Vasut3d5149c2025-03-02 02:24:42 +010099 ops->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200100 }
Marek Vasut3d5149c2025-03-02 02:24:42 +0100101 ops->delay(bus);
102 ops->set_mdc(bus, 1);
103 ops->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++) {
Marek Vasut3d5149c2025-03-02 02:24:42 +0100109 ops->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200110 if ((reg & 0x10) == 0) {
Marek Vasut3d5149c2025-03-02 02:24:42 +0100111 ops->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200112 } else {
Marek Vasut3d5149c2025-03-02 02:24:42 +0100113 ops->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200114 }
Marek Vasut3d5149c2025-03-02 02:24:42 +0100115 ops->delay(bus);
116 ops->set_mdc(bus, 1);
117 ops->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;
Marek Vasut3d5149c2025-03-02 02:24:42 +0100135 const struct bb_miiphy_bus_ops *ops;
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200136
Marek Vasutb6c2c382025-02-22 21:33:28 +0100137 bus = bb_miiphy_getbus(miidev);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200138 if (bus == NULL) {
139 return -1;
140 }
wdenkaffae2b2002-08-17 09:36:01 +0000141
Marek Vasut3d5149c2025-03-02 02:24:42 +0100142 ops = bus->ops;
143
144 miiphy_pre(bus, ops, 1, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000145
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200146 /* tri-state our MDIO I/O pin so we can read */
Marek Vasut3d5149c2025-03-02 02:24:42 +0100147 ops->set_mdc(bus, 0);
148 ops->mdio_tristate(bus);
149 ops->delay(bus);
150 ops->set_mdc(bus, 1);
151 ops->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000152
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200153 /* check the turnaround bit: the PHY should be driving it to zero */
Marek Vasut3d5149c2025-03-02 02:24:42 +0100154 ops->get_mdio(bus, &v);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200155 if (v != 0) {
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200156 /* puts ("PHY didn't drive TA low\n"); */
157 for (j = 0; j < 32; j++) {
Marek Vasut3d5149c2025-03-02 02:24:42 +0100158 ops->set_mdc(bus, 0);
159 ops->delay(bus);
160 ops->set_mdc(bus, 1);
161 ops->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200162 }
Joe Hershberger0c333192016-08-08 11:28:39 -0500163 /* There is no PHY, return */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200164 return -1;
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200165 }
wdenkaffae2b2002-08-17 09:36:01 +0000166
Marek Vasut3d5149c2025-03-02 02:24:42 +0100167 ops->set_mdc(bus, 0);
168 ops->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000169
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200170 /* read 16 bits of register data, MSB first */
171 rdreg = 0;
172 for (j = 0; j < 16; j++) {
Marek Vasut3d5149c2025-03-02 02:24:42 +0100173 ops->set_mdc(bus, 1);
174 ops->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200175 rdreg <<= 1;
Marek Vasut3d5149c2025-03-02 02:24:42 +0100176 ops->get_mdio(bus, &v);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200177 rdreg |= (v & 0x1);
Marek Vasut3d5149c2025-03-02 02:24:42 +0100178 ops->set_mdc(bus, 0);
179 ops->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200180 }
wdenkaffae2b2002-08-17 09:36:01 +0000181
Marek Vasut3d5149c2025-03-02 02:24:42 +0100182 ops->set_mdc(bus, 1);
183 ops->delay(bus);
184 ops->set_mdc(bus, 0);
185 ops->delay(bus);
186 ops->set_mdc(bus, 1);
187 ops->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000188
Marek Vasut06effa22025-01-25 13:28:30 +0100189 debug("%s[%s](0x%x) @ 0x%x = 0x%04x\n", __func__, miidev->name, reg, addr, rdreg);
wdenkaffae2b2002-08-17 09:36:01 +0000190
Joe Hershberger0c333192016-08-08 11:28:39 -0500191 return rdreg;
wdenkaffae2b2002-08-17 09:36:01 +0000192}
193
wdenkaffae2b2002-08-17 09:36:01 +0000194/*****************************************************************************
195 *
196 * Write a MII PHY register.
197 *
198 * Returns:
199 * 0 on success
200 */
Joe Hershberger0c333192016-08-08 11:28:39 -0500201int bb_miiphy_write(struct mii_dev *miidev, int addr, int devad, int reg,
202 u16 value)
wdenkaffae2b2002-08-17 09:36:01 +0000203{
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200204 struct bb_miiphy_bus *bus;
Marek Vasut3d5149c2025-03-02 02:24:42 +0100205 const struct bb_miiphy_bus_ops *ops;
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200206 int j; /* counter */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200207
Marek Vasutb6c2c382025-02-22 21:33:28 +0100208 bus = bb_miiphy_getbus(miidev);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200209 if (bus == NULL) {
210 /* Bus not found! */
211 return -1;
212 }
wdenkaffae2b2002-08-17 09:36:01 +0000213
Marek Vasut3d5149c2025-03-02 02:24:42 +0100214 ops = bus->ops;
215
216 miiphy_pre(bus, ops, 0, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000217
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200218 /* send the turnaround (10) */
Marek Vasut3d5149c2025-03-02 02:24:42 +0100219 ops->set_mdc(bus, 0);
220 ops->set_mdio(bus, 1);
221 ops->delay(bus);
222 ops->set_mdc(bus, 1);
223 ops->delay(bus);
224 ops->set_mdc(bus, 0);
225 ops->set_mdio(bus, 0);
226 ops->delay(bus);
227 ops->set_mdc(bus, 1);
228 ops->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000229
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200230 /* write 16 bits of register data, MSB first */
231 for (j = 0; j < 16; j++) {
Marek Vasut3d5149c2025-03-02 02:24:42 +0100232 ops->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200233 if ((value & 0x00008000) == 0) {
Marek Vasut3d5149c2025-03-02 02:24:42 +0100234 ops->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200235 } else {
Marek Vasut3d5149c2025-03-02 02:24:42 +0100236 ops->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200237 }
Marek Vasut3d5149c2025-03-02 02:24:42 +0100238 ops->delay(bus);
239 ops->set_mdc(bus, 1);
240 ops->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200241 value <<= 1;
242 }
wdenkaffae2b2002-08-17 09:36:01 +0000243
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200244 /*
245 * Tri-state the MDIO line.
246 */
Marek Vasut3d5149c2025-03-02 02:24:42 +0100247 ops->mdio_tristate(bus);
248 ops->set_mdc(bus, 0);
249 ops->delay(bus);
250 ops->set_mdc(bus, 1);
251 ops->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000252
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200253 return 0;
Wolfgang Denk9235e0c2009-10-25 23:00:09 +0100254}