The realtek.c patch:
--- a/drivers/net/phy/realtek.c
+++ b/drivers/net/phy/realtek.c
@@ -15,6 +15,7 @@
*/
#include <linux/phy.h>
#include <linux/module.h>
+#include <linux/delay.h>
#define RTL821x_PHYSR 0x11
#define RTL821x_PHYSR_DUPLEX BIT(13)
@@ -51,6 +52,15 @@
#define RTL8366RB_POWER_SAVE 0x15
#define RTL8366RB_POWER_SAVE_ON BIT(12)
+#define RTL8211FS_FIBER_ESR 0x0F
+#define RTL8211FS_MODE_MASK 0xC000
+
+enum rtl8211fs_mode {
+ RTL8211FS_MODE_COPPER = 0,
+ RTL8211FS_MODE_FIBER = 1,
+ RTL8211FS_MODE_AUTO = 2
+};
+
#define RTL_SUPPORTS_5000FULL BIT(14)
#define RTL_SUPPORTS_2500FULL BIT(13)
#define RTL_SUPPORTS_10000FULL BIT(0)
@@ -127,21 +137,115 @@
return err;
}
-static int rtl8211f_config_init(struct phy_device *phydev)
+static int rtl8211f_wait_for_ready(struct phy_device *phydev)
+{
+ udelay(1000);
+ return 0;
+}
+
+static int rtl8211f_phy_read(struct phy_device *phydev, u16 page, u32 regnum)
{
- u16 txdly = 0, rxdly = 0;
u16 val;
+ u16 old_page;
- /* set to page 0xa43 */
- phy_write(phydev, RTL8211F_PAGE_SELECT, 0xa43);
+ old_page = phy_read(phydev, RTL8211F_PAGE_SELECT);
- val = phy_read(phydev, RTL8211F_PHYCR1);
- val |= RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_XTAL_OFF;
+ phy_write(phydev, RTL8211F_PAGE_SELECT, page);
+ rtl8211f_wait_for_ready(phydev);
+ val = phy_read(phydev, regnum);
+ rtl8211f_wait_for_ready(phydev);
- phy_write(phydev, RTL8211F_PHYCR1, val);
+ phy_write(phydev, RTL8211F_PAGE_SELECT, old_page);
+ rtl8211f_wait_for_ready(phydev);
- /* restore to default page 0 */
- phy_write(phydev, RTL8211F_PAGE_SELECT, 0x0);
+ return val;
+}
+
+static int rtl8211f_phy_write(struct phy_device *phydev, u16 page, u32 regnum, u16 val)
+{
+ int ret;
+ u16 old_page;
+
+ old_page = phy_read(phydev, RTL8211F_PAGE_SELECT);
+
+ phy_write(phydev, RTL8211F_PAGE_SELECT, page);
+ rtl8211f_wait_for_ready(phydev);
+ phy_write(phydev, regnum, val);
+ rtl8211f_wait_for_ready(phydev);
+
+ phy_write(phydev, RTL8211F_PAGE_SELECT, old_page);
+ ret = rtl8211f_wait_for_ready(phydev);
+ return ret;
+}
+
+static int rtl8211f_phy_soft_reset(struct phy_device *phydev)
+{
+ u16 val;
+
+ dev_info(&phydev->dev, "soft reset.\n");
+
+ val = rtl8211f_phy_read(phydev, 0, 0);
+
+ val |= BIT(15);
+
+ phy_write(phydev, 0, val);
+
+ return rtl8211f_wait_for_ready(phydev);
+}
+
+static int rtl8211f_mode(struct phy_device *phydev)
+{
+ u16 val;
+
+ val = phy_read(phydev, RTL8211FS_FIBER_ESR);
+ val &= RTL8211FS_MODE_MASK;
+
+ if(val)
+ return RTL8211FS_MODE_FIBER;
+ else
+ return RTL8211FS_MODE_COPPER;
+}
+
+static int rtl8211f_set_mode(struct phy_device *phydev, enum rtl8211fs_mode mode)
+{
+ u16 val;
+
+ dev_info(&phydev->dev, "force set %s mode !", mode == RTL8211FS_MODE_AUTO? "auto": mode == RTL8211FS_MODE_FIBER? "fiber" : "copper");
+
+ val = rtl8211f_phy_read(phydev, 0xd40, 16);
+
+ val &= (u16)(~RTL8211FS_MODE_MASK);
+
+ val |= mode;
+
+ rtl8211f_phy_write(phydev, 0xd40, 16, val);
+
+ return rtl8211f_wait_for_ready(phydev);
+}
+
+static void rtl8211f_disable_broadcast(struct phy_device *phydev)
+{
+ u16 phy_val;
+
+ /* Disable response on MDIO addr 0 (!) */
+ phy_val = rtl8211f_phy_read(phydev, 0xa43, 24);
+ phy_val &= ~(1<<13); // PHYAD_0 Disable
+ rtl8211f_phy_write(phydev, 0xa43, 24, phy_val);
+}
+
+static int rtl8211f_set_rgmii_delay(struct phy_device *phydev)
+{
+ u16 txdly = 0, rxdly = 0;
+ u16 val;
+
+ if (rtl8211f_mode(phydev) == RTL8211FS_MODE_FIBER) {
+ dev_info(&phydev->dev, "phy working in fiber mode");
+ //rtl8211f_phy_write(phydev, 0, 0, 0x9100);
+ } else if (rtl8211f_mode(phydev) == RTL8211FS_MODE_COPPER) {
+ dev_info(&phydev->dev, "phy working in copper mode");
+ } else if (rtl8211f_mode(phydev) == RTL8211FS_MODE_AUTO) {
+ dev_info(&phydev->dev, "phy working in auto mode");
+ }
switch (phydev->interface) {
case PHY_INTERFACE_MODE_RGMII:
@@ -150,8 +254,8 @@
break;
case PHY_INTERFACE_MODE_RGMII_RXID:
- txdly = 1;
- rxdly = 0;
+ txdly = 0;
+ rxdly = 1;
break;
case PHY_INTERFACE_MODE_RGMII_TXID:
@@ -168,29 +272,125 @@
return 0;
}
- /* set to page 0xd08 */
- phy_write(phydev, RTL8211F_PAGE_SELECT, 0xd08);
-
- if(txdly) {
- /* enable TXDLY */
- val = phy_read(phydev, 0x15);
+ /* Rx Delay */
+ val = rtl8211f_phy_read(phydev, 0xd08, 0x15);
+ val &= ~RTL8211F_RX_DELAY;
+ if(rxdly) {
val |= RTL8211F_RX_DELAY;
- phy_write(phydev, 0x15, val);
}
+ rtl8211f_phy_write(phydev, 0xd08, 0x15, val);
- if(rxdly) {
- /* enable RXDLY */
- val = phy_read(phydev, 0x11);
+ /* Tx Delay */
+ val = rtl8211f_phy_read(phydev, 0xd08, 0x11);
+ val &= ~RTL8211F_TX_DELAY;
+ if(txdly) {
val |= RTL8211F_TX_DELAY;
- phy_write(phydev, 0x11, val);
}
- /* restore to default page 0 */
- phy_write(phydev, RTL8211F_PAGE_SELECT, 0x0);
+ rtl8211f_phy_write(phydev, 0xd08, 0x11, val);
+
+ dev_info(&phydev->dev, "RGMII Rx delay %s, Tx delay %s.\n", rxdly?"enabled":"disable", txdly?"enabled":"disable");
+
+ return 0;
+}
+
+int rtl8211f_read_status(struct phy_device *phydev)
+{
+ u16 val;
+
+ if (rtl8211f_mode(phydev) == RTL8211FS_MODE_COPPER)
+ return genphy_read_status(phydev);
+
+ val = rtl8211f_phy_read(phydev, 0xdf0, 0x10);
+
+ /* for fiber status */
+ if(val & BIT(12)) {
+ phydev->pause = 0;
+ phydev->asym_pause = 0;
+
+ phydev->link = 1;
+ phydev->duplex = DUPLEX_FULL;
+ if(!(val & BIT(5)) && (val & BIT(4))) {
+ phydev->speed = SPEED_100;
+ } else if ((val & BIT(5)) && !(val & BIT(4))) {
+ phydev->speed = SPEED_1000;
+ } else {
+ phydev->speed = SPEED_1000;
+ }
+ } else
+ phydev->link = 0;
return 0;
}
+int rtl8211f_config_aneg(struct phy_device *phydev)
+{
+ rtl8211f_set_rgmii_delay(phydev);
+
+ return genphy_config_aneg(phydev);
+}
+
+static int rtl8211f_config_init(struct phy_device *phydev)
+{
+ u16 phy_val;
+ const char *force_mode;
+ int ret;
+
+ struct device *dev = &phydev->dev;
+ struct device_node *of_node = dev->of_node;
+
+ if (!of_node && dev->parent->of_node)
+ of_node = dev->parent->of_node;
+
+ rtl8211f_disable_broadcast(phydev);
+
+ ret = of_property_read_string(of_node, "realtek,mode", &force_mode);
+
+ if (!ret) {
+ if (!strcmp(force_mode, "fiber"))
+ rtl8211f_set_mode(phydev, RTL8211FS_MODE_FIBER);
+ else if (!strcmp(force_mode, "copper"))
+ rtl8211f_set_mode(phydev, RTL8211FS_MODE_COPPER);
+ else
+ rtl8211f_set_mode(phydev, RTL8211FS_MODE_AUTO);
+ }
+
+ /* FIXME: no reset need? */
+ rtl8211f_phy_soft_reset(phydev);
+
+ rtl8211f_set_rgmii_delay(phydev);
+#if 0
+ /* Disable Green Ethernet */
+ rtl8211f_phy_write(phydev, 0xa43, 27, 0x8011);
+ rtl8211f_phy_write(phydev, 0xa43, 27, 0x573f);
+
+ /* Enable flow control by default */
+ phy_val = rtl8211f_phy_read(phydev, 0xa43, 4);
+ phy_val |= (1<<10); // Enable pause ability
+ rtl8211f_phy_write(phydev, 0xa43, 4, phy_val);
+#endif
+ if (rtl8211f_mode(phydev) == RTL8211FS_MODE_FIBER) {
+ dev_info(&phydev->dev, "phy working in fiber mode");
+ } else if (rtl8211f_mode(phydev) == RTL8211FS_MODE_COPPER) {
+ dev_info(&phydev->dev, "phy working in copper mode");
+ } else if (rtl8211f_mode(phydev) == RTL8211FS_MODE_AUTO) {
+ dev_info(&phydev->dev, "phy working in auto mode");
+ }
+
+#if 0
+ /*
+ * The rtl8211fs not working under fiber/utp mode, comment it.
+ */
+
+ val = rtl8211f_phy_read(phydev, 0xa43, RTL8211F_PHYCR1);
+ val |= RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_XTAL_OFF;
+ rtl8211f_phy_write(phydev, 0xa43, RTL8211F_PHYCR1, val);
+#endif
+ return 0;
+}
+
static struct phy_driver realtek_drvs[] = {
{
.phy_id = 0x00008201,
@@ -244,11 +444,11 @@
.phy_id_mask = 0x001fffff,
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
- .config_aneg = &genphy_config_aneg,
+ .config_aneg = &rtl8211f_config_aneg,
.config_init = &rtl8211f_config_init,
- .read_status = &genphy_read_status,
.ack_interrupt = &rtl8211f_ack_interrupt,
.config_intr = &rtl8211f_config_intr,
+ .read_status = &rtl8211f_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
.driver = { .owner = THIS_MODULE },