1 From 4ac94f728a588e7096dd5010cd7141a309ea7805 Mon Sep 17 00:00:00 2001
2 From: Frank Sae <Frank.Sae@motor-comm.com>
3 Date: Thu, 2 Feb 2023 11:00:37 +0800
4 Subject: [PATCH] net: phy: Add driver for Motorcomm yt8531 gigabit ethernet
7 Add a driver for the motorcomm yt8531 gigabit ethernet phy. We have
8 verified the driver on AM335x platform with yt8531 board. On the
9 board, yt8531 gigabit ethernet phy works in utp mode, RGMII
10 interface, supports 1000M/100M/10M speeds, and wol(magic package).
12 Signed-off-by: Frank Sae <Frank.Sae@motor-comm.com>
13 Reviewed-by: Andrew Lunn <andrew@lunn.ch>
14 Signed-off-by: David S. Miller <davem@davemloft.net>
16 drivers/net/phy/Kconfig | 2 +-
17 drivers/net/phy/motorcomm.c | 208 +++++++++++++++++++++++++++++++++++-
18 2 files changed, 207 insertions(+), 3 deletions(-)
20 --- a/drivers/net/phy/Kconfig
21 +++ b/drivers/net/phy/Kconfig
22 @@ -260,7 +260,7 @@ config MOTORCOMM_PHY
23 tristate "Motorcomm PHYs"
25 Enables support for Motorcomm network PHYs.
26 - Currently supports the YT8511, YT8521, YT8531S Gigabit Ethernet PHYs.
27 + Currently supports YT85xx Gigabit Ethernet PHYs.
30 tristate "National Semiconductor PHYs"
31 --- a/drivers/net/phy/motorcomm.c
32 +++ b/drivers/net/phy/motorcomm.c
34 // SPDX-License-Identifier: GPL-2.0+
36 - * Motorcomm 8511/8521/8531S PHY driver.
37 + * Motorcomm 8511/8521/8531/8531S PHY driver.
39 * Author: Peter Geis <pgwipeout@gmail.com>
40 * Author: Frank <Frank.Sae@motor-comm.com>
43 #define PHY_ID_YT8511 0x0000010a
44 #define PHY_ID_YT8521 0x0000011a
45 +#define PHY_ID_YT8531 0x4f51e91b
46 #define PHY_ID_YT8531S 0x4f51e91a
48 /* YT8521/YT8531S Register Overview
49 @@ -517,6 +518,61 @@ err_restore_page:
50 return phy_restore_page(phydev, old_page, ret);
53 +static int yt8531_set_wol(struct phy_device *phydev,
54 + struct ethtool_wolinfo *wol)
56 + const u16 mac_addr_reg[] = {
57 + YTPHY_WOL_MACADDR2_REG,
58 + YTPHY_WOL_MACADDR1_REG,
59 + YTPHY_WOL_MACADDR0_REG,
66 + if (wol->wolopts & WAKE_MAGIC) {
67 + mac_addr = phydev->attached_dev->dev_addr;
69 + /* Store the device address for the magic packet */
70 + for (i = 0; i < 3; i++) {
71 + ret = ytphy_write_ext_with_lock(phydev, mac_addr_reg[i],
72 + ((mac_addr[i * 2] << 8)) |
73 + (mac_addr[i * 2 + 1]));
78 + /* Enable WOL feature */
79 + mask = YTPHY_WCR_PULSE_WIDTH_MASK | YTPHY_WCR_INTR_SEL;
80 + val = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
81 + val |= YTPHY_WCR_TYPE_PULSE | YTPHY_WCR_PULSE_WIDTH_672MS;
82 + ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG,
87 + /* Enable WOL interrupt */
88 + ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0,
93 + /* Disable WOL feature */
94 + mask = YTPHY_WCR_ENABLE | YTPHY_WCR_INTR_SEL;
95 + ret = ytphy_modify_ext_with_lock(phydev, YTPHY_WOL_CONFIG_REG,
98 + /* Disable WOL interrupt */
99 + ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG,
108 static int yt8511_read_page(struct phy_device *phydev)
110 return __phy_read(phydev, YT8511_PAGE_SELECT);
111 @@ -767,6 +823,17 @@ static int ytphy_rgmii_clk_delay_config(
112 return ytphy_modify_ext(phydev, YT8521_RGMII_CONFIG1_REG, mask, val);
115 +static int ytphy_rgmii_clk_delay_config_with_lock(struct phy_device *phydev)
119 + phy_lock_mdio_bus(phydev);
120 + ret = ytphy_rgmii_clk_delay_config(phydev);
121 + phy_unlock_mdio_bus(phydev);
127 * yt8521_probe() - read chip config then set suitable polling_mode
128 * @phydev: a pointer to a &struct phy_device
129 @@ -891,6 +958,43 @@ static int yt8521_probe(struct phy_devic
133 +static int yt8531_probe(struct phy_device *phydev)
135 + struct device_node *node = phydev->mdio.dev.of_node;
139 + if (of_property_read_u32(node, "motorcomm,clk-out-frequency-hz", &freq))
140 + freq = YTPHY_DTS_OUTPUT_CLK_DIS;
143 + case YTPHY_DTS_OUTPUT_CLK_DIS:
144 + mask = YT8531_SCR_SYNCE_ENABLE;
147 + case YTPHY_DTS_OUTPUT_CLK_25M:
148 + mask = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_SRC_MASK |
149 + YT8531_SCR_CLK_FRE_SEL_125M;
150 + val = YT8531_SCR_SYNCE_ENABLE |
151 + FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
152 + YT8531_SCR_CLK_SRC_REF_25M);
154 + case YTPHY_DTS_OUTPUT_CLK_125M:
155 + mask = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_SRC_MASK |
156 + YT8531_SCR_CLK_FRE_SEL_125M;
157 + val = YT8531_SCR_SYNCE_ENABLE | YT8531_SCR_CLK_FRE_SEL_125M |
158 + FIELD_PREP(YT8531_SCR_CLK_SRC_MASK,
159 + YT8531_SCR_CLK_SRC_PLL_125M);
162 + phydev_warn(phydev, "Freq err:%u\n", freq);
166 + return ytphy_modify_ext_with_lock(phydev, YTPHY_SYNCE_CFG_REG, mask,
171 * ytphy_utp_read_lpa() - read LPA then setup lp_advertising for utp
172 * @phydev: a pointer to a &struct phy_device
173 @@ -1387,6 +1491,94 @@ err_restore_page:
174 return phy_restore_page(phydev, old_page, ret);
177 +static int yt8531_config_init(struct phy_device *phydev)
179 + struct device_node *node = phydev->mdio.dev.of_node;
182 + ret = ytphy_rgmii_clk_delay_config_with_lock(phydev);
186 + if (of_property_read_bool(node, "motorcomm,auto-sleep-disabled")) {
187 + /* disable auto sleep */
188 + ret = ytphy_modify_ext_with_lock(phydev,
189 + YT8521_EXTREG_SLEEP_CONTROL1_REG,
190 + YT8521_ESC1R_SLEEP_SW, 0);
195 + if (of_property_read_bool(node, "motorcomm,keep-pll-enabled")) {
196 + /* enable RXC clock when no wire plug */
197 + ret = ytphy_modify_ext_with_lock(phydev,
198 + YT8521_CLOCK_GATING_REG,
199 + YT8521_CGR_RX_CLK_EN, 0);
208 + * yt8531_link_change_notify() - Adjust the tx clock direction according to
209 + * the current speed and dts config.
210 + * @phydev: a pointer to a &struct phy_device
212 + * NOTE: This function is only used to adapt to VF2 with JH7110 SoC. Please
213 + * keep "motorcomm,tx-clk-adj-enabled" not exist in dts when the soc is not
216 +static void yt8531_link_change_notify(struct phy_device *phydev)
218 + struct device_node *node = phydev->mdio.dev.of_node;
219 + bool tx_clk_adj_enabled = false;
220 + bool tx_clk_1000_inverted;
221 + bool tx_clk_100_inverted;
222 + bool tx_clk_10_inverted;
226 + if (of_property_read_bool(node, "motorcomm,tx-clk-adj-enabled"))
227 + tx_clk_adj_enabled = true;
229 + if (!tx_clk_adj_enabled)
232 + if (of_property_read_bool(node, "motorcomm,tx-clk-10-inverted"))
233 + tx_clk_10_inverted = true;
234 + if (of_property_read_bool(node, "motorcomm,tx-clk-100-inverted"))
235 + tx_clk_100_inverted = true;
236 + if (of_property_read_bool(node, "motorcomm,tx-clk-1000-inverted"))
237 + tx_clk_1000_inverted = true;
239 + if (phydev->speed < 0)
242 + switch (phydev->speed) {
244 + if (tx_clk_1000_inverted)
245 + val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
248 + if (tx_clk_100_inverted)
249 + val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
252 + if (tx_clk_10_inverted)
253 + val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
259 + ret = ytphy_modify_ext_with_lock(phydev, YT8521_RGMII_CONFIG1_REG,
260 + YT8521_RC1R_TX_CLK_SEL_INVERTED, val);
262 + phydev_warn(phydev, "Modify TX_CLK_SEL err:%d\n", ret);
266 * yt8521_prepare_fiber_features() - A small helper function that setup
268 @@ -1970,6 +2162,17 @@ static struct phy_driver motorcomm_phy_d
269 .resume = yt8521_resume,
272 + PHY_ID_MATCH_EXACT(PHY_ID_YT8531),
273 + .name = "YT8531 Gigabit Ethernet",
274 + .probe = yt8531_probe,
275 + .config_init = yt8531_config_init,
276 + .suspend = genphy_suspend,
277 + .resume = genphy_resume,
278 + .get_wol = ytphy_get_wol,
279 + .set_wol = yt8531_set_wol,
280 + .link_change_notify = yt8531_link_change_notify,
283 PHY_ID_MATCH_EXACT(PHY_ID_YT8531S),
284 .name = "YT8531S Gigabit Ethernet",
285 .get_features = yt8521_get_features,
286 @@ -1990,7 +2193,7 @@ static struct phy_driver motorcomm_phy_d
288 module_phy_driver(motorcomm_phy_drvs);
290 -MODULE_DESCRIPTION("Motorcomm 8511/8521/8531S PHY driver");
291 +MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S PHY driver");
292 MODULE_AUTHOR("Peter Geis");
293 MODULE_AUTHOR("Frank");
294 MODULE_LICENSE("GPL");
295 @@ -1998,6 +2201,7 @@ MODULE_LICENSE("GPL");
296 static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
297 { PHY_ID_MATCH_EXACT(PHY_ID_YT8511) },
298 { PHY_ID_MATCH_EXACT(PHY_ID_YT8521) },
299 + { PHY_ID_MATCH_EXACT(PHY_ID_YT8531) },
300 { PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },