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