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