blob: 516f4e8edcac1f6f4c0f1f1de2d8d83c981eff7f [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Dirk Eibachf74a0272014-11-13 19:21:18 +01002/*
3 * (C) Copyright 2014
Mario Sixb4893582018-03-06 08:04:58 +01004 * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc
Dirk Eibachf74a0272014-11-13 19:21:18 +01005 */
6
7#include <common.h>
Simon Glass0f2af882020-05-10 11:40:05 -06008#include <log.h>
Dirk Eibachf74a0272014-11-13 19:21:18 +01009
10#include <miiphy.h>
11
12enum {
13 MIICMD_SET,
14 MIICMD_MODIFY,
15 MIICMD_VERIFY_VALUE,
16 MIICMD_WAIT_FOR_VALUE,
17};
18
19struct mii_setupcmd {
20 u8 token;
21 u8 reg;
22 u16 data;
23 u16 mask;
24 u32 timeout;
25};
26
27/*
28 * verify we are talking to a 88e1518
29 */
30struct mii_setupcmd verify_88e1518[] = {
31 { MIICMD_SET, 22, 0x0000 },
32 { MIICMD_VERIFY_VALUE, 2, 0x0141, 0xffff },
33 { MIICMD_VERIFY_VALUE, 3, 0x0dd0, 0xfff0 },
34};
35
36/*
37 * workaround for erratum mentioned in 88E1518 release notes
38 */
39struct mii_setupcmd fixup_88e1518[] = {
40 { MIICMD_SET, 22, 0x00ff },
41 { MIICMD_SET, 17, 0x214b },
42 { MIICMD_SET, 16, 0x2144 },
43 { MIICMD_SET, 17, 0x0c28 },
44 { MIICMD_SET, 16, 0x2146 },
45 { MIICMD_SET, 17, 0xb233 },
46 { MIICMD_SET, 16, 0x214d },
47 { MIICMD_SET, 17, 0xcc0c },
48 { MIICMD_SET, 16, 0x2159 },
Dirk Eibachf74a0272014-11-13 19:21:18 +010049 { MIICMD_SET, 22, 0x0000 },
50};
51
52/*
53 * default initialization:
54 * - set RGMII receive timing to "receive clock transition when data stable"
55 * - set RGMII transmit timing to "transmit clock internally delayed"
56 * - set RGMII output impedance target to 78,8 Ohm
57 * - run output impedance calibration
58 * - set autonegotiation advertise to 1000FD only
59 */
60struct mii_setupcmd default_88e1518[] = {
61 { MIICMD_SET, 22, 0x0002 },
62 { MIICMD_MODIFY, 21, 0x0030, 0x0030 },
63 { MIICMD_MODIFY, 25, 0x0000, 0x0003 },
64 { MIICMD_MODIFY, 24, 0x8000, 0x8000 },
65 { MIICMD_WAIT_FOR_VALUE, 24, 0x4000, 0x4000, 2000 },
66 { MIICMD_SET, 22, 0x0000 },
67 { MIICMD_MODIFY, 4, 0x0000, 0x01e0 },
68 { MIICMD_MODIFY, 9, 0x0200, 0x0300 },
69};
70
71/*
72 * turn off CLK125 for PHY daughterboard
73 */
74struct mii_setupcmd ch1fix_88e1518[] = {
75 { MIICMD_SET, 22, 0x0002 },
76 { MIICMD_MODIFY, 16, 0x0006, 0x0006 },
77 { MIICMD_SET, 22, 0x0000 },
78};
79
80/*
81 * perform copper software reset
82 */
83struct mii_setupcmd swreset_88e1518[] = {
84 { MIICMD_SET, 22, 0x0000 },
85 { MIICMD_MODIFY, 0, 0x8000, 0x8000 },
86 { MIICMD_WAIT_FOR_VALUE, 0, 0x0000, 0x8000, 2000 },
87};
88
89/*
90 * special one for 88E1514:
91 * Force SGMII to Copper mode
92 */
93struct mii_setupcmd mii_to_copper_88e1514[] = {
94 { MIICMD_SET, 22, 0x0012 },
95 { MIICMD_MODIFY, 20, 0x0001, 0x0007 },
96 { MIICMD_MODIFY, 20, 0x8000, 0x8000 },
97 { MIICMD_SET, 22, 0x0000 },
98};
99
100/*
101 * turn off SGMII auto-negotiation
102 */
103struct mii_setupcmd sgmii_autoneg_off_88e1518[] = {
104 { MIICMD_SET, 22, 0x0001 },
105 { MIICMD_MODIFY, 0, 0x0000, 0x1000 },
106 { MIICMD_MODIFY, 0, 0x8000, 0x8000 },
107 { MIICMD_SET, 22, 0x0000 },
108};
109
110/*
111 * invert LED2 polarity
112 */
113struct mii_setupcmd invert_led2_88e1514[] = {
114 { MIICMD_SET, 22, 0x0003 },
115 { MIICMD_MODIFY, 17, 0x0030, 0x0010 },
116 { MIICMD_SET, 22, 0x0000 },
117};
118
119static int process_setupcmd(const char *bus, unsigned char addr,
120 struct mii_setupcmd *setupcmd)
121{
122 int res;
123 u8 reg = setupcmd->reg;
124 u16 data = setupcmd->data;
125 u16 mask = setupcmd->mask;
126 u32 timeout = setupcmd->timeout;
127 u16 orig_data;
128 unsigned long start;
129
130 debug("mii %s:%u reg %2u ", bus, addr, reg);
131
132 switch (setupcmd->token) {
133 case MIICMD_MODIFY:
134 res = miiphy_read(bus, addr, reg, &orig_data);
135 if (res)
136 break;
137 debug("is %04x. (value %04x mask %04x) ", orig_data, data,
138 mask);
139 data = (orig_data & ~mask) | (data & mask);
140 /* fallthrough */
141 case MIICMD_SET:
142 debug("=> %04x\n", data);
143 res = miiphy_write(bus, addr, reg, data);
144 break;
145 case MIICMD_VERIFY_VALUE:
146 res = miiphy_read(bus, addr, reg, &orig_data);
147 if (res)
148 break;
149 if ((orig_data & mask) != (data & mask))
150 res = -1;
151 debug("(value %04x mask %04x) == %04x? %s\n", data, mask,
152 orig_data, res ? "FAIL" : "PASS");
153 break;
154 case MIICMD_WAIT_FOR_VALUE:
155 res = -1;
156 start = get_timer(0);
157 while ((res != 0) && (get_timer(start) < timeout)) {
158 res = miiphy_read(bus, addr, reg, &orig_data);
159 if (res)
160 continue;
161 if ((orig_data & mask) != (data & mask))
162 res = -1;
163 }
164 debug("(value %04x mask %04x) == %04x? %s after %lu ms\n", data,
165 mask, orig_data, res ? "FAIL" : "PASS",
166 get_timer(start));
167 break;
168 default:
169 res = -1;
170 break;
171 }
172
173 return res;
174}
175
176static int process_setup(const char *bus, unsigned char addr,
177 struct mii_setupcmd *setupcmd, unsigned int count)
178{
179 int res = 0;
180 unsigned int k;
181
182 for (k = 0; k < count; ++k) {
183 res = process_setupcmd(bus, addr, &setupcmd[k]);
184 if (res) {
185 printf("mii cmd %u on bus %s addr %u failed, aborting setup\n",
186 setupcmd[k].token, bus, addr);
187 break;
188 }
189 }
190
191 return res;
192}
193
194int setup_88e1518(const char *bus, unsigned char addr)
195{
196 int res;
197
198 res = process_setup(bus, addr,
199 verify_88e1518, ARRAY_SIZE(verify_88e1518));
200 if (res)
201 return res;
202
203 res = process_setup(bus, addr,
204 fixup_88e1518, ARRAY_SIZE(fixup_88e1518));
205 if (res)
206 return res;
207
208 res = process_setup(bus, addr,
209 default_88e1518, ARRAY_SIZE(default_88e1518));
210 if (res)
211 return res;
212
213 if (addr) {
214 res = process_setup(bus, addr,
215 ch1fix_88e1518, ARRAY_SIZE(ch1fix_88e1518));
216 if (res)
217 return res;
218 }
219
220 res = process_setup(bus, addr,
221 swreset_88e1518, ARRAY_SIZE(swreset_88e1518));
222 if (res)
223 return res;
224
225 return 0;
226}
227
228int setup_88e1514(const char *bus, unsigned char addr)
229{
230 int res;
231
232 res = process_setup(bus, addr,
233 verify_88e1518, ARRAY_SIZE(verify_88e1518));
234 if (res)
235 return res;
236
237 res = process_setup(bus, addr,
238 fixup_88e1518, ARRAY_SIZE(fixup_88e1518));
239 if (res)
240 return res;
241
242 res = process_setup(bus, addr,
243 mii_to_copper_88e1514,
244 ARRAY_SIZE(mii_to_copper_88e1514));
245 if (res)
246 return res;
247
248 res = process_setup(bus, addr,
249 sgmii_autoneg_off_88e1518,
250 ARRAY_SIZE(sgmii_autoneg_off_88e1518));
251 if (res)
252 return res;
253
254 res = process_setup(bus, addr,
255 invert_led2_88e1514,
256 ARRAY_SIZE(invert_led2_88e1514));
257 if (res)
258 return res;
259
260 res = process_setup(bus, addr,
261 default_88e1518, ARRAY_SIZE(default_88e1518));
262 if (res)
263 return res;
264
265 if (addr) {
266 res = process_setup(bus, addr,
267 ch1fix_88e1518, ARRAY_SIZE(ch1fix_88e1518));
268 if (res)
269 return res;
270 }
271
272 res = process_setup(bus, addr,
273 swreset_88e1518, ARRAY_SIZE(swreset_88e1518));
274 if (res)
275 return res;
276
277 return 0;
278}