1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * dwmac-ingenic.c - Ingenic SoCs DWMAC specific glue layer |
4 | * |
5 | * Copyright (c) 2021 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com> |
6 | */ |
7 | |
8 | #include <linux/bitfield.h> |
9 | #include <linux/clk.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/mfd/syscon.h> |
12 | #include <linux/module.h> |
13 | #include <linux/of.h> |
14 | #include <linux/of_net.h> |
15 | #include <linux/phy.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/regmap.h> |
18 | #include <linux/slab.h> |
19 | #include <linux/stmmac.h> |
20 | |
21 | #include "stmmac_platform.h" |
22 | |
23 | #define MACPHYC_TXCLK_SEL_MASK GENMASK(31, 31) |
24 | #define MACPHYC_TXCLK_SEL_OUTPUT 0x1 |
25 | #define MACPHYC_TXCLK_SEL_INPUT 0x0 |
26 | #define MACPHYC_MODE_SEL_MASK GENMASK(31, 31) |
27 | #define MACPHYC_MODE_SEL_RMII 0x0 |
28 | #define MACPHYC_TX_SEL_MASK GENMASK(19, 19) |
29 | #define MACPHYC_TX_SEL_ORIGIN 0x0 |
30 | #define MACPHYC_TX_SEL_DELAY 0x1 |
31 | #define MACPHYC_TX_DELAY_MASK GENMASK(18, 12) |
32 | #define MACPHYC_RX_SEL_MASK GENMASK(11, 11) |
33 | #define MACPHYC_RX_SEL_ORIGIN 0x0 |
34 | #define MACPHYC_RX_SEL_DELAY 0x1 |
35 | #define MACPHYC_RX_DELAY_MASK GENMASK(10, 4) |
36 | #define MACPHYC_SOFT_RST_MASK GENMASK(3, 3) |
37 | #define MACPHYC_PHY_INFT_MASK GENMASK(2, 0) |
38 | #define MACPHYC_PHY_INFT_RMII 0x4 |
39 | #define MACPHYC_PHY_INFT_RGMII 0x1 |
40 | #define MACPHYC_PHY_INFT_GMII 0x0 |
41 | #define MACPHYC_PHY_INFT_MII 0x0 |
42 | |
43 | #define MACPHYC_TX_DELAY_PS_MAX 2496 |
44 | #define MACPHYC_TX_DELAY_PS_MIN 20 |
45 | |
46 | #define MACPHYC_RX_DELAY_PS_MAX 2496 |
47 | #define MACPHYC_RX_DELAY_PS_MIN 20 |
48 | |
49 | enum ingenic_mac_version { |
50 | ID_JZ4775, |
51 | ID_X1000, |
52 | ID_X1600, |
53 | ID_X1830, |
54 | ID_X2000, |
55 | }; |
56 | |
57 | struct ingenic_mac { |
58 | const struct ingenic_soc_info *soc_info; |
59 | struct device *dev; |
60 | struct regmap *regmap; |
61 | |
62 | int rx_delay; |
63 | int tx_delay; |
64 | }; |
65 | |
66 | struct ingenic_soc_info { |
67 | enum ingenic_mac_version version; |
68 | u32 mask; |
69 | |
70 | int (*set_mode)(struct plat_stmmacenet_data *plat_dat); |
71 | }; |
72 | |
73 | static int ingenic_mac_init(struct plat_stmmacenet_data *plat_dat) |
74 | { |
75 | struct ingenic_mac *mac = plat_dat->bsp_priv; |
76 | int ret; |
77 | |
78 | if (mac->soc_info->set_mode) { |
79 | ret = mac->soc_info->set_mode(plat_dat); |
80 | if (ret) |
81 | return ret; |
82 | } |
83 | |
84 | return 0; |
85 | } |
86 | |
87 | static int jz4775_mac_set_mode(struct plat_stmmacenet_data *plat_dat) |
88 | { |
89 | struct ingenic_mac *mac = plat_dat->bsp_priv; |
90 | unsigned int val; |
91 | |
92 | switch (plat_dat->mac_interface) { |
93 | case PHY_INTERFACE_MODE_MII: |
94 | val = FIELD_PREP(MACPHYC_TXCLK_SEL_MASK, MACPHYC_TXCLK_SEL_INPUT) | |
95 | FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_MII); |
96 | dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_MII\n" ); |
97 | break; |
98 | |
99 | case PHY_INTERFACE_MODE_GMII: |
100 | val = FIELD_PREP(MACPHYC_TXCLK_SEL_MASK, MACPHYC_TXCLK_SEL_INPUT) | |
101 | FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_GMII); |
102 | dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_GMII\n" ); |
103 | break; |
104 | |
105 | case PHY_INTERFACE_MODE_RMII: |
106 | val = FIELD_PREP(MACPHYC_TXCLK_SEL_MASK, MACPHYC_TXCLK_SEL_INPUT) | |
107 | FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RMII); |
108 | dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RMII\n" ); |
109 | break; |
110 | |
111 | case PHY_INTERFACE_MODE_RGMII: |
112 | case PHY_INTERFACE_MODE_RGMII_ID: |
113 | case PHY_INTERFACE_MODE_RGMII_TXID: |
114 | case PHY_INTERFACE_MODE_RGMII_RXID: |
115 | val = FIELD_PREP(MACPHYC_TXCLK_SEL_MASK, MACPHYC_TXCLK_SEL_INPUT) | |
116 | FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RGMII); |
117 | dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RGMII\n" ); |
118 | break; |
119 | |
120 | default: |
121 | dev_err(mac->dev, "Unsupported interface %d" , plat_dat->mac_interface); |
122 | return -EINVAL; |
123 | } |
124 | |
125 | /* Update MAC PHY control register */ |
126 | return regmap_update_bits(map: mac->regmap, reg: 0, mask: mac->soc_info->mask, val); |
127 | } |
128 | |
129 | static int x1000_mac_set_mode(struct plat_stmmacenet_data *plat_dat) |
130 | { |
131 | struct ingenic_mac *mac = plat_dat->bsp_priv; |
132 | |
133 | switch (plat_dat->mac_interface) { |
134 | case PHY_INTERFACE_MODE_RMII: |
135 | dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RMII\n" ); |
136 | break; |
137 | |
138 | default: |
139 | dev_err(mac->dev, "Unsupported interface %d" , plat_dat->mac_interface); |
140 | return -EINVAL; |
141 | } |
142 | |
143 | /* Update MAC PHY control register */ |
144 | return regmap_update_bits(map: mac->regmap, reg: 0, mask: mac->soc_info->mask, val: 0); |
145 | } |
146 | |
147 | static int x1600_mac_set_mode(struct plat_stmmacenet_data *plat_dat) |
148 | { |
149 | struct ingenic_mac *mac = plat_dat->bsp_priv; |
150 | unsigned int val; |
151 | |
152 | switch (plat_dat->mac_interface) { |
153 | case PHY_INTERFACE_MODE_RMII: |
154 | val = FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RMII); |
155 | dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RMII\n" ); |
156 | break; |
157 | |
158 | default: |
159 | dev_err(mac->dev, "Unsupported interface %d" , plat_dat->mac_interface); |
160 | return -EINVAL; |
161 | } |
162 | |
163 | /* Update MAC PHY control register */ |
164 | return regmap_update_bits(map: mac->regmap, reg: 0, mask: mac->soc_info->mask, val); |
165 | } |
166 | |
167 | static int x1830_mac_set_mode(struct plat_stmmacenet_data *plat_dat) |
168 | { |
169 | struct ingenic_mac *mac = plat_dat->bsp_priv; |
170 | unsigned int val; |
171 | |
172 | switch (plat_dat->mac_interface) { |
173 | case PHY_INTERFACE_MODE_RMII: |
174 | val = FIELD_PREP(MACPHYC_MODE_SEL_MASK, MACPHYC_MODE_SEL_RMII) | |
175 | FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RMII); |
176 | dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RMII\n" ); |
177 | break; |
178 | |
179 | default: |
180 | dev_err(mac->dev, "Unsupported interface %d" , plat_dat->mac_interface); |
181 | return -EINVAL; |
182 | } |
183 | |
184 | /* Update MAC PHY control register */ |
185 | return regmap_update_bits(map: mac->regmap, reg: 0, mask: mac->soc_info->mask, val); |
186 | } |
187 | |
188 | static int x2000_mac_set_mode(struct plat_stmmacenet_data *plat_dat) |
189 | { |
190 | struct ingenic_mac *mac = plat_dat->bsp_priv; |
191 | unsigned int val; |
192 | |
193 | switch (plat_dat->mac_interface) { |
194 | case PHY_INTERFACE_MODE_RMII: |
195 | val = FIELD_PREP(MACPHYC_TX_SEL_MASK, MACPHYC_TX_SEL_ORIGIN) | |
196 | FIELD_PREP(MACPHYC_RX_SEL_MASK, MACPHYC_RX_SEL_ORIGIN) | |
197 | FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RMII); |
198 | dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RMII\n" ); |
199 | break; |
200 | |
201 | case PHY_INTERFACE_MODE_RGMII: |
202 | case PHY_INTERFACE_MODE_RGMII_ID: |
203 | case PHY_INTERFACE_MODE_RGMII_TXID: |
204 | case PHY_INTERFACE_MODE_RGMII_RXID: |
205 | val = FIELD_PREP(MACPHYC_PHY_INFT_MASK, MACPHYC_PHY_INFT_RGMII); |
206 | |
207 | if (mac->tx_delay == 0) |
208 | val |= FIELD_PREP(MACPHYC_TX_SEL_MASK, MACPHYC_TX_SEL_ORIGIN); |
209 | else |
210 | val |= FIELD_PREP(MACPHYC_TX_SEL_MASK, MACPHYC_TX_SEL_DELAY) | |
211 | FIELD_PREP(MACPHYC_TX_DELAY_MASK, (mac->tx_delay + 9750) / 19500 - 1); |
212 | |
213 | if (mac->rx_delay == 0) |
214 | val |= FIELD_PREP(MACPHYC_RX_SEL_MASK, MACPHYC_RX_SEL_ORIGIN); |
215 | else |
216 | val |= FIELD_PREP(MACPHYC_RX_SEL_MASK, MACPHYC_RX_SEL_DELAY) | |
217 | FIELD_PREP(MACPHYC_RX_DELAY_MASK, (mac->rx_delay + 9750) / 19500 - 1); |
218 | |
219 | dev_dbg(mac->dev, "MAC PHY Control Register: PHY_INTERFACE_MODE_RGMII\n" ); |
220 | break; |
221 | |
222 | default: |
223 | dev_err(mac->dev, "Unsupported interface %d" , plat_dat->mac_interface); |
224 | return -EINVAL; |
225 | } |
226 | |
227 | /* Update MAC PHY control register */ |
228 | return regmap_update_bits(map: mac->regmap, reg: 0, mask: mac->soc_info->mask, val); |
229 | } |
230 | |
231 | static int ingenic_mac_probe(struct platform_device *pdev) |
232 | { |
233 | struct plat_stmmacenet_data *plat_dat; |
234 | struct stmmac_resources stmmac_res; |
235 | struct ingenic_mac *mac; |
236 | const struct ingenic_soc_info *data; |
237 | u32 tx_delay_ps, rx_delay_ps; |
238 | int ret; |
239 | |
240 | ret = stmmac_get_platform_resources(pdev, stmmac_res: &stmmac_res); |
241 | if (ret) |
242 | return ret; |
243 | |
244 | plat_dat = devm_stmmac_probe_config_dt(pdev, mac: stmmac_res.mac); |
245 | if (IS_ERR(ptr: plat_dat)) |
246 | return PTR_ERR(ptr: plat_dat); |
247 | |
248 | mac = devm_kzalloc(dev: &pdev->dev, size: sizeof(*mac), GFP_KERNEL); |
249 | if (!mac) |
250 | return -ENOMEM; |
251 | |
252 | data = of_device_get_match_data(dev: &pdev->dev); |
253 | if (!data) { |
254 | dev_err(&pdev->dev, "No of match data provided\n" ); |
255 | return -EINVAL; |
256 | } |
257 | |
258 | /* Get MAC PHY control register */ |
259 | mac->regmap = syscon_regmap_lookup_by_phandle(np: pdev->dev.of_node, property: "mode-reg" ); |
260 | if (IS_ERR(ptr: mac->regmap)) { |
261 | dev_err(&pdev->dev, "%s: Failed to get syscon regmap\n" , __func__); |
262 | return PTR_ERR(ptr: mac->regmap); |
263 | } |
264 | |
265 | if (!of_property_read_u32(np: pdev->dev.of_node, propname: "tx-clk-delay-ps" , out_value: &tx_delay_ps)) { |
266 | if (tx_delay_ps >= MACPHYC_TX_DELAY_PS_MIN && |
267 | tx_delay_ps <= MACPHYC_TX_DELAY_PS_MAX) { |
268 | mac->tx_delay = tx_delay_ps * 1000; |
269 | } else { |
270 | dev_err(&pdev->dev, "Invalid TX clock delay: %dps\n" , tx_delay_ps); |
271 | return -EINVAL; |
272 | } |
273 | } |
274 | |
275 | if (!of_property_read_u32(np: pdev->dev.of_node, propname: "rx-clk-delay-ps" , out_value: &rx_delay_ps)) { |
276 | if (rx_delay_ps >= MACPHYC_RX_DELAY_PS_MIN && |
277 | rx_delay_ps <= MACPHYC_RX_DELAY_PS_MAX) { |
278 | mac->rx_delay = rx_delay_ps * 1000; |
279 | } else { |
280 | dev_err(&pdev->dev, "Invalid RX clock delay: %dps\n" , rx_delay_ps); |
281 | return -EINVAL; |
282 | } |
283 | } |
284 | |
285 | mac->soc_info = data; |
286 | mac->dev = &pdev->dev; |
287 | |
288 | plat_dat->bsp_priv = mac; |
289 | |
290 | ret = ingenic_mac_init(plat_dat); |
291 | if (ret) |
292 | return ret; |
293 | |
294 | return stmmac_dvr_probe(device: &pdev->dev, plat_dat, res: &stmmac_res); |
295 | } |
296 | |
297 | #ifdef CONFIG_PM_SLEEP |
298 | static int ingenic_mac_suspend(struct device *dev) |
299 | { |
300 | int ret; |
301 | |
302 | ret = stmmac_suspend(dev); |
303 | |
304 | return ret; |
305 | } |
306 | |
307 | static int ingenic_mac_resume(struct device *dev) |
308 | { |
309 | struct net_device *ndev = dev_get_drvdata(dev); |
310 | struct stmmac_priv *priv = netdev_priv(dev: ndev); |
311 | int ret; |
312 | |
313 | ret = ingenic_mac_init(plat_dat: priv->plat); |
314 | if (ret) |
315 | return ret; |
316 | |
317 | ret = stmmac_resume(dev); |
318 | |
319 | return ret; |
320 | } |
321 | #endif /* CONFIG_PM_SLEEP */ |
322 | |
323 | static SIMPLE_DEV_PM_OPS(ingenic_mac_pm_ops, ingenic_mac_suspend, ingenic_mac_resume); |
324 | |
325 | static struct ingenic_soc_info jz4775_soc_info = { |
326 | .version = ID_JZ4775, |
327 | .mask = MACPHYC_TXCLK_SEL_MASK | MACPHYC_SOFT_RST_MASK | MACPHYC_PHY_INFT_MASK, |
328 | |
329 | .set_mode = jz4775_mac_set_mode, |
330 | }; |
331 | |
332 | static struct ingenic_soc_info x1000_soc_info = { |
333 | .version = ID_X1000, |
334 | .mask = MACPHYC_SOFT_RST_MASK, |
335 | |
336 | .set_mode = x1000_mac_set_mode, |
337 | }; |
338 | |
339 | static struct ingenic_soc_info x1600_soc_info = { |
340 | .version = ID_X1600, |
341 | .mask = MACPHYC_SOFT_RST_MASK | MACPHYC_PHY_INFT_MASK, |
342 | |
343 | .set_mode = x1600_mac_set_mode, |
344 | }; |
345 | |
346 | static struct ingenic_soc_info x1830_soc_info = { |
347 | .version = ID_X1830, |
348 | .mask = MACPHYC_MODE_SEL_MASK | MACPHYC_SOFT_RST_MASK | MACPHYC_PHY_INFT_MASK, |
349 | |
350 | .set_mode = x1830_mac_set_mode, |
351 | }; |
352 | |
353 | static struct ingenic_soc_info x2000_soc_info = { |
354 | .version = ID_X2000, |
355 | .mask = MACPHYC_TX_SEL_MASK | MACPHYC_TX_DELAY_MASK | MACPHYC_RX_SEL_MASK | |
356 | MACPHYC_RX_DELAY_MASK | MACPHYC_SOFT_RST_MASK | MACPHYC_PHY_INFT_MASK, |
357 | |
358 | .set_mode = x2000_mac_set_mode, |
359 | }; |
360 | |
361 | static const struct of_device_id ingenic_mac_of_matches[] = { |
362 | { .compatible = "ingenic,jz4775-mac" , .data = &jz4775_soc_info }, |
363 | { .compatible = "ingenic,x1000-mac" , .data = &x1000_soc_info }, |
364 | { .compatible = "ingenic,x1600-mac" , .data = &x1600_soc_info }, |
365 | { .compatible = "ingenic,x1830-mac" , .data = &x1830_soc_info }, |
366 | { .compatible = "ingenic,x2000-mac" , .data = &x2000_soc_info }, |
367 | { } |
368 | }; |
369 | MODULE_DEVICE_TABLE(of, ingenic_mac_of_matches); |
370 | |
371 | static struct platform_driver ingenic_mac_driver = { |
372 | .probe = ingenic_mac_probe, |
373 | .remove_new = stmmac_pltfr_remove, |
374 | .driver = { |
375 | .name = "ingenic-mac" , |
376 | .pm = pm_ptr(&ingenic_mac_pm_ops), |
377 | .of_match_table = ingenic_mac_of_matches, |
378 | }, |
379 | }; |
380 | module_platform_driver(ingenic_mac_driver); |
381 | |
382 | MODULE_AUTHOR("周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>" ); |
383 | MODULE_DESCRIPTION("Ingenic SoCs DWMAC specific glue layer" ); |
384 | MODULE_LICENSE("GPL v2" ); |
385 | |