blob: 75d9537b35543a7a1916904afd15d7d8bdebdf09 [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
Ben Warren97824d62010-07-29 12:56:11 -070020static inline struct bb_miiphy_bus *bb_miiphy_getbus(const char *devname)
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020021{
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020022 int i;
23
24 /* Search the correct bus */
25 for (i = 0; i < bb_miiphy_buses_num; i++) {
26 if (!strcmp(bb_miiphy_buses[i].name, devname)) {
27 return &bb_miiphy_buses[i];
28 }
29 }
30 return NULL;
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020031}
wdenkaffae2b2002-08-17 09:36:01 +000032
wdenkaffae2b2002-08-17 09:36:01 +000033/*****************************************************************************
34 *
35 * Utility to send the preamble, address, and register (common to read
36 * and write).
37 */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020038static void miiphy_pre(struct bb_miiphy_bus *bus, char read,
Wolfgang Denkd61fbcc2009-10-28 00:49:47 +010039 unsigned char addr, unsigned char reg)
wdenkaffae2b2002-08-17 09:36:01 +000040{
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020041 int j;
wdenkaffae2b2002-08-17 09:36:01 +000042
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020043 /*
44 * Send a 32 bit preamble ('1's) with an extra '1' bit for good measure.
45 * The IEEE spec says this is a PHY optional requirement. The AMD
46 * 79C874 requires one after power up and one after a MII communications
47 * error. This means that we are doing more preambles than we need,
48 * but it is safer and will be much more robust.
49 */
wdenkaffae2b2002-08-17 09:36:01 +000050
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020051 bus->mdio_active(bus);
52 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020053 for (j = 0; j < 32; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020054 bus->set_mdc(bus, 0);
55 bus->delay(bus);
56 bus->set_mdc(bus, 1);
57 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020058 }
wdenkaffae2b2002-08-17 09:36:01 +000059
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020060 /* send the start bit (01) and the read opcode (10) or write (10) */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020061 bus->set_mdc(bus, 0);
62 bus->set_mdio(bus, 0);
63 bus->delay(bus);
64 bus->set_mdc(bus, 1);
65 bus->delay(bus);
66 bus->set_mdc(bus, 0);
67 bus->set_mdio(bus, 1);
68 bus->delay(bus);
69 bus->set_mdc(bus, 1);
70 bus->delay(bus);
71 bus->set_mdc(bus, 0);
72 bus->set_mdio(bus, read);
73 bus->delay(bus);
74 bus->set_mdc(bus, 1);
75 bus->delay(bus);
76 bus->set_mdc(bus, 0);
77 bus->set_mdio(bus, !read);
78 bus->delay(bus);
79 bus->set_mdc(bus, 1);
80 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +000081
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020082 /* send the PHY address */
83 for (j = 0; j < 5; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020084 bus->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020085 if ((addr & 0x10) == 0) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020086 bus->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020087 } else {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020088 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020089 }
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020090 bus->delay(bus);
91 bus->set_mdc(bus, 1);
92 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020093 addr <<= 1;
94 }
wdenkaffae2b2002-08-17 09:36:01 +000095
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020096 /* send the register address */
97 for (j = 0; j < 5; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020098 bus->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020099 if ((reg & 0x10) == 0) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200100 bus->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200101 } else {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200102 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200103 }
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200104 bus->delay(bus);
105 bus->set_mdc(bus, 1);
106 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200107 reg <<= 1;
108 }
wdenkaffae2b2002-08-17 09:36:01 +0000109}
110
wdenkaffae2b2002-08-17 09:36:01 +0000111/*****************************************************************************
112 *
113 * Read a MII PHY register.
114 *
115 * Returns:
116 * 0 on success
117 */
Joe Hershberger0c333192016-08-08 11:28:39 -0500118int bb_miiphy_read(struct mii_dev *miidev, int addr, int devad, int reg)
wdenkaffae2b2002-08-17 09:36:01 +0000119{
Chris Brandt7e4d4d12017-11-03 08:30:13 -0500120 unsigned short rdreg; /* register working value */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200121 int v;
122 int j; /* counter */
123 struct bb_miiphy_bus *bus;
124
Joe Hershberger0c333192016-08-08 11:28:39 -0500125 bus = bb_miiphy_getbus(miidev->name);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200126 if (bus == NULL) {
127 return -1;
128 }
wdenkaffae2b2002-08-17 09:36:01 +0000129
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200130 miiphy_pre (bus, 1, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000131
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200132 /* tri-state our MDIO I/O pin so we can read */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200133 bus->set_mdc(bus, 0);
134 bus->mdio_tristate(bus);
135 bus->delay(bus);
136 bus->set_mdc(bus, 1);
137 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000138
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200139 /* check the turnaround bit: the PHY should be driving it to zero */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200140 bus->get_mdio(bus, &v);
141 if (v != 0) {
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200142 /* puts ("PHY didn't drive TA low\n"); */
143 for (j = 0; j < 32; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200144 bus->set_mdc(bus, 0);
145 bus->delay(bus);
146 bus->set_mdc(bus, 1);
147 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200148 }
Joe Hershberger0c333192016-08-08 11:28:39 -0500149 /* There is no PHY, return */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200150 return -1;
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200151 }
wdenkaffae2b2002-08-17 09:36:01 +0000152
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200153 bus->set_mdc(bus, 0);
154 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000155
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200156 /* read 16 bits of register data, MSB first */
157 rdreg = 0;
158 for (j = 0; j < 16; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200159 bus->set_mdc(bus, 1);
160 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200161 rdreg <<= 1;
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200162 bus->get_mdio(bus, &v);
163 rdreg |= (v & 0x1);
164 bus->set_mdc(bus, 0);
165 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200166 }
wdenkaffae2b2002-08-17 09:36:01 +0000167
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200168 bus->set_mdc(bus, 1);
169 bus->delay(bus);
170 bus->set_mdc(bus, 0);
171 bus->delay(bus);
172 bus->set_mdc(bus, 1);
173 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000174
Marek Vasut06effa22025-01-25 13:28:30 +0100175 debug("%s[%s](0x%x) @ 0x%x = 0x%04x\n", __func__, miidev->name, reg, addr, rdreg);
wdenkaffae2b2002-08-17 09:36:01 +0000176
Joe Hershberger0c333192016-08-08 11:28:39 -0500177 return rdreg;
wdenkaffae2b2002-08-17 09:36:01 +0000178}
179
wdenkaffae2b2002-08-17 09:36:01 +0000180/*****************************************************************************
181 *
182 * Write a MII PHY register.
183 *
184 * Returns:
185 * 0 on success
186 */
Joe Hershberger0c333192016-08-08 11:28:39 -0500187int bb_miiphy_write(struct mii_dev *miidev, int addr, int devad, int reg,
188 u16 value)
wdenkaffae2b2002-08-17 09:36:01 +0000189{
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200190 struct bb_miiphy_bus *bus;
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200191 int j; /* counter */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200192
Joe Hershberger0c333192016-08-08 11:28:39 -0500193 bus = bb_miiphy_getbus(miidev->name);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200194 if (bus == NULL) {
195 /* Bus not found! */
196 return -1;
197 }
wdenkaffae2b2002-08-17 09:36:01 +0000198
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200199 miiphy_pre (bus, 0, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000200
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200201 /* send the turnaround (10) */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200202 bus->set_mdc(bus, 0);
203 bus->set_mdio(bus, 1);
204 bus->delay(bus);
205 bus->set_mdc(bus, 1);
206 bus->delay(bus);
207 bus->set_mdc(bus, 0);
208 bus->set_mdio(bus, 0);
209 bus->delay(bus);
210 bus->set_mdc(bus, 1);
211 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000212
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200213 /* write 16 bits of register data, MSB first */
214 for (j = 0; j < 16; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200215 bus->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200216 if ((value & 0x00008000) == 0) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200217 bus->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200218 } else {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200219 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200220 }
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200221 bus->delay(bus);
222 bus->set_mdc(bus, 1);
223 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200224 value <<= 1;
225 }
wdenkaffae2b2002-08-17 09:36:01 +0000226
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200227 /*
228 * Tri-state the MDIO line.
229 */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200230 bus->mdio_tristate(bus);
231 bus->set_mdc(bus, 0);
232 bus->delay(bus);
233 bus->set_mdc(bus, 1);
234 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000235
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200236 return 0;
Wolfgang Denk9235e0c2009-10-25 23:00:09 +0100237}