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;
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
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,
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 */
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 */
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);
}
// 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;
}
// TODO: unused
-static void rtl8380_rtl8218b_green_set(int mac, bool enable)
+void rtl8380_rtl8218b_green_set(int mac, bool enable)
{
u32 val;
}
// 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;
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);
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;
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;
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),
.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)",
.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);