diff options
author | Birger Koblitz <git@birger-koblitz.de> | 2020-10-03 11:20:48 +0200 |
---|---|---|
committer | John Crispin <john@phrozen.org> | 2020-10-15 08:32:23 +0200 |
commit | 121ef9ddc9dbf7ed16fc4700c71279d71048051f (patch) | |
tree | 95481d4b32b1bca9a0933d890335b9ff737ea4d5 /target/linux | |
parent | 3c4063f715e1adcfe5710c0efe1c5f06ccd1768f (diff) | |
download | upstream-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')
-rw-r--r-- | target/linux/rtl838x/files-5.4/drivers/net/dsa/rtl838x.h | 8 | ||||
-rw-r--r-- | target/linux/rtl838x/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, |