aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux
diff options
context:
space:
mode:
authorBirger Koblitz <git@birger-koblitz.de>2021-01-21 15:12:36 +0100
committerPetr Štetiar <ynezz@true.cz>2021-01-26 15:06:50 +0100
commitd140b9fc23f6b58ce6be696844e216c91e62b4c5 (patch)
tree5230746fe92f45e0088fee52187b68fbb94aec55 /target/linux
parente1bca7c77d80b3f35f5045cd8983349819165f1e (diff)
downloadupstream-d140b9fc23f6b58ce6be696844e216c91e62b4c5.tar.gz
upstream-d140b9fc23f6b58ce6be696844e216c91e62b4c5.tar.bz2
upstream-d140b9fc23f6b58ce6be696844e216c91e62b4c5.zip
realtek: add internal and external SDS/PHYs of RTL9300 devices
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>
Diffstat (limited to 'target/linux')
-rw-r--r--target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c529
-rw-r--r--target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.h22
2 files changed, 523 insertions, 28 deletions
diff --git a/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c b/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c
index eba416934f..78953c6d17 100644
--- a/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c
+++ b/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.c
@@ -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),
@@ -1341,6 +1784,29 @@ static struct phy_driver rtl83xx_phy_driver[] = {
.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)",
.features = PHY_GBIT_FEATURES,
@@ -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);
diff --git a/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.h b/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.h
index 7af35a3583..031ec8a0e9 100644
--- a/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.h
+++ b/target/linux/realtek/files-5.4/drivers/net/phy/rtl83xx-phy.h
@@ -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)