kernel: realtek phy driver supports fiber

This commit is contained in:
Liangbin Lian 2024-10-17 15:57:53 +08:00
parent b95e6f8556
commit 9c3bb24409

View File

@ -0,0 +1,375 @@
From 01769b49c825399828844ad4f34176f6d5c9ae77 Mon Sep 17 00:00:00 2001
From: jjm2473 <1129525450@qq.com>
Date: Wed, 16 Oct 2024 17:43:55 +0800
Subject: [PATCH] net: phy: realtek: supports fiber
---
drivers/net/phy/realtek.c | 293 +++++++++++++++++++++++++++++++++++++-
1 file changed, 287 insertions(+), 6 deletions(-)
diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c
index b4879306b..1c4b0626a 100644
--- a/drivers/net/phy/realtek.c
+++ b/drivers/net/phy/realtek.c
@@ -8,6 +8,7 @@
* Copyright (c) 2004 Freescale Semiconductor, Inc.
*/
#include <linux/bitops.h>
+#include <linux/of.h>
#include <linux/phy.h>
#include <linux/module.h>
#include <linux/delay.h>
@@ -62,6 +63,139 @@ MODULE_DESCRIPTION("Realtek PHY driver");
MODULE_AUTHOR("Johnson Leung");
MODULE_LICENSE("GPL");
+struct reg_t {
+ u16 id;
+ u16 reg;
+ u16 page;
+};
+
+typedef enum __reg_idx_e {
+ UTP_BMCR,
+ UTP_BMSR,
+ UTP_PHYID1,
+ UTP_PHYID2,
+ UTP_ANAR,
+ UTP_ANLPAR, // 5
+ UTP_ANER,
+ UTP_ANNPTR,
+ UTP_ANNPRR,
+ UTP_GBCR,
+ UTP_GBSR, // 10
+ UTP_RSVD1,
+ UTP_RSVD2,
+ UTP_MACR,
+ UTP_MAADR,
+ UTP_GBESR, // 15
+ UTP_RSVD3,
+ UTP_RSVD4,
+ UTP_INER,
+ UTP_RSVD5,
+ UTP_RSVD6, // 20
+ UTP_RSVD7,
+ UTP_RSVD8,
+ UTP_RSVD9,
+ UTP_PHYCR1,
+ UTP_PHYCR2, // 25
+ UTP_PHYSR,
+ UTP_RSVD10,
+ UTP_RSVD11,
+ UTP_INSR,
+ UTP_RSVD12, // 30
+ UTP_PAGSR,
+ UTP_PHYSCR,
+ UTP_LCR,
+ UTP_EEELCR,
+ UTP_FLCR, // 35
+ UTP_MIICR,
+ UTP_INTBCR,
+ FIBER_BMCR,
+ FIBER_BMSR,
+ FIBER_PHYID1, // 40
+ FIBER_PHYID2,
+ FIBER_ANAR,
+ FIBER_ANLPAR,
+ FIBER_RSVD1,
+ FIBER_RSVD2, // 45
+ FIBER_RSVD3,
+ FIBER_RSVD4,
+ FIBER_RSVD5,
+ FIBER_RSVD6,
+ FIBER_RSVD7, // 50
+ FIBER_RSVD8,
+ FIBER_RSVD9,
+ FIBER_ESR,
+ FIBER_SSR,
+} reg_idx_e;
+
+struct reg_t reg[] = {
+ // Standard Register 0~15
+ {0,0x00,0x00},
+ {1,0x01,0x00},
+ {2,0x02,0x00},
+ {3,0x03,0x00},
+ {4,0x04,0x00},
+ {5,0x05,0x00},
+ {6,0x06,0x00},
+ {7,0x07,0x00},
+ {8,0x08,0x00},
+ {9,0x09,0x00},
+ {10,0x0a,0x00},
+ {11,0x0b,0x00},
+ {12,0x0c,0x00},
+ {13,0x0d,0x00},
+ {14,0x0e,0x00},
+ {15,0x0f,0x00},
+
+ {16,0x10,0xa42},
+ {17,0x11,0xa42},
+ {18,0x12,0xa42},
+ {19,0x13,0xa42},
+ {20,0x14,0xa42},
+ {21,0x15,0xa42},
+ {22,0x16,0xa42},
+ {23,0x17,0xa42},
+ {24,0x18,0xa43},
+ {25,0x19,0xa43},
+ {26,0x1a,0xa43},
+ {27,0x1b,0xa43},
+ {28,0x1c,0xa43},
+ {29,0x1d,0xa43},
+ {30,0x1e,0xa43},
+ {31,0x1f,0xa43},
+
+ {32,0x14,0xa46},
+ {33,0x10,0xd04},
+ {34,0x11,0xd04},
+ {35,0x12,0xd04},
+ {36,0x15,0xd08},
+ {37,0x16,0xd40},
+
+ {38,0x10,0xdc0},
+ {39,0x11,0xdc0},
+ {40,0x12,0xdc0},
+ {41,0x13,0xdc0},
+ {42,0x14,0xdc0},
+ {43,0x15,0xdc0},
+ {44,0x16,0xdc0},
+ {45,0x17,0xdc0},
+ {46,0x18,0xdc1},
+ {47,0x19,0xdc1},
+ {48,0x1a,0xdc1},
+ {49,0x1b,0xdc1},
+ {50,0x1c,0xdc1},
+ {51,0x1d,0xdc1},
+ {52,0x1e,0xdc1},
+ {53,0x1f,0xdc1},
+ {54,0x10,0xdf0},
+
+};
+
+struct rtl821x_priv {
+ u8 fiber_enabled;
+ u8 utp_link;
+ u8 fiber_link;
+};
+
static int rtl821x_read_page(struct phy_device *phydev)
{
return __phy_read(phydev, RTL821x_PAGE_SELECT);
@@ -72,6 +206,25 @@ static int rtl821x_write_page(struct phy_device *phydev, int page)
return __phy_write(phydev, RTL821x_PAGE_SELECT, page);
}
+static int rtl821x_probe(struct phy_device *phydev)
+{
+ struct device *dev = &phydev->mdio.dev;
+ struct rtl821x_priv *priv;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ if (of_property_read_bool(dev->of_node, "realtek,fiber")) {
+ priv->fiber_enabled = 1;
+ phydev_info(phydev, "Enable RTL8211FS Fiber Mode by DT.\n");
+ }
+
+ phydev->priv = priv;
+
+ return 0;
+}
+
static int rtl8201_ack_interrupt(struct phy_device *phydev)
{
int err;
@@ -178,13 +331,35 @@ static int rtl8211c_config_init(struct phy_device *phydev)
CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER);
}
+static int rtl8211f_led_init(struct phy_device *phydev)
+{
+ int oldpage;
+ /*switch to extension page 0xd04*/
+ oldpage = phy_select_page(phydev, 0x0d04);
+
+ if (oldpage >= 0) {
+ // disable ieee led config
+ __phy_write(phydev, reg[UTP_EEELCR].reg, 0x00);
+ // __phy_write(phydev, reg[UTP_FLCR].reg, 0xd3e9);
+ // __phy_write(phydev, reg[UTP_LCR].reg, 0x8910);
+ __phy_write(phydev, reg[UTP_LCR].reg, 0x616b);
+ }
+ phy_restore_page(phydev, oldpage, 0);
+
+ return 0;
+}
+
static int rtl8211f_config_init(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
+ struct rtl821x_priv *priv = phydev->priv;
u16 val_txdly, val_rxdly;
u16 val;
int ret;
+ if (priv->fiber_enabled)
+ rtl8211f_led_init(phydev);
+
val = RTL8211F_ALDPS_ENABLE | RTL8211F_ALDPS_PLL_OFF | RTL8211F_ALDPS_XTAL_OFF;
phy_modify_paged_changed(phydev, 0xa43, RTL8211F_PHYCR1, val, val);
@@ -248,17 +423,45 @@ static int rtl8211f_config_init(struct phy_device *phydev)
static int rtl821x_resume(struct phy_device *phydev)
{
- int ret;
+ int ret, value;
+ struct rtl821x_priv *priv = phydev->priv;
- ret = genphy_resume(phydev);
- if (ret < 0)
- return ret;
+ if (!priv->fiber_enabled || priv->utp_link) {
+ ret = genphy_resume(phydev);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (priv->fiber_enabled && priv->fiber_link) {
+ value = phy_read_paged(phydev, reg[FIBER_BMCR].page, reg[FIBER_BMCR].reg);
+ value &= ~BMCR_PDOWN;
+ phy_write_paged(phydev, reg[FIBER_BMCR].page, reg[FIBER_BMCR].reg, value);
+ }
msleep(20);
return 0;
}
+static int rtl8211f_suspend(struct phy_device *phydev)
+{
+ int ret,value;
+ struct rtl821x_priv *priv = phydev->priv;
+
+ if (!priv->fiber_enabled || priv->utp_link) {
+ ret = genphy_suspend(phydev);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (priv->fiber_enabled && priv->fiber_link) {
+ value = phy_read_paged(phydev, reg[FIBER_BMCR].page, reg[FIBER_BMCR].reg);
+ value |= BMCR_PDOWN;
+ phy_write_paged(phydev, reg[FIBER_BMCR].page, reg[FIBER_BMCR].reg, value);
+ }
+ return 0;
+}
+
static int rtl8211e_config_init(struct phy_device *phydev)
{
int ret = 0, oldpage;
@@ -335,6 +538,82 @@ static int rtl8366rb_config_init(struct phy_device *phydev)
return ret;
}
+static int rtl8211f_update_link(struct phy_device *phydev)
+{
+ int val;
+ int led_cfg;
+ struct rtl821x_priv *priv = phydev->priv;
+
+ priv->fiber_link = 0;
+ priv->utp_link = 0;
+
+ //led cfg
+ led_cfg = phy_read_paged(phydev, reg[UTP_FLCR].page, reg[UTP_FLCR].reg);
+ //serdes SSR
+ val = phy_read_paged(phydev, reg[FIBER_SSR].page, reg[FIBER_SSR].reg);
+ if (val & BIT(12)) {
+ priv->fiber_link = 1;
+ phydev->port = PORT_FIBRE;
+ led_cfg &= ~(0x03f << 10);
+ led_cfg |= BIT(12)|BIT(15);
+ } else {
+ val = phy_read_paged(phydev, reg[UTP_PHYSR].page, reg[UTP_PHYSR].reg);
+ if(val & BIT(2)){
+ priv->utp_link = 1;
+ phydev->port = PORT_TP;
+
+ //switch led to utp
+ led_cfg &= ~(0x03f << 10);
+ led_cfg |= BIT(13);
+ }
+ }
+
+ if (priv->fiber_link || priv->utp_link) {
+ phy_write_paged(phydev, reg[UTP_FLCR].page, reg[UTP_FLCR].reg, led_cfg);
+ }
+ return 0;
+}
+
+static int rtl8211f_fiber_read_status(struct phy_device *phydev)
+{
+ int bmcr;
+
+ bmcr = phy_read_paged(phydev, reg[FIBER_BMCR].page, reg[FIBER_BMCR].reg);
+
+ if (bmcr < 0)
+ return bmcr;
+
+ if (bmcr & BMCR_FULLDPLX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ if (bmcr & BMCR_SPEED1000)
+ phydev->speed = SPEED_1000;
+ else if (bmcr & BMCR_SPEED100)
+ phydev->speed = SPEED_100;
+ else
+ phydev->speed = SPEED_10;
+
+ phydev->pause = 0;
+ phydev->asym_pause = 0;
+
+ return 0;
+}
+
+static int rtl8211f_read_status(struct phy_device *phydev)
+{
+ struct rtl821x_priv *priv = phydev->priv;
+ if (priv->fiber_enabled) {
+ rtl8211f_update_link(phydev);
+ if (priv->fiber_link) {
+ phydev->link = 1;
+ return rtl8211f_fiber_read_status(phydev);
+ }
+ }
+ return genphy_read_status(phydev);
+}
+
/* get actual speed to cover the downshift case */
static int rtlgen_get_speed(struct phy_device *phydev)
{
@@ -632,11 +911,13 @@ static struct phy_driver realtek_drvs[] = {
.write_page = rtl821x_write_page,
}, {
PHY_ID_MATCH_EXACT(0x001cc916),
- .name = "RTL8211F Gigabit Ethernet",
+ .name = "RTL8211F(S) Gigabit Ethernet",
+ .probe = rtl821x_probe,
.config_init = &rtl8211f_config_init,
.ack_interrupt = &rtl8211f_ack_interrupt,
+ .read_status = &rtl8211f_read_status,
.config_intr = &rtl8211f_config_intr,
- .suspend = genphy_suspend,
+ .suspend = rtl8211f_suspend,
.resume = rtl821x_resume,
.read_page = rtl821x_read_page,
.write_page = rtl821x_write_page,
--
2.41.0