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