aboutsummaryrefslogtreecommitdiff
path: root/board/gdsys/common/phy.c
blob: c4b2256da3a45b4aa51d362d12dfbf1fa4f51b73 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
// SPDX-License-Identifier: GPL-2.0+
/*
 * (C) Copyright 2014
 * Dirk Eibach,  Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc
 */

#include <common.h>

#include <miiphy.h>

enum {
	MIICMD_SET,
	MIICMD_MODIFY,
	MIICMD_VERIFY_VALUE,
	MIICMD_WAIT_FOR_VALUE,
};

struct mii_setupcmd {
	u8 token;
	u8 reg;
	u16 data;
	u16 mask;
	u32 timeout;
};

/*
 * verify we are talking to a 88e1518
 */
struct mii_setupcmd verify_88e1518[] = {
	{ MIICMD_SET, 22, 0x0000 },
	{ MIICMD_VERIFY_VALUE, 2, 0x0141, 0xffff },
	{ MIICMD_VERIFY_VALUE, 3, 0x0dd0, 0xfff0 },
};

/*
 * workaround for erratum mentioned in 88E1518 release notes
 */
struct mii_setupcmd fixup_88e1518[] = {
	{ MIICMD_SET, 22, 0x00ff },
	{ MIICMD_SET, 17, 0x214b },
	{ MIICMD_SET, 16, 0x2144 },
	{ MIICMD_SET, 17, 0x0c28 },
	{ MIICMD_SET, 16, 0x2146 },
	{ MIICMD_SET, 17, 0xb233 },
	{ MIICMD_SET, 16, 0x214d },
	{ MIICMD_SET, 17, 0xcc0c },
	{ MIICMD_SET, 16, 0x2159 },
	{ MIICMD_SET, 22, 0x00fb },
	{ MIICMD_SET,  7, 0xc00d },
	{ MIICMD_SET, 22, 0x0000 },
};

/*
 * default initialization:
 * - set RGMII receive timing to "receive clock transition when data stable"
 * - set RGMII transmit timing to "transmit clock internally delayed"
 * - set RGMII output impedance target to 78,8 Ohm
 * - run output impedance calibration
 * - set autonegotiation advertise to 1000FD only
 */
struct mii_setupcmd default_88e1518[] = {
	{ MIICMD_SET, 22, 0x0002 },
	{ MIICMD_MODIFY, 21, 0x0030, 0x0030 },
	{ MIICMD_MODIFY, 25, 0x0000, 0x0003 },
	{ MIICMD_MODIFY, 24, 0x8000, 0x8000 },
	{ MIICMD_WAIT_FOR_VALUE, 24, 0x4000, 0x4000, 2000 },
	{ MIICMD_SET, 22, 0x0000 },
	{ MIICMD_MODIFY, 4, 0x0000, 0x01e0 },
	{ MIICMD_MODIFY, 9, 0x0200, 0x0300 },
};

/*
 * turn off CLK125 for PHY daughterboard
 */
struct mii_setupcmd ch1fix_88e1518[] = {
	{ MIICMD_SET, 22, 0x0002 },
	{ MIICMD_MODIFY, 16, 0x0006, 0x0006 },
	{ MIICMD_SET, 22, 0x0000 },
};

/*
 * perform copper software reset
 */
struct mii_setupcmd swreset_88e1518[] = {
	{ MIICMD_SET, 22, 0x0000 },
	{ MIICMD_MODIFY, 0, 0x8000, 0x8000 },
	{ MIICMD_WAIT_FOR_VALUE, 0, 0x0000, 0x8000, 2000 },
};

/*
 * special one for 88E1514:
 * Force SGMII to Copper mode
 */
struct mii_setupcmd mii_to_copper_88e1514[] = {
	{ MIICMD_SET, 22, 0x0012 },
	{ MIICMD_MODIFY, 20, 0x0001, 0x0007 },
	{ MIICMD_MODIFY, 20, 0x8000, 0x8000 },
	{ MIICMD_SET, 22, 0x0000 },
};

/*
 * turn off SGMII auto-negotiation
 */
struct mii_setupcmd sgmii_autoneg_off_88e1518[] = {
	{ MIICMD_SET, 22, 0x0001 },
	{ MIICMD_MODIFY, 0, 0x0000, 0x1000 },
	{ MIICMD_MODIFY, 0, 0x8000, 0x8000 },
	{ MIICMD_SET, 22, 0x0000 },
};

/*
 * invert LED2 polarity
 */
struct mii_setupcmd invert_led2_88e1514[] = {
	{ MIICMD_SET, 22, 0x0003 },
	{ MIICMD_MODIFY, 17, 0x0030, 0x0010 },
	{ MIICMD_SET, 22, 0x0000 },
};

static int process_setupcmd(const char *bus, unsigned char addr,
			    struct mii_setupcmd *setupcmd)
{
	int res;
	u8 reg = setupcmd->reg;
	u16 data = setupcmd->data;
	u16 mask = setupcmd->mask;
	u32 timeout = setupcmd->timeout;
	u16 orig_data;
	unsigned long start;

	debug("mii %s:%u reg %2u ", bus, addr, reg);

	switch (setupcmd->token) {
	case MIICMD_MODIFY:
		res = miiphy_read(bus, addr, reg, &orig_data);
		if (res)
			break;
		debug("is %04x. (value %04x mask %04x) ", orig_data, data,
		      mask);
		data = (orig_data & ~mask) | (data & mask);
		/* fallthrough */
	case MIICMD_SET:
		debug("=> %04x\n", data);
		res = miiphy_write(bus, addr, reg, data);
		break;
	case MIICMD_VERIFY_VALUE:
		res = miiphy_read(bus, addr, reg, &orig_data);
		if (res)
			break;
		if ((orig_data & mask) != (data & mask))
			res = -1;
		debug("(value %04x mask %04x) == %04x? %s\n", data, mask,
		      orig_data, res ? "FAIL" : "PASS");
		break;
	case MIICMD_WAIT_FOR_VALUE:
		res = -1;
		start = get_timer(0);
		while ((res != 0) && (get_timer(start) < timeout)) {
			res = miiphy_read(bus, addr, reg, &orig_data);
			if (res)
				continue;
			if ((orig_data & mask) != (data & mask))
				res = -1;
		}
		debug("(value %04x mask %04x) == %04x? %s after %lu ms\n", data,
		      mask, orig_data, res ? "FAIL" : "PASS",
		      get_timer(start));
		break;
	default:
		res = -1;
		break;
	}

	return res;
}

static int process_setup(const char *bus, unsigned char addr,
			    struct mii_setupcmd *setupcmd, unsigned int count)
{
	int res = 0;
	unsigned int k;

	for (k = 0; k < count; ++k) {
		res = process_setupcmd(bus, addr, &setupcmd[k]);
		if (res) {
			printf("mii cmd %u on bus %s addr %u failed, aborting setup\n",
			       setupcmd[k].token, bus, addr);
			break;
		}
	}

	return res;
}

int setup_88e1518(const char *bus, unsigned char addr)
{
	int res;

	res = process_setup(bus, addr,
			    verify_88e1518, ARRAY_SIZE(verify_88e1518));
	if (res)
		return res;

	res = process_setup(bus, addr,
			    fixup_88e1518, ARRAY_SIZE(fixup_88e1518));
	if (res)
		return res;

	res = process_setup(bus, addr,
			    default_88e1518, ARRAY_SIZE(default_88e1518));
	if (res)
		return res;

	if (addr) {
		res = process_setup(bus, addr,
				    ch1fix_88e1518, ARRAY_SIZE(ch1fix_88e1518));
		if (res)
			return res;
	}

	res = process_setup(bus, addr,
			    swreset_88e1518, ARRAY_SIZE(swreset_88e1518));
	if (res)
		return res;

	return 0;
}

int setup_88e1514(const char *bus, unsigned char addr)
{
	int res;

	res = process_setup(bus, addr,
			    verify_88e1518, ARRAY_SIZE(verify_88e1518));
	if (res)
		return res;

	res = process_setup(bus, addr,
			    fixup_88e1518, ARRAY_SIZE(fixup_88e1518));
	if (res)
		return res;

	res = process_setup(bus, addr,
			    mii_to_copper_88e1514,
			    ARRAY_SIZE(mii_to_copper_88e1514));
	if (res)
		return res;

	res = process_setup(bus, addr,
			    sgmii_autoneg_off_88e1518,
			    ARRAY_SIZE(sgmii_autoneg_off_88e1518));
	if (res)
		return res;

	res = process_setup(bus, addr,
			    invert_led2_88e1514,
			    ARRAY_SIZE(invert_led2_88e1514));
	if (res)
		return res;

	res = process_setup(bus, addr,
			    default_88e1518, ARRAY_SIZE(default_88e1518));
	if (res)
		return res;

	if (addr) {
		res = process_setup(bus, addr,
				    ch1fix_88e1518, ARRAY_SIZE(ch1fix_88e1518));
		if (res)
			return res;
	}

	res = process_setup(bus, addr,
			    swreset_88e1518, ARRAY_SIZE(swreset_88e1518));
	if (res)
		return res;

	return 0;
}