1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* Copyright Altera Corporation (C) 2014. All rights reserved. |
3 | * |
4 | * Adopted from dwmac-sti.c |
5 | */ |
6 | |
7 | #include <linux/mfd/altera-sysmgr.h> |
8 | #include <linux/of.h> |
9 | #include <linux/of_address.h> |
10 | #include <linux/of_net.h> |
11 | #include <linux/phy.h> |
12 | #include <linux/regmap.h> |
13 | #include <linux/mdio/mdio-regmap.h> |
14 | #include <linux/pcs-lynx.h> |
15 | #include <linux/reset.h> |
16 | #include <linux/stmmac.h> |
17 | |
18 | #include "stmmac.h" |
19 | #include "stmmac_platform.h" |
20 | |
21 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0x0 |
22 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII 0x1 |
23 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII 0x2 |
24 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_WIDTH 2 |
25 | #define SYSMGR_EMACGRP_CTRL_PHYSEL_MASK 0x00000003 |
26 | #define SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK 0x00000010 |
27 | #define SYSMGR_GEN10_EMACGRP_CTRL_PTP_REF_CLK_MASK 0x00000100 |
28 | |
29 | #define SYSMGR_FPGAGRP_MODULE_REG 0x00000028 |
30 | #define SYSMGR_FPGAGRP_MODULE_EMAC 0x00000004 |
31 | #define SYSMGR_FPGAINTF_EMAC_REG 0x00000070 |
32 | #define SYSMGR_FPGAINTF_EMAC_BIT 0x1 |
33 | |
34 | #define EMAC_SPLITTER_CTRL_REG 0x0 |
35 | #define EMAC_SPLITTER_CTRL_SPEED_MASK 0x3 |
36 | #define EMAC_SPLITTER_CTRL_SPEED_10 0x2 |
37 | #define EMAC_SPLITTER_CTRL_SPEED_100 0x3 |
38 | #define EMAC_SPLITTER_CTRL_SPEED_1000 0x0 |
39 | |
40 | #define SGMII_ADAPTER_CTRL_REG 0x00 |
41 | #define SGMII_ADAPTER_ENABLE 0x0000 |
42 | #define SGMII_ADAPTER_DISABLE 0x0001 |
43 | |
44 | struct socfpga_dwmac; |
45 | struct socfpga_dwmac_ops { |
46 | int (*set_phy_mode)(struct socfpga_dwmac *dwmac_priv); |
47 | }; |
48 | |
49 | struct socfpga_dwmac { |
50 | u32 reg_offset; |
51 | u32 reg_shift; |
52 | struct device *dev; |
53 | struct regmap *sys_mgr_base_addr; |
54 | struct reset_control *stmmac_rst; |
55 | struct reset_control *stmmac_ocp_rst; |
56 | void __iomem *splitter_base; |
57 | void __iomem *tse_pcs_base; |
58 | void __iomem *sgmii_adapter_base; |
59 | bool f2h_ptp_ref_clk; |
60 | const struct socfpga_dwmac_ops *ops; |
61 | struct mdio_device *pcs_mdiodev; |
62 | }; |
63 | |
64 | static void socfpga_dwmac_fix_mac_speed(void *priv, unsigned int speed, unsigned int mode) |
65 | { |
66 | struct socfpga_dwmac *dwmac = (struct socfpga_dwmac *)priv; |
67 | void __iomem *splitter_base = dwmac->splitter_base; |
68 | void __iomem *sgmii_adapter_base = dwmac->sgmii_adapter_base; |
69 | struct device *dev = dwmac->dev; |
70 | struct net_device *ndev = dev_get_drvdata(dev); |
71 | struct phy_device *phy_dev = ndev->phydev; |
72 | u32 val; |
73 | |
74 | if (sgmii_adapter_base) |
75 | writew(SGMII_ADAPTER_DISABLE, |
76 | addr: sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG); |
77 | |
78 | if (splitter_base) { |
79 | val = readl(addr: splitter_base + EMAC_SPLITTER_CTRL_REG); |
80 | val &= ~EMAC_SPLITTER_CTRL_SPEED_MASK; |
81 | |
82 | switch (speed) { |
83 | case 1000: |
84 | val |= EMAC_SPLITTER_CTRL_SPEED_1000; |
85 | break; |
86 | case 100: |
87 | val |= EMAC_SPLITTER_CTRL_SPEED_100; |
88 | break; |
89 | case 10: |
90 | val |= EMAC_SPLITTER_CTRL_SPEED_10; |
91 | break; |
92 | default: |
93 | return; |
94 | } |
95 | writel(val, addr: splitter_base + EMAC_SPLITTER_CTRL_REG); |
96 | } |
97 | |
98 | if (phy_dev && sgmii_adapter_base) |
99 | writew(SGMII_ADAPTER_ENABLE, |
100 | addr: sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG); |
101 | } |
102 | |
103 | static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device *dev) |
104 | { |
105 | struct device_node *np = dev->of_node; |
106 | struct regmap *sys_mgr_base_addr; |
107 | u32 reg_offset, reg_shift; |
108 | int ret, index; |
109 | struct device_node *np_splitter = NULL; |
110 | struct device_node *np_sgmii_adapter = NULL; |
111 | struct resource res_splitter; |
112 | struct resource res_tse_pcs; |
113 | struct resource res_sgmii_adapter; |
114 | |
115 | sys_mgr_base_addr = |
116 | altr_sysmgr_regmap_lookup_by_phandle(np, property: "altr,sysmgr-syscon" ); |
117 | if (IS_ERR(ptr: sys_mgr_base_addr)) { |
118 | dev_info(dev, "No sysmgr-syscon node found\n" ); |
119 | return PTR_ERR(ptr: sys_mgr_base_addr); |
120 | } |
121 | |
122 | ret = of_property_read_u32_index(np, propname: "altr,sysmgr-syscon" , index: 1, out_value: ®_offset); |
123 | if (ret) { |
124 | dev_info(dev, "Could not read reg_offset from sysmgr-syscon!\n" ); |
125 | return -EINVAL; |
126 | } |
127 | |
128 | ret = of_property_read_u32_index(np, propname: "altr,sysmgr-syscon" , index: 2, out_value: ®_shift); |
129 | if (ret) { |
130 | dev_info(dev, "Could not read reg_shift from sysmgr-syscon!\n" ); |
131 | return -EINVAL; |
132 | } |
133 | |
134 | dwmac->f2h_ptp_ref_clk = of_property_read_bool(np, propname: "altr,f2h_ptp_ref_clk" ); |
135 | |
136 | np_splitter = of_parse_phandle(np, phandle_name: "altr,emac-splitter" , index: 0); |
137 | if (np_splitter) { |
138 | ret = of_address_to_resource(dev: np_splitter, index: 0, r: &res_splitter); |
139 | of_node_put(node: np_splitter); |
140 | if (ret) { |
141 | dev_info(dev, "Missing emac splitter address\n" ); |
142 | return -EINVAL; |
143 | } |
144 | |
145 | dwmac->splitter_base = devm_ioremap_resource(dev, res: &res_splitter); |
146 | if (IS_ERR(ptr: dwmac->splitter_base)) { |
147 | dev_info(dev, "Failed to mapping emac splitter\n" ); |
148 | return PTR_ERR(ptr: dwmac->splitter_base); |
149 | } |
150 | } |
151 | |
152 | np_sgmii_adapter = of_parse_phandle(np, |
153 | phandle_name: "altr,gmii-to-sgmii-converter" , index: 0); |
154 | if (np_sgmii_adapter) { |
155 | index = of_property_match_string(np: np_sgmii_adapter, propname: "reg-names" , |
156 | string: "hps_emac_interface_splitter_avalon_slave" ); |
157 | |
158 | if (index >= 0) { |
159 | if (of_address_to_resource(dev: np_sgmii_adapter, index, |
160 | r: &res_splitter)) { |
161 | dev_err(dev, |
162 | "%s: ERROR: missing emac splitter address\n" , |
163 | __func__); |
164 | ret = -EINVAL; |
165 | goto err_node_put; |
166 | } |
167 | |
168 | dwmac->splitter_base = |
169 | devm_ioremap_resource(dev, res: &res_splitter); |
170 | |
171 | if (IS_ERR(ptr: dwmac->splitter_base)) { |
172 | ret = PTR_ERR(ptr: dwmac->splitter_base); |
173 | goto err_node_put; |
174 | } |
175 | } |
176 | |
177 | index = of_property_match_string(np: np_sgmii_adapter, propname: "reg-names" , |
178 | string: "gmii_to_sgmii_adapter_avalon_slave" ); |
179 | |
180 | if (index >= 0) { |
181 | if (of_address_to_resource(dev: np_sgmii_adapter, index, |
182 | r: &res_sgmii_adapter)) { |
183 | dev_err(dev, |
184 | "%s: ERROR: failed mapping adapter\n" , |
185 | __func__); |
186 | ret = -EINVAL; |
187 | goto err_node_put; |
188 | } |
189 | |
190 | dwmac->sgmii_adapter_base = |
191 | devm_ioremap_resource(dev, res: &res_sgmii_adapter); |
192 | |
193 | if (IS_ERR(ptr: dwmac->sgmii_adapter_base)) { |
194 | ret = PTR_ERR(ptr: dwmac->sgmii_adapter_base); |
195 | goto err_node_put; |
196 | } |
197 | } |
198 | |
199 | index = of_property_match_string(np: np_sgmii_adapter, propname: "reg-names" , |
200 | string: "eth_tse_control_port" ); |
201 | |
202 | if (index >= 0) { |
203 | if (of_address_to_resource(dev: np_sgmii_adapter, index, |
204 | r: &res_tse_pcs)) { |
205 | dev_err(dev, |
206 | "%s: ERROR: failed mapping tse control port\n" , |
207 | __func__); |
208 | ret = -EINVAL; |
209 | goto err_node_put; |
210 | } |
211 | |
212 | dwmac->tse_pcs_base = |
213 | devm_ioremap_resource(dev, res: &res_tse_pcs); |
214 | |
215 | if (IS_ERR(ptr: dwmac->tse_pcs_base)) { |
216 | ret = PTR_ERR(ptr: dwmac->tse_pcs_base); |
217 | goto err_node_put; |
218 | } |
219 | } |
220 | } |
221 | dwmac->reg_offset = reg_offset; |
222 | dwmac->reg_shift = reg_shift; |
223 | dwmac->sys_mgr_base_addr = sys_mgr_base_addr; |
224 | dwmac->dev = dev; |
225 | of_node_put(node: np_sgmii_adapter); |
226 | |
227 | return 0; |
228 | |
229 | err_node_put: |
230 | of_node_put(node: np_sgmii_adapter); |
231 | return ret; |
232 | } |
233 | |
234 | static int socfpga_get_plat_phymode(struct socfpga_dwmac *dwmac) |
235 | { |
236 | struct net_device *ndev = dev_get_drvdata(dev: dwmac->dev); |
237 | struct stmmac_priv *priv = netdev_priv(dev: ndev); |
238 | |
239 | return priv->plat->mac_interface; |
240 | } |
241 | |
242 | static void socfpga_sgmii_config(struct socfpga_dwmac *dwmac, bool enable) |
243 | { |
244 | u16 val = enable ? SGMII_ADAPTER_ENABLE : SGMII_ADAPTER_DISABLE; |
245 | |
246 | writew(val, addr: dwmac->sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG); |
247 | } |
248 | |
249 | static int socfpga_set_phy_mode_common(int phymode, u32 *val) |
250 | { |
251 | switch (phymode) { |
252 | case PHY_INTERFACE_MODE_RGMII: |
253 | case PHY_INTERFACE_MODE_RGMII_ID: |
254 | case PHY_INTERFACE_MODE_RGMII_RXID: |
255 | case PHY_INTERFACE_MODE_RGMII_TXID: |
256 | *val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII; |
257 | break; |
258 | case PHY_INTERFACE_MODE_MII: |
259 | case PHY_INTERFACE_MODE_GMII: |
260 | case PHY_INTERFACE_MODE_SGMII: |
261 | *val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII; |
262 | break; |
263 | case PHY_INTERFACE_MODE_RMII: |
264 | *val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII; |
265 | break; |
266 | default: |
267 | return -EINVAL; |
268 | } |
269 | return 0; |
270 | } |
271 | |
272 | static int socfpga_gen5_set_phy_mode(struct socfpga_dwmac *dwmac) |
273 | { |
274 | struct regmap *sys_mgr_base_addr = dwmac->sys_mgr_base_addr; |
275 | int phymode = socfpga_get_plat_phymode(dwmac); |
276 | u32 reg_offset = dwmac->reg_offset; |
277 | u32 reg_shift = dwmac->reg_shift; |
278 | u32 ctrl, val, module; |
279 | |
280 | if (socfpga_set_phy_mode_common(phymode, val: &val)) { |
281 | dev_err(dwmac->dev, "bad phy mode %d\n" , phymode); |
282 | return -EINVAL; |
283 | } |
284 | |
285 | /* Overwrite val to GMII if splitter core is enabled. The phymode here |
286 | * is the actual phy mode on phy hardware, but phy interface from |
287 | * EMAC core is GMII. |
288 | */ |
289 | if (dwmac->splitter_base) |
290 | val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII; |
291 | |
292 | /* Assert reset to the enet controller before changing the phy mode */ |
293 | reset_control_assert(rstc: dwmac->stmmac_ocp_rst); |
294 | reset_control_assert(rstc: dwmac->stmmac_rst); |
295 | |
296 | regmap_read(map: sys_mgr_base_addr, reg: reg_offset, val: &ctrl); |
297 | ctrl &= ~(SYSMGR_EMACGRP_CTRL_PHYSEL_MASK << reg_shift); |
298 | ctrl |= val << reg_shift; |
299 | |
300 | if (dwmac->f2h_ptp_ref_clk || |
301 | phymode == PHY_INTERFACE_MODE_MII || |
302 | phymode == PHY_INTERFACE_MODE_GMII || |
303 | phymode == PHY_INTERFACE_MODE_SGMII) { |
304 | regmap_read(map: sys_mgr_base_addr, SYSMGR_FPGAGRP_MODULE_REG, |
305 | val: &module); |
306 | module |= (SYSMGR_FPGAGRP_MODULE_EMAC << (reg_shift / 2)); |
307 | regmap_write(map: sys_mgr_base_addr, SYSMGR_FPGAGRP_MODULE_REG, |
308 | val: module); |
309 | } |
310 | |
311 | if (dwmac->f2h_ptp_ref_clk) |
312 | ctrl |= SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK << (reg_shift / 2); |
313 | else |
314 | ctrl &= ~(SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK << |
315 | (reg_shift / 2)); |
316 | |
317 | regmap_write(map: sys_mgr_base_addr, reg: reg_offset, val: ctrl); |
318 | |
319 | /* Deassert reset for the phy configuration to be sampled by |
320 | * the enet controller, and operation to start in requested mode |
321 | */ |
322 | reset_control_deassert(rstc: dwmac->stmmac_ocp_rst); |
323 | reset_control_deassert(rstc: dwmac->stmmac_rst); |
324 | if (phymode == PHY_INTERFACE_MODE_SGMII) |
325 | socfpga_sgmii_config(dwmac, enable: true); |
326 | |
327 | return 0; |
328 | } |
329 | |
330 | static int socfpga_gen10_set_phy_mode(struct socfpga_dwmac *dwmac) |
331 | { |
332 | struct regmap *sys_mgr_base_addr = dwmac->sys_mgr_base_addr; |
333 | int phymode = socfpga_get_plat_phymode(dwmac); |
334 | u32 reg_offset = dwmac->reg_offset; |
335 | u32 reg_shift = dwmac->reg_shift; |
336 | u32 ctrl, val, module; |
337 | |
338 | if (socfpga_set_phy_mode_common(phymode, val: &val)) |
339 | return -EINVAL; |
340 | |
341 | /* Overwrite val to GMII if splitter core is enabled. The phymode here |
342 | * is the actual phy mode on phy hardware, but phy interface from |
343 | * EMAC core is GMII. |
344 | */ |
345 | if (dwmac->splitter_base) |
346 | val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII; |
347 | |
348 | /* Assert reset to the enet controller before changing the phy mode */ |
349 | reset_control_assert(rstc: dwmac->stmmac_ocp_rst); |
350 | reset_control_assert(rstc: dwmac->stmmac_rst); |
351 | |
352 | regmap_read(map: sys_mgr_base_addr, reg: reg_offset, val: &ctrl); |
353 | ctrl &= ~(SYSMGR_EMACGRP_CTRL_PHYSEL_MASK); |
354 | ctrl |= val; |
355 | |
356 | if (dwmac->f2h_ptp_ref_clk || |
357 | phymode == PHY_INTERFACE_MODE_MII || |
358 | phymode == PHY_INTERFACE_MODE_GMII || |
359 | phymode == PHY_INTERFACE_MODE_SGMII) { |
360 | ctrl |= SYSMGR_GEN10_EMACGRP_CTRL_PTP_REF_CLK_MASK; |
361 | regmap_read(map: sys_mgr_base_addr, SYSMGR_FPGAINTF_EMAC_REG, |
362 | val: &module); |
363 | module |= (SYSMGR_FPGAINTF_EMAC_BIT << reg_shift); |
364 | regmap_write(map: sys_mgr_base_addr, SYSMGR_FPGAINTF_EMAC_REG, |
365 | val: module); |
366 | } else { |
367 | ctrl &= ~SYSMGR_GEN10_EMACGRP_CTRL_PTP_REF_CLK_MASK; |
368 | } |
369 | |
370 | regmap_write(map: sys_mgr_base_addr, reg: reg_offset, val: ctrl); |
371 | |
372 | /* Deassert reset for the phy configuration to be sampled by |
373 | * the enet controller, and operation to start in requested mode |
374 | */ |
375 | reset_control_deassert(rstc: dwmac->stmmac_ocp_rst); |
376 | reset_control_deassert(rstc: dwmac->stmmac_rst); |
377 | if (phymode == PHY_INTERFACE_MODE_SGMII) |
378 | socfpga_sgmii_config(dwmac, enable: true); |
379 | return 0; |
380 | } |
381 | |
382 | static int socfpga_dwmac_probe(struct platform_device *pdev) |
383 | { |
384 | struct plat_stmmacenet_data *plat_dat; |
385 | struct stmmac_resources stmmac_res; |
386 | struct device *dev = &pdev->dev; |
387 | int ret; |
388 | struct socfpga_dwmac *dwmac; |
389 | struct net_device *ndev; |
390 | struct stmmac_priv *stpriv; |
391 | const struct socfpga_dwmac_ops *ops; |
392 | |
393 | ops = device_get_match_data(dev: &pdev->dev); |
394 | if (!ops) { |
395 | dev_err(&pdev->dev, "no of match data provided\n" ); |
396 | return -EINVAL; |
397 | } |
398 | |
399 | ret = stmmac_get_platform_resources(pdev, stmmac_res: &stmmac_res); |
400 | if (ret) |
401 | return ret; |
402 | |
403 | plat_dat = devm_stmmac_probe_config_dt(pdev, mac: stmmac_res.mac); |
404 | if (IS_ERR(ptr: plat_dat)) |
405 | return PTR_ERR(ptr: plat_dat); |
406 | |
407 | dwmac = devm_kzalloc(dev, size: sizeof(*dwmac), GFP_KERNEL); |
408 | if (!dwmac) |
409 | return -ENOMEM; |
410 | |
411 | dwmac->stmmac_ocp_rst = devm_reset_control_get_optional(dev, id: "stmmaceth-ocp" ); |
412 | if (IS_ERR(ptr: dwmac->stmmac_ocp_rst)) { |
413 | ret = PTR_ERR(ptr: dwmac->stmmac_ocp_rst); |
414 | dev_err(dev, "error getting reset control of ocp %d\n" , ret); |
415 | return ret; |
416 | } |
417 | |
418 | reset_control_deassert(rstc: dwmac->stmmac_ocp_rst); |
419 | |
420 | ret = socfpga_dwmac_parse_data(dwmac, dev); |
421 | if (ret) { |
422 | dev_err(dev, "Unable to parse OF data\n" ); |
423 | return ret; |
424 | } |
425 | |
426 | dwmac->ops = ops; |
427 | plat_dat->bsp_priv = dwmac; |
428 | plat_dat->fix_mac_speed = socfpga_dwmac_fix_mac_speed; |
429 | |
430 | ret = stmmac_dvr_probe(device: &pdev->dev, plat_dat, res: &stmmac_res); |
431 | if (ret) |
432 | return ret; |
433 | |
434 | ndev = platform_get_drvdata(pdev); |
435 | stpriv = netdev_priv(dev: ndev); |
436 | |
437 | /* The socfpga driver needs to control the stmmac reset to set the phy |
438 | * mode. Create a copy of the core reset handle so it can be used by |
439 | * the driver later. |
440 | */ |
441 | dwmac->stmmac_rst = stpriv->plat->stmmac_rst; |
442 | |
443 | ret = ops->set_phy_mode(dwmac); |
444 | if (ret) |
445 | goto err_dvr_remove; |
446 | |
447 | /* Create a regmap for the PCS so that it can be used by the PCS driver, |
448 | * if we have such a PCS |
449 | */ |
450 | if (dwmac->tse_pcs_base) { |
451 | struct regmap_config pcs_regmap_cfg; |
452 | struct mdio_regmap_config mrc; |
453 | struct regmap *pcs_regmap; |
454 | struct mii_bus *pcs_bus; |
455 | |
456 | memset(&pcs_regmap_cfg, 0, sizeof(pcs_regmap_cfg)); |
457 | memset(&mrc, 0, sizeof(mrc)); |
458 | |
459 | pcs_regmap_cfg.reg_bits = 16; |
460 | pcs_regmap_cfg.val_bits = 16; |
461 | pcs_regmap_cfg.reg_shift = REGMAP_UPSHIFT(1); |
462 | |
463 | pcs_regmap = devm_regmap_init_mmio(&pdev->dev, dwmac->tse_pcs_base, |
464 | &pcs_regmap_cfg); |
465 | if (IS_ERR(ptr: pcs_regmap)) { |
466 | ret = PTR_ERR(ptr: pcs_regmap); |
467 | goto err_dvr_remove; |
468 | } |
469 | |
470 | mrc.regmap = pcs_regmap; |
471 | mrc.parent = &pdev->dev; |
472 | mrc.valid_addr = 0x0; |
473 | mrc.autoscan = false; |
474 | |
475 | snprintf(buf: mrc.name, MII_BUS_ID_SIZE, fmt: "%s-pcs-mii" , ndev->name); |
476 | pcs_bus = devm_mdio_regmap_register(dev: &pdev->dev, config: &mrc); |
477 | if (IS_ERR(ptr: pcs_bus)) { |
478 | ret = PTR_ERR(ptr: pcs_bus); |
479 | goto err_dvr_remove; |
480 | } |
481 | |
482 | stpriv->hw->lynx_pcs = lynx_pcs_create_mdiodev(bus: pcs_bus, addr: 0); |
483 | if (IS_ERR(ptr: stpriv->hw->lynx_pcs)) { |
484 | ret = PTR_ERR(ptr: stpriv->hw->lynx_pcs); |
485 | goto err_dvr_remove; |
486 | } |
487 | } |
488 | |
489 | return 0; |
490 | |
491 | err_dvr_remove: |
492 | stmmac_dvr_remove(dev: &pdev->dev); |
493 | |
494 | return ret; |
495 | } |
496 | |
497 | static void socfpga_dwmac_remove(struct platform_device *pdev) |
498 | { |
499 | struct net_device *ndev = platform_get_drvdata(pdev); |
500 | struct stmmac_priv *priv = netdev_priv(dev: ndev); |
501 | struct phylink_pcs *pcs = priv->hw->lynx_pcs; |
502 | |
503 | stmmac_pltfr_remove(pdev); |
504 | |
505 | lynx_pcs_destroy(pcs); |
506 | } |
507 | |
508 | #ifdef CONFIG_PM_SLEEP |
509 | static int socfpga_dwmac_resume(struct device *dev) |
510 | { |
511 | struct net_device *ndev = dev_get_drvdata(dev); |
512 | struct stmmac_priv *priv = netdev_priv(dev: ndev); |
513 | struct socfpga_dwmac *dwmac_priv = get_stmmac_bsp_priv(dev); |
514 | |
515 | dwmac_priv->ops->set_phy_mode(priv->plat->bsp_priv); |
516 | |
517 | /* Before the enet controller is suspended, the phy is suspended. |
518 | * This causes the phy clock to be gated. The enet controller is |
519 | * resumed before the phy, so the clock is still gated "off" when |
520 | * the enet controller is resumed. This code makes sure the phy |
521 | * is "resumed" before reinitializing the enet controller since |
522 | * the enet controller depends on an active phy clock to complete |
523 | * a DMA reset. A DMA reset will "time out" if executed |
524 | * with no phy clock input on the Synopsys enet controller. |
525 | * Verified through Synopsys Case #8000711656. |
526 | * |
527 | * Note that the phy clock is also gated when the phy is isolated. |
528 | * Phy "suspend" and "isolate" controls are located in phy basic |
529 | * control register 0, and can be modified by the phy driver |
530 | * framework. |
531 | */ |
532 | if (ndev->phydev) |
533 | phy_resume(phydev: ndev->phydev); |
534 | |
535 | return stmmac_resume(dev); |
536 | } |
537 | #endif /* CONFIG_PM_SLEEP */ |
538 | |
539 | static int __maybe_unused socfpga_dwmac_runtime_suspend(struct device *dev) |
540 | { |
541 | struct net_device *ndev = dev_get_drvdata(dev); |
542 | struct stmmac_priv *priv = netdev_priv(dev: ndev); |
543 | |
544 | stmmac_bus_clks_config(priv, enabled: false); |
545 | |
546 | return 0; |
547 | } |
548 | |
549 | static int __maybe_unused socfpga_dwmac_runtime_resume(struct device *dev) |
550 | { |
551 | struct net_device *ndev = dev_get_drvdata(dev); |
552 | struct stmmac_priv *priv = netdev_priv(dev: ndev); |
553 | |
554 | return stmmac_bus_clks_config(priv, enabled: true); |
555 | } |
556 | |
557 | static const struct dev_pm_ops socfpga_dwmac_pm_ops = { |
558 | SET_SYSTEM_SLEEP_PM_OPS(stmmac_suspend, socfpga_dwmac_resume) |
559 | SET_RUNTIME_PM_OPS(socfpga_dwmac_runtime_suspend, socfpga_dwmac_runtime_resume, NULL) |
560 | }; |
561 | |
562 | static const struct socfpga_dwmac_ops socfpga_gen5_ops = { |
563 | .set_phy_mode = socfpga_gen5_set_phy_mode, |
564 | }; |
565 | |
566 | static const struct socfpga_dwmac_ops socfpga_gen10_ops = { |
567 | .set_phy_mode = socfpga_gen10_set_phy_mode, |
568 | }; |
569 | |
570 | static const struct of_device_id socfpga_dwmac_match[] = { |
571 | { .compatible = "altr,socfpga-stmmac" , .data = &socfpga_gen5_ops }, |
572 | { .compatible = "altr,socfpga-stmmac-a10-s10" , .data = &socfpga_gen10_ops }, |
573 | { } |
574 | }; |
575 | MODULE_DEVICE_TABLE(of, socfpga_dwmac_match); |
576 | |
577 | static struct platform_driver socfpga_dwmac_driver = { |
578 | .probe = socfpga_dwmac_probe, |
579 | .remove_new = socfpga_dwmac_remove, |
580 | .driver = { |
581 | .name = "socfpga-dwmac" , |
582 | .pm = &socfpga_dwmac_pm_ops, |
583 | .of_match_table = socfpga_dwmac_match, |
584 | }, |
585 | }; |
586 | module_platform_driver(socfpga_dwmac_driver); |
587 | |
588 | MODULE_DESCRIPTION("Altera SOC DWMAC Specific Glue layer" ); |
589 | MODULE_LICENSE("GPL v2" ); |
590 | |