blob: 76463da72995000ff9ee53c3a6cf4b44c9947a67 [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
wdenkaffae2b2002-08-17 09:36:01 +000020/*****************************************************************************
21 *
22 * Utility to send the preamble, address, and register (common to read
23 * and write).
24 */
Marek Vasut183c10a2025-03-02 02:24:45 +010025static void miiphy_pre(struct mii_dev *miidev, const struct bb_miiphy_bus_ops *ops,
Marek Vasut3d5149c2025-03-02 02:24:42 +010026 char read, unsigned char addr, unsigned char reg)
wdenkaffae2b2002-08-17 09:36:01 +000027{
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +020028 int j;
wdenkaffae2b2002-08-17 09:36:01 +000029
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020030 /*
31 * Send a 32 bit preamble ('1's) with an extra '1' bit for good measure.
32 * The IEEE spec says this is a PHY optional requirement. The AMD
33 * 79C874 requires one after power up and one after a MII communications
34 * error. This means that we are doing more preambles than we need,
35 * but it is safer and will be much more robust.
36 */
wdenkaffae2b2002-08-17 09:36:01 +000037
Marek Vasut183c10a2025-03-02 02:24:45 +010038 ops->mdio_active(miidev);
39 ops->set_mdio(miidev, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020040 for (j = 0; j < 32; j++) {
Marek Vasut183c10a2025-03-02 02:24:45 +010041 ops->set_mdc(miidev, 0);
42 ops->delay(miidev);
43 ops->set_mdc(miidev, 1);
44 ops->delay(miidev);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020045 }
wdenkaffae2b2002-08-17 09:36:01 +000046
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020047 /* send the start bit (01) and the read opcode (10) or write (10) */
Marek Vasut183c10a2025-03-02 02:24:45 +010048 ops->set_mdc(miidev, 0);
49 ops->set_mdio(miidev, 0);
50 ops->delay(miidev);
51 ops->set_mdc(miidev, 1);
52 ops->delay(miidev);
53 ops->set_mdc(miidev, 0);
54 ops->set_mdio(miidev, 1);
55 ops->delay(miidev);
56 ops->set_mdc(miidev, 1);
57 ops->delay(miidev);
58 ops->set_mdc(miidev, 0);
59 ops->set_mdio(miidev, read);
60 ops->delay(miidev);
61 ops->set_mdc(miidev, 1);
62 ops->delay(miidev);
63 ops->set_mdc(miidev, 0);
64 ops->set_mdio(miidev, !read);
65 ops->delay(miidev);
66 ops->set_mdc(miidev, 1);
67 ops->delay(miidev);
wdenkaffae2b2002-08-17 09:36:01 +000068
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020069 /* send the PHY address */
70 for (j = 0; j < 5; j++) {
Marek Vasut183c10a2025-03-02 02:24:45 +010071 ops->set_mdc(miidev, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020072 if ((addr & 0x10) == 0) {
Marek Vasut183c10a2025-03-02 02:24:45 +010073 ops->set_mdio(miidev, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020074 } else {
Marek Vasut183c10a2025-03-02 02:24:45 +010075 ops->set_mdio(miidev, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020076 }
Marek Vasut183c10a2025-03-02 02:24:45 +010077 ops->delay(miidev);
78 ops->set_mdc(miidev, 1);
79 ops->delay(miidev);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020080 addr <<= 1;
81 }
wdenkaffae2b2002-08-17 09:36:01 +000082
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020083 /* send the register address */
84 for (j = 0; j < 5; j++) {
Marek Vasut183c10a2025-03-02 02:24:45 +010085 ops->set_mdc(miidev, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020086 if ((reg & 0x10) == 0) {
Marek Vasut183c10a2025-03-02 02:24:45 +010087 ops->set_mdio(miidev, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020088 } else {
Marek Vasut183c10a2025-03-02 02:24:45 +010089 ops->set_mdio(miidev, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020090 }
Marek Vasut183c10a2025-03-02 02:24:45 +010091 ops->delay(miidev);
92 ops->set_mdc(miidev, 1);
93 ops->delay(miidev);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +020094 reg <<= 1;
95 }
wdenkaffae2b2002-08-17 09:36:01 +000096}
97
wdenkaffae2b2002-08-17 09:36:01 +000098/*****************************************************************************
99 *
100 * Read a MII PHY register.
101 *
102 * Returns:
103 * 0 on success
104 */
Marek Vasut65867d32025-03-02 02:24:44 +0100105int bb_miiphy_read(struct mii_dev *miidev, const struct bb_miiphy_bus_ops *ops,
106 int addr, int devad, int reg)
wdenkaffae2b2002-08-17 09:36:01 +0000107{
Chris Brandt7e4d4d12017-11-03 08:30:13 -0500108 unsigned short rdreg; /* register working value */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200109 int v;
110 int j; /* counter */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200111
Marek Vasut183c10a2025-03-02 02:24:45 +0100112 miiphy_pre(miidev, ops, 1, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000113
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200114 /* tri-state our MDIO I/O pin so we can read */
Marek Vasut183c10a2025-03-02 02:24:45 +0100115 ops->set_mdc(miidev, 0);
116 ops->mdio_tristate(miidev);
117 ops->delay(miidev);
118 ops->set_mdc(miidev, 1);
119 ops->delay(miidev);
wdenkaffae2b2002-08-17 09:36:01 +0000120
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200121 /* check the turnaround bit: the PHY should be driving it to zero */
Marek Vasut183c10a2025-03-02 02:24:45 +0100122 ops->get_mdio(miidev, &v);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200123 if (v != 0) {
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200124 /* puts ("PHY didn't drive TA low\n"); */
125 for (j = 0; j < 32; j++) {
Marek Vasut183c10a2025-03-02 02:24:45 +0100126 ops->set_mdc(miidev, 0);
127 ops->delay(miidev);
128 ops->set_mdc(miidev, 1);
129 ops->delay(miidev);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200130 }
Joe Hershberger0c333192016-08-08 11:28:39 -0500131 /* There is no PHY, return */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200132 return -1;
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200133 }
wdenkaffae2b2002-08-17 09:36:01 +0000134
Marek Vasut183c10a2025-03-02 02:24:45 +0100135 ops->set_mdc(miidev, 0);
136 ops->delay(miidev);
wdenkaffae2b2002-08-17 09:36:01 +0000137
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200138 /* read 16 bits of register data, MSB first */
139 rdreg = 0;
140 for (j = 0; j < 16; j++) {
Marek Vasut183c10a2025-03-02 02:24:45 +0100141 ops->set_mdc(miidev, 1);
142 ops->delay(miidev);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200143 rdreg <<= 1;
Marek Vasut183c10a2025-03-02 02:24:45 +0100144 ops->get_mdio(miidev, &v);
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200145 rdreg |= (v & 0x1);
Marek Vasut183c10a2025-03-02 02:24:45 +0100146 ops->set_mdc(miidev, 0);
147 ops->delay(miidev);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200148 }
wdenkaffae2b2002-08-17 09:36:01 +0000149
Marek Vasut183c10a2025-03-02 02:24:45 +0100150 ops->set_mdc(miidev, 1);
151 ops->delay(miidev);
152 ops->set_mdc(miidev, 0);
153 ops->delay(miidev);
154 ops->set_mdc(miidev, 1);
155 ops->delay(miidev);
wdenkaffae2b2002-08-17 09:36:01 +0000156
Marek Vasut06effa22025-01-25 13:28:30 +0100157 debug("%s[%s](0x%x) @ 0x%x = 0x%04x\n", __func__, miidev->name, reg, addr, rdreg);
wdenkaffae2b2002-08-17 09:36:01 +0000158
Joe Hershberger0c333192016-08-08 11:28:39 -0500159 return rdreg;
wdenkaffae2b2002-08-17 09:36:01 +0000160}
161
wdenkaffae2b2002-08-17 09:36:01 +0000162/*****************************************************************************
163 *
164 * Write a MII PHY register.
165 *
166 * Returns:
167 * 0 on success
168 */
Marek Vasut65867d32025-03-02 02:24:44 +0100169int bb_miiphy_write(struct mii_dev *miidev, const struct bb_miiphy_bus_ops *ops,
170 int addr, int devad, int reg, u16 value)
wdenkaffae2b2002-08-17 09:36:01 +0000171{
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200172 int j; /* counter */
Luigi 'Comio' Mantellini466827e2009-10-10 12:42:20 +0200173
Marek Vasut183c10a2025-03-02 02:24:45 +0100174 miiphy_pre(miidev, ops, 0, addr, reg);
wdenkaffae2b2002-08-17 09:36:01 +0000175
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200176 /* send the turnaround (10) */
Marek Vasut183c10a2025-03-02 02:24:45 +0100177 ops->set_mdc(miidev, 0);
178 ops->set_mdio(miidev, 1);
179 ops->delay(miidev);
180 ops->set_mdc(miidev, 1);
181 ops->delay(miidev);
182 ops->set_mdc(miidev, 0);
183 ops->set_mdio(miidev, 0);
184 ops->delay(miidev);
185 ops->set_mdc(miidev, 1);
186 ops->delay(miidev);
wdenkaffae2b2002-08-17 09:36:01 +0000187
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200188 /* write 16 bits of register data, MSB first */
189 for (j = 0; j < 16; j++) {
Marek Vasut183c10a2025-03-02 02:24:45 +0100190 ops->set_mdc(miidev, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200191 if ((value & 0x00008000) == 0) {
Marek Vasut183c10a2025-03-02 02:24:45 +0100192 ops->set_mdio(miidev, 0);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200193 } else {
Marek Vasut183c10a2025-03-02 02:24:45 +0100194 ops->set_mdio(miidev, 1);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200195 }
Marek Vasut183c10a2025-03-02 02:24:45 +0100196 ops->delay(miidev);
197 ops->set_mdc(miidev, 1);
198 ops->delay(miidev);
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200199 value <<= 1;
200 }
wdenkaffae2b2002-08-17 09:36:01 +0000201
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200202 /*
203 * Tri-state the MDIO line.
204 */
Marek Vasut183c10a2025-03-02 02:24:45 +0100205 ops->mdio_tristate(miidev);
206 ops->set_mdc(miidev, 0);
207 ops->delay(miidev);
208 ops->set_mdc(miidev, 1);
209 ops->delay(miidev);
wdenkaffae2b2002-08-17 09:36:01 +0000210
Wolfgang Denk7b4e3472005-08-13 02:04:37 +0200211 return 0;
Wolfgang Denk9235e0c2009-10-25 23:00:09 +0100212}