[openwrt/openwrt] rtl838x: Add support for RTL839x internal PHY

LEDE Commits lede-commits at lists.infradead.org
Thu Oct 15 02:32:36 EDT 2020


blogic pushed a commit to openwrt/openwrt.git, branch master:
https://git.openwrt.org/121ef9ddc9dbf7ed16fc4700c71279d71048051f

commit 121ef9ddc9dbf7ed16fc4700c71279d71048051f
Author: Birger Koblitz <git at birger-koblitz.de>
AuthorDate: Sat Oct 3 11:20:48 2020 +0200

    rtl838x: Add support for RTL839x internal PHY
    
    This adds basic support for reading the internal PHYs of the RTL839x SoCs
    and full support for the 2 PHYs connected to the 1000Base-X SerDes of
    the RTL8393 SoC.
    
    Signed-off-by: Birger Koblitz <git at birger-koblitz.de>
---
 .../rtl838x/files-5.4/drivers/net/dsa/rtl838x.h    |   8 +
 .../files-5.4/drivers/net/dsa/rtl838x_phy.c        | 189 ++++++++++++++++++++-
 2 files changed, 188 insertions(+), 9 deletions(-)

diff --git a/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x.h b/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x.h
index 127a31dca1..2e420a9db4 100644
--- a/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x.h
+++ b/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x.h
@@ -38,6 +38,14 @@
 #define RTL838X_STAT_CTRL			(0x3108)
 #define RTL839X_STAT_CTRL			(0x04cc)
 
+/* Registers of the internal Serdes of the 8390 */
+#define RTL8390_SDS0_1_XSG0			(0xA000)
+#define RTL8390_SDS0_1_XSG1			(0xA100)
+#define RTL839X_SDS12_13_XSG0			(0xB800)
+#define RTL839X_SDS12_13_XSG1			(0xB900)
+#define RTL839X_SDS12_13_PWR0			(0xb880)
+#define RTL839X_SDS12_13_PWR1			(0xb980)
+
 /* Registers of the internal Serdes of the 8380 */
 #define MAPLE_SDS4_REG0r			RTL838X_SDS4_REG28
 #define MAPLE_SDS5_REG0r			(RTL838X_SDS4_REG28 + 0x100)
diff --git a/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x_phy.c b/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x_phy.c
index 209ea50f04..aa6ac62c27 100644
--- a/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x_phy.c
+++ b/target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x_phy.c
@@ -23,6 +23,7 @@
 #define PHY_ID_RTL8218B_E	0x001cc981
 #define PHY_ID_RTL8218B_I	0x001cca40
 #define PHY_ID_RTL8390_GENERIC	0x001ccab0
+#define PHY_ID_RTL8393_I	0x001c8393
 
 struct __attribute__ ((__packed__)) part {
 	uint16_t start;
@@ -118,6 +119,116 @@ void rtl8380_sds_rst(int mac)
 	pr_info("SERDES reset: %d\n", mac);
 }
 
+int rtl839x_read_sds_phy(int phy_addr, int phy_reg)
+{
+	int offset = 0;
+	int reg;
+	u32 val;
+
+	if (phy_addr == 49)
+		offset = 0x100;
+
+	/* For the RTL8393 internal SerDes, we simulate a PHY ID in registers 2/3
+	 * which would otherwise read as 0
+	 */
+	if (soc_info.id == 0x8393) {
+		if (phy_reg == 2)
+			return 0x1c;
+		if (phy_reg == 3)
+			return 0x8393;
+	}
+
+	reg = (phy_reg << 1) & 0xfc;
+	val = sw_r32(RTL839X_SDS12_13_XSG0 + offset + 0x80 + reg);
+
+	if (phy_reg & 1)
+		val = (val >> 16) & 0xffff;
+	else
+		val &= 0xffff;
+	return val;
+}
+
+int rtl838x_read_sds_phy(int phy_addr, int phy_reg)
+{
+	int offset = 0;
+	u32 val;
+
+	if (phy_addr == 26)
+		offset = 0x100;
+	val = sw_r32(MAPLE_SDS4_FIB_REG0r + offset + (phy_reg << 2)) & 0xffff;
+
+	return val;
+}
+
+int rtl839x_write_sds_phy(int phy_addr, int phy_reg, u16 v)
+{
+	int offset = 0;
+	int reg;
+	u32 val;
+
+	if (phy_addr == 49)
+		offset = 0x100;
+
+	reg = (phy_reg << 1) & 0xfc;
+	val = v;
+	if (phy_reg & 1) {
+		val = val << 16;
+		sw_w32_mask(0xffff0000, val,
+			    RTL839X_SDS12_13_XSG0 + offset + 0x80 + reg);
+	} else {
+		sw_w32_mask(0xffff, val,
+			    RTL839X_SDS12_13_XSG0 + offset + 0x80 + reg);
+	}
+
+	return 0;
+}
+
+/* Read the link and speed status of the 2 internal SGMII/1000Base-X
+ * ports of the RTL838x SoCs
+ */
+static int rtl8380_read_status(struct phy_device *phydev)
+{
+	int err;
+	int phy_addr = phydev->mdio.addr;
+
+	err = genphy_read_status(phydev);
+
+	if (phydev->link) {
+		phydev->speed = SPEED_1000;
+		phydev->duplex = DUPLEX_FULL;
+	}
+
+	return err;
+}
+
+/* Read the link and speed status of the 2 internal SGMII/1000Base-X
+ * ports of the RTL8393 SoC
+ */
+static int rtl8393_read_status(struct phy_device *phydev)
+{
+	int offset = 0;
+	int err;
+	int phy_addr = phydev->mdio.addr;
+	u32 v;
+
+	err = genphy_read_status(phydev);
+	if (phy_addr == 49)
+		offset = 0x100;
+
+	if (phydev->link) {
+		phydev->speed = SPEED_100;
+		/* Read SPD_RD_00 (bit 13) and SPD_RD_01 (bit 6) out of the internal
+		 * PHY registers
+		 */
+		v = sw_r32(RTL839X_SDS12_13_XSG0 + offset + 0x80);
+		if (!(v & (1 << 13)) && (v & (1 << 6)))
+			phydev->speed = SPEED_1000;
+		phydev->duplex = DUPLEX_FULL;
+	}
+
+	return err;
+}
+
 static struct fw_header *
 rtl838x_request_fw(struct phy_device *phydev, const struct firmware *fw,
 		   const char *name)
@@ -406,7 +517,15 @@ static int rtl8218b_ext_match_phy_device(struct phy_device *phydev)
 {
 	int addr = phydev->mdio.addr;
 
-	return phydev->phy_id == PHY_ID_RTL8218B_E && addr < 8;
+	/* Both the RTL8214FC and the external RTL8218B have the same
+	 * PHY ID. On the RTL838x, the RTL8218B can only be attached_dev
+	 * at PHY IDs 0-7, while the RTL8214FC must be attached via
+	 * the pair of SGMII/1000Base-X with higher PHY-IDs
+	 */
+	if (soc_info.family == RTL8380_FAMILY_ID)
+		return phydev->phy_id == PHY_ID_RTL8218B_E && addr < 8;
+	else
+		return phydev->phy_id == PHY_ID_RTL8218B_E;
 }
 
 
@@ -1065,12 +1184,32 @@ static int rtl8380_configure_serdes(struct phy_device *phydev)
 	return 0;
 }
 
+static int rtl8390_configure_serdes(struct phy_device *phydev)
+{
+	phydev_info(phydev, "Detected internal RTL8390 SERDES\n");
+
+	/* In autoneg state, force link, set SR4_CFG_EN_LINK_FIB1G */
+	sw_w32_mask(0, 1 << 18, RTL839X_SDS12_13_XSG0 + 0x0a);
+
+	/* Disable EEE: Clear FRE16_EEE_RSG_FIB1G, FRE16_EEE_STD_FIB1G,
+	 * FRE16_C1_PWRSAV_EN_FIB1G, FRE16_C2_PWRSAV_EN_FIB1G
+	 * and FRE16_EEE_QUIET_FIB1G
+	 */
+	sw_w32_mask(0x1f << 10, 0, RTL839X_SDS12_13_XSG0 + 0xe0);
+
+	return 0;
+}
+
 static int rtl8214fc_phy_probe(struct phy_device *phydev)
 {
 	struct device *dev = &phydev->mdio.dev;
 	struct rtl838x_phy_priv *priv;
 	int addr = phydev->mdio.addr;
 
+	/* 839x has internal SerDes */
+	if (soc_info.id == 0x8393)
+		return -ENODEV;
+
 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 	if (!priv)
 		return -ENOMEM;
@@ -1118,7 +1257,7 @@ static int rtl8218b_ext_phy_probe(struct phy_device *phydev)
 	priv->name = "RTL8218B (external)";
 
 	/* All base addresses of the PHYs start at multiples of 8 */
-	if (!(addr % 8)) {
+	if (!(addr % 8) && soc_info.family == RTL8380_FAMILY_ID) {
 		/* Configuration must be done while patching still possible */
 		return rtl8380_configure_ext_rtl8218b(phydev);
 	}
@@ -1176,6 +1315,27 @@ static int rtl838x_serdes_probe(struct phy_device *phydev)
 	return -ENODEV;
 }
 
+static int rtl8393_serdes_probe(struct phy_device *phydev)
+{
+	struct device *dev = &phydev->mdio.dev;
+	struct rtl838x_phy_priv *priv;
+	int addr = phydev->mdio.addr;
+
+	pr_info("%s: id: %d\n", __func__, addr);
+	if (soc_info.family != RTL8390_FAMILY_ID)
+		return -ENODEV;
+
+	if (addr < 24)
+		return -ENODEV;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->name = "RTL8393 Serdes";
+	return rtl8390_configure_serdes(phydev);
+}
+
 static int rtl8390_serdes_probe(struct phy_device *phydev)
 {
 	struct device *dev = &phydev->mdio.dev;
@@ -1198,8 +1358,8 @@ static int rtl8390_serdes_probe(struct phy_device *phydev)
 
 static struct phy_driver rtl838x_phy_driver[] = {
 	{
-		PHY_ID_MATCH_MODEL(PHY_ID_RTL8214FC),
-		.name		= "REATLTEK RTL8214C",
+		PHY_ID_MATCH_MODEL(PHY_ID_RTL8214C),
+		.name		= "REALTEK RTL8214C",
 		.features	= PHY_GBIT_FEATURES,
 		.match_phy_device = rtl8214c_match_phy_device,
 		.probe		= rtl8214c_phy_probe,
@@ -1209,7 +1369,7 @@ static struct phy_driver rtl838x_phy_driver[] = {
 	},
 	{
 		PHY_ID_MATCH_MODEL(PHY_ID_RTL8214FC),
-		.name		= "REATLTEK RTL8214FC",
+		.name		= "REALTEK RTL8214FC",
 		.features	= PHY_GBIT_FIBRE_FEATURES,
 		.match_phy_device = rtl8214fc_match_phy_device,
 		.probe		= rtl8214fc_phy_probe,
@@ -1225,7 +1385,7 @@ static struct phy_driver rtl838x_phy_driver[] = {
 	},
 	{
 		PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_E),
-		.name		= "REATLTEK RTL8218B (external)",
+		.name		= "REALTEK RTL8218B (external)",
 		.features	= PHY_GBIT_FEATURES,
 		.match_phy_device = rtl8218b_ext_match_phy_device,
 		.probe		= rtl8218b_ext_phy_probe,
@@ -1239,7 +1399,7 @@ static struct phy_driver rtl838x_phy_driver[] = {
 	},
 	{
 		PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_I),
-		.name		= "REATLTEK RTL8218B (internal)",
+		.name		= "REALTEK RTL8218B (internal)",
 		.features	= PHY_GBIT_FEATURES,
 		.probe		= rtl8218b_int_phy_probe,
 		.suspend	= genphy_suspend,
@@ -1252,7 +1412,7 @@ static struct phy_driver rtl838x_phy_driver[] = {
 	},
 	{
 		PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_I),
-		.name		= "REATLTEK RTL8380 SERDES",
+		.name		= "REALTEK RTL8380 SERDES",
 		.features	= PHY_GBIT_FIBRE_FEATURES,
 		.probe		= rtl838x_serdes_probe,
 		.suspend	= genphy_suspend,
@@ -1260,10 +1420,21 @@ static struct phy_driver rtl838x_phy_driver[] = {
 		.set_loopback	= genphy_loopback,
 		.read_mmd	= rtl8380_rtl8218b_read_mmd,
 		.write_mmd	= rtl8380_rtl8218b_write_mmd,
+		.read_status	= rtl8380_read_status,
+	},
+	{
+		PHY_ID_MATCH_MODEL(PHY_ID_RTL8393_I),
+		.name		= "REALTEK RTL8393 SERDES",
+		.features	= PHY_GBIT_FIBRE_FEATURES,
+		.probe		= rtl8393_serdes_probe,
+		.suspend	= genphy_suspend,
+		.resume		= genphy_resume,
+		.set_loopback	= genphy_loopback,
+		.read_status	= rtl8393_read_status,
 	},
 	{
 		PHY_ID_MATCH_MODEL(PHY_ID_RTL8390_GENERIC),
-		.name		= "REATLTEK RTL8390 Generic",
+		.name		= "REALTEK RTL8390 Generic",
 		.features	= PHY_GBIT_FIBRE_FEATURES,
 		.probe		= rtl8390_serdes_probe,
 		.suspend	= genphy_suspend,



More information about the lede-commits mailing list