0b76e3de502d3b5255921a51425d420f9ec888c0
[linux-2.6-block.git] / drivers / net / ethernet / stmicro / stmmac / stmmac_platform.c
1 /*******************************************************************************
2   This contains the functions to handle the platform driver.
3
4   Copyright (C) 2007-2011  STMicroelectronics Ltd
5
6   This program is free software; you can redistribute it and/or modify it
7   under the terms and conditions of the GNU General Public License,
8   version 2, as published by the Free Software Foundation.
9
10   This program is distributed in the hope it will be useful, but WITHOUT
11   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13   more details.
14
15   The full GNU General Public License is included in this distribution in
16   the file called "COPYING".
17
18   Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
19 *******************************************************************************/
20
21 #include <linux/platform_device.h>
22 #include <linux/module.h>
23 #include <linux/io.h>
24 #include <linux/of.h>
25 #include <linux/of_net.h>
26 #include <linux/of_device.h>
27 #include <linux/of_mdio.h>
28
29 #include "stmmac.h"
30 #include "stmmac_platform.h"
31
32 #ifdef CONFIG_OF
33
34 /**
35  * dwmac1000_validate_mcast_bins - validates the number of Multicast filter bins
36  * @mcast_bins: Multicast filtering bins
37  * Description:
38  * this function validates the number of Multicast filtering bins specified
39  * by the configuration through the device tree. The Synopsys GMAC supports
40  * 64 bins, 128 bins, or 256 bins. "bins" refer to the division of CRC
41  * number space. 64 bins correspond to 6 bits of the CRC, 128 corresponds
42  * to 7 bits, and 256 refers to 8 bits of the CRC. Any other setting is
43  * invalid and will cause the filtering algorithm to use Multicast
44  * promiscuous mode.
45  */
46 static int dwmac1000_validate_mcast_bins(int mcast_bins)
47 {
48         int x = mcast_bins;
49
50         switch (x) {
51         case HASH_TABLE_SIZE:
52         case 128:
53         case 256:
54                 break;
55         default:
56                 x = 0;
57                 pr_info("Hash table entries set to unexpected value %d",
58                         mcast_bins);
59                 break;
60         }
61         return x;
62 }
63
64 /**
65  * dwmac1000_validate_ucast_entries - validate the Unicast address entries
66  * @ucast_entries: number of Unicast address entries
67  * Description:
68  * This function validates the number of Unicast address entries supported
69  * by a particular Synopsys 10/100/1000 controller. The Synopsys controller
70  * supports 1, 32, 64, or 128 Unicast filter entries for it's Unicast filter
71  * logic. This function validates a valid, supported configuration is
72  * selected, and defaults to 1 Unicast address if an unsupported
73  * configuration is selected.
74  */
75 static int dwmac1000_validate_ucast_entries(int ucast_entries)
76 {
77         int x = ucast_entries;
78
79         switch (x) {
80         case 1:
81         case 32:
82         case 64:
83         case 128:
84                 break;
85         default:
86                 x = 1;
87                 pr_info("Unicast table entries set to unexpected value %d\n",
88                         ucast_entries);
89                 break;
90         }
91         return x;
92 }
93
94 /**
95  * stmmac_axi_setup - parse DT parameters for programming the AXI register
96  * @pdev: platform device
97  * @priv: driver private struct.
98  * Description:
99  * if required, from device-tree the AXI internal register can be tuned
100  * by using platform parameters.
101  */
102 static struct stmmac_axi *stmmac_axi_setup(struct platform_device *pdev)
103 {
104         struct device_node *np;
105         struct stmmac_axi *axi;
106
107         np = of_parse_phandle(pdev->dev.of_node, "snps,axi-config", 0);
108         if (!np)
109                 return NULL;
110
111         axi = devm_kzalloc(&pdev->dev, sizeof(*axi), GFP_KERNEL);
112         if (!axi) {
113                 of_node_put(np);
114                 return ERR_PTR(-ENOMEM);
115         }
116
117         axi->axi_lpi_en = of_property_read_bool(np, "snps,lpi_en");
118         axi->axi_xit_frm = of_property_read_bool(np, "snps,xit_frm");
119         axi->axi_kbbe = of_property_read_bool(np, "snps,axi_kbbe");
120         axi->axi_fb = of_property_read_bool(np, "snps,axi_fb");
121         axi->axi_mb = of_property_read_bool(np, "snps,axi_mb");
122         axi->axi_rb =  of_property_read_bool(np, "snps,axi_rb");
123
124         if (of_property_read_u32(np, "snps,wr_osr_lmt", &axi->axi_wr_osr_lmt))
125                 axi->axi_wr_osr_lmt = 1;
126         if (of_property_read_u32(np, "snps,rd_osr_lmt", &axi->axi_rd_osr_lmt))
127                 axi->axi_rd_osr_lmt = 1;
128         of_property_read_u32_array(np, "snps,blen", axi->axi_blen, AXI_BLEN);
129         of_node_put(np);
130
131         return axi;
132 }
133
134 /**
135  * stmmac_mtl_setup - parse DT parameters for multiple queues configuration
136  * @pdev: platform device
137  */
138 static void stmmac_mtl_setup(struct platform_device *pdev,
139                              struct plat_stmmacenet_data *plat)
140 {
141         struct device_node *q_node;
142         struct device_node *rx_node;
143         struct device_node *tx_node;
144         u8 queue = 0;
145
146         rx_node = of_parse_phandle(pdev->dev.of_node, "snps,mtl-rx-config", 0);
147         if (!rx_node)
148                 return;
149
150         tx_node = of_parse_phandle(pdev->dev.of_node, "snps,mtl-tx-config", 0);
151         if (!tx_node) {
152                 of_node_put(rx_node);
153                 return;
154         }
155
156         /* Processing RX queues common config */
157         if (of_property_read_u8(rx_node, "snps,rx-queues-to-use",
158                                 &plat->rx_queues_to_use))
159                 plat->rx_queues_to_use = 1;
160
161         if (of_property_read_bool(rx_node, "snps,rx-sched-sp"))
162                 plat->rx_sched_algorithm = MTL_RX_ALGORITHM_SP;
163         else if (of_property_read_bool(rx_node, "snps,rx-sched-wsp"))
164                 plat->rx_sched_algorithm = MTL_RX_ALGORITHM_WSP;
165         else
166                 plat->rx_sched_algorithm = MTL_RX_ALGORITHM_SP;
167
168         /* Processing individual RX queue config */
169         for_each_child_of_node(rx_node, q_node) {
170                 if (queue >= plat->rx_queues_to_use)
171                         break;
172
173                 if (of_property_read_bool(q_node, "snps,dcb-algorithm"))
174                         plat->rx_queues_cfg[queue].mode_to_use = MTL_RX_DCB;
175                 else if (of_property_read_bool(q_node, "snps,avb-algorithm"))
176                         plat->rx_queues_cfg[queue].mode_to_use = MTL_RX_AVB;
177                 else
178                         plat->rx_queues_cfg[queue].mode_to_use = MTL_RX_DCB;
179
180                 if (of_property_read_u8(q_node, "snps,map-to-dma-channel",
181                                         &plat->rx_queues_cfg[queue].chan))
182                         plat->rx_queues_cfg[queue].chan = queue;
183                 /* TODO: Dynamic mapping to be included in the future */
184
185                 queue++;
186         }
187
188         /* Processing TX queues common config */
189         if (of_property_read_u8(tx_node, "snps,tx-queues-to-use",
190                                 &plat->tx_queues_to_use))
191                 plat->tx_queues_to_use = 1;
192
193         if (of_property_read_bool(tx_node, "snps,tx-sched-wrr"))
194                 plat->tx_sched_algorithm = MTL_TX_ALGORITHM_WRR;
195         else if (of_property_read_bool(tx_node, "snps,tx-sched-wfq"))
196                 plat->tx_sched_algorithm = MTL_TX_ALGORITHM_WFQ;
197         else if (of_property_read_bool(tx_node, "snps,tx-sched-dwrr"))
198                 plat->tx_sched_algorithm = MTL_TX_ALGORITHM_DWRR;
199         else if (of_property_read_bool(tx_node, "snps,tx-sched-sp"))
200                 plat->tx_sched_algorithm = MTL_TX_ALGORITHM_SP;
201         else
202                 plat->tx_sched_algorithm = MTL_TX_ALGORITHM_SP;
203
204         queue = 0;
205
206         /* Processing individual TX queue config */
207         for_each_child_of_node(tx_node, q_node) {
208                 if (queue >= plat->tx_queues_to_use)
209                         break;
210
211                 if (of_property_read_u8(q_node, "snps,weight",
212                                         &plat->tx_queues_cfg[queue].weight))
213                         plat->tx_queues_cfg[queue].weight = 0x10 + queue;
214
215                 queue++;
216         }
217
218         of_node_put(rx_node);
219         of_node_put(tx_node);
220         of_node_put(q_node);
221 }
222
223 /**
224  * stmmac_dt_phy - parse device-tree driver parameters to allocate PHY resources
225  * @plat: driver data platform structure
226  * @np: device tree node
227  * @dev: device pointer
228  * Description:
229  * The mdio bus will be allocated in case of a phy transceiver is on board;
230  * it will be NULL if the fixed-link is configured.
231  * If there is the "snps,dwmac-mdio" sub-node the mdio will be allocated
232  * in any case (for DSA, mdio must be registered even if fixed-link).
233  * The table below sums the supported configurations:
234  *      -------------------------------
235  *      snps,phy-addr   |     Y
236  *      -------------------------------
237  *      phy-handle      |     Y
238  *      -------------------------------
239  *      fixed-link      |     N
240  *      -------------------------------
241  *      snps,dwmac-mdio |
242  *        even if       |     Y
243  *      fixed-link      |
244  *      -------------------------------
245  *
246  * It returns 0 in case of success otherwise -ENODEV.
247  */
248 static int stmmac_dt_phy(struct plat_stmmacenet_data *plat,
249                          struct device_node *np, struct device *dev)
250 {
251         bool mdio = true;
252
253         /* If phy-handle property is passed from DT, use it as the PHY */
254         plat->phy_node = of_parse_phandle(np, "phy-handle", 0);
255         if (plat->phy_node)
256                 dev_dbg(dev, "Found phy-handle subnode\n");
257
258         /* If phy-handle is not specified, check if we have a fixed-phy */
259         if (!plat->phy_node && of_phy_is_fixed_link(np)) {
260                 if ((of_phy_register_fixed_link(np) < 0))
261                         return -ENODEV;
262
263                 dev_dbg(dev, "Found fixed-link subnode\n");
264                 plat->phy_node = of_node_get(np);
265                 mdio = false;
266         }
267
268         /* exception for dwmac-dwc-qos-eth glue logic */
269         if (of_device_is_compatible(np, "snps,dwc-qos-ethernet-4.10")) {
270                 plat->mdio_node = of_get_child_by_name(np, "mdio");
271         } else {
272                 /**
273                  * If snps,dwmac-mdio is passed from DT, always register
274                  * the MDIO
275                  */
276                 for_each_child_of_node(np, plat->mdio_node) {
277                         if (of_device_is_compatible(plat->mdio_node,
278                                                     "snps,dwmac-mdio"))
279                                 break;
280                 }
281         }
282
283         if (plat->mdio_node) {
284                 dev_dbg(dev, "Found MDIO subnode\n");
285                 mdio = true;
286         }
287
288         if (mdio)
289                 plat->mdio_bus_data =
290                         devm_kzalloc(dev, sizeof(struct stmmac_mdio_bus_data),
291                                      GFP_KERNEL);
292         return 0;
293 }
294
295 /**
296  * stmmac_probe_config_dt - parse device-tree driver parameters
297  * @pdev: platform_device structure
298  * @mac: MAC address to use
299  * Description:
300  * this function is to read the driver parameters from device-tree and
301  * set some private fields that will be used by the main at runtime.
302  */
303 struct plat_stmmacenet_data *
304 stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
305 {
306         struct device_node *np = pdev->dev.of_node;
307         struct plat_stmmacenet_data *plat;
308         struct stmmac_dma_cfg *dma_cfg;
309
310         plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
311         if (!plat)
312                 return ERR_PTR(-ENOMEM);
313
314         *mac = of_get_mac_address(np);
315         plat->interface = of_get_phy_mode(np);
316
317         /* Get max speed of operation from device tree */
318         if (of_property_read_u32(np, "max-speed", &plat->max_speed))
319                 plat->max_speed = -1;
320
321         plat->bus_id = of_alias_get_id(np, "ethernet");
322         if (plat->bus_id < 0)
323                 plat->bus_id = 0;
324
325         /* Default to phy auto-detection */
326         plat->phy_addr = -1;
327
328         /* "snps,phy-addr" is not a standard property. Mark it as deprecated
329          * and warn of its use. Remove this when phy node support is added.
330          */
331         if (of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr) == 0)
332                 dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n");
333
334         /* To Configure PHY by using all device-tree supported properties */
335         if (stmmac_dt_phy(plat, np, &pdev->dev))
336                 return ERR_PTR(-ENODEV);
337
338         of_property_read_u32(np, "tx-fifo-depth", &plat->tx_fifo_size);
339
340         of_property_read_u32(np, "rx-fifo-depth", &plat->rx_fifo_size);
341
342         plat->force_sf_dma_mode =
343                 of_property_read_bool(np, "snps,force_sf_dma_mode");
344
345         plat->en_tx_lpi_clockgating =
346                 of_property_read_bool(np, "snps,en-tx-lpi-clockgating");
347
348         /* Set the maxmtu to a default of JUMBO_LEN in case the
349          * parameter is not present in the device tree.
350          */
351         plat->maxmtu = JUMBO_LEN;
352
353         /* Set default value for multicast hash bins */
354         plat->multicast_filter_bins = HASH_TABLE_SIZE;
355
356         /* Set default value for unicast filter entries */
357         plat->unicast_filter_entries = 1;
358
359         /*
360          * Currently only the properties needed on SPEAr600
361          * are provided. All other properties should be added
362          * once needed on other platforms.
363          */
364         if (of_device_is_compatible(np, "st,spear600-gmac") ||
365                 of_device_is_compatible(np, "snps,dwmac-3.50a") ||
366                 of_device_is_compatible(np, "snps,dwmac-3.70a") ||
367                 of_device_is_compatible(np, "snps,dwmac")) {
368                 /* Note that the max-frame-size parameter as defined in the
369                  * ePAPR v1.1 spec is defined as max-frame-size, it's
370                  * actually used as the IEEE definition of MAC Client
371                  * data, or MTU. The ePAPR specification is confusing as
372                  * the definition is max-frame-size, but usage examples
373                  * are clearly MTUs
374                  */
375                 of_property_read_u32(np, "max-frame-size", &plat->maxmtu);
376                 of_property_read_u32(np, "snps,multicast-filter-bins",
377                                      &plat->multicast_filter_bins);
378                 of_property_read_u32(np, "snps,perfect-filter-entries",
379                                      &plat->unicast_filter_entries);
380                 plat->unicast_filter_entries = dwmac1000_validate_ucast_entries(
381                                                plat->unicast_filter_entries);
382                 plat->multicast_filter_bins = dwmac1000_validate_mcast_bins(
383                                               plat->multicast_filter_bins);
384                 plat->has_gmac = 1;
385                 plat->pmt = 1;
386         }
387
388         if (of_device_is_compatible(np, "snps,dwmac-4.00") ||
389             of_device_is_compatible(np, "snps,dwmac-4.10a")) {
390                 plat->has_gmac4 = 1;
391                 plat->has_gmac = 0;
392                 plat->pmt = 1;
393                 plat->tso_en = of_property_read_bool(np, "snps,tso");
394         }
395
396         if (of_device_is_compatible(np, "snps,dwmac-3.610") ||
397                 of_device_is_compatible(np, "snps,dwmac-3.710")) {
398                 plat->enh_desc = 1;
399                 plat->bugged_jumbo = 1;
400                 plat->force_sf_dma_mode = 1;
401         }
402
403         dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg),
404                                GFP_KERNEL);
405         if (!dma_cfg) {
406                 stmmac_remove_config_dt(pdev, plat);
407                 return ERR_PTR(-ENOMEM);
408         }
409         plat->dma_cfg = dma_cfg;
410
411         of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl);
412         if (!dma_cfg->pbl)
413                 dma_cfg->pbl = DEFAULT_DMA_PBL;
414         of_property_read_u32(np, "snps,txpbl", &dma_cfg->txpbl);
415         of_property_read_u32(np, "snps,rxpbl", &dma_cfg->rxpbl);
416         dma_cfg->pblx8 = !of_property_read_bool(np, "snps,no-pbl-x8");
417
418         dma_cfg->aal = of_property_read_bool(np, "snps,aal");
419         dma_cfg->fixed_burst = of_property_read_bool(np, "snps,fixed-burst");
420         dma_cfg->mixed_burst = of_property_read_bool(np, "snps,mixed-burst");
421
422         plat->force_thresh_dma_mode = of_property_read_bool(np, "snps,force_thresh_dma_mode");
423         if (plat->force_thresh_dma_mode) {
424                 plat->force_sf_dma_mode = 0;
425                 pr_warn("force_sf_dma_mode is ignored if force_thresh_dma_mode is set.");
426         }
427
428         of_property_read_u32(np, "snps,ps-speed", &plat->mac_port_sel_speed);
429
430         plat->axi = stmmac_axi_setup(pdev);
431
432         stmmac_mtl_setup(pdev, plat);
433
434         /* clock setup */
435         plat->stmmac_clk = devm_clk_get(&pdev->dev,
436                                         STMMAC_RESOURCE_NAME);
437         if (IS_ERR(plat->stmmac_clk)) {
438                 dev_warn(&pdev->dev, "Cannot get CSR clock\n");
439                 plat->stmmac_clk = NULL;
440         }
441         clk_prepare_enable(plat->stmmac_clk);
442
443         plat->pclk = devm_clk_get(&pdev->dev, "pclk");
444         if (IS_ERR(plat->pclk)) {
445                 if (PTR_ERR(plat->pclk) == -EPROBE_DEFER)
446                         goto error_pclk_get;
447
448                 plat->pclk = NULL;
449         }
450         clk_prepare_enable(plat->pclk);
451
452         /* Fall-back to main clock in case of no PTP ref is passed */
453         plat->clk_ptp_ref = devm_clk_get(&pdev->dev, "ptp_ref");
454         if (IS_ERR(plat->clk_ptp_ref)) {
455                 plat->clk_ptp_rate = clk_get_rate(plat->stmmac_clk);
456                 plat->clk_ptp_ref = NULL;
457                 dev_warn(&pdev->dev, "PTP uses main clock\n");
458         } else {
459                 plat->clk_ptp_rate = clk_get_rate(plat->clk_ptp_ref);
460                 dev_dbg(&pdev->dev, "PTP rate %d\n", plat->clk_ptp_rate);
461         }
462
463         plat->stmmac_rst = devm_reset_control_get(&pdev->dev,
464                                                   STMMAC_RESOURCE_NAME);
465         if (IS_ERR(plat->stmmac_rst)) {
466                 if (PTR_ERR(plat->stmmac_rst) == -EPROBE_DEFER)
467                         goto error_hw_init;
468
469                 dev_info(&pdev->dev, "no reset control found\n");
470                 plat->stmmac_rst = NULL;
471         }
472
473         return plat;
474
475 error_hw_init:
476         clk_disable_unprepare(plat->pclk);
477 error_pclk_get:
478         clk_disable_unprepare(plat->stmmac_clk);
479
480         return ERR_PTR(-EPROBE_DEFER);
481 }
482
483 /**
484  * stmmac_remove_config_dt - undo the effects of stmmac_probe_config_dt()
485  * @pdev: platform_device structure
486  * @plat: driver data platform structure
487  *
488  * Release resources claimed by stmmac_probe_config_dt().
489  */
490 void stmmac_remove_config_dt(struct platform_device *pdev,
491                              struct plat_stmmacenet_data *plat)
492 {
493         struct device_node *np = pdev->dev.of_node;
494
495         if (of_phy_is_fixed_link(np))
496                 of_phy_deregister_fixed_link(np);
497         of_node_put(plat->phy_node);
498         of_node_put(plat->mdio_node);
499 }
500 #else
501 struct plat_stmmacenet_data *
502 stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
503 {
504         return ERR_PTR(-EINVAL);
505 }
506
507 void stmmac_remove_config_dt(struct platform_device *pdev,
508                              struct plat_stmmacenet_data *plat)
509 {
510 }
511 #endif /* CONFIG_OF */
512 EXPORT_SYMBOL_GPL(stmmac_probe_config_dt);
513 EXPORT_SYMBOL_GPL(stmmac_remove_config_dt);
514
515 int stmmac_get_platform_resources(struct platform_device *pdev,
516                                   struct stmmac_resources *stmmac_res)
517 {
518         struct resource *res;
519
520         memset(stmmac_res, 0, sizeof(*stmmac_res));
521
522         /* Get IRQ information early to have an ability to ask for deferred
523          * probe if needed before we went too far with resource allocation.
524          */
525         stmmac_res->irq = platform_get_irq_byname(pdev, "macirq");
526         if (stmmac_res->irq < 0) {
527                 if (stmmac_res->irq != -EPROBE_DEFER) {
528                         dev_err(&pdev->dev,
529                                 "MAC IRQ configuration information not found\n");
530                 }
531                 return stmmac_res->irq;
532         }
533
534         /* On some platforms e.g. SPEAr the wake up irq differs from the mac irq
535          * The external wake up irq can be passed through the platform code
536          * named as "eth_wake_irq"
537          *
538          * In case the wake up interrupt is not passed from the platform
539          * so the driver will continue to use the mac irq (ndev->irq)
540          */
541         stmmac_res->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq");
542         if (stmmac_res->wol_irq < 0) {
543                 if (stmmac_res->wol_irq == -EPROBE_DEFER)
544                         return -EPROBE_DEFER;
545                 stmmac_res->wol_irq = stmmac_res->irq;
546         }
547
548         stmmac_res->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi");
549         if (stmmac_res->lpi_irq == -EPROBE_DEFER)
550                 return -EPROBE_DEFER;
551
552         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
553         stmmac_res->addr = devm_ioremap_resource(&pdev->dev, res);
554
555         return PTR_ERR_OR_ZERO(stmmac_res->addr);
556 }
557 EXPORT_SYMBOL_GPL(stmmac_get_platform_resources);
558
559 /**
560  * stmmac_pltfr_remove
561  * @pdev: platform device pointer
562  * Description: this function calls the main to free the net resources
563  * and calls the platforms hook and release the resources (e.g. mem).
564  */
565 int stmmac_pltfr_remove(struct platform_device *pdev)
566 {
567         struct net_device *ndev = platform_get_drvdata(pdev);
568         struct stmmac_priv *priv = netdev_priv(ndev);
569         struct plat_stmmacenet_data *plat = priv->plat;
570         int ret = stmmac_dvr_remove(&pdev->dev);
571
572         if (plat->exit)
573                 plat->exit(pdev, plat->bsp_priv);
574
575         stmmac_remove_config_dt(pdev, plat);
576
577         return ret;
578 }
579 EXPORT_SYMBOL_GPL(stmmac_pltfr_remove);
580
581 #ifdef CONFIG_PM_SLEEP
582 /**
583  * stmmac_pltfr_suspend
584  * @dev: device pointer
585  * Description: this function is invoked when suspend the driver and it direcly
586  * call the main suspend function and then, if required, on some platform, it
587  * can call an exit helper.
588  */
589 static int stmmac_pltfr_suspend(struct device *dev)
590 {
591         int ret;
592         struct net_device *ndev = dev_get_drvdata(dev);
593         struct stmmac_priv *priv = netdev_priv(ndev);
594         struct platform_device *pdev = to_platform_device(dev);
595
596         ret = stmmac_suspend(dev);
597         if (priv->plat->exit)
598                 priv->plat->exit(pdev, priv->plat->bsp_priv);
599
600         return ret;
601 }
602
603 /**
604  * stmmac_pltfr_resume
605  * @dev: device pointer
606  * Description: this function is invoked when resume the driver before calling
607  * the main resume function, on some platforms, it can call own init helper
608  * if required.
609  */
610 static int stmmac_pltfr_resume(struct device *dev)
611 {
612         struct net_device *ndev = dev_get_drvdata(dev);
613         struct stmmac_priv *priv = netdev_priv(ndev);
614         struct platform_device *pdev = to_platform_device(dev);
615
616         if (priv->plat->init)
617                 priv->plat->init(pdev, priv->plat->bsp_priv);
618
619         return stmmac_resume(dev);
620 }
621 #endif /* CONFIG_PM_SLEEP */
622
623 SIMPLE_DEV_PM_OPS(stmmac_pltfr_pm_ops, stmmac_pltfr_suspend,
624                                        stmmac_pltfr_resume);
625 EXPORT_SYMBOL_GPL(stmmac_pltfr_pm_ops);
626
627 MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet platform support");
628 MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
629 MODULE_LICENSE("GPL");