mirror of
git://git.openwrt.org/openwrt/openwrt.git
synced 2024-12-25 08:02:32 +00:00
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>
This commit is contained in:
parent
3c4063f715
commit
121ef9ddc9
@ -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)
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user