blob: 171c1719b5bc666b0f177a94f2a69627c997bb9f [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
Ovidiu Panait69d1ddb2020-11-28 10:43:17 +020020int bb_miiphy_init(void)
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020021{
22 int i;
23
Marek Vasutab7c4be2023-09-06 23:30:08 +020024 for (i = 0; i < bb_miiphy_buses_num; i++)
25 if (bb_miiphy_buses[i].init != NULL)
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020026 bb_miiphy_buses[i].init(&bb_miiphy_buses[i]);
Ovidiu Panait69d1ddb2020-11-28 10:43:17 +020027
28 return 0;
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020029}
30
Ben Warren97824d62010-07-29 12:56:11 -070031static inline struct bb_miiphy_bus *bb_miiphy_getbus(const char *devname)
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020032{
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020033 int i;
34
35 /* Search the correct bus */
36 for (i = 0; i < bb_miiphy_buses_num; i++) {
37 if (!strcmp(bb_miiphy_buses[i].name, devname)) {
38 return &bb_miiphy_buses[i];
39 }
40 }
41 return NULL;
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020042}
wdenkaffae2b2002-08-17 09:36:01 +000043
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
Joe Hershberger0c333192016-08-08 11:28:39 -0500136 bus = bb_miiphy_getbus(miidev->name);
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
wdenkaffae2b2002-08-17 09:36:01 +0000186#ifdef DEBUG
Joe Hershberger0c333192016-08-08 11:28:39 -0500187 printf("miiphy_read(0x%x) @ 0x%x = 0x%04x\n", reg, addr, rdreg);
wdenkaffae2b2002-08-17 09:36:01 +0000188#endif
189
Joe Hershberger0c333192016-08-08 11:28:39 -0500190 return rdreg;
wdenkaffae2b2002-08-17 09:36:01 +0000191}
192
wdenkaffae2b2002-08-17 09:36:01 +0000193/*****************************************************************************
194 *
195 * Write a MII PHY register.
196 *
197 * Returns:
198 * 0 on success
199 */
Joe Hershberger0c333192016-08-08 11:28:39 -0500200int bb_miiphy_write(struct mii_dev *miidev, int addr, int devad, int reg,
201 u16 value)
wdenkaffae2b2002-08-17 09:36:01 +0000202{
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200203 struct bb_miiphy_bus *bus;
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200204 int j; /* counter */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200205
Joe Hershberger0c333192016-08-08 11:28:39 -0500206 bus = bb_miiphy_getbus(miidev->name);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200207 if (bus == NULL) {
208 /* Bus not found! */
209 return -1;
210 }
wdenkaffae2b2002-08-17 09:36:01 +0000211
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200212 miiphy_pre (bus, 0, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000213
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200214 /* send the turnaround (10) */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200215 bus->set_mdc(bus, 0);
216 bus->set_mdio(bus, 1);
217 bus->delay(bus);
218 bus->set_mdc(bus, 1);
219 bus->delay(bus);
220 bus->set_mdc(bus, 0);
221 bus->set_mdio(bus, 0);
222 bus->delay(bus);
223 bus->set_mdc(bus, 1);
224 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000225
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200226 /* write 16 bits of register data, MSB first */
227 for (j = 0; j < 16; j++) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200228 bus->set_mdc(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200229 if ((value & 0x00008000) == 0) {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200230 bus->set_mdio(bus, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200231 } else {
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200232 bus->set_mdio(bus, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200233 }
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200234 bus->delay(bus);
235 bus->set_mdc(bus, 1);
236 bus->delay(bus);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200237 value <<= 1;
238 }
wdenkaffae2b2002-08-17 09:36:01 +0000239
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200240 /*
241 * Tri-state the MDIO line.
242 */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200243 bus->mdio_tristate(bus);
244 bus->set_mdc(bus, 0);
245 bus->delay(bus);
246 bus->set_mdc(bus, 1);
247 bus->delay(bus);
wdenkaffae2b2002-08-17 09:36:01 +0000248
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200249 return 0;
Wolfgang Denk9235e0c2009-10-25 23:00:09 +0100250}