1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /******************************************************************************* |
3 | This contains the functions to handle the platform driver. |
4 | |
5 | Copyright (C) 2007-2011 STMicroelectronics Ltd |
6 | |
7 | |
8 | Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> |
9 | *******************************************************************************/ |
10 | |
11 | #include <linux/device.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/pm_runtime.h> |
14 | #include <linux/module.h> |
15 | #include <linux/io.h> |
16 | #include <linux/of.h> |
17 | #include <linux/of_net.h> |
18 | #include <linux/of_mdio.h> |
19 | |
20 | #include "stmmac.h" |
21 | #include "stmmac_platform.h" |
22 | |
23 | #ifdef CONFIG_OF |
24 | |
25 | /** |
26 | * dwmac1000_validate_mcast_bins - validates the number of Multicast filter bins |
27 | * @dev: struct device of the platform device |
28 | * @mcast_bins: Multicast filtering bins |
29 | * Description: |
30 | * this function validates the number of Multicast filtering bins specified |
31 | * by the configuration through the device tree. The Synopsys GMAC supports |
32 | * 64 bins, 128 bins, or 256 bins. "bins" refer to the division of CRC |
33 | * number space. 64 bins correspond to 6 bits of the CRC, 128 corresponds |
34 | * to 7 bits, and 256 refers to 8 bits of the CRC. Any other setting is |
35 | * invalid and will cause the filtering algorithm to use Multicast |
36 | * promiscuous mode. |
37 | */ |
38 | static int dwmac1000_validate_mcast_bins(struct device *dev, int mcast_bins) |
39 | { |
40 | int x = mcast_bins; |
41 | |
42 | switch (x) { |
43 | case HASH_TABLE_SIZE: |
44 | case 128: |
45 | case 256: |
46 | break; |
47 | default: |
48 | x = 0; |
49 | dev_info(dev, "Hash table entries set to unexpected value %d\n" , |
50 | mcast_bins); |
51 | break; |
52 | } |
53 | return x; |
54 | } |
55 | |
56 | /** |
57 | * dwmac1000_validate_ucast_entries - validate the Unicast address entries |
58 | * @dev: struct device of the platform device |
59 | * @ucast_entries: number of Unicast address entries |
60 | * Description: |
61 | * This function validates the number of Unicast address entries supported |
62 | * by a particular Synopsys 10/100/1000 controller. The Synopsys controller |
63 | * supports 1..32, 64, or 128 Unicast filter entries for it's Unicast filter |
64 | * logic. This function validates a valid, supported configuration is |
65 | * selected, and defaults to 1 Unicast address if an unsupported |
66 | * configuration is selected. |
67 | */ |
68 | static int dwmac1000_validate_ucast_entries(struct device *dev, |
69 | int ucast_entries) |
70 | { |
71 | int x = ucast_entries; |
72 | |
73 | switch (x) { |
74 | case 1 ... 32: |
75 | case 64: |
76 | case 128: |
77 | break; |
78 | default: |
79 | x = 1; |
80 | dev_info(dev, "Unicast table entries set to unexpected value %d\n" , |
81 | ucast_entries); |
82 | break; |
83 | } |
84 | return x; |
85 | } |
86 | |
87 | /** |
88 | * stmmac_axi_setup - parse DT parameters for programming the AXI register |
89 | * @pdev: platform device |
90 | * Description: |
91 | * if required, from device-tree the AXI internal register can be tuned |
92 | * by using platform parameters. |
93 | */ |
94 | static struct stmmac_axi *stmmac_axi_setup(struct platform_device *pdev) |
95 | { |
96 | struct device_node *np; |
97 | struct stmmac_axi *axi; |
98 | |
99 | np = of_parse_phandle(np: pdev->dev.of_node, phandle_name: "snps,axi-config" , index: 0); |
100 | if (!np) |
101 | return NULL; |
102 | |
103 | axi = devm_kzalloc(dev: &pdev->dev, size: sizeof(*axi), GFP_KERNEL); |
104 | if (!axi) { |
105 | of_node_put(node: np); |
106 | return ERR_PTR(error: -ENOMEM); |
107 | } |
108 | |
109 | axi->axi_lpi_en = of_property_read_bool(np, propname: "snps,lpi_en" ); |
110 | axi->axi_xit_frm = of_property_read_bool(np, propname: "snps,xit_frm" ); |
111 | axi->axi_kbbe = of_property_read_bool(np, propname: "snps,kbbe" ); |
112 | axi->axi_fb = of_property_read_bool(np, propname: "snps,fb" ); |
113 | axi->axi_mb = of_property_read_bool(np, propname: "snps,mb" ); |
114 | axi->axi_rb = of_property_read_bool(np, propname: "snps,rb" ); |
115 | |
116 | if (of_property_read_u32(np, propname: "snps,wr_osr_lmt" , out_value: &axi->axi_wr_osr_lmt)) |
117 | axi->axi_wr_osr_lmt = 1; |
118 | if (of_property_read_u32(np, propname: "snps,rd_osr_lmt" , out_value: &axi->axi_rd_osr_lmt)) |
119 | axi->axi_rd_osr_lmt = 1; |
120 | of_property_read_u32_array(np, propname: "snps,blen" , out_values: axi->axi_blen, AXI_BLEN); |
121 | of_node_put(node: np); |
122 | |
123 | return axi; |
124 | } |
125 | |
126 | /** |
127 | * stmmac_mtl_setup - parse DT parameters for multiple queues configuration |
128 | * @pdev: platform device |
129 | * @plat: enet data |
130 | */ |
131 | static int stmmac_mtl_setup(struct platform_device *pdev, |
132 | struct plat_stmmacenet_data *plat) |
133 | { |
134 | struct device_node *q_node; |
135 | struct device_node *rx_node; |
136 | struct device_node *tx_node; |
137 | u8 queue = 0; |
138 | int ret = 0; |
139 | |
140 | /* For backwards-compatibility with device trees that don't have any |
141 | * snps,mtl-rx-config or snps,mtl-tx-config properties, we fall back |
142 | * to one RX and TX queues each. |
143 | */ |
144 | plat->rx_queues_to_use = 1; |
145 | plat->tx_queues_to_use = 1; |
146 | |
147 | /* First Queue must always be in DCB mode. As MTL_QUEUE_DCB = 1 we need |
148 | * to always set this, otherwise Queue will be classified as AVB |
149 | * (because MTL_QUEUE_AVB = 0). |
150 | */ |
151 | plat->rx_queues_cfg[0].mode_to_use = MTL_QUEUE_DCB; |
152 | plat->tx_queues_cfg[0].mode_to_use = MTL_QUEUE_DCB; |
153 | |
154 | rx_node = of_parse_phandle(np: pdev->dev.of_node, phandle_name: "snps,mtl-rx-config" , index: 0); |
155 | if (!rx_node) |
156 | return ret; |
157 | |
158 | tx_node = of_parse_phandle(np: pdev->dev.of_node, phandle_name: "snps,mtl-tx-config" , index: 0); |
159 | if (!tx_node) { |
160 | of_node_put(node: rx_node); |
161 | return ret; |
162 | } |
163 | |
164 | /* Processing RX queues common config */ |
165 | if (of_property_read_u32(np: rx_node, propname: "snps,rx-queues-to-use" , |
166 | out_value: &plat->rx_queues_to_use)) |
167 | plat->rx_queues_to_use = 1; |
168 | |
169 | if (of_property_read_bool(np: rx_node, propname: "snps,rx-sched-sp" )) |
170 | plat->rx_sched_algorithm = MTL_RX_ALGORITHM_SP; |
171 | else if (of_property_read_bool(np: rx_node, propname: "snps,rx-sched-wsp" )) |
172 | plat->rx_sched_algorithm = MTL_RX_ALGORITHM_WSP; |
173 | else |
174 | plat->rx_sched_algorithm = MTL_RX_ALGORITHM_SP; |
175 | |
176 | /* Processing individual RX queue config */ |
177 | for_each_child_of_node(rx_node, q_node) { |
178 | if (queue >= plat->rx_queues_to_use) |
179 | break; |
180 | |
181 | if (of_property_read_bool(np: q_node, propname: "snps,dcb-algorithm" )) |
182 | plat->rx_queues_cfg[queue].mode_to_use = MTL_QUEUE_DCB; |
183 | else if (of_property_read_bool(np: q_node, propname: "snps,avb-algorithm" )) |
184 | plat->rx_queues_cfg[queue].mode_to_use = MTL_QUEUE_AVB; |
185 | else |
186 | plat->rx_queues_cfg[queue].mode_to_use = MTL_QUEUE_DCB; |
187 | |
188 | if (of_property_read_u32(np: q_node, propname: "snps,map-to-dma-channel" , |
189 | out_value: &plat->rx_queues_cfg[queue].chan)) |
190 | plat->rx_queues_cfg[queue].chan = queue; |
191 | /* TODO: Dynamic mapping to be included in the future */ |
192 | |
193 | if (of_property_read_u32(np: q_node, propname: "snps,priority" , |
194 | out_value: &plat->rx_queues_cfg[queue].prio)) { |
195 | plat->rx_queues_cfg[queue].prio = 0; |
196 | plat->rx_queues_cfg[queue].use_prio = false; |
197 | } else { |
198 | plat->rx_queues_cfg[queue].use_prio = true; |
199 | } |
200 | |
201 | /* RX queue specific packet type routing */ |
202 | if (of_property_read_bool(np: q_node, propname: "snps,route-avcp" )) |
203 | plat->rx_queues_cfg[queue].pkt_route = PACKET_AVCPQ; |
204 | else if (of_property_read_bool(np: q_node, propname: "snps,route-ptp" )) |
205 | plat->rx_queues_cfg[queue].pkt_route = PACKET_PTPQ; |
206 | else if (of_property_read_bool(np: q_node, propname: "snps,route-dcbcp" )) |
207 | plat->rx_queues_cfg[queue].pkt_route = PACKET_DCBCPQ; |
208 | else if (of_property_read_bool(np: q_node, propname: "snps,route-up" )) |
209 | plat->rx_queues_cfg[queue].pkt_route = PACKET_UPQ; |
210 | else if (of_property_read_bool(np: q_node, propname: "snps,route-multi-broad" )) |
211 | plat->rx_queues_cfg[queue].pkt_route = PACKET_MCBCQ; |
212 | else |
213 | plat->rx_queues_cfg[queue].pkt_route = 0x0; |
214 | |
215 | queue++; |
216 | } |
217 | if (queue != plat->rx_queues_to_use) { |
218 | ret = -EINVAL; |
219 | dev_err(&pdev->dev, "Not all RX queues were configured\n" ); |
220 | goto out; |
221 | } |
222 | |
223 | /* Processing TX queues common config */ |
224 | if (of_property_read_u32(np: tx_node, propname: "snps,tx-queues-to-use" , |
225 | out_value: &plat->tx_queues_to_use)) |
226 | plat->tx_queues_to_use = 1; |
227 | |
228 | if (of_property_read_bool(np: tx_node, propname: "snps,tx-sched-wrr" )) |
229 | plat->tx_sched_algorithm = MTL_TX_ALGORITHM_WRR; |
230 | else if (of_property_read_bool(np: tx_node, propname: "snps,tx-sched-wfq" )) |
231 | plat->tx_sched_algorithm = MTL_TX_ALGORITHM_WFQ; |
232 | else if (of_property_read_bool(np: tx_node, propname: "snps,tx-sched-dwrr" )) |
233 | plat->tx_sched_algorithm = MTL_TX_ALGORITHM_DWRR; |
234 | else |
235 | plat->tx_sched_algorithm = MTL_TX_ALGORITHM_SP; |
236 | |
237 | queue = 0; |
238 | |
239 | /* Processing individual TX queue config */ |
240 | for_each_child_of_node(tx_node, q_node) { |
241 | if (queue >= plat->tx_queues_to_use) |
242 | break; |
243 | |
244 | if (of_property_read_u32(np: q_node, propname: "snps,weight" , |
245 | out_value: &plat->tx_queues_cfg[queue].weight)) |
246 | plat->tx_queues_cfg[queue].weight = 0x10 + queue; |
247 | |
248 | if (of_property_read_bool(np: q_node, propname: "snps,dcb-algorithm" )) { |
249 | plat->tx_queues_cfg[queue].mode_to_use = MTL_QUEUE_DCB; |
250 | } else if (of_property_read_bool(np: q_node, |
251 | propname: "snps,avb-algorithm" )) { |
252 | plat->tx_queues_cfg[queue].mode_to_use = MTL_QUEUE_AVB; |
253 | |
254 | /* Credit Base Shaper parameters used by AVB */ |
255 | if (of_property_read_u32(np: q_node, propname: "snps,send_slope" , |
256 | out_value: &plat->tx_queues_cfg[queue].send_slope)) |
257 | plat->tx_queues_cfg[queue].send_slope = 0x0; |
258 | if (of_property_read_u32(np: q_node, propname: "snps,idle_slope" , |
259 | out_value: &plat->tx_queues_cfg[queue].idle_slope)) |
260 | plat->tx_queues_cfg[queue].idle_slope = 0x0; |
261 | if (of_property_read_u32(np: q_node, propname: "snps,high_credit" , |
262 | out_value: &plat->tx_queues_cfg[queue].high_credit)) |
263 | plat->tx_queues_cfg[queue].high_credit = 0x0; |
264 | if (of_property_read_u32(np: q_node, propname: "snps,low_credit" , |
265 | out_value: &plat->tx_queues_cfg[queue].low_credit)) |
266 | plat->tx_queues_cfg[queue].low_credit = 0x0; |
267 | } else { |
268 | plat->tx_queues_cfg[queue].mode_to_use = MTL_QUEUE_DCB; |
269 | } |
270 | |
271 | if (of_property_read_u32(np: q_node, propname: "snps,priority" , |
272 | out_value: &plat->tx_queues_cfg[queue].prio)) { |
273 | plat->tx_queues_cfg[queue].prio = 0; |
274 | plat->tx_queues_cfg[queue].use_prio = false; |
275 | } else { |
276 | plat->tx_queues_cfg[queue].use_prio = true; |
277 | } |
278 | |
279 | plat->tx_queues_cfg[queue].coe_unsupported = |
280 | of_property_read_bool(np: q_node, propname: "snps,coe-unsupported" ); |
281 | |
282 | queue++; |
283 | } |
284 | if (queue != plat->tx_queues_to_use) { |
285 | ret = -EINVAL; |
286 | dev_err(&pdev->dev, "Not all TX queues were configured\n" ); |
287 | goto out; |
288 | } |
289 | |
290 | out: |
291 | of_node_put(node: rx_node); |
292 | of_node_put(node: tx_node); |
293 | of_node_put(node: q_node); |
294 | |
295 | return ret; |
296 | } |
297 | |
298 | /** |
299 | * stmmac_of_get_mdio() - Gets the MDIO bus from the devicetree. |
300 | * @np: devicetree node |
301 | * |
302 | * The MDIO bus will be searched for in the following ways: |
303 | * 1. The compatible is "snps,dwc-qos-ethernet-4.10" && a "mdio" named |
304 | * child node exists |
305 | * 2. A child node with the "snps,dwmac-mdio" compatible is present |
306 | * |
307 | * Return: The MDIO node if present otherwise NULL |
308 | */ |
309 | static struct device_node *stmmac_of_get_mdio(struct device_node *np) |
310 | { |
311 | static const struct of_device_id need_mdio_ids[] = { |
312 | { .compatible = "snps,dwc-qos-ethernet-4.10" }, |
313 | {}, |
314 | }; |
315 | struct device_node *mdio_node = NULL; |
316 | |
317 | if (of_match_node(matches: need_mdio_ids, node: np)) { |
318 | mdio_node = of_get_child_by_name(node: np, name: "mdio" ); |
319 | } else { |
320 | /** |
321 | * If snps,dwmac-mdio is passed from DT, always register |
322 | * the MDIO |
323 | */ |
324 | for_each_child_of_node(np, mdio_node) { |
325 | if (of_device_is_compatible(device: mdio_node, |
326 | "snps,dwmac-mdio" )) |
327 | break; |
328 | } |
329 | } |
330 | |
331 | return mdio_node; |
332 | } |
333 | |
334 | /** |
335 | * stmmac_mdio_setup() - Populate platform related MDIO structures. |
336 | * @plat: driver data platform structure |
337 | * @np: devicetree node |
338 | * @dev: device pointer |
339 | * |
340 | * This searches for MDIO information from the devicetree. |
341 | * If an MDIO node is found, it's assigned to plat->mdio_node and |
342 | * plat->mdio_bus_data is allocated. |
343 | * If no connection can be determined, just plat->mdio_bus_data is allocated |
344 | * to indicate a bus should be created and scanned for a phy. |
345 | * If it's determined there's no MDIO bus needed, both are left NULL. |
346 | * |
347 | * This expects that plat->phy_node has already been searched for. |
348 | * |
349 | * Return: 0 on success, errno otherwise. |
350 | */ |
351 | static int stmmac_mdio_setup(struct plat_stmmacenet_data *plat, |
352 | struct device_node *np, struct device *dev) |
353 | { |
354 | bool legacy_mdio; |
355 | |
356 | plat->mdio_node = stmmac_of_get_mdio(np); |
357 | if (plat->mdio_node) |
358 | dev_dbg(dev, "Found MDIO subnode\n" ); |
359 | |
360 | /* Legacy devicetrees allowed for no MDIO bus description and expect |
361 | * the bus to be scanned for devices. If there's no phy or fixed-link |
362 | * described assume this is the case since there must be something |
363 | * connected to the MAC. |
364 | */ |
365 | legacy_mdio = !of_phy_is_fixed_link(np) && !plat->phy_node; |
366 | if (legacy_mdio) |
367 | dev_info(dev, "Deprecated MDIO bus assumption used\n" ); |
368 | |
369 | if (plat->mdio_node || legacy_mdio) { |
370 | plat->mdio_bus_data = devm_kzalloc(dev, |
371 | size: sizeof(*plat->mdio_bus_data), |
372 | GFP_KERNEL); |
373 | if (!plat->mdio_bus_data) |
374 | return -ENOMEM; |
375 | |
376 | plat->mdio_bus_data->needs_reset = true; |
377 | } |
378 | |
379 | return 0; |
380 | } |
381 | |
382 | /** |
383 | * stmmac_of_get_mac_mode - retrieves the interface of the MAC |
384 | * @np: - device-tree node |
385 | * Description: |
386 | * Similar to `of_get_phy_mode()`, this function will retrieve (from |
387 | * the device-tree) the interface mode on the MAC side. This assumes |
388 | * that there is mode converter in-between the MAC & PHY |
389 | * (e.g. GMII-to-RGMII). |
390 | */ |
391 | static int stmmac_of_get_mac_mode(struct device_node *np) |
392 | { |
393 | const char *pm; |
394 | int err, i; |
395 | |
396 | err = of_property_read_string(np, propname: "mac-mode" , out_string: &pm); |
397 | if (err < 0) |
398 | return err; |
399 | |
400 | for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) { |
401 | if (!strcasecmp(s1: pm, s2: phy_modes(interface: i))) |
402 | return i; |
403 | } |
404 | |
405 | return -ENODEV; |
406 | } |
407 | |
408 | /** |
409 | * stmmac_remove_config_dt - undo the effects of stmmac_probe_config_dt() |
410 | * @pdev: platform_device structure |
411 | * @plat: driver data platform structure |
412 | * |
413 | * Release resources claimed by stmmac_probe_config_dt(). |
414 | */ |
415 | static void stmmac_remove_config_dt(struct platform_device *pdev, |
416 | struct plat_stmmacenet_data *plat) |
417 | { |
418 | clk_disable_unprepare(clk: plat->stmmac_clk); |
419 | clk_disable_unprepare(clk: plat->pclk); |
420 | of_node_put(node: plat->phy_node); |
421 | of_node_put(node: plat->mdio_node); |
422 | } |
423 | |
424 | /** |
425 | * stmmac_probe_config_dt - parse device-tree driver parameters |
426 | * @pdev: platform_device structure |
427 | * @mac: MAC address to use |
428 | * Description: |
429 | * this function is to read the driver parameters from device-tree and |
430 | * set some private fields that will be used by the main at runtime. |
431 | */ |
432 | static struct plat_stmmacenet_data * |
433 | stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac) |
434 | { |
435 | struct device_node *np = pdev->dev.of_node; |
436 | struct plat_stmmacenet_data *plat; |
437 | struct stmmac_dma_cfg *dma_cfg; |
438 | int phy_mode; |
439 | void *ret; |
440 | int rc; |
441 | |
442 | plat = devm_kzalloc(dev: &pdev->dev, size: sizeof(*plat), GFP_KERNEL); |
443 | if (!plat) |
444 | return ERR_PTR(error: -ENOMEM); |
445 | |
446 | rc = of_get_mac_address(np, mac); |
447 | if (rc) { |
448 | if (rc == -EPROBE_DEFER) |
449 | return ERR_PTR(error: rc); |
450 | |
451 | eth_zero_addr(addr: mac); |
452 | } |
453 | |
454 | phy_mode = device_get_phy_mode(dev: &pdev->dev); |
455 | if (phy_mode < 0) |
456 | return ERR_PTR(error: phy_mode); |
457 | |
458 | plat->phy_interface = phy_mode; |
459 | rc = stmmac_of_get_mac_mode(np); |
460 | plat->mac_interface = rc < 0 ? plat->phy_interface : rc; |
461 | |
462 | /* Some wrapper drivers still rely on phy_node. Let's save it while |
463 | * they are not converted to phylink. */ |
464 | plat->phy_node = of_parse_phandle(np, phandle_name: "phy-handle" , index: 0); |
465 | |
466 | /* PHYLINK automatically parses the phy-handle property */ |
467 | plat->port_node = of_fwnode_handle(np); |
468 | |
469 | /* Get max speed of operation from device tree */ |
470 | of_property_read_u32(np, propname: "max-speed" , out_value: &plat->max_speed); |
471 | |
472 | plat->bus_id = of_alias_get_id(np, stem: "ethernet" ); |
473 | if (plat->bus_id < 0) |
474 | plat->bus_id = 0; |
475 | |
476 | /* Default to phy auto-detection */ |
477 | plat->phy_addr = -1; |
478 | |
479 | /* Default to get clk_csr from stmmac_clk_csr_set(), |
480 | * or get clk_csr from device tree. |
481 | */ |
482 | plat->clk_csr = -1; |
483 | if (of_property_read_u32(np, propname: "snps,clk-csr" , out_value: &plat->clk_csr)) |
484 | of_property_read_u32(np, propname: "clk_csr" , out_value: &plat->clk_csr); |
485 | |
486 | /* "snps,phy-addr" is not a standard property. Mark it as deprecated |
487 | * and warn of its use. Remove this when phy node support is added. |
488 | */ |
489 | if (of_property_read_u32(np, propname: "snps,phy-addr" , out_value: &plat->phy_addr) == 0) |
490 | dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n" ); |
491 | |
492 | rc = stmmac_mdio_setup(plat, np, dev: &pdev->dev); |
493 | if (rc) |
494 | return ERR_PTR(error: rc); |
495 | |
496 | of_property_read_u32(np, propname: "tx-fifo-depth" , out_value: &plat->tx_fifo_size); |
497 | |
498 | of_property_read_u32(np, propname: "rx-fifo-depth" , out_value: &plat->rx_fifo_size); |
499 | |
500 | plat->force_sf_dma_mode = |
501 | of_property_read_bool(np, propname: "snps,force_sf_dma_mode" ); |
502 | |
503 | if (of_property_read_bool(np, propname: "snps,en-tx-lpi-clockgating" )) |
504 | plat->flags |= STMMAC_FLAG_EN_TX_LPI_CLOCKGATING; |
505 | |
506 | /* Set the maxmtu to a default of JUMBO_LEN in case the |
507 | * parameter is not present in the device tree. |
508 | */ |
509 | plat->maxmtu = JUMBO_LEN; |
510 | |
511 | /* Set default value for multicast hash bins */ |
512 | plat->multicast_filter_bins = HASH_TABLE_SIZE; |
513 | |
514 | /* Set default value for unicast filter entries */ |
515 | plat->unicast_filter_entries = 1; |
516 | |
517 | /* |
518 | * Currently only the properties needed on SPEAr600 |
519 | * are provided. All other properties should be added |
520 | * once needed on other platforms. |
521 | */ |
522 | if (of_device_is_compatible(device: np, "st,spear600-gmac" ) || |
523 | of_device_is_compatible(device: np, "snps,dwmac-3.50a" ) || |
524 | of_device_is_compatible(device: np, "snps,dwmac-3.70a" ) || |
525 | of_device_is_compatible(device: np, "snps,dwmac" )) { |
526 | /* Note that the max-frame-size parameter as defined in the |
527 | * ePAPR v1.1 spec is defined as max-frame-size, it's |
528 | * actually used as the IEEE definition of MAC Client |
529 | * data, or MTU. The ePAPR specification is confusing as |
530 | * the definition is max-frame-size, but usage examples |
531 | * are clearly MTUs |
532 | */ |
533 | of_property_read_u32(np, propname: "max-frame-size" , out_value: &plat->maxmtu); |
534 | of_property_read_u32(np, propname: "snps,multicast-filter-bins" , |
535 | out_value: &plat->multicast_filter_bins); |
536 | of_property_read_u32(np, propname: "snps,perfect-filter-entries" , |
537 | out_value: &plat->unicast_filter_entries); |
538 | plat->unicast_filter_entries = dwmac1000_validate_ucast_entries( |
539 | dev: &pdev->dev, ucast_entries: plat->unicast_filter_entries); |
540 | plat->multicast_filter_bins = dwmac1000_validate_mcast_bins( |
541 | dev: &pdev->dev, mcast_bins: plat->multicast_filter_bins); |
542 | plat->has_gmac = 1; |
543 | plat->pmt = 1; |
544 | } |
545 | |
546 | if (of_device_is_compatible(device: np, "snps,dwmac-3.40a" )) { |
547 | plat->has_gmac = 1; |
548 | plat->enh_desc = 1; |
549 | plat->tx_coe = 1; |
550 | plat->bugged_jumbo = 1; |
551 | plat->pmt = 1; |
552 | } |
553 | |
554 | if (of_device_is_compatible(device: np, "snps,dwmac-4.00" ) || |
555 | of_device_is_compatible(device: np, "snps,dwmac-4.10a" ) || |
556 | of_device_is_compatible(device: np, "snps,dwmac-4.20a" ) || |
557 | of_device_is_compatible(device: np, "snps,dwmac-5.10a" ) || |
558 | of_device_is_compatible(device: np, "snps,dwmac-5.20" )) { |
559 | plat->has_gmac4 = 1; |
560 | plat->has_gmac = 0; |
561 | plat->pmt = 1; |
562 | if (of_property_read_bool(np, propname: "snps,tso" )) |
563 | plat->flags |= STMMAC_FLAG_TSO_EN; |
564 | } |
565 | |
566 | if (of_device_is_compatible(device: np, "snps,dwmac-3.610" ) || |
567 | of_device_is_compatible(device: np, "snps,dwmac-3.710" )) { |
568 | plat->enh_desc = 1; |
569 | plat->bugged_jumbo = 1; |
570 | plat->force_sf_dma_mode = 1; |
571 | } |
572 | |
573 | if (of_device_is_compatible(device: np, "snps,dwxgmac" )) { |
574 | plat->has_xgmac = 1; |
575 | plat->pmt = 1; |
576 | if (of_property_read_bool(np, propname: "snps,tso" )) |
577 | plat->flags |= STMMAC_FLAG_TSO_EN; |
578 | } |
579 | |
580 | dma_cfg = devm_kzalloc(dev: &pdev->dev, size: sizeof(*dma_cfg), |
581 | GFP_KERNEL); |
582 | if (!dma_cfg) { |
583 | stmmac_remove_config_dt(pdev, plat); |
584 | return ERR_PTR(error: -ENOMEM); |
585 | } |
586 | plat->dma_cfg = dma_cfg; |
587 | |
588 | of_property_read_u32(np, propname: "snps,pbl" , out_value: &dma_cfg->pbl); |
589 | if (!dma_cfg->pbl) |
590 | dma_cfg->pbl = DEFAULT_DMA_PBL; |
591 | of_property_read_u32(np, propname: "snps,txpbl" , out_value: &dma_cfg->txpbl); |
592 | of_property_read_u32(np, propname: "snps,rxpbl" , out_value: &dma_cfg->rxpbl); |
593 | dma_cfg->pblx8 = !of_property_read_bool(np, propname: "snps,no-pbl-x8" ); |
594 | |
595 | dma_cfg->aal = of_property_read_bool(np, propname: "snps,aal" ); |
596 | dma_cfg->fixed_burst = of_property_read_bool(np, propname: "snps,fixed-burst" ); |
597 | dma_cfg->mixed_burst = of_property_read_bool(np, propname: "snps,mixed-burst" ); |
598 | |
599 | plat->force_thresh_dma_mode = of_property_read_bool(np, propname: "snps,force_thresh_dma_mode" ); |
600 | if (plat->force_thresh_dma_mode && plat->force_sf_dma_mode) { |
601 | plat->force_sf_dma_mode = 0; |
602 | dev_warn(&pdev->dev, |
603 | "force_sf_dma_mode is ignored if force_thresh_dma_mode is set.\n" ); |
604 | } |
605 | |
606 | of_property_read_u32(np, propname: "snps,ps-speed" , out_value: &plat->mac_port_sel_speed); |
607 | |
608 | plat->axi = stmmac_axi_setup(pdev); |
609 | |
610 | rc = stmmac_mtl_setup(pdev, plat); |
611 | if (rc) { |
612 | stmmac_remove_config_dt(pdev, plat); |
613 | return ERR_PTR(error: rc); |
614 | } |
615 | |
616 | /* clock setup */ |
617 | if (!of_device_is_compatible(device: np, "snps,dwc-qos-ethernet-4.10" )) { |
618 | plat->stmmac_clk = devm_clk_get(dev: &pdev->dev, |
619 | STMMAC_RESOURCE_NAME); |
620 | if (IS_ERR(ptr: plat->stmmac_clk)) { |
621 | dev_warn(&pdev->dev, "Cannot get CSR clock\n" ); |
622 | plat->stmmac_clk = NULL; |
623 | } |
624 | clk_prepare_enable(clk: plat->stmmac_clk); |
625 | } |
626 | |
627 | plat->pclk = devm_clk_get_optional(dev: &pdev->dev, id: "pclk" ); |
628 | if (IS_ERR(ptr: plat->pclk)) { |
629 | ret = plat->pclk; |
630 | goto error_pclk_get; |
631 | } |
632 | clk_prepare_enable(clk: plat->pclk); |
633 | |
634 | /* Fall-back to main clock in case of no PTP ref is passed */ |
635 | plat->clk_ptp_ref = devm_clk_get(dev: &pdev->dev, id: "ptp_ref" ); |
636 | if (IS_ERR(ptr: plat->clk_ptp_ref)) { |
637 | plat->clk_ptp_rate = clk_get_rate(clk: plat->stmmac_clk); |
638 | plat->clk_ptp_ref = NULL; |
639 | dev_info(&pdev->dev, "PTP uses main clock\n" ); |
640 | } else { |
641 | plat->clk_ptp_rate = clk_get_rate(clk: plat->clk_ptp_ref); |
642 | dev_dbg(&pdev->dev, "PTP rate %d\n" , plat->clk_ptp_rate); |
643 | } |
644 | |
645 | plat->stmmac_rst = devm_reset_control_get_optional(dev: &pdev->dev, |
646 | STMMAC_RESOURCE_NAME); |
647 | if (IS_ERR(ptr: plat->stmmac_rst)) { |
648 | ret = plat->stmmac_rst; |
649 | goto error_hw_init; |
650 | } |
651 | |
652 | plat->stmmac_ahb_rst = devm_reset_control_get_optional_shared( |
653 | dev: &pdev->dev, id: "ahb" ); |
654 | if (IS_ERR(ptr: plat->stmmac_ahb_rst)) { |
655 | ret = plat->stmmac_ahb_rst; |
656 | goto error_hw_init; |
657 | } |
658 | |
659 | return plat; |
660 | |
661 | error_hw_init: |
662 | clk_disable_unprepare(clk: plat->pclk); |
663 | error_pclk_get: |
664 | clk_disable_unprepare(clk: plat->stmmac_clk); |
665 | |
666 | return ret; |
667 | } |
668 | |
669 | static void devm_stmmac_remove_config_dt(void *data) |
670 | { |
671 | struct plat_stmmacenet_data *plat = data; |
672 | |
673 | /* Platform data argument is unused */ |
674 | stmmac_remove_config_dt(NULL, plat); |
675 | } |
676 | |
677 | /** |
678 | * devm_stmmac_probe_config_dt |
679 | * @pdev: platform_device structure |
680 | * @mac: MAC address to use |
681 | * Description: Devres variant of stmmac_probe_config_dt(). Does not require |
682 | * the user to call stmmac_remove_config_dt() at driver detach. |
683 | */ |
684 | struct plat_stmmacenet_data * |
685 | devm_stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac) |
686 | { |
687 | struct plat_stmmacenet_data *plat; |
688 | int ret; |
689 | |
690 | plat = stmmac_probe_config_dt(pdev, mac); |
691 | if (IS_ERR(ptr: plat)) |
692 | return plat; |
693 | |
694 | ret = devm_add_action_or_reset(&pdev->dev, |
695 | devm_stmmac_remove_config_dt, plat); |
696 | if (ret) |
697 | return ERR_PTR(error: ret); |
698 | |
699 | return plat; |
700 | } |
701 | #else |
702 | struct plat_stmmacenet_data * |
703 | devm_stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac) |
704 | { |
705 | return ERR_PTR(-EINVAL); |
706 | } |
707 | #endif /* CONFIG_OF */ |
708 | EXPORT_SYMBOL_GPL(devm_stmmac_probe_config_dt); |
709 | |
710 | int stmmac_get_platform_resources(struct platform_device *pdev, |
711 | struct stmmac_resources *stmmac_res) |
712 | { |
713 | memset(stmmac_res, 0, sizeof(*stmmac_res)); |
714 | |
715 | /* Get IRQ information early to have an ability to ask for deferred |
716 | * probe if needed before we went too far with resource allocation. |
717 | */ |
718 | stmmac_res->irq = platform_get_irq_byname(pdev, "macirq" ); |
719 | if (stmmac_res->irq < 0) |
720 | return stmmac_res->irq; |
721 | |
722 | /* On some platforms e.g. SPEAr the wake up irq differs from the mac irq |
723 | * The external wake up irq can be passed through the platform code |
724 | * named as "eth_wake_irq" |
725 | * |
726 | * In case the wake up interrupt is not passed from the platform |
727 | * so the driver will continue to use the mac irq (ndev->irq) |
728 | */ |
729 | stmmac_res->wol_irq = |
730 | platform_get_irq_byname_optional(dev: pdev, name: "eth_wake_irq" ); |
731 | if (stmmac_res->wol_irq < 0) { |
732 | if (stmmac_res->wol_irq == -EPROBE_DEFER) |
733 | return -EPROBE_DEFER; |
734 | dev_info(&pdev->dev, "IRQ eth_wake_irq not found\n" ); |
735 | stmmac_res->wol_irq = stmmac_res->irq; |
736 | } |
737 | |
738 | stmmac_res->lpi_irq = |
739 | platform_get_irq_byname_optional(dev: pdev, name: "eth_lpi" ); |
740 | if (stmmac_res->lpi_irq < 0) { |
741 | if (stmmac_res->lpi_irq == -EPROBE_DEFER) |
742 | return -EPROBE_DEFER; |
743 | dev_info(&pdev->dev, "IRQ eth_lpi not found\n" ); |
744 | } |
745 | |
746 | stmmac_res->sfty_irq = |
747 | platform_get_irq_byname_optional(dev: pdev, name: "sfty" ); |
748 | if (stmmac_res->sfty_irq < 0) { |
749 | if (stmmac_res->sfty_irq == -EPROBE_DEFER) |
750 | return -EPROBE_DEFER; |
751 | dev_info(&pdev->dev, "IRQ sfty not found\n" ); |
752 | } |
753 | |
754 | stmmac_res->addr = devm_platform_ioremap_resource(pdev, index: 0); |
755 | |
756 | return PTR_ERR_OR_ZERO(ptr: stmmac_res->addr); |
757 | } |
758 | EXPORT_SYMBOL_GPL(stmmac_get_platform_resources); |
759 | |
760 | /** |
761 | * stmmac_pltfr_init |
762 | * @pdev: pointer to the platform device |
763 | * @plat: driver data platform structure |
764 | * Description: Call the platform's init callback (if any) and propagate |
765 | * the return value. |
766 | */ |
767 | int stmmac_pltfr_init(struct platform_device *pdev, |
768 | struct plat_stmmacenet_data *plat) |
769 | { |
770 | int ret = 0; |
771 | |
772 | if (plat->init) |
773 | ret = plat->init(pdev, plat->bsp_priv); |
774 | |
775 | return ret; |
776 | } |
777 | EXPORT_SYMBOL_GPL(stmmac_pltfr_init); |
778 | |
779 | /** |
780 | * stmmac_pltfr_exit |
781 | * @pdev: pointer to the platform device |
782 | * @plat: driver data platform structure |
783 | * Description: Call the platform's exit callback (if any). |
784 | */ |
785 | void stmmac_pltfr_exit(struct platform_device *pdev, |
786 | struct plat_stmmacenet_data *plat) |
787 | { |
788 | if (plat->exit) |
789 | plat->exit(pdev, plat->bsp_priv); |
790 | } |
791 | EXPORT_SYMBOL_GPL(stmmac_pltfr_exit); |
792 | |
793 | /** |
794 | * stmmac_pltfr_probe |
795 | * @pdev: platform device pointer |
796 | * @plat: driver data platform structure |
797 | * @res: stmmac resources structure |
798 | * Description: This calls the platform's init() callback and probes the |
799 | * stmmac driver. |
800 | */ |
801 | int stmmac_pltfr_probe(struct platform_device *pdev, |
802 | struct plat_stmmacenet_data *plat, |
803 | struct stmmac_resources *res) |
804 | { |
805 | int ret; |
806 | |
807 | ret = stmmac_pltfr_init(pdev, plat); |
808 | if (ret) |
809 | return ret; |
810 | |
811 | ret = stmmac_dvr_probe(device: &pdev->dev, plat_dat: plat, res); |
812 | if (ret) { |
813 | stmmac_pltfr_exit(pdev, plat); |
814 | return ret; |
815 | } |
816 | |
817 | return ret; |
818 | } |
819 | EXPORT_SYMBOL_GPL(stmmac_pltfr_probe); |
820 | |
821 | static void devm_stmmac_pltfr_remove(void *data) |
822 | { |
823 | struct platform_device *pdev = data; |
824 | |
825 | stmmac_pltfr_remove(pdev); |
826 | } |
827 | |
828 | /** |
829 | * devm_stmmac_pltfr_probe |
830 | * @pdev: pointer to the platform device |
831 | * @plat: driver data platform structure |
832 | * @res: stmmac resources |
833 | * Description: Devres variant of stmmac_pltfr_probe(). Allows users to skip |
834 | * calling stmmac_pltfr_remove() on driver detach. |
835 | */ |
836 | int devm_stmmac_pltfr_probe(struct platform_device *pdev, |
837 | struct plat_stmmacenet_data *plat, |
838 | struct stmmac_resources *res) |
839 | { |
840 | int ret; |
841 | |
842 | ret = stmmac_pltfr_probe(pdev, plat, res); |
843 | if (ret) |
844 | return ret; |
845 | |
846 | return devm_add_action_or_reset(&pdev->dev, devm_stmmac_pltfr_remove, |
847 | pdev); |
848 | } |
849 | EXPORT_SYMBOL_GPL(devm_stmmac_pltfr_probe); |
850 | |
851 | /** |
852 | * stmmac_pltfr_remove |
853 | * @pdev: pointer to the platform device |
854 | * Description: This undoes the effects of stmmac_pltfr_probe() by removing the |
855 | * driver and calling the platform's exit() callback. |
856 | */ |
857 | void stmmac_pltfr_remove(struct platform_device *pdev) |
858 | { |
859 | struct net_device *ndev = platform_get_drvdata(pdev); |
860 | struct stmmac_priv *priv = netdev_priv(dev: ndev); |
861 | struct plat_stmmacenet_data *plat = priv->plat; |
862 | |
863 | stmmac_dvr_remove(dev: &pdev->dev); |
864 | stmmac_pltfr_exit(pdev, plat); |
865 | } |
866 | EXPORT_SYMBOL_GPL(stmmac_pltfr_remove); |
867 | |
868 | /** |
869 | * stmmac_pltfr_suspend |
870 | * @dev: device pointer |
871 | * Description: this function is invoked when suspend the driver and it direcly |
872 | * call the main suspend function and then, if required, on some platform, it |
873 | * can call an exit helper. |
874 | */ |
875 | static int __maybe_unused stmmac_pltfr_suspend(struct device *dev) |
876 | { |
877 | int ret; |
878 | struct net_device *ndev = dev_get_drvdata(dev); |
879 | struct stmmac_priv *priv = netdev_priv(dev: ndev); |
880 | struct platform_device *pdev = to_platform_device(dev); |
881 | |
882 | ret = stmmac_suspend(dev); |
883 | stmmac_pltfr_exit(pdev, priv->plat); |
884 | |
885 | return ret; |
886 | } |
887 | |
888 | /** |
889 | * stmmac_pltfr_resume |
890 | * @dev: device pointer |
891 | * Description: this function is invoked when resume the driver before calling |
892 | * the main resume function, on some platforms, it can call own init helper |
893 | * if required. |
894 | */ |
895 | static int __maybe_unused stmmac_pltfr_resume(struct device *dev) |
896 | { |
897 | struct net_device *ndev = dev_get_drvdata(dev); |
898 | struct stmmac_priv *priv = netdev_priv(dev: ndev); |
899 | struct platform_device *pdev = to_platform_device(dev); |
900 | int ret; |
901 | |
902 | ret = stmmac_pltfr_init(pdev, priv->plat); |
903 | if (ret) |
904 | return ret; |
905 | |
906 | return stmmac_resume(dev); |
907 | } |
908 | |
909 | static int __maybe_unused stmmac_runtime_suspend(struct device *dev) |
910 | { |
911 | struct net_device *ndev = dev_get_drvdata(dev); |
912 | struct stmmac_priv *priv = netdev_priv(dev: ndev); |
913 | |
914 | stmmac_bus_clks_config(priv, enabled: false); |
915 | |
916 | return 0; |
917 | } |
918 | |
919 | static int __maybe_unused stmmac_runtime_resume(struct device *dev) |
920 | { |
921 | struct net_device *ndev = dev_get_drvdata(dev); |
922 | struct stmmac_priv *priv = netdev_priv(dev: ndev); |
923 | |
924 | return stmmac_bus_clks_config(priv, enabled: true); |
925 | } |
926 | |
927 | static int __maybe_unused stmmac_pltfr_noirq_suspend(struct device *dev) |
928 | { |
929 | struct net_device *ndev = dev_get_drvdata(dev); |
930 | struct stmmac_priv *priv = netdev_priv(dev: ndev); |
931 | int ret; |
932 | |
933 | if (!netif_running(dev: ndev)) |
934 | return 0; |
935 | |
936 | if (!device_may_wakeup(dev: priv->device) || !priv->plat->pmt) { |
937 | /* Disable clock in case of PWM is off */ |
938 | clk_disable_unprepare(clk: priv->plat->clk_ptp_ref); |
939 | |
940 | ret = pm_runtime_force_suspend(dev); |
941 | if (ret) |
942 | return ret; |
943 | } |
944 | |
945 | return 0; |
946 | } |
947 | |
948 | static int __maybe_unused stmmac_pltfr_noirq_resume(struct device *dev) |
949 | { |
950 | struct net_device *ndev = dev_get_drvdata(dev); |
951 | struct stmmac_priv *priv = netdev_priv(dev: ndev); |
952 | int ret; |
953 | |
954 | if (!netif_running(dev: ndev)) |
955 | return 0; |
956 | |
957 | if (!device_may_wakeup(dev: priv->device) || !priv->plat->pmt) { |
958 | /* enable the clk previously disabled */ |
959 | ret = pm_runtime_force_resume(dev); |
960 | if (ret) |
961 | return ret; |
962 | |
963 | ret = clk_prepare_enable(clk: priv->plat->clk_ptp_ref); |
964 | if (ret < 0) { |
965 | netdev_warn(dev: priv->dev, |
966 | format: "failed to enable PTP reference clock: %pe\n" , |
967 | ERR_PTR(error: ret)); |
968 | return ret; |
969 | } |
970 | } |
971 | |
972 | return 0; |
973 | } |
974 | |
975 | const struct dev_pm_ops stmmac_pltfr_pm_ops = { |
976 | SET_SYSTEM_SLEEP_PM_OPS(stmmac_pltfr_suspend, stmmac_pltfr_resume) |
977 | SET_RUNTIME_PM_OPS(stmmac_runtime_suspend, stmmac_runtime_resume, NULL) |
978 | SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(stmmac_pltfr_noirq_suspend, stmmac_pltfr_noirq_resume) |
979 | }; |
980 | EXPORT_SYMBOL_GPL(stmmac_pltfr_pm_ops); |
981 | |
982 | MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet platform support" ); |
983 | MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>" ); |
984 | MODULE_LICENSE("GPL" ); |
985 | |