kernel: realtek phy driver supports fiber
This commit is contained in:
parent
b95e6f8556
commit
9c3bb24409
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user