整理 (KSZ8863RLL & KSZ8081) 軔體設置

本文章針對 ksz8863rll 以及 ksz8081 在 i.MX6ULL 平台上進行 linux driver porting
閱讀全文以看更詳細的說明以及終於遇到的問題與心得

我在公司的工作內容中,需要 porting 兩個 phy ic
分別是


設定 ksz8081 (之前的機種常用)

設定大致上不需要更動,只需要更動 LED mode 就可以了

Set ksz8081 led mode to active mode

Datasheet 這邊有解釋 reg address 0x1f / bit[5..4] 需要設成 [00]

之後在 drivers/net/phy/micrel.c 中找到設定 led mode 的 code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static int kszphy_probe(struct phy_device *phydev)
{
/* ...... */
/* ...... */
if (type->led_mode_reg) {
ret = of_property_read_u32(np, "micrel,led-mode", &priv->led_mode);
if (ret)
priv->led_mode = -1;

if (priv->led_mode > 3) {
dev_err(&phydev->dev, "invalid led mode: 0x%02x\n", priv->led_mode);
priv->led_mode = -1;
}
} else {
priv->led_mode = -1;
}
/* ...... */
}

表示 device tree 會抓取 “micrel,led-mode” 這個字串
之後我們可以在 Documentation/devicetree/bindings/net/micrel.txt 中看到設定的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Optional properties:

- micrel,led-mode : LED mode value to set for PHYs with configurable LEDs.

Configure the LED mode with single value. The list of PHYs and the
bits that are currently supported:

KSZ8001: register 0x1e, bits 15..14
KSZ8041: register 0x1e, bits 15..14
KSZ8021: register 0x1f, bits 5..4
KSZ8031: register 0x1f, bits 5..4
KSZ8051: register 0x1f, bits 5..4
KSZ8081: register 0x1f, bits 5..4
KSZ8091: register 0x1f, bits 5..4

See the respective PHY datasheet for the mode values.

然後透過 grep -r “micrel,led-mode” 找到一些例子可將值寫進 0x1f 暫存器
但最後因為 arch/arm/mach-imx/mach-imx6ul.c 會重新定義此 reg 的值,因此直接修改此部份即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
diff --git a/arch/arm/mach-imx/mach-imx6ul.c b/arch/arm/mach-imx/mach-imx6ul.c
index a01cf09..833b926 100644
--- a/arch/arm/mach-imx/mach-imx6ul.c
+++ b/arch/arm/mach-imx/mach-imx6ul.c
@@ -270,35 +270,18 @@ static int ksz8081_phy_fixup(struct phy_device *dev)
phy_write(dev, 0x1f, 0x8110);
phy_write(dev, 0x16, 0x201);
} else if (dev && dev->interface == PHY_INTERFACE_MODE_RMII) {
- phy_write(dev, 0x1f, 0x8190);
+ phy_write(dev, 0x1f, 0x8180);
phy_write(dev, 0x16, 0x202);
}

return 0;

設定 ksz8863rll

MDIO 相關討論

一開始 HW 的電路是與 micrel 代理商的 FAE 討論出使用 SPI 進行設定
之後查詢 datasheet 知道這顆 IC 除了支援 SPI 外,還有支援 I2C 以及 MDIO

因為如果要透過 spi 介面來進行初始化的話, kernel 就需要加入 spi interface 的 driver ,對我來說
可能會拉長開發時間。因此與 Ted 討論後,認為應可以使用 default 的介面來設定 ksz8863rll

剛開始設計完後,發現 phy 無法認到 phy id,之後 FAE 請我們先檢查兩個 port 是否相通,如果初始化使用預設設定,並連通兩個同網段的網路,應該是可以連通才是

[name=KSZ8863RLL FAE]
Dear Duncan,
這是我們新官網上KSZ8863RLL 的連結:
http://www.microchip.com/wwwproducts/en/ksz8863
至於你們家S/W認不到PHY的部分,可以請你先確認一件事,RMII 那邊先不看
先確認另外兩個port是否有相通,如果有通表示KSZ8863RLL應該是沒問題,就是RMII對MAC這邊的問題.
另外一個可以你們S/W RD 提供Linux kernel log file,我請我們原廠幫他確認是否有其他建議.
如果可以提供給我分機聯繫上會比較方便.

之後 確認可以連通 ,晶片有些許溫度,應該是有進入工作狀態

不過將 mdio 電路修改還不夠,花了一點時間,找到了以下的資訊
https://blog.csdn.net/yimiyangguang1314/article/details/43759695

這篇雖然不是 i.MX6 ,不過也有提到介面初始化的重點,mdio 就是所謂的 SMI 介面,需要控制 pin 腳去修改模式,根據 ksz8863rll datasheet 可以看到

在最下方有提到,[P2LED1, P2LED0] = [1, 1] 為 SMI/MIIM mode,因此請 Duncan 將電路的 P45 P46 改成 pull-high 後,就要修改 kernel 設定,在修改之前,我們要知道 mdio address 才可以實行

mdio address

datasheet 初始值如下,也可以透過 mdio 進行設置

太好了!初始值為 0x01,因為 ksz8081 的 phy address = 0x0,這樣就不會因為衝突而需要重新設定它,後面還有提到 Port2 PHY address = Port 1 PHY address + 1 = 0x2,這樣我們就可以透過 phytest 來取得更多 ic 的資訊了

使用 phytest 來設定數值或取得 id

phytest 的相關資訊

repo: git@192.168.1.178:yocto/packages_test.git
binary source: packages_test/src/phytest
shell script: packages_test/scripts/phytest/ksz8081 or ksz8863rll

我們從下圖可以獲得 reigister 的一些慨述

可得知
phy id 1 需要讀取 phyad = 0x1, regad = 0x2, 0x3
phy id 2 需要讀取 phyad = 0x2, regad = 0x2, 0x3

之後我們可以透過 phytest 取得晶片 id,使用 phytest 取得的方式為

1
2
3
/root/phytest <interface> <reg>
example:
/root/phytest eth1 0x02 <---read phy 1 id (high)

若再 devicetree 帶入 phyad 後確實可以獲得,不過這個對我們而言不是很重要,重要的是,要如何將兩個phy都可以自動改成 auto linked-up,不需要插入網路線後就可以取得 link status

Ted 查出可以使用 fix-linked 的語法進行調整
kernel主要修改如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
diff --git a/arch/arm/boot/dts/imx6ul.dts b/arch/arm/boot/dts/imx6ul.dts
index a3afe8e..d5da597 100644
--- a/arch/arm/boot/dts/imx6ul.dts
+++ b/arch/arm/boot/dts/imx6ul.dts
@@ -45,10 +45,12 @@
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_enet2>;
phy-mode = "rmii";
- phy-handle = <&ethphy1>;
status = "okay";
-
-
+ fixed-link {
+ speed = <100>;
+ full-duplex;
+ pause;
+ };
};

其中 phy-handle 移除是因為:兩個 phy 接上去都需要自動 link-up,而加入了 fixed-link,就可以自動執行 link-up 動作,描述的介紹都在 Documentation/devicetree/bindings/net/fixed-link.txt
這樣就可以初始化完成,關閉 spi driver 與其他不相關設定

ENET_REF_TX_CLK 的設置與確認 RX_ER 的設置

這部份為了要 work 也是花了不少時間,因為雖然 mdio 那部份看似已經設定完成,但是封包依舊沒有辦法在進入系統後 ping 出去,表示軔體設置或是硬體電路還是有問題需要解決,因此我們將注意力轉到 reference clock 身上

之後與 KSZ8863 vendor FAE 討論後,FAE 誤會了我們的需求,因此我們將電路改成跟 IR 系列相同的電路

由上圖我們得知,我們透過 X1/X2 振盪出一個頻率 50MHz 的 Reference clock,之後再將 REF_CLK 同步到 i.MX6ULL 以及 KSZ8863RLL 上

因此我們需要修改 “Other Chip” 也就是 i.MX6ULL 的 REF_CLK 方向,將其設成 input
設定的方式……當時有點忘記有沒有針對此部份進行設定了,不過我們知道,還有另一種 clk 傳遞方式,如下圖:

因為之前的 clk 預設都是設定成 output,因此我跟 HW 想說,好!把 SMTXD2 SMTXD3 都設定成 pull down,來進行嘗試,結果,也是沒有辦法運作,因此我們去比對 ksz8081 與 ksz8863rll 的差異,發現 RX_ER 這隻腳似乎不一樣,8081 有接上,而 8863 是空接的狀態,因此我請 duncan 將 pin RX_ER 接到 pin 24 (SMTXER3),之後用之前的 kernel 初始數值竟然可以 work了!!,但這個方式很奇怪,因為有兩個部份跟 IR 系列的電路不同

  • REF_CLK 的方向不對,我們不是透過 SMRXD3 輸出 50MHz clock 給 i.MX6,而是從 i.MX6 輸出50MHz
  • IR系列並沒有接上 RX_ER 這隻 pin

針對這個問題,我們將問題與電路傳給 i.MX6 的 FAE,請他給予協助,我們希望可以透過軟體來處理這個問題
之後我們將 RX_ER 的阻值進行修改

[name=freescale FAE]
Device tree

MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER 0x1b0b0

Change to

MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER 0x130b0

我們將 RX_ER 透過軟體強制 pull-down 最後是有效果的,因此我們只剩下 REF_CLK 需要解決,因為我們希望電路可以與 IR 系列相同

這時,我查到 reference manual 有一個部份是可以處理 reference clk 的相關設定的

就是這個了 GPR register!!要修改
clk 來源設成 external

clk 方向設成 input

針對以上,好!!我們只要針對 bit18 以及 bit14 進行修正就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
diff --git a/arch/arm/mach-imx/mach-imx6ul.c b/arch/arm/mach-imx/mach-imx6ul.c
index 7da3a79..66bb082 100644
--- a/arch/arm/mach-imx/mach-imx6ul.c
+++ b/arch/arm/mach-imx/mach-imx6ul.c
@@ -173,9 +173,14 @@ static void __init imx6ul_enet_clk_init(void)
struct regmap *gpr;

gpr = syscon_regmap_lookup_by_compatible("fsl,imx6ul-iomuxc-gpr");
- if (!IS_ERR(gpr))
- regmap_update_bits(gpr, IOMUXC_GPR1, IMX6UL_GPR1_ENET_CLK_DIR,
- IMX6UL_GPR1_ENET_CLK_OUTPUT);
+ if (!IS_ERR(gpr)) {
+ /* Set enet1 reference clock as output (enet2 as input) */
+ regmap_update_bits(gpr, IOMUXC_GPR1, IMX6UL_GPR1_ENET1_CLK_DIR,
+ IMX6UL_GPR1_ENET1_CLK_OUTPUT);
+ /* Use external clock as enet2 reference clock */
+ regmap_update_bits(gpr, IOMUXC_GPR1, IMX6UL_GPR1_ENET2_CLK_SEL,
+ IMX6UL_GPR1_ENET2_CLK_SET_EXTERNAL);
+ }
else
pr_err("failed to find fsl,imx6ul-iomux-gpr regmap\n");

diff --git a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
index 96230ad..1bf91c8 100644
--- a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
+++ b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
@@ -490,5 +490,7 @@
#define IMX6UL_GPR1_ENET2_CLK_OUTPUT (0x1 << 18)
#define IMX6UL_GPR1_ENET_CLK_DIR (0x3 << 17)
#define IMX6UL_GPR1_ENET_CLK_OUTPUT (0x3 << 17)
+#define IMX6UL_GPR1_ENET2_CLK_SEL (0x1 << 14)
+#define IMX6UL_GPR1_ENET2_CLK_SET_EXTERNAL (0x1 << 14)

#endif /* __LINUX_IMX6Q_IOMUXC_GPR_H */

好,這樣設定的動作就是 enet1 clk 都是設成 output,而使用 ksz8863RLL 的 enet2,reference clk 則是設成 input(default) 並且將clk source 設定成 EXTERNAL CLOCK

之後還是有詢問 Apollo

[name=Weintek-Timmyliu]
若將 ENET2_REF_CLK2 設成 input 的話,那 MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 的阻值是否要改??
並幫我確認以上的 GPR register 是否設定有誤

之後 APOLLO 回信

[name=APOLLO]
On 2018年05月17日 15:11, APOLLO CHANG wrote:

MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b031

Change to

MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x0a0b1

之後 duncan 將此部份的電路短路,就可以運作了

大功告成,ksz8863rll 大致上設定完成

針對 KSZ8863RLL vendor FAE 的 PHY mode 以及 MAC mode 進行解釋

RLL 當然是走 rmii interface ,所以 datasheet 我們只要看 rmii signal connections 就可以了

這邊提到的是:PHY-MAC 以及 MAC-MAC 連線的兩種模式

FAE 並沒有強調這兩個 mode 的意思,但根據我們研究下來,發現 MAC-MAC 的意思就是晶片的 MAC 接過去的對象,而我們的對象就是 8863RLL MAC2 接出去的目標,也就是 i.MX6ULL 的 MAC controller,因此接線必須參考右邊,左邊則是如果 KSZ8863RLL 接出去的是 External PHY IC,則接線就依據左邊,所以我們可以知道 RX_ER 是可以不用接的

Ping 大封包會 ping 不出去

原因是因為 HW 電路將 8081 與 8863 都用相同的電容大小處理,之後拿掉 REF_CLK 電容後就可以穩定的 ping

硬體修改如下

  1. 原本SPI的設定改成SMI mode
  2. 原本8863 REFCLK output與REFCLK IN是斷開的,後來用0R把它接上
  3. REFCLK上的電容拿掉

軔體修改如下

  1. 修改 REF_CLK input resistor value
  2. RX_ER 設 pulldown
  3. set enet2_ref_clk as input

Reference

https://community.nxp.com/thread/323536
http://www.microchip.com/wwwproducts/en/ksz8863

https://www.spinics.net/lists/netdev/msg265530.html
http://ww1.microchip.com/downloads/en/softwarelibrary/ksz_switch_usage_guide/micrel_switch_usage_guide.pdf

http://www.variwiki.com/index.php?title=DART-6UL_Ethernet