blob: 66d98d6cc26858ffeeb462baf0fa4a01545a7de5 [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
Ben Warren97824d62010-07-29 12:56:11 -070021static inline struct bb_miiphy_bus *bb_miiphy_getbus(const char *devname)
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020022{
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020023 int i;
24
25 /* Search the correct bus */
26 for (i = 0; i < bb_miiphy_buses_num; i++) {
27 if (!strcmp(bb_miiphy_buses[i].name, devname)) {
28 return &bb_miiphy_buses[i];
29 }
30 }
31 return NULL;
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020032}
wdenkaffae2b2002-08-17 09:36:01 +000033
Marek Vasuta392ff52025-02-22 21:33:23 +010034struct bb_miiphy_bus *bb_miiphy_alloc(void)
35{
36 struct bb_miiphy_bus *bus;
37
38 bus = malloc(sizeof(*bus));
39 if (!bus)
40 return bus;
41
42 mdio_init(&bus->mii);
43
44 return bus;
45}
46
47void bb_miiphy_free(struct bb_miiphy_bus *bus)
48{
49 free(bus);
50}
51
wdenkaffae2b2002-08-17 09:36:01 +000052/*****************************************************************************
53 *
54 * Utility to send the preamble, address, and register (common to read
55 * and write).
56 */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020057static void miiphy_pre(struct bb_miiphy_bus *bus, char read,
Wolfgang Denkd61fbcc2009-10-28 00:49:47 +010058 unsigned char addr, unsigned char reg)
wdenkaffae2b2002-08-17 09:36:01 +000059{
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020060 int j;
wdenkaffae2b2002-08-17 09:36:01 +000061
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020062 /*
63 * Send a 32 bit preamble ('1's) with an extra '1' bit for good measure.
64 * The IEEE spec says this is a PHY optional requirement. The AMD
65 * 79C874 requires one after power up and one after a MII communications
66 * error. This means that we are doing more preambles than we need,
67 * but it is safer and will be much more robust.
68 */
wdenkaffae2b2002-08-17 09:36:01 +000069
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020070 bus->mdio_active(bus);
71 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020072 for (j = 0; j < 32; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020073 bus->set_mdc(bus, 0);
74 bus->delay(bus);
75 bus->set_mdc(bus, 1);
76 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020077 }
wdenkaffae2b2002-08-17 09:36:01 +000078
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020079 /* send the start bit (01) and the read opcode (10) or write (10) */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020080 bus->set_mdc(bus, 0);
81 bus->set_mdio(bus, 0);
82 bus->delay(bus);
83 bus->set_mdc(bus, 1);
84 bus->delay(bus);
85 bus->set_mdc(bus, 0);
86 bus->set_mdio(bus, 1);
87 bus->delay(bus);
88 bus->set_mdc(bus, 1);
89 bus->delay(bus);
90 bus->set_mdc(bus, 0);
91 bus->set_mdio(bus, read);
92 bus->delay(bus);
93 bus->set_mdc(bus, 1);
94 bus->delay(bus);
95 bus->set_mdc(bus, 0);
96 bus->set_mdio(bus, !read);
97 bus->delay(bus);
98 bus->set_mdc(bus, 1);
99 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000100
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200101 /* send the PHY address */
102 for (j = 0; j < 5; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200103 bus->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200104 if ((addr & 0x10) == 0) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200105 bus->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200106 } else {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200107 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200108 }
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200109 bus->delay(bus);
110 bus->set_mdc(bus, 1);
111 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200112 addr <<= 1;
113 }
wdenkaffae2b2002-08-17 09:36:01 +0000114
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200115 /* send the register address */
116 for (j = 0; j < 5; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200117 bus->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200118 if ((reg & 0x10) == 0) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200119 bus->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200120 } else {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200121 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200122 }
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200123 bus->delay(bus);
124 bus->set_mdc(bus, 1);
125 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200126 reg <<= 1;
127 }
wdenkaffae2b2002-08-17 09:36:01 +0000128}
129
wdenkaffae2b2002-08-17 09:36:01 +0000130/*****************************************************************************
131 *
132 * Read a MII PHY register.
133 *
134 * Returns:
135 * 0 on success
136 */
Joe Hershberger0c333192016-08-08 11:28:39 -0500137int bb_miiphy_read(struct mii_dev *miidev, int addr, int devad, int reg)
wdenkaffae2b2002-08-17 09:36:01 +0000138{
Chris Brandt7e4d4d12017-11-03 08:30:13 -0500139 unsigned short rdreg; /* register working value */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200140 int v;
141 int j; /* counter */
142 struct bb_miiphy_bus *bus;
143
Joe Hershberger0c333192016-08-08 11:28:39 -0500144 bus = bb_miiphy_getbus(miidev->name);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200145 if (bus == NULL) {
146 return -1;
147 }
wdenkaffae2b2002-08-17 09:36:01 +0000148
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200149 miiphy_pre (bus, 1, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000150
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200151 /* tri-state our MDIO I/O pin so we can read */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200152 bus->set_mdc(bus, 0);
153 bus->mdio_tristate(bus);
154 bus->delay(bus);
155 bus->set_mdc(bus, 1);
156 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000157
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200158 /* check the turnaround bit: the PHY should be driving it to zero */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200159 bus->get_mdio(bus, &v);
160 if (v != 0) {
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200161 /* puts ("PHY didn't drive TA low\n"); */
162 for (j = 0; j < 32; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200163 bus->set_mdc(bus, 0);
164 bus->delay(bus);
165 bus->set_mdc(bus, 1);
166 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200167 }
Joe Hershberger0c333192016-08-08 11:28:39 -0500168 /* There is no PHY, return */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200169 return -1;
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200170 }
wdenkaffae2b2002-08-17 09:36:01 +0000171
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200172 bus->set_mdc(bus, 0);
173 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000174
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200175 /* read 16 bits of register data, MSB first */
176 rdreg = 0;
177 for (j = 0; j < 16; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200178 bus->set_mdc(bus, 1);
179 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200180 rdreg <<= 1;
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200181 bus->get_mdio(bus, &v);
182 rdreg |= (v & 0x1);
183 bus->set_mdc(bus, 0);
184 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200185 }
wdenkaffae2b2002-08-17 09:36:01 +0000186
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200187 bus->set_mdc(bus, 1);
188 bus->delay(bus);
189 bus->set_mdc(bus, 0);
190 bus->delay(bus);
191 bus->set_mdc(bus, 1);
192 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000193
Marek Vasut06effa22025-01-25 13:28:30 +0100194 debug("%s[%s](0x%x) @ 0x%x = 0x%04x\n", __func__, miidev->name, reg, addr, rdreg);
wdenkaffae2b2002-08-17 09:36:01 +0000195
Joe Hershberger0c333192016-08-08 11:28:39 -0500196 return rdreg;
wdenkaffae2b2002-08-17 09:36:01 +0000197}
198
wdenkaffae2b2002-08-17 09:36:01 +0000199/*****************************************************************************
200 *
201 * Write a MII PHY register.
202 *
203 * Returns:
204 * 0 on success
205 */
Joe Hershberger0c333192016-08-08 11:28:39 -0500206int bb_miiphy_write(struct mii_dev *miidev, int addr, int devad, int reg,
207 u16 value)
wdenkaffae2b2002-08-17 09:36:01 +0000208{
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200209 struct bb_miiphy_bus *bus;
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200210 int j; /* counter */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200211
Joe Hershberger0c333192016-08-08 11:28:39 -0500212 bus = bb_miiphy_getbus(miidev->name);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200213 if (bus == NULL) {
214 /* Bus not found! */
215 return -1;
216 }
wdenkaffae2b2002-08-17 09:36:01 +0000217
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200218 miiphy_pre (bus, 0, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000219
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200220 /* send the turnaround (10) */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200221 bus->set_mdc(bus, 0);
222 bus->set_mdio(bus, 1);
223 bus->delay(bus);
224 bus->set_mdc(bus, 1);
225 bus->delay(bus);
226 bus->set_mdc(bus, 0);
227 bus->set_mdio(bus, 0);
228 bus->delay(bus);
229 bus->set_mdc(bus, 1);
230 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000231
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200232 /* write 16 bits of register data, MSB first */
233 for (j = 0; j < 16; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200234 bus->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200235 if ((value & 0x00008000) == 0) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200236 bus->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200237 } else {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200238 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200239 }
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200240 bus->delay(bus);
241 bus->set_mdc(bus, 1);
242 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200243 value <<= 1;
244 }
wdenkaffae2b2002-08-17 09:36:01 +0000245
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200246 /*
247 * Tri-state the MDIO line.
248 */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200249 bus->mdio_tristate(bus);
250 bus->set_mdc(bus, 0);
251 bus->delay(bus);
252 bus->set_mdc(bus, 1);
253 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000254
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200255 return 0;
Wolfgang Denk9235e0c2009-10-25 23:00:09 +0100256}