aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/rtl838x
diff options
context:
space:
mode:
authorBirger Koblitz <git@birger-koblitz.de>2020-10-03 11:20:48 +0200
committerJohn Crispin <john@phrozen.org>2020-10-15 08:32:23 +0200
commit121ef9ddc9dbf7ed16fc4700c71279d71048051f (patch)
tree95481d4b32b1bca9a0933d890335b9ff737ea4d5 /target/linux/rtl838x
parent3c4063f715e1adcfe5710c0efe1c5f06ccd1768f (diff)
downloadupstream-121ef9ddc9dbf7ed16fc4700c71279d71048051f.tar.gz
upstream-121ef9ddc9dbf7ed16fc4700c71279d71048051f.tar.bz2
upstream-121ef9ddc9dbf7ed16fc4700c71279d71048051f.zip
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@birger-koblitz.de>
Diffstat (limited to 'target/linux/rtl838x')
-rw-r--r--target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x.h8
-rw-r--r--target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x_phy.c189
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,