Home | History | Annotate | Download | only in common
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * (C) Copyright 2014
      4  * Dirk Eibach,  Guntermann & Drunck GmbH, dirk.eibach (at) 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 }
    280