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 280
|
/*
* (C) Copyright 2014
* Dirk Eibach, Guntermann & Drunck GmbH, eibach@gdsys.de
*
* SPDX-License-Identifier: GPL-2.0+
*/
#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;
}
|