blob: 9481ba76f51b1063da11e09d1b7ff843472c2cb5 [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 */
Marek Vasut65867d32025-03-02 02:24:44 +0100129int bb_miiphy_read(struct mii_dev *miidev, const struct bb_miiphy_bus_ops *ops,
130 int addr, int devad, int reg)
wdenkaffae2b2002-08-17 09:36:01 +0000131{
Chris Brandt7e4d4d12017-11-03 08:30:13 -0500132 unsigned short rdreg; /* register working value */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200133 int v;
134 int j; /* counter */
135 struct bb_miiphy_bus *bus;
136
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 miiphy_pre(bus, ops, 1, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000143
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200144 /* tri-state our MDIO I/O pin so we can read */
Marek Vasut3d5149c2025-03-02 02:24:42 +0100145 ops->set_mdc(bus, 0);
146 ops->mdio_tristate(bus);
147 ops->delay(bus);
148 ops->set_mdc(bus, 1);
149 ops->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000150
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200151 /* check the turnaround bit: the PHY should be driving it to zero */
Marek Vasut3d5149c2025-03-02 02:24:42 +0100152 ops->get_mdio(bus, &v);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200153 if (v != 0) {
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200154 /* puts ("PHY didn't drive TA low\n"); */
155 for (j = 0; j < 32; j++) {
Marek Vasut3d5149c2025-03-02 02:24:42 +0100156 ops->set_mdc(bus, 0);
157 ops->delay(bus);
158 ops->set_mdc(bus, 1);
159 ops->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200160 }
Joe Hershberger0c333192016-08-08 11:28:39 -0500161 /* There is no PHY, return */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200162 return -1;
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200163 }
wdenkaffae2b2002-08-17 09:36:01 +0000164
Marek Vasut3d5149c2025-03-02 02:24:42 +0100165 ops->set_mdc(bus, 0);
166 ops->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000167
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200168 /* read 16 bits of register data, MSB first */
169 rdreg = 0;
170 for (j = 0; j < 16; j++) {
Marek Vasut3d5149c2025-03-02 02:24:42 +0100171 ops->set_mdc(bus, 1);
172 ops->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200173 rdreg <<= 1;
Marek Vasut3d5149c2025-03-02 02:24:42 +0100174 ops->get_mdio(bus, &v);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200175 rdreg |= (v & 0x1);
Marek Vasut3d5149c2025-03-02 02:24:42 +0100176 ops->set_mdc(bus, 0);
177 ops->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200178 }
wdenkaffae2b2002-08-17 09:36:01 +0000179
Marek Vasut3d5149c2025-03-02 02:24:42 +0100180 ops->set_mdc(bus, 1);
181 ops->delay(bus);
182 ops->set_mdc(bus, 0);
183 ops->delay(bus);
184 ops->set_mdc(bus, 1);
185 ops->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000186
Marek Vasut06effa22025-01-25 13:28:30 +0100187 debug("%s[%s](0x%x) @ 0x%x = 0x%04x\n", __func__, miidev->name, reg, addr, rdreg);
wdenkaffae2b2002-08-17 09:36:01 +0000188
Joe Hershberger0c333192016-08-08 11:28:39 -0500189 return rdreg;
wdenkaffae2b2002-08-17 09:36:01 +0000190}
191
wdenkaffae2b2002-08-17 09:36:01 +0000192/*****************************************************************************
193 *
194 * Write a MII PHY register.
195 *
196 * Returns:
197 * 0 on success
198 */
Marek Vasut65867d32025-03-02 02:24:44 +0100199int bb_miiphy_write(struct mii_dev *miidev, const struct bb_miiphy_bus_ops *ops,
200 int addr, int devad, int reg, u16 value)
wdenkaffae2b2002-08-17 09:36:01 +0000201{
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200202 struct bb_miiphy_bus *bus;
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200203 int j; /* counter */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200204
Marek Vasutb6c2c382025-02-22 21:33:28 +0100205 bus = bb_miiphy_getbus(miidev);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200206 if (bus == NULL) {
207 /* Bus not found! */
208 return -1;
209 }
wdenkaffae2b2002-08-17 09:36:01 +0000210
Marek Vasut3d5149c2025-03-02 02:24:42 +0100211 miiphy_pre(bus, ops, 0, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000212
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200213 /* send the turnaround (10) */
Marek Vasut3d5149c2025-03-02 02:24:42 +0100214 ops->set_mdc(bus, 0);
215 ops->set_mdio(bus, 1);
216 ops->delay(bus);
217 ops->set_mdc(bus, 1);
218 ops->delay(bus);
219 ops->set_mdc(bus, 0);
220 ops->set_mdio(bus, 0);
221 ops->delay(bus);
222 ops->set_mdc(bus, 1);
223 ops->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000224
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200225 /* write 16 bits of register data, MSB first */
226 for (j = 0; j < 16; j++) {
Marek Vasut3d5149c2025-03-02 02:24:42 +0100227 ops->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200228 if ((value & 0x00008000) == 0) {
Marek Vasut3d5149c2025-03-02 02:24:42 +0100229 ops->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200230 } else {
Marek Vasut3d5149c2025-03-02 02:24:42 +0100231 ops->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200232 }
Marek Vasut3d5149c2025-03-02 02:24:42 +0100233 ops->delay(bus);
234 ops->set_mdc(bus, 1);
235 ops->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200236 value <<= 1;
237 }
wdenkaffae2b2002-08-17 09:36:01 +0000238
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200239 /*
240 * Tri-state the MDIO line.
241 */
Marek Vasut3d5149c2025-03-02 02:24:42 +0100242 ops->mdio_tristate(bus);
243 ops->set_mdc(bus, 0);
244 ops->delay(bus);
245 ops->set_mdc(bus, 1);
246 ops->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000247
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200248 return 0;
Wolfgang Denk9235e0c2009-10-25 23:00:09 +0100249}