9dce5a8add95f40f283cd402bee9b2954b4b87af
[openwrt/staging/robimarko.git] /
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
5 phy
6
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).
11
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>
15 ---
16 drivers/net/phy/Kconfig | 2 +-
17 drivers/net/phy/motorcomm.c | 208 +++++++++++++++++++++++++++++++++++-
18 2 files changed, 207 insertions(+), 3 deletions(-)
19
20 --- a/drivers/net/phy/Kconfig
21 +++ b/drivers/net/phy/Kconfig
22 @@ -245,7 +245,7 @@ config MOTORCOMM_PHY
23 tristate "Motorcomm PHYs"
24 help
25 Enables support for Motorcomm network PHYs.
26 - Currently supports the YT8511, YT8521, YT8531S Gigabit Ethernet PHYs.
27 + Currently supports YT85xx Gigabit Ethernet PHYs.
28
29 config NATIONAL_PHY
30 tristate "National Semiconductor PHYs"
31 --- a/drivers/net/phy/motorcomm.c
32 +++ b/drivers/net/phy/motorcomm.c
33 @@ -1,6 +1,6 @@
34 // SPDX-License-Identifier: GPL-2.0+
35 /*
36 - * Motorcomm 8511/8521/8531S PHY driver.
37 + * Motorcomm 8511/8521/8531/8531S PHY driver.
38 *
39 * Author: Peter Geis <pgwipeout@gmail.com>
40 * Author: Frank <Frank.Sae@motor-comm.com>
41 @@ -14,6 +14,7 @@
42
43 #define PHY_ID_YT8511 0x0000010a
44 #define PHY_ID_YT8521 0x0000011a
45 +#define PHY_ID_YT8531 0x4f51e91b
46 #define PHY_ID_YT8531S 0x4f51e91a
47
48 /* YT8521/YT8531S Register Overview
49 @@ -517,6 +518,61 @@ err_restore_page:
50 return phy_restore_page(phydev, old_page, ret);
51 }
52
53 +static int yt8531_set_wol(struct phy_device *phydev,
54 + struct ethtool_wolinfo *wol)
55 +{
56 + const u16 mac_addr_reg[] = {
57 + YTPHY_WOL_MACADDR2_REG,
58 + YTPHY_WOL_MACADDR1_REG,
59 + YTPHY_WOL_MACADDR0_REG,
60 + };
61 + const u8 *mac_addr;
62 + u16 mask, val;
63 + int ret;
64 + u8 i;
65 +
66 + if (wol->wolopts & WAKE_MAGIC) {
67 + mac_addr = phydev->attached_dev->dev_addr;
68 +
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]));
74 + if (ret < 0)
75 + return ret;
76 + }
77 +
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,
83 + mask, val);
84 + if (ret < 0)
85 + return ret;
86 +
87 + /* Enable WOL interrupt */
88 + ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG, 0,
89 + YTPHY_IER_WOL);
90 + if (ret < 0)
91 + return ret;
92 + } else {
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,
96 + mask, 0);
97 +
98 + /* Disable WOL interrupt */
99 + ret = phy_modify(phydev, YTPHY_INTERRUPT_ENABLE_REG,
100 + YTPHY_IER_WOL, 0);
101 + if (ret < 0)
102 + return ret;
103 + }
104 +
105 + return 0;
106 +}
107 +
108 static int yt8511_read_page(struct phy_device *phydev)
109 {
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);
113 }
114
115 +static int ytphy_rgmii_clk_delay_config_with_lock(struct phy_device *phydev)
116 +{
117 + int ret;
118 +
119 + phy_lock_mdio_bus(phydev);
120 + ret = ytphy_rgmii_clk_delay_config(phydev);
121 + phy_unlock_mdio_bus(phydev);
122 +
123 + return ret;
124 +}
125 +
126 /**
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
130 val);
131 }
132
133 +static int yt8531_probe(struct phy_device *phydev)
134 +{
135 + struct device_node *node = phydev->mdio.dev.of_node;
136 + u16 mask, val;
137 + u32 freq;
138 +
139 + if (of_property_read_u32(node, "motorcomm,clk-out-frequency-hz", &freq))
140 + freq = YTPHY_DTS_OUTPUT_CLK_DIS;
141 +
142 + switch (freq) {
143 + case YTPHY_DTS_OUTPUT_CLK_DIS:
144 + mask = YT8531_SCR_SYNCE_ENABLE;
145 + val = 0;
146 + break;
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);
153 + break;
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);
160 + break;
161 + default:
162 + phydev_warn(phydev, "Freq err:%u\n", freq);
163 + return -EINVAL;
164 + }
165 +
166 + return ytphy_modify_ext_with_lock(phydev, YTPHY_SYNCE_CFG_REG, mask,
167 + val);
168 +}
169 +
170 /**
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);
175 }
176
177 +static int yt8531_config_init(struct phy_device *phydev)
178 +{
179 + struct device_node *node = phydev->mdio.dev.of_node;
180 + int ret;
181 +
182 + ret = ytphy_rgmii_clk_delay_config_with_lock(phydev);
183 + if (ret < 0)
184 + return ret;
185 +
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);
191 + if (ret < 0)
192 + return ret;
193 + }
194 +
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);
200 + if (ret < 0)
201 + return ret;
202 + }
203 +
204 + return 0;
205 +}
206 +
207 +/**
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
211 + *
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
214 + * JH7110.
215 + */
216 +static void yt8531_link_change_notify(struct phy_device *phydev)
217 +{
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;
223 + u16 val = 0;
224 + int ret;
225 +
226 + if (of_property_read_bool(node, "motorcomm,tx-clk-adj-enabled"))
227 + tx_clk_adj_enabled = true;
228 +
229 + if (!tx_clk_adj_enabled)
230 + return;
231 +
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;
238 +
239 + if (phydev->speed < 0)
240 + return;
241 +
242 + switch (phydev->speed) {
243 + case SPEED_1000:
244 + if (tx_clk_1000_inverted)
245 + val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
246 + break;
247 + case SPEED_100:
248 + if (tx_clk_100_inverted)
249 + val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
250 + break;
251 + case SPEED_10:
252 + if (tx_clk_10_inverted)
253 + val = YT8521_RC1R_TX_CLK_SEL_INVERTED;
254 + break;
255 + default:
256 + return;
257 + }
258 +
259 + ret = ytphy_modify_ext_with_lock(phydev, YT8521_RGMII_CONFIG1_REG,
260 + YT8521_RC1R_TX_CLK_SEL_INVERTED, val);
261 + if (ret < 0)
262 + phydev_warn(phydev, "Modify TX_CLK_SEL err:%d\n", ret);
263 +}
264 +
265 /**
266 * yt8521_prepare_fiber_features() - A small helper function that setup
267 * fiber's features.
268 @@ -1970,6 +2162,17 @@ static struct phy_driver motorcomm_phy_d
269 .resume = yt8521_resume,
270 },
271 {
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,
281 + },
282 + {
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
287
288 module_phy_driver(motorcomm_phy_drvs);
289
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) },
301 { /* sentinel */ }
302 };