Skip to content

Commit 00d70d8

Browse files
QSchulzdavem330
authored andcommitted
net: phy: mscc: add support for VSC8574 PHY
The VSC8574 PHY is a 4-ports PHY that is 10/100/1000BASE-T, 100BASE-FX, 1000BASE-X and triple-speed copper SFP capable, can communicate with the MAC via SGMII, QSGMII or 1000BASE-X, supports WOL, downshifting and can set the blinking pattern of each of its 4 LEDs, supports SyncE as well as HP Auto-MDIX detection. This adds support for 10/100/1000BASE-T, SGMII/QSGMII link with the MAC, WOL, downshifting, HP Auto-MDIX detection and blinking pattern for its 4 LEDs. The VSC8574 has also an internal Intel 8051 microcontroller whose firmware needs to be patched when the PHY is reset. If the 8051's firmware has the expected CRC, its patching can be skipped. The microcontroller can be accessed from any port of the PHY, though the CRC function can only be done through the PHY that is the base PHY of the package (internal address 0) due to a limitation of the firmware. The GPIO register bank is a set of registers that are common to all PHYs in the package. So any modification in any register of this bank affects all PHYs of the package. If the PHYs haven't been reset before booting the Linux kernel and were configured to use interrupts for e.g. link status updates, it is required to clear the interrupts mask register of all PHYs before being able to use interrupts with any PHY. The first PHY of the package that will be init will take care of clearing all PHYs interrupts mask registers. Thus, we need to keep track of the init sequence in the package, if it's already been done or if it's to be done. Most of the init sequence of a PHY of the package is common to all PHYs in the package, thus we use the SMI broadcast feature which enables us to propagate a write in one register of one PHY to all PHYs in the same package. Signed-off-by: Quentin Schulz <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent a5afc16 commit 00d70d8

File tree

1 file changed

+319
-1
lines changed

1 file changed

+319
-1
lines changed

drivers/net/phy/mscc.c

Lines changed: 319 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ enum rgmii_rx_clock_delay {
6565
#define MEDIA_OP_MODE_AMS_COPPER_100BASEFX 7
6666
#define MEDIA_OP_MODE_POS 8
6767

68+
#define MSCC_PHY_EXT_PHY_CNTL_2 24
69+
6870
#define MII_VSC85XX_INT_MASK 25
6971
#define MII_VSC85XX_INT_MASK_MASK 0xa000
7072
#define MII_VSC85XX_INT_MASK_WOL 0x0040
@@ -151,6 +153,7 @@ enum rgmii_rx_clock_delay {
151153
#define DW8051_CLK_EN 0x0010
152154
#define MICRO_CLK_EN 0x0008
153155
#define MICRO_CLK_DIVIDE(x) ((x) >> 1)
156+
#define MSCC_DW8051_VLD_MASK 0xf1ff
154157

155158
/* x Address in range 1-4 */
156159
#define MSCC_TRAP_ROM_ADDR(x) ((x) * 2 + 1)
@@ -184,7 +187,9 @@ enum rgmii_rx_clock_delay {
184187
#define PROC_CMD_SGMII_MAC 0x0030
185188
#define PROC_CMD_QSGMII_MAC 0x0020
186189
#define PROC_CMD_NO_MAC_CONF 0x0000
190+
#define PROC_CMD_1588_DEFAULT_INIT 0x0010
187191
#define PROC_CMD_NOP 0x000f
192+
#define PROC_CMD_PHY_INIT 0x000a
188193
#define PROC_CMD_CRC16 0x0008
189194
#define PROC_CMD_FIBER_MEDIA_CONF 0x0001
190195
#define PROC_CMD_MCB_ACCESS_MAC_CONF 0x0000
@@ -198,6 +203,9 @@ enum rgmii_rx_clock_delay {
198203
/* Test page Registers */
199204
#define MSCC_PHY_TEST_PAGE_5 5
200205
#define MSCC_PHY_TEST_PAGE_8 8
206+
#define MSCC_PHY_TEST_PAGE_9 9
207+
#define MSCC_PHY_TEST_PAGE_20 20
208+
#define MSCC_PHY_TEST_PAGE_24 24
201209

202210
/* Token ring page Registers */
203211
#define MSCC_PHY_TR_CNTL 16
@@ -211,6 +219,7 @@ enum rgmii_rx_clock_delay {
211219
#define PHY_ID_VSC8531 0x00070570
212220
#define PHY_ID_VSC8540 0x00070760
213221
#define PHY_ID_VSC8541 0x00070770
222+
#define PHY_ID_VSC8574 0x000704a0
214223
#define PHY_ID_VSC8584 0x000707c0
215224

216225
#define MSCC_VDDMAC_1500 1500
@@ -258,6 +267,10 @@ enum rgmii_rx_clock_delay {
258267
#define MSCC_VSC8584_REVB_INT8051_FW_START_ADDR 0xe800
259268
#define MSCC_VSC8584_REVB_INT8051_FW_CRC 0xfb48
260269

270+
#define MSCC_VSC8574_REVB_INT8051_FW "mscc_vsc8574_revb_int8051_29e8.bin"
271+
#define MSCC_VSC8574_REVB_INT8051_FW_START_ADDR 0x4000
272+
#define MSCC_VSC8574_REVB_INT8051_FW_CRC 0x29e8
273+
261274
#define VSC8584_REVB 0x0001
262275
#define MSCC_DEV_REV_MASK GENMASK(3, 0)
263276

@@ -1087,6 +1100,250 @@ static int vsc8584_patch_fw(struct phy_device *phydev,
10871100
return 0;
10881101
}
10891102

1103+
/* bus->mdio_lock should be locked when using this function */
1104+
static bool vsc8574_is_serdes_init(struct phy_device *phydev)
1105+
{
1106+
u16 reg;
1107+
bool ret;
1108+
1109+
phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS,
1110+
MSCC_PHY_PAGE_EXTENDED_GPIO);
1111+
1112+
reg = phy_base_read(phydev, MSCC_TRAP_ROM_ADDR(1));
1113+
if (reg != 0x3eb7) {
1114+
ret = false;
1115+
goto out;
1116+
}
1117+
1118+
reg = phy_base_read(phydev, MSCC_PATCH_RAM_ADDR(1));
1119+
if (reg != 0x4012) {
1120+
ret = false;
1121+
goto out;
1122+
}
1123+
1124+
reg = phy_base_read(phydev, MSCC_INT_MEM_CNTL);
1125+
if (reg != EN_PATCH_RAM_TRAP_ADDR(1)) {
1126+
ret = false;
1127+
goto out;
1128+
}
1129+
1130+
reg = phy_base_read(phydev, MSCC_DW8051_CNTL_STATUS);
1131+
if ((MICRO_NSOFT_RESET | RUN_FROM_INT_ROM | DW8051_CLK_EN |
1132+
MICRO_CLK_EN) != (reg & MSCC_DW8051_VLD_MASK)) {
1133+
ret = false;
1134+
goto out;
1135+
}
1136+
1137+
ret = true;
1138+
out:
1139+
phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD);
1140+
1141+
return ret;
1142+
}
1143+
1144+
/* bus->mdio_lock should be locked when using this function */
1145+
static int vsc8574_config_pre_init(struct phy_device *phydev)
1146+
{
1147+
const struct reg_val pre_init1[] = {
1148+
{0x0fae, 0x000401bd},
1149+
{0x0fac, 0x000f000f},
1150+
{0x17a0, 0x00a0f147},
1151+
{0x0fe4, 0x00052f54},
1152+
{0x1792, 0x0027303d},
1153+
{0x07fe, 0x00000704},
1154+
{0x0fe0, 0x00060150},
1155+
{0x0f82, 0x0012b00a},
1156+
{0x0f80, 0x00000d74},
1157+
{0x02e0, 0x00000012},
1158+
{0x03a2, 0x00050208},
1159+
{0x03b2, 0x00009186},
1160+
{0x0fb0, 0x000e3700},
1161+
{0x1688, 0x00049f81},
1162+
{0x0fd2, 0x0000ffff},
1163+
{0x168a, 0x00039fa2},
1164+
{0x1690, 0x0020640b},
1165+
{0x0258, 0x00002220},
1166+
{0x025a, 0x00002a20},
1167+
{0x025c, 0x00003060},
1168+
{0x025e, 0x00003fa0},
1169+
{0x03a6, 0x0000e0f0},
1170+
{0x0f92, 0x00001489},
1171+
{0x16a2, 0x00007000},
1172+
{0x16a6, 0x00071448},
1173+
{0x16a0, 0x00eeffdd},
1174+
{0x0fe8, 0x0091b06c},
1175+
{0x0fea, 0x00041600},
1176+
{0x16b0, 0x00eeff00},
1177+
{0x16b2, 0x00007000},
1178+
{0x16b4, 0x00000814},
1179+
{0x0f90, 0x00688980},
1180+
{0x03a4, 0x0000d8f0},
1181+
{0x0fc0, 0x00000400},
1182+
{0x07fa, 0x0050100f},
1183+
{0x0796, 0x00000003},
1184+
{0x07f8, 0x00c3ff98},
1185+
{0x0fa4, 0x0018292a},
1186+
{0x168c, 0x00d2c46f},
1187+
{0x17a2, 0x00000620},
1188+
{0x16a4, 0x0013132f},
1189+
{0x16a8, 0x00000000},
1190+
{0x0ffc, 0x00c0a028},
1191+
{0x0fec, 0x00901c09},
1192+
{0x0fee, 0x0004a6a1},
1193+
{0x0ffe, 0x00b01807},
1194+
};
1195+
const struct reg_val pre_init2[] = {
1196+
{0x0486, 0x0008a518},
1197+
{0x0488, 0x006dc696},
1198+
{0x048a, 0x00000912},
1199+
{0x048e, 0x00000db6},
1200+
{0x049c, 0x00596596},
1201+
{0x049e, 0x00000514},
1202+
{0x04a2, 0x00410280},
1203+
{0x04a4, 0x00000000},
1204+
{0x04a6, 0x00000000},
1205+
{0x04a8, 0x00000000},
1206+
{0x04aa, 0x00000000},
1207+
{0x04ae, 0x007df7dd},
1208+
{0x04b0, 0x006d95d4},
1209+
{0x04b2, 0x00492410},
1210+
};
1211+
struct device *dev = &phydev->mdio.dev;
1212+
const struct firmware *fw;
1213+
unsigned int i;
1214+
u16 crc, reg;
1215+
bool serdes_init;
1216+
int ret;
1217+
1218+
phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD);
1219+
1220+
/* all writes below are broadcasted to all PHYs in the same package */
1221+
reg = phy_base_read(phydev, MSCC_PHY_EXT_CNTL_STATUS);
1222+
reg |= SMI_BROADCAST_WR_EN;
1223+
phy_base_write(phydev, MSCC_PHY_EXT_CNTL_STATUS, reg);
1224+
1225+
phy_base_write(phydev, MII_VSC85XX_INT_MASK, 0);
1226+
1227+
/* The below register writes are tweaking analog and electrical
1228+
* configuration that were determined through characterization by PHY
1229+
* engineers. These don't mean anything more than "these are the best
1230+
* values".
1231+
*/
1232+
phy_base_write(phydev, MSCC_PHY_EXT_PHY_CNTL_2, 0x0040);
1233+
1234+
phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TEST);
1235+
1236+
phy_base_write(phydev, MSCC_PHY_TEST_PAGE_20, 0x4320);
1237+
phy_base_write(phydev, MSCC_PHY_TEST_PAGE_24, 0x0c00);
1238+
phy_base_write(phydev, MSCC_PHY_TEST_PAGE_9, 0x18ca);
1239+
phy_base_write(phydev, MSCC_PHY_TEST_PAGE_5, 0x1b20);
1240+
1241+
reg = phy_base_read(phydev, MSCC_PHY_TEST_PAGE_8);
1242+
reg |= 0x8000;
1243+
phy_base_write(phydev, MSCC_PHY_TEST_PAGE_8, reg);
1244+
1245+
phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TR);
1246+
1247+
for (i = 0; i < ARRAY_SIZE(pre_init1); i++)
1248+
vsc8584_csr_write(phydev, pre_init1[i].reg, pre_init1[i].val);
1249+
1250+
phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_EXTENDED_2);
1251+
1252+
phy_base_write(phydev, MSCC_PHY_CU_PMD_TX_CNTL, 0x028e);
1253+
1254+
phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TR);
1255+
1256+
for (i = 0; i < ARRAY_SIZE(pre_init2); i++)
1257+
vsc8584_csr_write(phydev, pre_init2[i].reg, pre_init2[i].val);
1258+
1259+
phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TEST);
1260+
1261+
reg = phy_base_read(phydev, MSCC_PHY_TEST_PAGE_8);
1262+
reg &= ~0x8000;
1263+
phy_base_write(phydev, MSCC_PHY_TEST_PAGE_8, reg);
1264+
1265+
phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD);
1266+
1267+
/* end of write broadcasting */
1268+
reg = phy_base_read(phydev, MSCC_PHY_EXT_CNTL_STATUS);
1269+
reg &= ~SMI_BROADCAST_WR_EN;
1270+
phy_base_write(phydev, MSCC_PHY_EXT_CNTL_STATUS, reg);
1271+
1272+
ret = request_firmware(&fw, MSCC_VSC8574_REVB_INT8051_FW, dev);
1273+
if (ret) {
1274+
dev_err(dev, "failed to load firmware %s, ret: %d\n",
1275+
MSCC_VSC8574_REVB_INT8051_FW, ret);
1276+
return ret;
1277+
}
1278+
1279+
/* Add one byte to size for the one added by the patch_fw function */
1280+
ret = vsc8584_get_fw_crc(phydev,
1281+
MSCC_VSC8574_REVB_INT8051_FW_START_ADDR,
1282+
fw->size + 1, &crc);
1283+
if (ret)
1284+
goto out;
1285+
1286+
if (crc == MSCC_VSC8574_REVB_INT8051_FW_CRC) {
1287+
serdes_init = vsc8574_is_serdes_init(phydev);
1288+
1289+
if (!serdes_init) {
1290+
ret = vsc8584_micro_assert_reset(phydev);
1291+
if (ret) {
1292+
dev_err(dev,
1293+
"%s: failed to assert reset of micro\n",
1294+
__func__);
1295+
return ret;
1296+
}
1297+
}
1298+
} else {
1299+
dev_dbg(dev, "FW CRC is not the expected one, patching FW\n");
1300+
1301+
serdes_init = false;
1302+
1303+
if (vsc8584_patch_fw(phydev, fw))
1304+
dev_warn(dev,
1305+
"failed to patch FW, expect non-optimal device\n");
1306+
}
1307+
1308+
if (!serdes_init) {
1309+
phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS,
1310+
MSCC_PHY_PAGE_EXTENDED_GPIO);
1311+
1312+
phy_base_write(phydev, MSCC_TRAP_ROM_ADDR(1), 0x3eb7);
1313+
phy_base_write(phydev, MSCC_PATCH_RAM_ADDR(1), 0x4012);
1314+
phy_base_write(phydev, MSCC_INT_MEM_CNTL,
1315+
EN_PATCH_RAM_TRAP_ADDR(1));
1316+
1317+
vsc8584_micro_deassert_reset(phydev, false);
1318+
1319+
/* Add one byte to size for the one added by the patch_fw
1320+
* function
1321+
*/
1322+
ret = vsc8584_get_fw_crc(phydev,
1323+
MSCC_VSC8574_REVB_INT8051_FW_START_ADDR,
1324+
fw->size + 1, &crc);
1325+
if (ret)
1326+
goto out;
1327+
1328+
if (crc != MSCC_VSC8574_REVB_INT8051_FW_CRC)
1329+
dev_warn(dev,
1330+
"FW CRC after patching is not the expected one, expect non-optimal device\n");
1331+
}
1332+
1333+
phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS,
1334+
MSCC_PHY_PAGE_EXTENDED_GPIO);
1335+
1336+
ret = vsc8584_cmd(phydev, PROC_CMD_1588_DEFAULT_INIT |
1337+
PROC_CMD_PHY_INIT);
1338+
1339+
out:
1340+
phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD);
1341+
1342+
release_firmware(fw);
1343+
1344+
return ret;
1345+
}
1346+
10901347
/* bus->mdio_lock should be locked when using this function */
10911348
static int vsc8584_config_pre_init(struct phy_device *phydev)
10921349
{
@@ -1310,7 +1567,15 @@ static int vsc8584_config_init(struct phy_device *phydev)
13101567
* in this pre-init function.
13111568
*/
13121569
if (!vsc8584_is_pkg_init(phydev, val & PHY_ADDR_REVERSED ? 1 : 0)) {
1313-
ret = vsc8584_config_pre_init(phydev);
1570+
if ((phydev->phy_id & phydev->drv->phy_id_mask) ==
1571+
(PHY_ID_VSC8574 & phydev->drv->phy_id_mask))
1572+
ret = vsc8574_config_pre_init(phydev);
1573+
else if ((phydev->phy_id & phydev->drv->phy_id_mask) ==
1574+
(PHY_ID_VSC8584 & phydev->drv->phy_id_mask))
1575+
ret = vsc8584_config_pre_init(phydev);
1576+
else
1577+
ret = -EINVAL;
1578+
13141579
if (ret)
13151580
goto err;
13161581
}
@@ -1476,6 +1741,31 @@ static int vsc85xx_read_status(struct phy_device *phydev)
14761741
return genphy_read_status(phydev);
14771742
}
14781743

1744+
static int vsc8574_probe(struct phy_device *phydev)
1745+
{
1746+
struct vsc8531_private *vsc8531;
1747+
u32 default_mode[4] = {VSC8531_LINK_1000_ACTIVITY,
1748+
VSC8531_LINK_100_ACTIVITY, VSC8531_LINK_ACTIVITY,
1749+
VSC8531_DUPLEX_COLLISION};
1750+
1751+
vsc8531 = devm_kzalloc(&phydev->mdio.dev, sizeof(*vsc8531), GFP_KERNEL);
1752+
if (!vsc8531)
1753+
return -ENOMEM;
1754+
1755+
phydev->priv = vsc8531;
1756+
1757+
vsc8531->nleds = 4;
1758+
vsc8531->supp_led_modes = VSC8584_SUPP_LED_MODES;
1759+
vsc8531->hw_stats = vsc8584_hw_stats;
1760+
vsc8531->nstats = ARRAY_SIZE(vsc8584_hw_stats);
1761+
vsc8531->stats = devm_kmalloc_array(&phydev->mdio.dev, vsc8531->nstats,
1762+
sizeof(u64), GFP_KERNEL);
1763+
if (!vsc8531->stats)
1764+
return -ENOMEM;
1765+
1766+
return vsc85xx_dt_led_modes_get(phydev, default_mode);
1767+
}
1768+
14791769
static int vsc8584_probe(struct phy_device *phydev)
14801770
{
14811771
struct vsc8531_private *vsc8531;
@@ -1642,6 +1932,33 @@ static struct phy_driver vsc85xx_driver[] = {
16421932
.get_strings = &vsc85xx_get_strings,
16431933
.get_stats = &vsc85xx_get_stats,
16441934
},
1935+
{
1936+
.phy_id = PHY_ID_VSC8574,
1937+
.name = "Microsemi GE VSC8574 SyncE",
1938+
.phy_id_mask = 0xfffffff0,
1939+
.features = PHY_GBIT_FEATURES,
1940+
.flags = PHY_HAS_INTERRUPT,
1941+
.soft_reset = &genphy_soft_reset,
1942+
.config_init = &vsc8584_config_init,
1943+
.config_aneg = &vsc85xx_config_aneg,
1944+
.aneg_done = &genphy_aneg_done,
1945+
.read_status = &vsc85xx_read_status,
1946+
.ack_interrupt = &vsc85xx_ack_interrupt,
1947+
.config_intr = &vsc85xx_config_intr,
1948+
.did_interrupt = &vsc8584_did_interrupt,
1949+
.suspend = &genphy_suspend,
1950+
.resume = &genphy_resume,
1951+
.probe = &vsc8574_probe,
1952+
.set_wol = &vsc85xx_wol_set,
1953+
.get_wol = &vsc85xx_wol_get,
1954+
.get_tunable = &vsc85xx_get_tunable,
1955+
.set_tunable = &vsc85xx_set_tunable,
1956+
.read_page = &vsc85xx_phy_read_page,
1957+
.write_page = &vsc85xx_phy_write_page,
1958+
.get_sset_count = &vsc85xx_get_sset_count,
1959+
.get_strings = &vsc85xx_get_strings,
1960+
.get_stats = &vsc85xx_get_stats,
1961+
},
16451962
{
16461963
.phy_id = PHY_ID_VSC8584,
16471964
.name = "Microsemi GE VSC8584 SyncE",
@@ -1677,6 +1994,7 @@ static struct mdio_device_id __maybe_unused vsc85xx_tbl[] = {
16771994
{ PHY_ID_VSC8531, 0xfffffff0, },
16781995
{ PHY_ID_VSC8540, 0xfffffff0, },
16791996
{ PHY_ID_VSC8541, 0xfffffff0, },
1997+
{ PHY_ID_VSC8574, 0xfffffff0, },
16801998
{ PHY_ID_VSC8584, 0xfffffff0, },
16811999
{ }
16822000
};

0 commit comments

Comments
 (0)