realtek: Add support for clause45 PHYs

This adds support for the MMD access registers the RTL-SoCs use to access clause 45
PHYs via mdio.
This new interface is used to add EEE-support for the RTL8226

Signed-off-by: Birger Koblitz <git@birger-koblitz.de>
This commit is contained in:
Birger Koblitz 2021-05-01 09:18:48 +02:00 committed by Petr Štetiar
parent 515d9c85f2
commit 408990464c
6 changed files with 564 additions and 235 deletions

View File

@ -313,8 +313,29 @@
#define RTL839X_SMI_PORT_POLLING_CTRL (0x03fc) #define RTL839X_SMI_PORT_POLLING_CTRL (0x03fc)
#define RTL839X_PHYREG_ACCESS_CTRL (0x03DC) #define RTL839X_PHYREG_ACCESS_CTRL (0x03DC)
#define RTL839X_PHYREG_CTRL (0x03E0) #define RTL839X_PHYREG_CTRL (0x03E0)
#define RTL839X_PHYREG_PORT_CTRL(p) (0x03E4 + ((p >> 5) << 2)) #define RTL839X_PHYREG_PORT_CTRL (0x03E4)
#define RTL839X_PHYREG_DATA_CTRL (0x03F0) #define RTL839X_PHYREG_DATA_CTRL (0x03F0)
#define RTL839X_PHYREG_MMD_CTRL (0x3F4)
#define RTL930X_SMI_GLB_CTRL (0xCA00)
#define RTL930X_SMI_POLL_CTRL (0xca90)
#define RTL930X_SMI_PORT0_15_POLLING_SEL (0xCA08)
#define RTL930X_SMI_PORT16_27_POLLING_SEL (0xCA0C)
#define RTL930X_SMI_PORT0_5_ADDR (0xCB80)
#define RTL930X_SMI_ACCESS_PHY_CTRL_0 (0xCB70)
#define RTL930X_SMI_ACCESS_PHY_CTRL_1 (0xCB74)
#define RTL930X_SMI_ACCESS_PHY_CTRL_2 (0xCB78)
#define RTL930X_SMI_ACCESS_PHY_CTRL_3 (0xCB7C)
#define RTL931X_SMI_GLB_CTRL1 (0x0CBC)
#define RTL931X_SMI_GLB_CTRL0 (0x0CC0)
#define RTL931X_SMI_PORT_POLLING_CTRL (0x0CCC)
#define RTL931X_SMI_INDRT_ACCESS_CTRL_0 (0x0C00)
#define RTL931X_SMI_INDRT_ACCESS_CTRL_1 (0x0C04)
#define RTL931X_SMI_INDRT_ACCESS_CTRL_2 (0x0C08)
#define RTL931X_SMI_INDRT_ACCESS_CTRL_3 (0x0C10)
#define RTL931X_SMI_INDRT_ACCESS_BC_PHYID_CTRL (0x0C14)
#define RTL931X_SMI_INDRT_ACCESS_MMD_CTRL (0xC18)
#define RTL930X_SMI_GLB_CTRL (0xCA00) #define RTL930X_SMI_GLB_CTRL (0xCA00)
#define RTL930X_SMI_POLL_CTRL (0xca90) #define RTL930X_SMI_POLL_CTRL (0xca90)

View File

@ -513,6 +513,79 @@ timeout:
return -ETIMEDOUT; return -ETIMEDOUT;
} }
/*
* Read an mmd register of a PHY
*/
int rtl838x_read_mmd_phy(u32 port, u32 addr, u32 reg, u32 *val)
{
u32 v;
mutex_lock(&smi_lock);
if (rtl838x_smi_wait_op(10000))
goto timeout;
sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0);
mdelay(10);
sw_w32_mask(0xffff0000, port << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2);
v = addr << 16 | reg;
sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_3);
/* mmd-access | read | cmd-start */
v = 1 << 1 | 0 << 2 | 1;
sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1);
if (rtl838x_smi_wait_op(10000))
goto timeout;
*val = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_2) & 0xffff;
mutex_unlock(&smi_lock);
return 0;
timeout:
mutex_unlock(&smi_lock);
return -ETIMEDOUT;
}
/*
* Write to an mmd register of a PHY
*/
int rtl838x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val)
{
u32 v;
pr_debug("MMD write: port %d, dev %d, reg %d, val %x\n", port, addr, reg, val);
val &= 0xffff;
mutex_lock(&smi_lock);
if (rtl838x_smi_wait_op(10000))
goto timeout;
sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0);
mdelay(10);
sw_w32_mask(0xffff0000, val << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2);
sw_w32_mask(0x1f << 16, addr << 16, RTL838X_SMI_ACCESS_PHY_CTRL_3);
sw_w32_mask(0xffff, reg, RTL838X_SMI_ACCESS_PHY_CTRL_3);
/* mmd-access | write | cmd-start */
v = 1 << 1 | 1 << 2 | 1;
sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1);
if (rtl838x_smi_wait_op(10000))
goto timeout;
mutex_unlock(&smi_lock);
return 0;
timeout:
mutex_unlock(&smi_lock);
return -ETIMEDOUT;
}
void rtl8380_get_version(struct rtl838x_switch_priv *priv) void rtl8380_get_version(struct rtl838x_switch_priv *priv)
{ {
u32 rw_save, info_save; u32 rw_save, info_save;

View File

@ -321,6 +321,7 @@ int rtl8390_sds_power(int mac, int val)
return 0; return 0;
} }
int rtl839x_read_phy(u32 port, u32 page, u32 reg, u32 *val) int rtl839x_read_phy(u32 port, u32 page, u32 reg, u32 *val)
{ {
u32 v; u32 v;
@ -358,10 +359,9 @@ int rtl839x_write_phy(u32 port, u32 page, u32 reg, u32 val)
return -ENOTSUPP; return -ENOTSUPP;
mutex_lock(&smi_lock); mutex_lock(&smi_lock);
/* Clear both port registers */
sw_w32(0, RTL839X_PHYREG_PORT_CTRL(0)); // Set PHY to access
sw_w32(0, RTL839X_PHYREG_PORT_CTRL(0) + 4); rtl839x_set_port_reg_le(BIT_ULL(port), RTL839X_PHYREG_PORT_CTRL);
sw_w32_mask(0, BIT(port), RTL839X_PHYREG_PORT_CTRL(port));
sw_w32_mask(0xffff0000, val << 16, RTL839X_PHYREG_DATA_CTRL); sw_w32_mask(0xffff0000, val << 16, RTL839X_PHYREG_DATA_CTRL);
@ -383,6 +383,68 @@ int rtl839x_write_phy(u32 port, u32 page, u32 reg, u32 val)
return err; return err;
} }
/*
* Read an mmd register of the PHY
*/
int rtl839x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val)
{
int err = 0;
u32 v;
mutex_lock(&smi_lock);
// Set PHY to access
sw_w32_mask(0xffff << 16, port << 16, RTL839X_PHYREG_DATA_CTRL);
// Set MMD device number and register to write to
sw_w32(devnum << 16 | (regnum & 0xffff), RTL839X_PHYREG_MMD_CTRL);
v = BIT(2) | BIT(0); // MMD-access | EXEC
sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL);
do {
v = sw_r32(RTL839X_PHYREG_ACCESS_CTRL);
} while (v & BIT(0));
// There is no error-checking via BIT 1 of v, as it does not seem to be set correctly
*val = (sw_r32(RTL839X_PHYREG_DATA_CTRL) & 0xffff);
pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, *val, err);
mutex_unlock(&smi_lock);
return err;
}
/*
* Write to an mmd register of the PHY
*/
int rtl839x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val)
{
int err = 0;
u32 v;
mutex_lock(&smi_lock);
// Set PHY to access
rtl839x_set_port_reg_le(BIT_ULL(port), RTL839X_PHYREG_PORT_CTRL);
// Set data to write
sw_w32_mask(0xffff << 16, val << 16, RTL839X_PHYREG_DATA_CTRL);
// Set MMD device number and register to write to
sw_w32(devnum << 16 | (regnum & 0xffff), RTL839X_PHYREG_MMD_CTRL);
v = BIT(3) | BIT(2) | BIT(0); // WRITE | MMD-access | EXEC
sw_w32(v, RTL839X_PHYREG_ACCESS_CTRL);
do {
v = sw_r32(RTL839X_PHYREG_ACCESS_CTRL);
} while (v & BIT(0));
pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, val, err);
mutex_unlock(&smi_lock);
return err;
}
void rtl8390_get_version(struct rtl838x_switch_priv *priv) void rtl8390_get_version(struct rtl838x_switch_priv *priv)
{ {
u32 info; u32 info;

View File

@ -382,7 +382,6 @@ int rtl9300_sds_power(int mac, int val)
return 0; return 0;
} }
int rtl930x_write_phy(u32 port, u32 page, u32 reg, u32 val) int rtl930x_write_phy(u32 port, u32 page, u32 reg, u32 val)
{ {
u32 v; u32 v;
@ -445,7 +444,6 @@ int rtl930x_read_phy(u32 port, u32 page, u32 reg, u32 *val)
return err; return err;
} }
/* /*
* Write to an mmd register of the PHY * Write to an mmd register of the PHY
*/ */

View File

@ -175,6 +175,7 @@ static u64 rtl931x_read_cam(int idx, struct rtl838x_l2_entry *e)
// TODO: Implement // TODO: Implement
return entry; return entry;
} }
irqreturn_t rtl931x_switch_irq(int irq, void *dev_id) irqreturn_t rtl931x_switch_irq(int irq, void *dev_id)
{ {
struct dsa_switch *ds = dev_id; struct dsa_switch *ds = dev_id;
@ -199,7 +200,6 @@ irqreturn_t rtl931x_switch_irq(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
int rtl931x_write_phy(u32 port, u32 page, u32 reg, u32 val) int rtl931x_write_phy(u32 port, u32 page, u32 reg, u32 val)
{ {
u32 v; u32 v;
@ -264,6 +264,73 @@ int rtl931x_read_phy(u32 port, u32 page, u32 reg, u32 *val)
return 0; return 0;
} }
/*
* Read an mmd register of the PHY
*/
int rtl931x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val)
{
int err = 0;
u32 v;
int type = 1; // TODO: For C45 PHYs need to set to 2
mutex_lock(&smi_lock);
// Set PHY to access via port-number
sw_w32(port << 5, RTL931X_SMI_INDRT_ACCESS_BC_PHYID_CTRL);
// Set MMD device number and register to write to
sw_w32(devnum << 16 | (regnum & 0xffff), RTL931X_SMI_INDRT_ACCESS_MMD_CTRL);
v = type << 2 | BIT(0); // MMD-access-type | EXEC
sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0);
do {
v = sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0);
} while (v & BIT(0));
// There is no error-checking via BIT 1 of v, as it does not seem to be set correctly
*val = (sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_3) & 0xffff);
pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, *val, err);
mutex_unlock(&smi_lock);
return err;
}
/*
* Write to an mmd register of the PHY
*/
int rtl931x_write_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 val)
{
int err = 0;
u32 v;
int type = 1; // TODO: For C45 PHYs need to set to 2
mutex_lock(&smi_lock);
// Set PHY to access via port-number
sw_w32(port << 5, RTL931X_SMI_INDRT_ACCESS_BC_PHYID_CTRL);
// Set data to write
sw_w32_mask(0xffff << 16, val << 16, RTL931X_SMI_INDRT_ACCESS_CTRL_3);
// Set MMD device number and register to write to
sw_w32(devnum << 16 | (regnum & 0xffff), RTL931X_SMI_INDRT_ACCESS_MMD_CTRL);
v = BIT(4) | type << 2 | BIT(0); // WRITE | MMD-access-type | EXEC
sw_w32(v, RTL931X_SMI_INDRT_ACCESS_CTRL_0);
do {
v = sw_r32(RTL931X_SMI_INDRT_ACCESS_CTRL_0);
} while (v & BIT(0));
pr_debug("%s: port %d, regnum: %x, val: %x (err %d)\n", __func__, port, regnum, val, err);
mutex_unlock(&smi_lock);
return err;
}
void rtl931x_print_matrix(void) void rtl931x_print_matrix(void)
{ {
volatile u64 *ptr = RTL838X_SW_BASE + RTL839X_PORT_ISO_CTRL(0); volatile u64 *ptr = RTL838X_SW_BASE + RTL839X_PORT_ISO_CTRL(0);

View File

@ -18,12 +18,26 @@
extern struct rtl83xx_soc_info soc_info; extern struct rtl83xx_soc_info soc_info;
extern struct mutex smi_lock; extern struct mutex smi_lock;
/*
* This lock protects the state of the SoC automatically polling the PHYs over the SMI
* bus to detect e.g. link and media changes. For operations on the PHYs such as
* patching or other configuration changes such as EEE, polling needs to be disabled
* since otherwise these operations may fails or lead to unpredictable results.
*/
DEFINE_MUTEX(poll_lock);
static const struct firmware rtl838x_8380_fw; static const struct firmware rtl838x_8380_fw;
static const struct firmware rtl838x_8214fc_fw; static const struct firmware rtl838x_8214fc_fw;
static const struct firmware rtl838x_8218b_fw; static const struct firmware rtl838x_8218b_fw;
int rtl838x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
int rtl838x_write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val);
int rtl839x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
int rtl839x_write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val);
int rtl930x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val); 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); int rtl930x_write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val);
int rtl931x_read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val);
int rtl931x_write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val);
static int read_phy(u32 port, u32 page, u32 reg, u32 *val) static int read_phy(u32 port, u32 page, u32 reg, u32 *val)
{ switch (soc_info.family) { { switch (soc_info.family) {
@ -54,6 +68,93 @@ static int write_phy(u32 port, u32 page, u32 reg, u32 val)
return -1; return -1;
} }
static int read_mmd_phy(u32 port, u32 devnum, u32 regnum, u32 *val)
{
switch (soc_info.family) {
case RTL8380_FAMILY_ID:
return rtl838x_read_mmd_phy(port, devnum, regnum, val);
case RTL8390_FAMILY_ID:
return rtl839x_read_mmd_phy(port, devnum, regnum, val);
case RTL9300_FAMILY_ID:
return rtl930x_read_mmd_phy(port, devnum, regnum, val);
case RTL9310_FAMILY_ID:
return rtl931x_read_mmd_phy(port, devnum, regnum, val);
}
return -1;
}
int write_mmd_phy(u32 port, u32 devnum, u32 reg, u32 val)
{
switch (soc_info.family) {
case RTL8380_FAMILY_ID:
return rtl838x_write_mmd_phy(port, devnum, reg, val);
case RTL8390_FAMILY_ID:
return rtl839x_write_mmd_phy(port, devnum, reg, val);
case RTL9300_FAMILY_ID:
return rtl930x_write_mmd_phy(port, devnum, reg, val);
case RTL9310_FAMILY_ID:
return rtl931x_write_mmd_phy(port, devnum, reg, val);
}
return -1;
}
static u64 disable_polling(int port)
{
u64 saved_state;
mutex_lock(&poll_lock);
switch (soc_info.family) {
case RTL8380_FAMILY_ID:
saved_state = sw_r32(RTL838X_SMI_POLL_CTRL);
sw_w32_mask(BIT(port), 0, RTL838X_SMI_POLL_CTRL);
break;
case RTL8390_FAMILY_ID:
saved_state = sw_r32(RTL839X_SMI_PORT_POLLING_CTRL + 4);
saved_state <<= 32;
saved_state |= sw_r32(RTL839X_SMI_PORT_POLLING_CTRL);
sw_w32_mask(BIT(port % 32), 0,
RTL839X_SMI_PORT_POLLING_CTRL + ((port >> 5) << 2));
break;
case RTL9300_FAMILY_ID:
saved_state = sw_r32(RTL930X_SMI_POLL_CTRL);
sw_w32_mask(BIT(port), 0, RTL930X_SMI_POLL_CTRL);
break;
case RTL9310_FAMILY_ID:
pr_warn("%s not implemented for RTL931X\n", __func__);
break;
}
mutex_unlock(&poll_lock);
return saved_state;
}
static int resume_polling(u64 saved_state)
{
mutex_lock(&poll_lock);
switch (soc_info.family) {
case RTL8380_FAMILY_ID:
sw_w32(saved_state, RTL838X_SMI_POLL_CTRL);
break;
case RTL8390_FAMILY_ID:
sw_w32(saved_state >> 32, RTL839X_SMI_PORT_POLLING_CTRL + 4);
sw_w32(saved_state, RTL839X_SMI_PORT_POLLING_CTRL);
break;
case RTL9300_FAMILY_ID:
sw_w32(saved_state, RTL930X_SMI_POLL_CTRL);
break;
case RTL9310_FAMILY_ID:
pr_warn("%s not implemented for RTL931X\n", __func__);
break;
}
mutex_unlock(&poll_lock);
return 0;
}
static void rtl8380_int_phy_on_off(int mac, bool on) static void rtl8380_int_phy_on_off(int mac, bool on)
{ {
u32 val; u32 val;
@ -94,18 +195,6 @@ static void rtl8380_phy_reset(int mac)
write_phy(mac, 0, 0, val | BIT(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 * Reset the SerDes by powering it off and set a new operations mode
* of the SerDes. 0x1f is off. Other modes are * of the SerDes. 0x1f is off. Other modes are
@ -307,6 +396,7 @@ static int rtl8393_read_status(struct phy_device *phydev)
return err; return err;
} }
static int rtl8226_read_page(struct phy_device *phydev) static int rtl8226_read_page(struct phy_device *phydev)
{ {
return __phy_read(phydev, 0x1f); return __phy_read(phydev, 0x1f);
@ -331,20 +421,20 @@ static int rtl8226_read_status(struct phy_device *phydev)
// Link status must be read twice // Link status must be read twice
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA402, &val); read_mmd_phy(port, MMD_VEND2, 0xA402, &val);
} }
phydev->link = val & BIT(2) ? 1 : 0; phydev->link = val & BIT(2) ? 1 : 0;
if (!phydev->link) if (!phydev->link)
goto out; goto out;
// Read duplex status // Read duplex status
ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA434, &val); ret = read_mmd_phy(port, MMD_VEND2, 0xA434, &val);
if (ret) if (ret)
goto out; goto out;
phydev->duplex = !!(val & BIT(3)); phydev->duplex = !!(val & BIT(3));
// Read speed // Read speed
ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA434, &val); ret = read_mmd_phy(port, MMD_VEND2, 0xA434, &val);
switch (val & 0x0630) { switch (val & 0x0630) {
case 0x0000: case 0x0000:
phydev->speed = SPEED_10; phydev->speed = SPEED_10;
@ -371,7 +461,7 @@ out:
return ret; return ret;
} }
static int rtl8266_advertise_aneg(struct phy_device *phydev) static int rtl8226_advertise_aneg(struct phy_device *phydev)
{ {
int ret = 0; int ret = 0;
u32 v; u32 v;
@ -379,7 +469,7 @@ static int rtl8266_advertise_aneg(struct phy_device *phydev)
pr_info("In %s\n", __func__); pr_info("In %s\n", __func__);
ret = rtl930x_read_mmd_phy(port, MMD_AN, 16, &v); ret = read_mmd_phy(port, MMD_AN, 16, &v);
if (ret) if (ret)
goto out; goto out;
@ -388,31 +478,30 @@ static int rtl8266_advertise_aneg(struct phy_device *phydev)
v |= BIT(7); // HD 100M v |= BIT(7); // HD 100M
v |= BIT(8); // FD 100M v |= BIT(8); // FD 100M
ret = rtl930x_write_mmd_phy(port, MMD_AN, 16, v); ret = write_mmd_phy(port, MMD_AN, 16, v);
// Allow 1GBit // Allow 1GBit
ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA412, &v); ret = read_mmd_phy(port, MMD_VEND2, 0xA412, &v);
if (ret) if (ret)
goto out; goto out;
v |= BIT(9); // FD 1000M v |= BIT(9); // FD 1000M
ret = rtl930x_write_mmd_phy(port, MMD_VEND2, 0xA412, v); ret = write_mmd_phy(port, MMD_VEND2, 0xA412, v);
if (ret) if (ret)
goto out; goto out;
// Allow 2.5G // Allow 2.5G
ret = rtl930x_read_mmd_phy(port, MMD_AN, 32, &v); ret = read_mmd_phy(port, MMD_AN, 32, &v);
if (ret) if (ret)
goto out; goto out;
v |= BIT(7); v |= BIT(7);
ret = rtl930x_write_mmd_phy(port, MMD_AN, 32, v); ret = write_mmd_phy(port, MMD_AN, 32, v);
out: out:
return ret; return ret;
} }
static int rtl8226_config_aneg(struct phy_device *phydev) static int rtl8226_config_aneg(struct phy_device *phydev)
{ {
int ret = 0; int ret = 0;
@ -421,26 +510,26 @@ static int rtl8226_config_aneg(struct phy_device *phydev)
pr_info("In %s\n", __func__); pr_info("In %s\n", __func__);
if (phydev->autoneg == AUTONEG_ENABLE) { if (phydev->autoneg == AUTONEG_ENABLE) {
ret = rtl8266_advertise_aneg(phydev); ret = rtl8226_advertise_aneg(phydev);
if (ret) if (ret)
goto out; goto out;
// AutoNegotiationEnable // AutoNegotiationEnable
ret = rtl930x_read_mmd_phy(port, MMD_AN, 0, &v); ret = read_mmd_phy(port, MMD_AN, 0, &v);
if (ret) if (ret)
goto out; goto out;
v |= BIT(12); // Enable AN v |= BIT(12); // Enable AN
ret = rtl930x_write_mmd_phy(port, MMD_AN, 0, v); ret = write_mmd_phy(port, MMD_AN, 0, v);
if (ret) if (ret)
goto out; goto out;
// RestartAutoNegotiation // RestartAutoNegotiation
ret = rtl930x_read_mmd_phy(port, MMD_VEND2, 0xA400, &v); ret = read_mmd_phy(port, MMD_VEND2, 0xA400, &v);
if (ret) if (ret)
goto out; goto out;
v |= BIT(9); v |= BIT(9);
ret = rtl930x_write_mmd_phy(port, MMD_VEND2, 0xA400, v); ret = write_mmd_phy(port, MMD_VEND2, 0xA400, v);
} }
pr_info("%s: Ret is already: %d\n", __func__, ret); pr_info("%s: Ret is already: %d\n", __func__, ret);
@ -451,6 +540,68 @@ out:
return ret; return ret;
} }
static int rtl8226_get_eee(struct phy_device *phydev,
struct ethtool_eee *e)
{
u32 val;
int addr = phydev->mdio.addr;
pr_debug("In %s, port %d, was enabled: %d\n", __func__, addr, e->eee_enabled);
read_mmd_phy(addr, MMD_AN, 60, &val);
if (e->eee_enabled) {
e->eee_enabled = !!(val & BIT(1));
if (!e->eee_enabled) {
read_mmd_phy(addr, MMD_AN, 62, &val);
e->eee_enabled = !!(val & BIT(0));
}
}
pr_debug("%s: enabled: %d\n", __func__, e->eee_enabled);
return 0;
}
static int rtl8226_set_eee(struct phy_device *phydev, struct ethtool_eee *e)
{
int port = phydev->mdio.addr;
u64 poll_state;
bool an_enabled;
u32 val;
pr_info("In %s, port %d, enabled %d\n", __func__, port, e->eee_enabled);
poll_state = disable_polling(port);
// Remember aneg state
read_mmd_phy(port, MMD_AN, 0, &val);
an_enabled = !!(val & BIT(12));
// Setup 100/1000MBit
read_mmd_phy(port, MMD_AN, 60, &val);
if (e->eee_enabled)
val |= 0x6;
else
val &= 0x6;
write_mmd_phy(port, MMD_AN, 60, val);
// Setup 2.5GBit
read_mmd_phy(port, MMD_AN, 62, &val);
if (e->eee_enabled)
val |= 0x1;
else
val &= 0x1;
write_mmd_phy(port, MMD_AN, 62, val);
// RestartAutoNegotiation
read_mmd_phy(port, MMD_VEND2, 0xA400, &val);
val |= BIT(9);
write_mmd_phy(port, MMD_VEND2, 0xA400, val);
resume_polling(poll_state);
return 0;
}
static struct fw_header *rtl838x_request_fw(struct phy_device *phydev, static struct fw_header *rtl838x_request_fw(struct phy_device *phydev,
const struct firmware *fw, const struct firmware *fw,
const char *name) const char *name)
@ -750,79 +901,6 @@ static int rtl8218b_ext_match_phy_device(struct phy_device *phydev)
return phydev->phy_id == PHY_ID_RTL8218B_E; return phydev->phy_id == PHY_ID_RTL8218B_E;
} }
/*
* Read an mmd register of the PHY
*/
static int rtl83xx_read_mmd_phy(u32 port, u32 addr, u32 reg, u32 *val)
{
u32 v;
mutex_lock(&smi_lock);
if (rtl838x_smi_wait_op(10000))
goto timeout;
sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0);
mdelay(10);
sw_w32_mask(0xffff0000, port << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2);
v = addr << 16 | reg;
sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_3);
/* mmd-access | read | cmd-start */
v = 1 << 1 | 0 << 2 | 1;
sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1);
if (rtl838x_smi_wait_op(10000))
goto timeout;
*val = sw_r32(RTL838X_SMI_ACCESS_PHY_CTRL_2) & 0xffff;
mutex_unlock(&smi_lock);
return 0;
timeout:
mutex_unlock(&smi_lock);
return -ETIMEDOUT;
}
/*
* Write to an mmd register of the PHY
*/
static int rtl838x_write_mmd_phy(u32 port, u32 addr, u32 reg, u32 val)
{
u32 v;
pr_debug("MMD write: port %d, dev %d, reg %d, val %x\n", port, addr, reg, val);
val &= 0xffff;
mutex_lock(&smi_lock);
if (rtl838x_smi_wait_op(10000))
goto timeout;
sw_w32(1 << port, RTL838X_SMI_ACCESS_PHY_CTRL_0);
mdelay(10);
sw_w32_mask(0xffff0000, val << 16, RTL838X_SMI_ACCESS_PHY_CTRL_2);
sw_w32_mask(0x1f << 16, addr << 16, RTL838X_SMI_ACCESS_PHY_CTRL_3);
sw_w32_mask(0xffff, reg, RTL838X_SMI_ACCESS_PHY_CTRL_3);
/* mmd-access | write | cmd-start */
v = 1 << 1 | 1 << 2 | 1;
sw_w32(v, RTL838X_SMI_ACCESS_PHY_CTRL_1);
if (rtl838x_smi_wait_op(10000))
goto timeout;
mutex_unlock(&smi_lock);
return 0;
timeout:
mutex_unlock(&smi_lock);
return -ETIMEDOUT;
}
static int rtl8218b_read_mmd(struct phy_device *phydev, static int rtl8218b_read_mmd(struct phy_device *phydev,
int devnum, u16 regnum) int devnum, u16 regnum)
{ {
@ -830,7 +908,7 @@ static int rtl8218b_read_mmd(struct phy_device *phydev,
u32 val; u32 val;
int addr = phydev->mdio.addr; int addr = phydev->mdio.addr;
ret = rtl83xx_read_mmd_phy(addr, devnum, regnum, &val); ret = read_mmd_phy(addr, devnum, regnum, &val);
if (ret) if (ret)
return ret; return ret;
return val; return val;
@ -850,8 +928,7 @@ static int rtl8226_read_mmd(struct phy_device *phydev, int devnum, u16 regnum)
int err; int err;
u32 val; u32 val;
err = rtl930x_read_mmd_phy(port, devnum, regnum, &val); err = read_mmd_phy(port, devnum, regnum, &val);
if (err) if (err)
return err; return err;
return val; return val;
@ -861,7 +938,7 @@ static int rtl8226_write_mmd(struct phy_device *phydev, int devnum, u16 regnum,
{ {
int port = phydev->mdio.addr; // the SoC translates port addresses to PHY addr int port = phydev->mdio.addr; // the SoC translates port addresses to PHY addr
return rtl930x_write_mmd_phy(port, devnum, regnum, val); return write_mmd_phy(port, devnum, regnum, val);
} }
static void rtl8380_rtl8214fc_media_set(int mac, bool set_fibre) static void rtl8380_rtl8214fc_media_set(int mac, bool set_fibre)
@ -957,90 +1034,46 @@ static int rtl8214fc_get_port(struct phy_device *phydev)
return PORT_MII; return PORT_MII;
} }
static void rtl8218b_eee_set_u_boot(int port, bool enable) /*
{ * Enable EEE on the RTL8218B PHYs
u32 val; * The method used is not the preferred way (which would be based on the MAC-EEE state,
bool an_enabled; * but the only way that works since the kernel first enables EEE in the MAC
* and then sets up the PHY. The MAC-based approach would require the oppsite.
/* Set GPHY page to copper */ */
write_phy(port, 0, 30, 0x0001); void rtl8218d_eee_set(int port, bool enable)
read_phy(port, 0, 0, &val);
an_enabled = val & (1 << 12);
if (enable) {
/* 100/1000M EEE Capability */
write_phy(port, 0, 13, 0x0007);
write_phy(port, 0, 14, 0x003C);
write_phy(port, 0, 13, 0x4007);
write_phy(port, 0, 14, 0x0006);
read_phy(port, 0x0A43, 25, &val);
val |= 1 << 4;
write_phy(port, 0x0A43, 25, val);
} else {
/* 100/1000M EEE Capability */
write_phy(port, 0, 13, 0x0007);
write_phy(port, 0, 14, 0x003C);
write_phy(port, 0, 13, 0x0007);
write_phy(port, 0, 14, 0x0000);
read_phy(port, 0x0A43, 25, &val);
val &= ~(1 << 4);
write_phy(port, 0x0A43, 25, val);
}
/* Restart AN if enabled */
if (an_enabled) {
read_phy(port, 0, 0, &val);
val |= (1 << 12) | (1 << 9);
write_phy(port, 0, 0, val);
}
/* GPHY page back to auto*/
write_phy(port, 0xa42, 29, 0);
}
// TODO: unused
void rtl8380_rtl8218b_eee_set(int port, bool enable)
{ {
u32 val; u32 val;
bool an_enabled; bool an_enabled;
pr_debug("In %s %d, enable %d\n", __func__, port, enable); pr_debug("In %s %d, enable %d\n", __func__, port, enable);
/* Set GPHY page to copper */ /* Set GPHY page to copper */
write_phy(port, 0xa42, 29, 0x0001); write_phy(port, 0xa42, 30, 0x0001);
read_phy(port, 0, 0, &val); read_phy(port, 0, 0, &val);
an_enabled = val & (1 << 12); an_enabled = val & BIT(12);
/* MAC based EEE */ /* Enable 100M (bit 1) / 1000M (bit 2) EEE */
read_phy(port, 0xa43, 25, &val); read_mmd_phy(port, 7, 60, &val);
val &= ~(1 << 5); val |= BIT(2) | BIT(1);
write_phy(port, 0xa43, 25, val); write_mmd_phy(port, 7, 60, enable ? 0x6 : 0);
/* 100M / 1000M EEE */
if (enable)
rtl838x_write_mmd_phy(port, 7, 60, 0x6);
else
rtl838x_write_mmd_phy(port, 7, 60, 0);
/* 500M EEE ability */ /* 500M EEE ability */
read_phy(port, 0xa42, 20, &val); read_phy(port, 0xa42, 20, &val);
if (enable) if (enable)
val |= 1 << 7; val |= BIT(7);
else else
val &= ~(1 << 7); val &= ~BIT(7);
write_phy(port, 0xa42, 20, val); write_phy(port, 0xa42, 20, val);
/* Restart AN if enabled */ /* Restart AN if enabled */
if (an_enabled) { if (an_enabled) {
read_phy(port, 0, 0, &val); read_phy(port, 0, 0, &val);
val |= (1 << 12) | (1 << 9); val |= BIT(9);
write_phy(port, 0, 0, val); write_phy(port, 0, 0, val);
} }
/* GPHY page back to auto*/ /* GPHY page back to auto*/
write_phy(port, 0xa42, 29, 0); write_phy(port, 0xa42, 30, 0);
} }
static int rtl8218b_get_eee(struct phy_device *phydev, static int rtl8218b_get_eee(struct phy_device *phydev,
@ -1049,16 +1082,21 @@ static int rtl8218b_get_eee(struct phy_device *phydev,
u32 val; u32 val;
int addr = phydev->mdio.addr; int addr = phydev->mdio.addr;
pr_debug("In %s, port %d\n", __func__, addr); pr_debug("In %s, port %d, was enabled: %d\n", __func__, addr, e->eee_enabled);
/* Set GPHY page to copper */ /* Set GPHY page to copper */
write_phy(addr, 0xa42, 29, 0x0001); write_phy(addr, 0xa42, 29, 0x0001);
rtl83xx_read_mmd_phy(addr, 7, 60, &val); read_phy(addr, 7, 60, &val);
if (e->eee_enabled && (!!(val & (1 << 7)))) if (e->eee_enabled) {
e->eee_enabled = !!(val & (1 << 7)); // Verify vs MAC-based EEE
else e->eee_enabled = !!(val & BIT(7));
e->eee_enabled = 0; if (!e->eee_enabled) {
read_phy(addr, 0x0A43, 25, &val);
e->eee_enabled = !!(val & BIT(4));
}
}
pr_debug("%s: enabled: %d\n", __func__, e->eee_enabled);
/* GPHY page to auto */ /* GPHY page to auto */
write_phy(addr, 0xa42, 29, 0x0000); write_phy(addr, 0xa42, 29, 0x0000);
@ -1066,49 +1104,24 @@ static int rtl8218b_get_eee(struct phy_device *phydev,
return 0; return 0;
} }
// TODO: unused static int rtl8218d_get_eee(struct phy_device *phydev,
void rtl8380_rtl8218b_green_set(int mac, bool enable) struct ethtool_eee *e)
{
u32 val;
/* Set GPHY page to copper */
write_phy(mac, 0xa42, 29, 0x0001);
write_phy(mac, 0, 27, 0x8011);
read_phy(mac, 0, 28, &val);
if (enable) {
val |= 1 << 9;
write_phy(mac, 0, 27, 0x8011);
write_phy(mac, 0, 28, val);
} else {
val &= ~(1 << 9);
write_phy(mac, 0, 27, 0x8011);
write_phy(mac, 0, 28, val);
}
/* GPHY page to auto */
write_phy(mac, 0xa42, 29, 0x0000);
}
// TODO: unused
int rtl8380_rtl8214fc_get_green(struct phy_device *phydev, struct ethtool_eee *e)
{ {
u32 val; u32 val;
int addr = phydev->mdio.addr; int addr = phydev->mdio.addr;
pr_debug("In %s %d\n", __func__, addr); pr_debug("In %s, port %d, was enabled: %d\n", __func__, addr, e->eee_enabled);
/* Set GPHY page to copper */
write_phy(addr, 0xa42, 29, 0x0001);
write_phy(addr, 0, 27, 0x8011); /* Set GPHY page to copper */
read_phy(addr, 0, 28, &val); write_phy(addr, 0xa42, 30, 0x0001);
if (e->eee_enabled && (!!(val & (1 << 9))))
e->eee_enabled = !!(val & (1 << 9)); read_phy(addr, 7, 60, &val);
else if (e->eee_enabled)
e->eee_enabled = 0; e->eee_enabled = !!(val & BIT(7));
pr_debug("%s: enabled: %d\n", __func__, e->eee_enabled);
/* GPHY page to auto */ /* GPHY page to auto */
write_phy(addr, 0xa42, 29, 0x0000); write_phy(addr, 0xa42, 30, 0x0000);
return 0; return 0;
} }
@ -1116,20 +1129,56 @@ int rtl8380_rtl8214fc_get_green(struct phy_device *phydev, struct ethtool_eee *e
static int rtl8214fc_set_eee(struct phy_device *phydev, static int rtl8214fc_set_eee(struct phy_device *phydev,
struct ethtool_eee *e) struct ethtool_eee *e)
{ {
u32 pollMask; u32 poll_state;
int addr = phydev->mdio.addr; int port = phydev->mdio.addr;
bool an_enabled;
u32 val;
pr_debug("In %s port %d, enabled %d\n", __func__, addr, e->eee_enabled); pr_debug("In %s port %d, enabled %d\n", __func__, port, e->eee_enabled);
if (rtl8380_rtl8214fc_media_is_fibre(addr)) { if (rtl8380_rtl8214fc_media_is_fibre(port)) {
netdev_err(phydev->attached_dev, "Port %d configured for FIBRE", addr); netdev_err(phydev->attached_dev, "Port %d configured for FIBRE", port);
return -ENOTSUPP; return -ENOTSUPP;
} }
pollMask = sw_r32(RTL838X_SMI_POLL_CTRL); poll_state = disable_polling(port);
sw_w32(0, RTL838X_SMI_POLL_CTRL);
rtl8218b_eee_set_u_boot(addr, (bool) e->eee_enabled); /* Set GPHY page to copper */
sw_w32(pollMask, RTL838X_SMI_POLL_CTRL); write_phy(port, 0xa42, 29, 0x0001);
// Get auto-negotiation status
read_phy(port, 0, 0, &val);
an_enabled = val & BIT(12);
pr_info("%s: aneg: %d\n", __func__, an_enabled);
read_phy(port, 0x0A43, 25, &val);
val &= ~BIT(5); // Use MAC-based EEE
write_phy(port, 0x0A43, 25, val);
/* Enable 100M (bit 1) / 1000M (bit 2) EEE */
write_phy(port, 7, 60, e->eee_enabled ? 0x6 : 0);
/* 500M EEE ability */
read_phy(port, 0xa42, 20, &val);
if (e->eee_enabled)
val |= BIT(7);
else
val &= ~BIT(7);
write_phy(port, 0xa42, 20, val);
/* Restart AN if enabled */
if (an_enabled) {
pr_info("%s: doing aneg\n", __func__);
read_phy(port, 0, 0, &val);
val |= BIT(9);
write_phy(port, 0, 0, val);
}
/* GPHY page back to auto*/
write_phy(port, 0xa42, 29, 0);
resume_polling(poll_state);
return 0; return 0;
} }
@ -1147,18 +1196,72 @@ static int rtl8214fc_get_eee(struct phy_device *phydev,
return rtl8218b_get_eee(phydev, e); return rtl8218b_get_eee(phydev, e);
} }
static int rtl8218b_set_eee(struct phy_device *phydev, static int rtl8218b_set_eee(struct phy_device *phydev, struct ethtool_eee *e)
struct ethtool_eee *e) {
int port = phydev->mdio.addr;
u64 poll_state;
u32 val;
bool an_enabled;
pr_info("In %s, port %d, enabled %d\n", __func__, port, e->eee_enabled);
poll_state = disable_polling(port);
/* Set GPHY page to copper */
write_phy(port, 0, 30, 0x0001);
read_phy(port, 0, 0, &val);
an_enabled = val & BIT(12);
if (e->eee_enabled) {
/* 100/1000M EEE Capability */
write_phy(port, 0, 13, 0x0007);
write_phy(port, 0, 14, 0x003C);
write_phy(port, 0, 13, 0x4007);
write_phy(port, 0, 14, 0x0006);
read_phy(port, 0x0A43, 25, &val);
val |= BIT(4);
write_phy(port, 0x0A43, 25, val);
} else {
/* 100/1000M EEE Capability */
write_phy(port, 0, 13, 0x0007);
write_phy(port, 0, 14, 0x003C);
write_phy(port, 0, 13, 0x0007);
write_phy(port, 0, 14, 0x0000);
read_phy(port, 0x0A43, 25, &val);
val &= ~BIT(4);
write_phy(port, 0x0A43, 25, val);
}
/* Restart AN if enabled */
if (an_enabled) {
read_phy(port, 0, 0, &val);
val |= BIT(9);
write_phy(port, 0, 0, val);
}
/* GPHY page back to auto*/
write_phy(port, 0xa42, 30, 0);
pr_info("%s done\n", __func__);
resume_polling(poll_state);
return 0;
}
static int rtl8218d_set_eee(struct phy_device *phydev, struct ethtool_eee *e)
{ {
u32 pollMask;
int addr = phydev->mdio.addr; int addr = phydev->mdio.addr;
u64 poll_state;
pr_debug("In %s, port %d, enabled %d\n", __func__, addr, e->eee_enabled); pr_info("In %s, port %d, enabled %d\n", __func__, addr, e->eee_enabled);
pollMask = sw_r32(RTL838X_SMI_POLL_CTRL); poll_state = disable_polling(addr);
sw_w32(0, RTL838X_SMI_POLL_CTRL);
rtl8218b_eee_set_u_boot(addr, (bool) e->eee_enabled); rtl8218d_eee_set(addr, (bool) e->eee_enabled);
sw_w32(pollMask, RTL838X_SMI_POLL_CTRL);
resume_polling(poll_state);
return 0; return 0;
} }
@ -1791,7 +1894,10 @@ static struct phy_driver rtl83xx_phy_driver[] = {
.suspend = genphy_suspend, .suspend = genphy_suspend,
.resume = genphy_resume, .resume = genphy_resume,
.set_loopback = genphy_loopback, .set_loopback = genphy_loopback,
}, { .set_eee = rtl8218d_set_eee,
.get_eee = rtl8218d_get_eee,
},
{
PHY_ID_MATCH_MODEL(PHY_ID_RTL8226), PHY_ID_MATCH_MODEL(PHY_ID_RTL8226),
.name = "REALTEK RTL8226", .name = "REALTEK RTL8226",
.features = PHY_GBIT_FEATURES, .features = PHY_GBIT_FEATURES,
@ -1805,6 +1911,8 @@ static struct phy_driver rtl83xx_phy_driver[] = {
.write_page = rtl8226_write_page, .write_page = rtl8226_write_page,
.read_status = rtl8226_read_status, .read_status = rtl8226_read_status,
.config_aneg = rtl8226_config_aneg, .config_aneg = rtl8226_config_aneg,
.set_eee = rtl8226_set_eee,
.get_eee = rtl8226_get_eee,
}, },
{ {
PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_I), PHY_ID_MATCH_MODEL(PHY_ID_RTL8218B_I),