realtek: add internal and external SDS/PHYs of RTL9300 devices
authorBirger Koblitz <git@birger-koblitz.de>
Thu, 21 Jan 2021 14:12:36 +0000 (15:12 +0100)
committerPetr Štetiar <ynezz@true.cz>
Tue, 26 Jan 2021 14:06:50 +0000 (15:06 +0100)
This adds support for the internal SerDes of the RTL9300 SoC
and for the RTL8218D and RTL8226B phys found in combination
with this SoC in switches.

Signed-off-by: Birger Koblitz <git@birger-koblitz.de>
target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c
target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.h

index eba416934fb5cb4eba5f452b7be3c345b1c8fe47..78953c6d17e55228186c387a081310fb5d5b8c0c 100644 (file)
@@ -22,35 +22,50 @@ static const struct firmware rtl838x_8380_fw;
 static const struct firmware rtl838x_8214fc_fw;
 static const struct firmware rtl838x_8218b_fw;
 
+int rtl930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
+int rtl930x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val);
 
 static int read_phy(u32 port, u32 page, u32 reg, u32 *val)
-{
-       if (soc_info.family == RTL8390_FAMILY_ID)
-               return rtl839x_read_phy(port, page, reg, val);
-       else
+{      switch (soc_info.family) {
+       case RTL8380_FAMILY_ID:
                return rtl838x_read_phy(port, page, reg, val);
+       case RTL8390_FAMILY_ID:
+               return rtl839x_read_phy(port, page, reg, val);
+       case RTL9300_FAMILY_ID:
+               return rtl930x_read_phy(port, page, reg, val);
+       case RTL9310_FAMILY_ID:
+               return rtl931x_read_phy(port, page, reg, val);
+       }
+       return -1;
 }
 
 static int write_phy(u32 port, u32 page, u32 reg, u32 val)
 {
-       if (soc_info.family == RTL8390_FAMILY_ID)
-               return rtl839x_write_phy(port, page, reg, val);
-       else
+       switch (soc_info.family) {
+       case RTL8380_FAMILY_ID:
                return rtl838x_write_phy(port, page, reg, val);
+       case RTL8390_FAMILY_ID:
+               return rtl839x_write_phy(port, page, reg, val);
+       case RTL9300_FAMILY_ID:
+               return rtl930x_write_phy(port, page, reg, val);
+       case RTL9310_FAMILY_ID:
+               return rtl931x_write_phy(port, page, reg, val);
+       }
+       return -1;
 }
 
-static void int_phy_on_off(int mac, bool on)
+static void rtl8380_int_phy_on_off(int mac, bool on)
 {
        u32 val;
 
        read_phy(mac, 0, 0, &val);
        if (on)
-               write_phy(mac, 0, 0, val & ~(1 << 11));
+               write_phy(mac, 0, 0, val & ~BIT(11));
        else
-               write_phy(mac, 0, 0, val | (1 << 11));
+               write_phy(mac, 0, 0, val | BIT(11));
 }
 
-static void rtl8214fc_on_off(int mac, bool on)
+static void rtl8380_rtl8214fc_on_off(int mac, bool on)
 {
        u32 val;
 
@@ -58,25 +73,194 @@ static void rtl8214fc_on_off(int mac, bool on)
        write_phy(mac, 4095, 30, 3);
        read_phy(mac, 0, 16, &val);
        if (on)
-               write_phy(mac, 0, 16, val & ~(1 << 11));
+               write_phy(mac, 0, 16, val & ~BIT(11));
        else
-               write_phy(mac, 0, 16, val | (1 << 11));
+               write_phy(mac, 0, 16, val | BIT(11));
 
        /* copper ports */
        write_phy(mac, 4095, 30, 1);
        read_phy(mac, 0, 16, &val);
        if (on)
-               write_phy(mac, 0xa40, 16, val & ~(1 << 11));
+               write_phy(mac, 0xa40, 16, val & ~BIT(11));
        else
-               write_phy(mac, 0xa40, 16, val | (1 << 11));
+               write_phy(mac, 0xa40, 16, val | BIT(11));
 }
 
-static void phy_reset(int mac)
+static void rtl8380_phy_reset(int mac)
 {
        u32 val;
 
        read_phy(mac, 0, 0, &val);
-       write_phy(mac, 0, 0, val | (0x1 << 15));
+       write_phy(mac, 0, 0, val | BIT(15));
+}
+
+static void rtl8380_sds_rst(int mac)
+{
+       u32 offset = (mac == 24) ? 0 : 0x100;
+
+       sw_w32_mask(1 << 11, 0, RTL8380_SDS4_FIB_REG0 + offset);
+       sw_w32_mask(0x3, 0, RTL838X_SDS4_REG28 + offset);
+       sw_w32_mask(0x3, 0x3, RTL838X_SDS4_REG28 + offset);
+       sw_w32_mask(0, 0x1 << 6, RTL838X_SDS4_DUMMY0 + offset);
+       sw_w32_mask(0x1 << 6, 0, RTL838X_SDS4_DUMMY0 + offset);
+       pr_info("SERDES reset: %d\n", mac);
+}
+
+/*
+ * Reset the SerDes by powering it off and set a new operations mode
+ * of the SerDes. 0x1f is off. Other modes are
+ * 0x01: QSGMII                0x04: 1000BX_FIBER      0x05: FIBER100
+ * 0x06: QSGMII                0x09: RSGMII            0x0d: USXGMII
+ * 0x10: XSGMII                0x12: HISGMII           0x16: 2500Base_X
+ * 0x17: RXAUI_LITE    0x19: RXAUI_PLUS        0x1a: 10G Base-R
+ * 0x1b: 10GR1000BX_AUTO                       0x1f: OFF
+ */
+void rtl9300_sds_rst(int sds_num, u32 mode)
+{
+       // The access registers for SDS_MODE_SEL and the LSB for each SDS within
+       u16 regs[] = { 0x0194, 0x0194, 0x0194, 0x0194, 0x02a0, 0x02a0, 0x02a0, 0x02a0,
+                      0x02A4, 0x02A4, 0x0198, 0x0198 };
+       u8  lsb[]  = { 0, 6, 12, 18, 0, 6, 12, 18, 0, 6, 0, 6};
+
+       pr_info("SerDes: %s %d\n", __func__, mode);
+       if (sds_num < 0 || sds_num > 11) {
+               pr_err("Wrong SerDes number: %d\n", sds_num);
+               return;
+       }
+
+       sw_w32_mask(0x1f << lsb[sds_num], 0x1f << lsb[sds_num], regs[sds_num]);
+       mdelay(10);
+
+       sw_w32_mask(0x1f << lsb[sds_num], mode << lsb[sds_num], regs[sds_num]);
+       mdelay(10);
+
+       pr_info("SDS: 194:%08x 198:%08x 2a0:%08x 2a4:%08x\n",
+               sw_r32(0x194), sw_r32(0x198), sw_r32(0x2a0), sw_r32(0x2a4));
+}
+
+/*
+ * On the RTL839x family of SoCs with inbuilt SerDes, these SerDes are accessed through
+ * a 2048 bit register that holds the contents of the PHY being simulated by the SoC.
+ */
+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;
+       }
+
+       /*
+        * Register RTL839X_SDS12_13_XSG0 is 2048 bit broad, the MSB (bit 15) of the
+        * 0th PHY register is bit 1023 (in byte 0x80). Because PHY-registers are 16
+        * bit broad, we offset by reg << 1. In the SoC 2 registers are stored in
+        * one 32 bit register.
+        */
+       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;
+}
+
+/*
+ * On the RTL930x family of SoCs, the internal SerDes are accessed through an IO
+ * register which simulates commands to an internal MDIO bus.
+ */
+int rtl930x_read_sds_phy(int phy_addr, int page, int phy_reg)
+{
+       int i;
+       u32 cmd = phy_addr << 2 | page << 7 | phy_reg << 13 | 1;
+
+       pr_info("%s: phy_addr %d, phy_reg: %d\n", __func__, phy_addr, phy_reg);
+       sw_w32(cmd, RTL930X_SDS_INDACS_CMD);
+
+       for (i = 0; i < 100; i++) {
+               if (!(sw_r32(RTL930X_SDS_INDACS_CMD) & 0x1))
+                       break;
+               mdelay(1);
+       }
+
+       if (i >= 100)
+               return -EIO;
+
+       pr_info("%s: returning %04x\n", __func__, sw_r32(RTL930X_SDS_INDACS_DATA) & 0xffff);
+       return sw_r32(RTL930X_SDS_INDACS_DATA) & 0xffff;
+}
+
+int rtl930x_write_sds_phy(int phy_addr, int page, int phy_reg, u16 v)
+{
+       int i;
+       u32 cmd;
+
+       sw_w32(v, RTL930X_SDS_INDACS_DATA);
+       cmd = phy_addr << 2 | page << 7 | phy_reg << 13 | 0x3;
+
+       for (i = 0; i < 100; i++) {
+               if (!(sw_r32(RTL930X_SDS_INDACS_CMD) & 0x1))
+                       break;
+               mdelay(1);
+       }
+
+       if (i >= 100)
+               return -EIO;
+
+       return 0;
+}
+
+/*
+ * On the RTL838x SoCs, the internal SerDes is accessed through direct access to
+ * standard PHY registers, where a 32 bit register holds a 16 bit word as found
+ * in a standard page 0 of a PHY
+ */
+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(RTL838X_SDS4_FIB_REG0 + 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
@@ -123,6 +307,149 @@ static int rtl8393_read_status(struct phy_device *phydev)
 
        return err;
 }
+static int rtl8226_read_page(struct phy_device *phydev)
+{
+       return __phy_read(phydev, 0x1f);
+}
+
+static int rtl8226_write_page(struct phy_device *phydev, int page)
+{
+       return __phy_write(phydev, 0x1f, page);
+}
+
+static int rtl8226_read_status(struct phy_device *phydev)
+{
+       int ret = 0, i;
+       u32 val;
+       int port = phydev->mdio.addr;
+
+// TODO: ret = genphy_read_status(phydev);
+//     if (ret < 0) {
+//             pr_info("%s: genphy_read_status failed\n", __func__);
+//             return ret;
+//     }
+
+       // Link status must be read twice
+       for (i = 0; i < 2; i++) {
+               rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA402, &val);
+       }
+       phydev->link = val & BIT(2) ? 1 : 0;
+       if (!phydev->link)
+               goto out;
+
+       // Read duplex status
+       ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA434, &val);
+       if (ret)
+               goto out;
+       phydev->duplex = !!(val & BIT(3));
+
+       // Read speed
+       ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA434, &val);
+       switch (val & 0x0630) {
+       case 0x0000:
+               phydev->speed = SPEED_10;
+               break;
+       case 0x0010:
+               phydev->speed = SPEED_100;
+               break;
+       case 0x0020:
+               phydev->speed = SPEED_1000;
+               break;
+       case 0x0200:
+               phydev->speed = SPEED_10000;
+               break;
+       case 0x0210:
+               phydev->speed = SPEED_2500;
+               break;
+       case 0x0220:
+               phydev->speed = SPEED_5000;
+               break;
+       default:
+               break;
+       }
+out:
+       return ret;
+}
+
+static int rtl8266_advertise_aneg(struct phy_device *phydev)
+{
+       int ret = 0;
+       u32 v;
+       int port = phydev->mdio.addr;
+
+       pr_info("In %s\n", __func__);
+
+       ret = rtl930x_read_mmd_phy(port, MMD_AN, 16, &v);
+       if (ret)
+               goto out;
+
+       v |= BIT(5); // HD 10M
+       v |= BIT(6); // FD 10M
+       v |= BIT(7); // HD 100M
+       v |= BIT(8); // FD 100M
+
+       ret = rtl930x_write_mmd_phy(port, MMD_AN, 16, v);
+
+       // Allow 1GBit
+       ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA412, &v);
+       if (ret)
+               goto out;
+       v |= BIT(9); // FD 1000M
+
+       ret = rtl930x_write_mmd_phy(port, MMD_VEND2, 0xA412, v);
+       if (ret)
+               goto out;
+
+       // Allow 2.5G
+       ret = rtl930x_read_mmd_phy(port, MMD_AN, 32, &v);
+       if (ret)
+               goto out;
+
+       v |= BIT(7);
+       ret = rtl930x_write_mmd_phy(port, MMD_AN, 32, v);
+
+out:
+       return ret;
+}
+
+
+static int rtl8226_config_aneg(struct phy_device *phydev)
+{
+       int ret = 0;
+       u32 v;
+       int port = phydev->mdio.addr;
+
+       pr_info("In %s\n", __func__);
+       if (phydev->autoneg == AUTONEG_ENABLE) {
+               ret = rtl8266_advertise_aneg(phydev);
+               if (ret)
+                       goto out;
+               // AutoNegotiationEnable
+               ret = rtl930x_read_mmd_phy(port, MMD_AN, 0, &v);
+               if (ret)
+                       goto out;
+
+               v |= BIT(12); // Enable AN
+               ret = rtl930x_write_mmd_phy(port, MMD_AN, 0, v);
+               if (ret)
+                       goto out;
+
+               // RestartAutoNegotiation
+               ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA400, &v);
+               if (ret)
+                       goto out;
+               v |= BIT(9);
+
+               ret = rtl930x_write_mmd_phy(port, MMD_VEND2, 0xA400, v);
+       }
+
+       pr_info("%s: Ret is already: %d\n", __func__, ret);
+//     TODO: ret = __genphy_config_aneg(phydev, ret);
+
+out:
+       pr_info("%s: And ret is now: %d\n", __func__, ret);
+       return ret;
+}
 
 static struct fw_header *rtl838x_request_fw(struct phy_device *phydev,
                                            const struct firmware *fw,
@@ -234,9 +561,9 @@ static int rtl8380_configure_int_rtl8218b(struct phy_device *phydev)
 
        read_phy(mac, 0, 0, &val);
        if (val & (1 << 11))
-               int_phy_on_off(mac, true);
+               rtl8380_int_phy_on_off(mac, true);
        else
-               phy_reset(mac);
+               rtl8380_phy_reset(mac);
        msleep(100);
 
        /* Ready PHY for patch */
@@ -326,9 +653,9 @@ static int rtl8380_configure_ext_rtl8218b(struct phy_device *phydev)
 
        read_phy(mac, 0, 0, &val);
        if (val & (1 << 11))
-               int_phy_on_off(mac, true);
+               rtl8380_int_phy_on_off(mac, true);
        else
-               phy_reset(mac);
+               rtl8380_phy_reset(mac);
        msleep(100);
 
        /* Get Chip revision */
@@ -517,6 +844,26 @@ static int rtl8218b_write_mmd(struct phy_device *phydev,
        return rtl838x_write_mmd_phy(addr, devnum, regnum, val);
 }
 
+static int rtl8226_read_mmd(struct phy_device *phydev, int devnum, u16 regnum)
+{
+       int port = phydev->mdio.addr;  // the SoC translates port addresses to PHY addr
+       int err;
+       u32 val;
+
+       err = rtl930x_read_mmd_phy(port, devnum, regnum, &val);
+
+       if (err)
+               return err;
+       return val;
+}
+
+static int rtl8226_write_mmd(struct phy_device *phydev, int devnum, u16 regnum, u16 val)
+{
+       int port = phydev->mdio.addr; // the SoC translates port addresses to PHY addr
+
+       return rtl930x_write_mmd_phy(port, devnum, regnum, val);
+}
+
 static void rtl8380_rtl8214fc_media_set(int mac, bool set_fibre)
 {
        int base = mac - (mac % 4);
@@ -654,7 +1001,7 @@ static void rtl8218b_eee_set_u_boot(int port, bool enable)
 }
 
 // TODO: unused
-static void rtl8380_rtl8218b_eee_set(int port, bool enable)
+void rtl8380_rtl8218b_eee_set(int port, bool enable)
 {
        u32 val;
        bool an_enabled;
@@ -720,7 +1067,7 @@ static int rtl8218b_get_eee(struct phy_device *phydev,
 }
 
 // TODO: unused
-static void rtl8380_rtl8218b_green_set(int mac, bool enable)
+void rtl8380_rtl8218b_green_set(int mac, bool enable)
 {
        u32 val;
 
@@ -744,7 +1091,7 @@ static void rtl8380_rtl8218b_green_set(int mac, bool enable)
 }
 
 // TODO: unused
-static int rtl8380_rtl8214fc_get_green(struct phy_device *phydev, struct ethtool_eee *e)
+int rtl8380_rtl8214fc_get_green(struct phy_device *phydev, struct ethtool_eee *e)
 {
        u32 val;
        int addr = phydev->mdio.addr;
@@ -886,9 +1233,9 @@ static int rtl8380_configure_rtl8214fc(struct phy_device *phydev)
 
        read_phy(mac, 0, 16, &val);
        if (val & (1 << 11))
-               rtl8214fc_on_off(mac, true);
+               rtl8380_rtl8214fc_on_off(mac, true);
        else
-               phy_reset(mac);
+               rtl8380_phy_reset(mac);
 
        msleep(100);
        write_phy(mac, 0, 30, 0x0001);
@@ -1143,6 +1490,45 @@ static int rtl8390_configure_serdes(struct phy_device *phydev)
        return 0;
 }
 
+int rtl9300_configure_serdes(struct phy_device *phydev)
+{
+       struct device *dev = &phydev->mdio.dev;
+       int phy_addr = phydev->mdio.addr;
+       int sds_num = 0;
+       int v;
+
+       phydev_info(phydev, "Configuring internal RTL9300 SERDES\n");
+
+       switch (phy_addr) {
+       case 26:
+               sds_num = 8;
+               break;
+       case 27:
+               sds_num = 9;
+               break;
+       default:
+               dev_err(dev, "Not a SerDes PHY\n");
+               return -EINVAL;
+       }
+
+       /* Set default Medium to fibre */
+       v = rtl930x_read_sds_phy(sds_num, 0x1f, 11);
+       if (v < 0) {
+               dev_err(dev, "Cannot access SerDes PHY %d\n", phy_addr);
+               return -EINVAL;
+       }
+       v |= BIT(2);
+       rtl930x_write_sds_phy(sds_num, 0x1f, 11, v);
+
+       // TODO: this needs to be configurable via ethtool/.dts
+       pr_info("Setting 10G/1000BX auto fibre medium\n");
+       rtl9300_sds_rst(sds_num, 0x1b);
+
+       // TODO: Apply patch set for fibre type
+
+       return 0;
+}
+
 static int rtl8214fc_phy_probe(struct phy_device *phydev)
 {
        struct device *dev = &phydev->mdio.dev;
@@ -1232,6 +1618,43 @@ static int rtl8218b_int_phy_probe(struct phy_device *phydev)
        return 0;
 }
 
+static int rtl8218d_phy_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);
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->name = "RTL8218D";
+
+       /* All base addresses of the PHYs start at multiples of 8 */
+       if (!(addr % 8)) {
+               /* Configuration must be done while patching still possible */
+// TODO:               return configure_rtl8218d(phydev);
+       }
+       return 0;
+}
+
+static int rtl8226_phy_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);
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->name = "RTL8226";
+
+       return 0;
+}
+
 static int rtl838x_serdes_probe(struct phy_device *phydev)
 {
        struct device *dev = &phydev->mdio.dev;
@@ -1299,6 +1722,26 @@ static int rtl8390_serdes_probe(struct phy_device *phydev)
        return rtl8390_configure_generic(phydev);
 }
 
+static int rtl9300_serdes_probe(struct phy_device *phydev)
+{
+       struct device *dev = &phydev->mdio.dev;
+       struct rtl838x_phy_priv *priv;
+       int addr = phydev->mdio.addr;
+
+       if (soc_info.family != RTL9300_FAMILY_ID)
+               return -ENODEV;
+
+       if (addr < 24)
+               return -ENODEV;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->name = "RTL9300 Serdes";
+       return rtl9300_configure_serdes(phydev);
+}
+
 static struct phy_driver rtl83xx_phy_driver[] = {
        {
                PHY_ID_MATCH_MODEL(PHY_ID_RTL8214C),
@@ -1340,6 +1783,29 @@ static struct phy_driver rtl83xx_phy_driver[] = {
                .set_eee        = rtl8218b_set_eee,
                .get_eee        = rtl8218b_get_eee,
        },
+       {
+               PHY_ID_MATCH_MODEL(PHY_ID_RTL8218D),
+               .name           = "REALTEK RTL8218D",
+               .features       = PHY_GBIT_FEATURES,
+               .probe          = rtl8218d_phy_probe,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
+               .set_loopback   = genphy_loopback,
+       },      {
+               PHY_ID_MATCH_MODEL(PHY_ID_RTL8226),
+               .name           = "REALTEK RTL8226",
+               .features       = PHY_GBIT_FEATURES,
+               .probe          = rtl8226_phy_probe,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
+               .set_loopback   = genphy_loopback,
+               .read_mmd       = rtl8226_read_mmd,
+               .write_mmd      = rtl8226_write_mmd,
+               .read_page      = rtl8226_read_page,
+               .write_page     = rtl8226_write_page,
+               .read_status    = rtl8226_read_status,
+               .config_aneg    = rtl8226_config_aneg,
+       },
        {
                PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_I),
                .name           = "Realtek RTL8218B (internal)",
@@ -1383,7 +1849,16 @@ static struct phy_driver rtl83xx_phy_driver[] = {
                .suspend        = genphy_suspend,
                .resume         = genphy_resume,
                .set_loopback   = genphy_loopback,
-       }
+       },
+       {
+               PHY_ID_MATCH_MODEL(PHY_ID_RTL9300_I),
+               .name           = "REALTEK RTL9300 SERDES",
+               .features       = PHY_GBIT_FIBRE_FEATURES,
+               .probe          = rtl9300_serdes_probe,
+               .suspend        = genphy_suspend,
+               .resume         = genphy_resume,
+               .set_loopback   = genphy_loopback,
+       },
 };
 
 module_phy_driver(rtl83xx_phy_driver);
index 7af35a358314cdcf81f2227048e6aeceb858f5c8..031ec8a0e91c2c71851656ffdc697b402115c36d 100644 (file)
@@ -28,13 +28,33 @@ struct __attribute__ ((__packed__)) fw_header {
 #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_RTL8226         0x001cc838
 #define PHY_ID_RTL8390_GENERIC 0x001ccab0
 #define PHY_ID_RTL8393_I       0x001c8393
+#define PHY_ID_RTL9300_I       0x70d03106
 
-#define RTL839X_SDS12_13_XSG0                  (0xB800)
+// PHY MMD devices
+#define MMD_AN         7
+#define MMD_VEND2      31
 
+/* Registers of the internal Serdes of the 8380 */
 #define RTL838X_SDS_MODE_SEL                   (0x0028)
 #define RTL838X_SDS_CFG_REG                    (0x0034)
 #define RTL838X_INT_MODE_CTRL                  (0x005c)
 #define RTL838X_DMY_REG31                      (0x3b28)
+
+#define RTL8380_SDS4_FIB_REG0                  (0xF800)
+#define RTL838X_SDS4_REG28                     (0xef80)
+#define RTL838X_SDS4_DUMMY0                    (0xef8c)
+#define RTL838X_SDS5_EXT_REG6                  (0xf18c)
+#define RTL838X_SDS4_FIB_REG0                  (RTL838X_SDS4_REG28 + 0x880)
+#define RTL838X_SDS5_FIB_REG0                  (RTL838X_SDS4_REG28 + 0x980)
+
+/* Registers of the internal SerDes of the RTL8390 */
+#define RTL839X_SDS12_13_XSG0                  (0xB800)
+
+/* Registers of the internal Serdes of the 9300 */
+#define RTL930X_SDS_INDACS_CMD                 (0x03B0)
+#define RTL930X_SDS_INDACS_DATA                        (0x03B4)