?
1. 簡介
- 本文是基于RK3588平臺,SDK版本:RK3588_ANDROID12.0 RTL8211FS-CG光口調試總結。
- 視頻橋接芯片:RTL8211FS-CG
- 驅動代碼:"kernel/drivers/net/phy/realtek.c"
- 本次調試的方案功能:RK3588 調試RTL8211FS-CG 轉接出光口
2. 硬件部分
硬件工程師參考RTL8211FS-CG發布的設計圖設計
以下為部分截圖
在設計過程中參考realtek發過的參考設計,建議咨詢一下phy廠家,看有哪些注意地方
注意: 8211FS使用外部3.3V,電平要與主控GMAC1相匹配;
使用UTP<->RGMII的接法,且CFG_MODE2:0=010兼容光口和電口;
3. 軟件部分
建議先調電口RJ45,調通后再接光口,可能更容易;調電口時先插百兆網線調百兆,成功后再換千兆網線
3.1 代碼
3.1.1 Realtek phy的內核配置
在kernel下輸入make menuconfig
Device Drivers --- >
[*] Network device support --- >
-*- PHY Device support and infrastructure --- >
-*- Realtek PHYs
這樣realtek.c就可以編譯到kernel了
3.1.2 dts配置
&gmac1 {
/* Use rgmii-rxid mode to disable rx delay inside Soc */
phy-mode = "rgmii-rxid";
clock_in_out = "output";
snps,reset-gpio = < &gpio3 RK_PB7 GPIO_ACTIVE_LOW >;
snps,reset-active-low;
/* Reset time is 20ms, 100ms for rtl8211f */
snps,reset-delays-us = < 0 20000 100000 >;
pinctrl-names = "default";
pinctrl-0 = < &gmac1_miim
&gmac1_tx_bus2
&gmac1_rx_bus2
&gmac1_rgmii_clk
&gmac1_rgmii_bus
&gmac1_clkinout >;
tx_delay = < 0x43 >;
/* rx_delay = < 0x4f >; */
phy-handle = < &rgmii_phy1 >;
status = "okay";
};
&mdio1 {
rgmii_phy1: phy@1 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = < 0x1 >;
};
};
3.1.3 代碼驗證
插千兆網線有相關打印且可以ping通百度
此時插光口沒有分配IP地址
3.1.4 調試
打開IO調試命令
CONFIG_DEVMEM=y
3.1.5 操作寄存器
find /sys -name phy_registers //先找到以太網寄存器的配置節點并進入所在目錄
echo 31 0xdc0 >phy_registers //切換到PHY的PAGE 0xdc0
cat phy_registers //讀取當前PAGE寄存器的值,核對PHYID1,PHYID2是否正確來確認寄存器是否正確
echo 0 0x value >phy_registers //改寫PAGE0第0個寄存器的值為需要的value
cat phy_registers //讀取值檢查是否修改成功
echo 31 0 > phy_registers //切回到PAGE0寄存器!!!!重要,一定寫完要切回來才會生效
如果修改無效,參閱PHY規格書的8.5章節修改
3.1.6 將芯片強制固定光口模式
setup_fiber_mode
#endif
+static int phy_8211fS_fiber_mode_fixup(struct phy_device *phydev)
+{ int i;
+ printk("%s in\\\\n", __func__);
+ phy_write(phydev, 31, 0xdc0 );
+ phy_write(phydev, 16, 0x79ad );
+ //phy_write(phydev, 20, 0x79ad );
+ printk("page 0xdc0 register\\\\n");
+ for(i =0; i< 8,i++)
+ printk("%d: %x\\\\n",i,phy_read(phydev,i));
+ phy_write(phydev, 31, 0xdc1 );
+ printk("23: %x\\\\n", phy_read(phydev,23));
+ phy_write(phydev, 31, 0x0 );
+ printk("page 0 register\\\\n");
+ for(i =0; i< 32,i++)
+ printk("%d: %x\\\\n",i,phy_read(phydev,i));
+
+ return 0;
+}
/**
* stmmac_dvr_probe
* @device: device pointer
#ifdef CONFIG_DWMAC_RK_AUTO_DELAYLINE
INIT_DELAYED_WORK(&priv- >scan_dwork, stmmac_scan_delayline_dwork);
#endif
+ ret = phy_register_fixup_for_uid(RTL_8211FS_PHY_ID, 0xffffffff, +phy_8211fS_fiber_mode_fixup);
+ if (ret)
+ pr_warn("Cannot register PHY board fixup.\\\\n");
return ret;
error_netdev_register:
編譯燒寫之后網口燈狀態已經變為光口模式了,此時插入光口還是無法分配IP地址
3.1.7 補丁
--- a/kernel/drivers/net/phy/realtek.c
+++ b/kernel/drivers/net/phy/realtek.c
@@ -46,6 +46,11 @@
#define RTL8366RB_POWER_SAVE 0x15
#define RTL8366RB_POWER_SAVE_ON BIT(12)
+#define RTL8211FS_FIBER_ESR 0x0F
+#define RTL8211FS_MODE_MASK 0xC000
+#define RTL8211F_MODE_COPPER 0
+#define RTL8211FS_MODE_FIBER 1
+
#define RTL_SUPPORTS_5000FULL BIT(14)
#define RTL_SUPPORTS_2500FULL BIT(13)
#define RTL_SUPPORTS_10000FULL BIT(0)
@@ -58,6 +63,10 @@
#define RTL_GENERIC_PHYID 0x001cc800
+struct rtl8211f_priv {
+ int lastmode;
+};
+
MODULE_DESCRIPTION("Realtek PHY driver");
MODULE_AUTHOR("Johnson Leung");
MODULE_LICENSE("GPL");
@@ -93,7 +102,6 @@ static int rtl821x_ack_interrupt(struct phy_device *phydev)
static int rtl8211f_ack_interrupt(struct phy_device *phydev)
{
int err;
-
err = phy_read_paged(phydev, 0xa43, RTL8211F_INSR);
return (err < 0) ? err : 0;
@@ -140,7 +148,6 @@ static int rtl8211e_config_intr(struct phy_device *phydev)
static int rtl8211f_config_intr(struct phy_device *phydev)
{
u16 val;
-
if (phydev- >interrupts == PHY_INTERRUPT_ENABLED)
val = RTL8211F_INER_LINK_STATUS;
else
@@ -242,7 +249,7 @@ static int rtl8211f_config_init(struct phy_device *phydev)
"2ns RX delay was already %s (by pin-strapping RXD0 or bootloader configuration)\\\\n",
val_rxdly ? "enabled" : "disabled");
}
-
+
return 0;
}
@@ -560,6 +567,89 @@ static int rtlgen_resume(struct phy_device *phydev)
return ret;
}
+static int rtl8211f_probe(struct phy_device *phydev)
+{
+ struct device *dev = &phydev- >mdio.dev;
+ struct rtl8211f_priv *priv;
+
+ priv = devm_kzalloc(dev, sizeof(struct rtl8211f_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ phydev- >priv = priv;
+
+ return 0;
+}
+
+static void rtl8211f_remove(struct phy_device *phydev)
+{
+ struct device *dev = &phydev- >mdio.dev;
+ struct rtl8211f_priv *priv = phydev- >priv;
+
+ if (priv)
+ devm_kfree(dev, priv);
+}
+
+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 RTL8211F_MODE_COPPER;
+}
+
+static int rtl8211f_config_aneg(struct phy_device *phydev)
+{
+ int ret;
+
+ struct rtl8211f_priv *priv = phydev- >priv;
+
+ ret = genphy_read_abilities(phydev);
+ if(ret < 0)
+ return ret;
+
+ linkmode_copy(phydev- >advertising, phydev- >supported);
+
+ if (rtl8211f_mode(phydev) == RTL8211FS_MODE_FIBER) {
+ dev_info(&phydev- >mdio.dev, "Fiber Mode");
+ priv- >lastmode = RTL8211FS_MODE_FIBER;
+ return genphy_c37_config_aneg(phydev);
+ }
+
+ dev_info(&phydev- >mdio.dev, "Copper Mode");
+
+ priv- >lastmode = RTL8211F_MODE_COPPER;
+
+ return genphy_config_aneg(phydev);
+}
+
+static int rtl8211f_read_status(struct phy_device *phydev)
+{
+ int ret;
+ struct rtl8211f_priv *priv = phydev- >priv;
+
+ if(rtl8211f_mode(phydev) != priv- >lastmode) {
+ ret = rtl8211f_config_aneg(phydev);
+ if(ret < 0)
+ return ret;
+
+ ret = genphy_restart_aneg(phydev);
+ if(ret < 0)
+ return ret;
+ }
+
+ if (rtl8211f_mode(phydev) == RTL8211FS_MODE_FIBER)
+ return genphy_c37_read_status(phydev);
+
+ return genphy_read_status(phydev);
+}
+
+
static struct phy_driver realtek_drvs[] = {
{
PHY_ID_MATCH_EXACT(0x00008201),
@@ -632,10 +722,15 @@ static struct phy_driver realtek_drvs[] = {
.write_page = rtl821x_write_page,
}, {
PHY_ID_MATCH_EXACT(0x001cc916),
- .name = "RTL8211F Gigabit Ethernet",
+ // .name = "RTL8211F Gigabit Ethernet",
+ .name = "RTL8211F(S) Gigabit Ethernet",
+ .probe = rtl8211f_probe,
+ .remove = rtl8211f_remove,
.config_init = &rtl8211f_config_init,
.ack_interrupt = &rtl8211f_ack_interrupt,
.config_intr = &rtl8211f_config_intr,
+ .config_aneg = rtl8211f_config_aneg,
+ .read_status = rtl8211f_read_status,
.suspend = genphy_suspend,
.resume = rtl821x_resume,
.read_page = rtl821x_read_page,