blob: 60c791b707b8bd86678822876262e9c14a72e120 [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 Vasuta392ff52025-02-22 21:33:23 +010021struct bb_miiphy_bus *bb_miiphy_alloc(void)
22{
23 struct bb_miiphy_bus *bus;
24
25 bus = malloc(sizeof(*bus));
26 if (!bus)
27 return bus;
28
29 mdio_init(&bus->mii);
30
31 return bus;
32}
33
34void bb_miiphy_free(struct bb_miiphy_bus *bus)
35{
36 free(bus);
37}
38
wdenkaffae2b2002-08-17 09:36:01 +000039/*****************************************************************************
40 *
41 * Utility to send the preamble, address, and register (common to read
42 * and write).
43 */
Marek Vasut183c10a2025-03-02 02:24:45 +010044static void miiphy_pre(struct mii_dev *miidev, const struct bb_miiphy_bus_ops *ops,
Marek Vasut3d5149c2025-03-02 02:24:42 +010045 char read, unsigned char addr, unsigned char reg)
wdenkaffae2b2002-08-17 09:36:01 +000046{
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020047 int j;
wdenkaffae2b2002-08-17 09:36:01 +000048
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020049 /*
50 * Send a 32 bit preamble ('1's) with an extra '1' bit for good measure.
51 * The IEEE spec says this is a PHY optional requirement. The AMD
52 * 79C874 requires one after power up and one after a MII communications
53 * error. This means that we are doing more preambles than we need,
54 * but it is safer and will be much more robust.
55 */
wdenkaffae2b2002-08-17 09:36:01 +000056
Marek Vasut183c10a2025-03-02 02:24:45 +010057 ops->mdio_active(miidev);
58 ops->set_mdio(miidev, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020059 for (j = 0; j < 32; j++) {
Marek Vasut183c10a2025-03-02 02:24:45 +010060 ops->set_mdc(miidev, 0);
61 ops->delay(miidev);
62 ops->set_mdc(miidev, 1);
63 ops->delay(miidev);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020064 }
wdenkaffae2b2002-08-17 09:36:01 +000065
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020066 /* send the start bit (01) and the read opcode (10) or write (10) */
Marek Vasut183c10a2025-03-02 02:24:45 +010067 ops->set_mdc(miidev, 0);
68 ops->set_mdio(miidev, 0);
69 ops->delay(miidev);
70 ops->set_mdc(miidev, 1);
71 ops->delay(miidev);
72 ops->set_mdc(miidev, 0);
73 ops->set_mdio(miidev, 1);
74 ops->delay(miidev);
75 ops->set_mdc(miidev, 1);
76 ops->delay(miidev);
77 ops->set_mdc(miidev, 0);
78 ops->set_mdio(miidev, read);
79 ops->delay(miidev);
80 ops->set_mdc(miidev, 1);
81 ops->delay(miidev);
82 ops->set_mdc(miidev, 0);
83 ops->set_mdio(miidev, !read);
84 ops->delay(miidev);
85 ops->set_mdc(miidev, 1);
86 ops->delay(miidev);
wdenkaffae2b2002-08-17 09:36:01 +000087
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020088 /* send the PHY address */
89 for (j = 0; j < 5; j++) {
Marek Vasut183c10a2025-03-02 02:24:45 +010090 ops->set_mdc(miidev, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020091 if ((addr & 0x10) == 0) {
Marek Vasut183c10a2025-03-02 02:24:45 +010092 ops->set_mdio(miidev, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020093 } else {
Marek Vasut183c10a2025-03-02 02:24:45 +010094 ops->set_mdio(miidev, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020095 }
Marek Vasut183c10a2025-03-02 02:24:45 +010096 ops->delay(miidev);
97 ops->set_mdc(miidev, 1);
98 ops->delay(miidev);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020099 addr <<= 1;
100 }
wdenkaffae2b2002-08-17 09:36:01 +0000101
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200102 /* send the register address */
103 for (j = 0; j < 5; j++) {
Marek Vasut183c10a2025-03-02 02:24:45 +0100104 ops->set_mdc(miidev, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200105 if ((reg & 0x10) == 0) {
Marek Vasut183c10a2025-03-02 02:24:45 +0100106 ops->set_mdio(miidev, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200107 } else {
Marek Vasut183c10a2025-03-02 02:24:45 +0100108 ops->set_mdio(miidev, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200109 }
Marek Vasut183c10a2025-03-02 02:24:45 +0100110 ops->delay(miidev);
111 ops->set_mdc(miidev, 1);
112 ops->delay(miidev);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200113 reg <<= 1;
114 }
wdenkaffae2b2002-08-17 09:36:01 +0000115}
116
wdenkaffae2b2002-08-17 09:36:01 +0000117/*****************************************************************************
118 *
119 * Read a MII PHY register.
120 *
121 * Returns:
122 * 0 on success
123 */
Marek Vasut65867d32025-03-02 02:24:44 +0100124int bb_miiphy_read(struct mii_dev *miidev, const struct bb_miiphy_bus_ops *ops,
125 int addr, int devad, int reg)
wdenkaffae2b2002-08-17 09:36:01 +0000126{
Chris Brandt7e4d4d12017-11-03 08:30:13 -0500127 unsigned short rdreg; /* register working value */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200128 int v;
129 int j; /* counter */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200130
Marek Vasut183c10a2025-03-02 02:24:45 +0100131 miiphy_pre(miidev, ops, 1, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000132
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200133 /* tri-state our MDIO I/O pin so we can read */
Marek Vasut183c10a2025-03-02 02:24:45 +0100134 ops->set_mdc(miidev, 0);
135 ops->mdio_tristate(miidev);
136 ops->delay(miidev);
137 ops->set_mdc(miidev, 1);
138 ops->delay(miidev);
wdenkaffae2b2002-08-17 09:36:01 +0000139
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200140 /* check the turnaround bit: the PHY should be driving it to zero */
Marek Vasut183c10a2025-03-02 02:24:45 +0100141 ops->get_mdio(miidev, &v);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200142 if (v != 0) {
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200143 /* puts ("PHY didn't drive TA low\n"); */
144 for (j = 0; j < 32; j++) {
Marek Vasut183c10a2025-03-02 02:24:45 +0100145 ops->set_mdc(miidev, 0);
146 ops->delay(miidev);
147 ops->set_mdc(miidev, 1);
148 ops->delay(miidev);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200149 }
Joe Hershberger0c333192016-08-08 11:28:39 -0500150 /* There is no PHY, return */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200151 return -1;
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200152 }
wdenkaffae2b2002-08-17 09:36:01 +0000153
Marek Vasut183c10a2025-03-02 02:24:45 +0100154 ops->set_mdc(miidev, 0);
155 ops->delay(miidev);
wdenkaffae2b2002-08-17 09:36:01 +0000156
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200157 /* read 16 bits of register data, MSB first */
158 rdreg = 0;
159 for (j = 0; j < 16; j++) {
Marek Vasut183c10a2025-03-02 02:24:45 +0100160 ops->set_mdc(miidev, 1);
161 ops->delay(miidev);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200162 rdreg <<= 1;
Marek Vasut183c10a2025-03-02 02:24:45 +0100163 ops->get_mdio(miidev, &v);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200164 rdreg |= (v & 0x1);
Marek Vasut183c10a2025-03-02 02:24:45 +0100165 ops->set_mdc(miidev, 0);
166 ops->delay(miidev);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200167 }
wdenkaffae2b2002-08-17 09:36:01 +0000168
Marek Vasut183c10a2025-03-02 02:24:45 +0100169 ops->set_mdc(miidev, 1);
170 ops->delay(miidev);
171 ops->set_mdc(miidev, 0);
172 ops->delay(miidev);
173 ops->set_mdc(miidev, 1);
174 ops->delay(miidev);
wdenkaffae2b2002-08-17 09:36:01 +0000175
Marek Vasut06effa22025-01-25 13:28:30 +0100176 debug("%s[%s](0x%x) @ 0x%x = 0x%04x\n", __func__, miidev->name, reg, addr, rdreg);
wdenkaffae2b2002-08-17 09:36:01 +0000177
Joe Hershberger0c333192016-08-08 11:28:39 -0500178 return rdreg;
wdenkaffae2b2002-08-17 09:36:01 +0000179}
180
wdenkaffae2b2002-08-17 09:36:01 +0000181/*****************************************************************************
182 *
183 * Write a MII PHY register.
184 *
185 * Returns:
186 * 0 on success
187 */
Marek Vasut65867d32025-03-02 02:24:44 +0100188int bb_miiphy_write(struct mii_dev *miidev, const struct bb_miiphy_bus_ops *ops,
189 int addr, int devad, int reg, u16 value)
wdenkaffae2b2002-08-17 09:36:01 +0000190{
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200191 int j; /* counter */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200192
Marek Vasut183c10a2025-03-02 02:24:45 +0100193 miiphy_pre(miidev, ops, 0, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000194
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200195 /* send the turnaround (10) */
Marek Vasut183c10a2025-03-02 02:24:45 +0100196 ops->set_mdc(miidev, 0);
197 ops->set_mdio(miidev, 1);
198 ops->delay(miidev);
199 ops->set_mdc(miidev, 1);
200 ops->delay(miidev);
201 ops->set_mdc(miidev, 0);
202 ops->set_mdio(miidev, 0);
203 ops->delay(miidev);
204 ops->set_mdc(miidev, 1);
205 ops->delay(miidev);
wdenkaffae2b2002-08-17 09:36:01 +0000206
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200207 /* write 16 bits of register data, MSB first */
208 for (j = 0; j < 16; j++) {
Marek Vasut183c10a2025-03-02 02:24:45 +0100209 ops->set_mdc(miidev, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200210 if ((value & 0x00008000) == 0) {
Marek Vasut183c10a2025-03-02 02:24:45 +0100211 ops->set_mdio(miidev, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200212 } else {
Marek Vasut183c10a2025-03-02 02:24:45 +0100213 ops->set_mdio(miidev, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200214 }
Marek Vasut183c10a2025-03-02 02:24:45 +0100215 ops->delay(miidev);
216 ops->set_mdc(miidev, 1);
217 ops->delay(miidev);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200218 value <<= 1;
219 }
wdenkaffae2b2002-08-17 09:36:01 +0000220
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200221 /*
222 * Tri-state the MDIO line.
223 */
Marek Vasut183c10a2025-03-02 02:24:45 +0100224 ops->mdio_tristate(miidev);
225 ops->set_mdc(miidev, 0);
226 ops->delay(miidev);
227 ops->set_mdc(miidev, 1);
228 ops->delay(miidev);
wdenkaffae2b2002-08-17 09:36:01 +0000229
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200230 return 0;
Wolfgang Denk9235e0c2009-10-25 23:00:09 +0100231}