realtek: phy: proper RTL8218B, RTL8214FC, RTL8214FB detection
authorMarkus Stockhausen <markus.stockhausen@gmx.de>
Sun, 22 Sep 2024 17:19:22 +0000 (13:19 -0400)
committerRobert Marko <robimarko@gmail.com>
Sun, 27 Oct 2024 21:26:25 +0000 (22:26 +0100)
Three PHYs share the same identifier. Until now we simply assume
the type depending of the bus address it is attached to. Make it
better and check the chip mode register instead.

The kernel will either detect by id/mask or by match_phy_device().
Remove the unneeded settings.

Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
Link: https://github.com/openwrt/openwrt/pull/16457
Signed-off-by: Robert Marko <robimarko@gmail.com>
target/linux/realtek/files-6.6/drivers/net/phy/rtl83xx-phy.c
target/linux/realtek/files-6.6/drivers/net/phy/rtl83xx-phy.h

index dcb7f1570bf6d8d5642bfc60e6c378d3df89f947..2e52924f80359678bdebb86dc6c057fe464684c8 100644 (file)
@@ -147,6 +147,79 @@ static int resume_polling(u64 saved_state)
        return 0;
 }
 
+int rtl821x_match_phy_device(struct phy_device *phydev)
+{
+       u64 poll_state;
+       int rawpage, port = phydev->mdio.addr & ~3;
+       int oldpage, chip_mode, chip_cfg_mode;
+
+       if (phydev->phy_id == PHY_ID_RTL8218B_E)
+               return PHY_IS_RTL8218B_E;
+
+       if (phydev->phy_id != PHY_ID_RTL8214_OR_8218)
+               return PHY_IS_NOT_RTL821X;
+
+       if (soc_info.family == RTL8380_FAMILY_ID)
+               rawpage = RTL838X_PAGE_RAW;
+       else if (soc_info.family == RTL8390_FAMILY_ID)
+               rawpage = RTL839X_PAGE_RAW;
+       else
+               return PHY_IS_NOT_RTL821X;
+
+       poll_state = disable_polling(port);
+       /*
+        * At this stage the write_page()/read_page() PHY functions are not yet
+        * registered and normal paged access is not possible. The following
+        * detection routine works because our MDIO bus has all the Realtek
+        * PHY page handling (register 31) integrated into the port functions.
+        */
+       oldpage = phy_port_read_paged(phydev, port, rawpage, 31);
+       phy_port_write_paged(phydev, port, rawpage, 31, 0xa42);
+       phy_port_write_paged(phydev, port, rawpage, 29, 0x008);
+       phy_port_write_paged(phydev, port, rawpage, 31, 0x278);
+       phy_port_write_paged(phydev, port, rawpage, 18, 0x455);
+       phy_port_write_paged(phydev, port, rawpage, 31, 0x260);
+       chip_mode = phy_port_read_paged(phydev, port, rawpage, 18);
+       phy_port_write_paged(phydev, port, rawpage, 31, 0xa42);
+       phy_port_write_paged(phydev, port, rawpage, 29, 0x000);
+       phy_port_write_paged(phydev, port, rawpage, 31, oldpage);
+
+       resume_polling(poll_state);
+
+       pr_debug("%s(%d): got chip mode %x\n", __func__, phydev->mdio.addr, chip_mode);
+
+       /* we checked the 4th port of a RTL8218B and got no config values */
+       if (!chip_mode)
+               return PHY_IS_RTL8218B_E;
+
+       chip_cfg_mode = (chip_mode >> 4) & 0xf;
+       chip_mode &= 0xf;
+
+       if (chip_mode == 0xd || chip_mode == 0xf)
+               return PHY_IS_RTL8218B_E;
+
+       if (chip_mode == 0x4 || chip_mode == 0x6)
+               return PHY_IS_RTL8214FC;
+
+       if (chip_mode != 0xc && chip_mode != 0xe)
+               return PHY_IS_NOT_RTL821X;
+
+       if (chip_cfg_mode == 0x4 || chip_cfg_mode == 0x6)
+               return PHY_IS_RTL8214FC;
+
+       return PHY_IS_RTL8214FB;
+}
+
+static int rtl8218b_ext_match_phy_device(struct phy_device *phydev)
+{
+       return rtl821x_match_phy_device(phydev) == PHY_IS_RTL8218B_E;
+}
+
+static int rtl8214fc_match_phy_device(struct phy_device *phydev)
+{
+       return rtl821x_match_phy_device(phydev) == PHY_IS_RTL8214FC;
+}
+
 static void rtl8380_int_phy_on_off(struct phy_device *phydev, bool on)
 {
        phy_modify(phydev, 0, BMCR_PDOWN, on ? 0 : BMCR_PDOWN);
@@ -936,21 +1009,6 @@ static int rtl8380_configure_ext_rtl8218b(struct phy_device *phydev)
        return 0;
 }
 
-static int rtl8218b_ext_match_phy_device(struct phy_device *phydev)
-{
-       int addr = phydev->mdio.addr;
-
-       /* 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;
-}
-
 static bool rtl8214fc_media_is_fibre(struct phy_device *phydev)
 {
        int mac = phydev->mdio.addr;
@@ -1459,13 +1517,6 @@ static int rtl8380_configure_rtl8214fc(struct phy_device *phydev)
        return 0;
 }
 
-static int rtl8214fc_match_phy_device(struct phy_device *phydev)
-{
-       int addr = phydev->mdio.addr;
-
-       return phydev->phy_id == PHY_ID_RTL8214FC && addr >= 24;
-}
-
 static int rtl8380_configure_serdes(struct phy_device *phydev)
 {
        u32 v;
@@ -3856,10 +3907,9 @@ static struct phy_driver rtl83xx_phy_driver[] = {
                .set_loopback   = genphy_loopback,
        },
        {
-               PHY_ID_MATCH_MODEL(PHY_ID_RTL8214FC),
+               .match_phy_device = rtl8214fc_match_phy_device,
                .name           = "Realtek RTL8214FC",
                .features       = PHY_GBIT_FIBRE_FEATURES,
-               .match_phy_device = rtl8214fc_match_phy_device,
                .probe          = rtl8214fc_phy_probe,
                .read_page      = rtl821x_read_page,
                .write_page     = rtl821x_write_page,
@@ -3872,10 +3922,9 @@ static struct phy_driver rtl83xx_phy_driver[] = {
                .get_eee        = rtl8214fc_get_eee,
        },
        {
-               PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_E),
+               .match_phy_device = rtl8218b_ext_match_phy_device,
                .name           = "Realtek RTL8218B (external)",
                .features       = PHY_GBIT_FEATURES,
-               .match_phy_device = rtl8218b_ext_match_phy_device,
                .probe          = rtl8218b_ext_phy_probe,
                .read_page      = rtl821x_read_page,
                .write_page     = rtl821x_write_page,
@@ -3991,7 +4040,7 @@ static struct phy_driver rtl83xx_phy_driver[] = {
 module_phy_driver(rtl83xx_phy_driver);
 
 static struct mdio_device_id __maybe_unused rtl83xx_tbl[] = {
-       { PHY_ID_MATCH_MODEL(PHY_ID_RTL8214FC) },
+       { PHY_ID_MATCH_MODEL(PHY_ID_RTL8214_OR_8218) },
        { }
 };
 
index fb79560e6b6948891c9989ec81ac9db0a515887f..781ecb3c7ca931b1c8128740df1efe91a666a1c5 100644 (file)
@@ -19,21 +19,26 @@ struct __attribute__ ((__packed__)) fw_header {
 };
 
 /* TODO: fixed path? */
-#define FIRMWARE_838X_8380_1   "rtl838x_phy/rtl838x_8380.fw"
-#define FIRMWARE_838X_8214FC_1 "rtl838x_phy/rtl838x_8214fc.fw"
-#define FIRMWARE_838X_8218b_1  "rtl838x_phy/rtl838x_8218b.fw"
+#define FIRMWARE_838X_8380_1                   "rtl838x_phy/rtl838x_8380.fw"
+#define FIRMWARE_838X_8214FC_1                 "rtl838x_phy/rtl838x_8214fc.fw"
+#define FIRMWARE_838X_8218b_1                  "rtl838x_phy/rtl838x_8218b.fw"
 
-/* External RTL8218B and RTL8214FC IDs are identical */
-#define PHY_ID_RTL8214C                0x001cc942
-#define PHY_ID_RTL8214FC       0x001cc981
-#define PHY_ID_RTL8218B_E      0x001cc981
-#define PHY_ID_RTL8218D                0x001cc983
-#define PHY_ID_RTL8218B_I      0x001cca40
-#define PHY_ID_RTL8221B                0x001cc849
-#define PHY_ID_RTL8226         0x001cc838
-#define PHY_ID_RTL8390_GENERIC 0x001ccab0
-#define PHY_ID_RTL8393_I       0x001c8393
-#define PHY_ID_RTL9300_I       0x70d03106
+#define PHY_ID_RTL8214C                                0x001cc942
+#define PHY_ID_RTL8218B_E                      0x001cc980
+#define PHY_ID_RTL8214_OR_8218                 0x001cc981
+#define PHY_ID_RTL8218D                                0x001cc983
+#define PHY_ID_RTL8218B_I                      0x001cca40
+#define PHY_ID_RTL8221B                                0x001cc849
+#define PHY_ID_RTL8226                         0x001cc838
+#define PHY_ID_RTL8390_GENERIC                 0x001ccab0
+#define PHY_ID_RTL8393_I                       0x001c8393
+#define PHY_ID_RTL9300_I                       0x70d03106
+
+/* These PHYs share the same id (0x001cc981) */
+#define PHY_IS_NOT_RTL821X                     0
+#define PHY_IS_RTL8214FC                       1
+#define PHY_IS_RTL8214FB                       2
+#define PHY_IS_RTL8218B_E                      3
 
 /* Registers of the internal Serdes of the 8380 */
 #define RTL838X_SDS_MODE_SEL                   (0x0028)