diff options
author | Florian Fainelli <f.fainelli@gmail.com> | 2014-09-18 20:31:22 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-09-22 14:41:23 -0400 |
commit | 2446254915a7d6f08bba9a755a34cc0402880472 (patch) | |
tree | 2b27382c1139a4a9e2b012fa7a9b9604e51bb031 /net/dsa | |
parent | 34f6b8745d421683ca0a268540869eb30721e970 (diff) |
net: dsa: allow switch drivers to implement suspend/resume hooks
Add an abstraction layer to suspend/resume switch devices, doing the
following split:
- suspend/resume the slave network devices and their corresponding PHY
devices
- suspend/resume the switch hardware using switch driver callbacks
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/dsa')
-rw-r--r-- | net/dsa/dsa.c | 80 | ||||
-rw-r--r-- | net/dsa/dsa_priv.h | 2 | ||||
-rw-r--r-- | net/dsa/slave.c | 31 |
3 files changed, 113 insertions, 0 deletions
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 6e40928ec0e7..6905f2d84c44 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c | |||
@@ -238,6 +238,49 @@ static void dsa_switch_destroy(struct dsa_switch *ds) | |||
238 | { | 238 | { |
239 | } | 239 | } |
240 | 240 | ||
241 | static int dsa_switch_suspend(struct dsa_switch *ds) | ||
242 | { | ||
243 | int i, ret = 0; | ||
244 | |||
245 | /* Suspend slave network devices */ | ||
246 | for (i = 0; i < DSA_MAX_PORTS; i++) { | ||
247 | if (!(ds->phys_port_mask & (1 << i))) | ||
248 | continue; | ||
249 | |||
250 | ret = dsa_slave_suspend(ds->ports[i]); | ||
251 | if (ret) | ||
252 | return ret; | ||
253 | } | ||
254 | |||
255 | if (ds->drv->suspend) | ||
256 | ret = ds->drv->suspend(ds); | ||
257 | |||
258 | return ret; | ||
259 | } | ||
260 | |||
261 | static int dsa_switch_resume(struct dsa_switch *ds) | ||
262 | { | ||
263 | int i, ret = 0; | ||
264 | |||
265 | if (ds->drv->resume) | ||
266 | ret = ds->drv->resume(ds); | ||
267 | |||
268 | if (ret) | ||
269 | return ret; | ||
270 | |||
271 | /* Resume slave network devices */ | ||
272 | for (i = 0; i < DSA_MAX_PORTS; i++) { | ||
273 | if (!(ds->phys_port_mask & (1 << i))) | ||
274 | continue; | ||
275 | |||
276 | ret = dsa_slave_resume(ds->ports[i]); | ||
277 | if (ret) | ||
278 | return ret; | ||
279 | } | ||
280 | |||
281 | return 0; | ||
282 | } | ||
283 | |||
241 | 284 | ||
242 | /* link polling *************************************************************/ | 285 | /* link polling *************************************************************/ |
243 | static void dsa_link_poll_work(struct work_struct *ugly) | 286 | static void dsa_link_poll_work(struct work_struct *ugly) |
@@ -650,6 +693,42 @@ static struct packet_type dsa_pack_type __read_mostly = { | |||
650 | .func = dsa_switch_rcv, | 693 | .func = dsa_switch_rcv, |
651 | }; | 694 | }; |
652 | 695 | ||
696 | #ifdef CONFIG_PM_SLEEP | ||
697 | static int dsa_suspend(struct device *d) | ||
698 | { | ||
699 | struct platform_device *pdev = to_platform_device(d); | ||
700 | struct dsa_switch_tree *dst = platform_get_drvdata(pdev); | ||
701 | int i, ret = 0; | ||
702 | |||
703 | for (i = 0; i < dst->pd->nr_chips; i++) { | ||
704 | struct dsa_switch *ds = dst->ds[i]; | ||
705 | |||
706 | if (ds != NULL) | ||
707 | ret = dsa_switch_suspend(ds); | ||
708 | } | ||
709 | |||
710 | return ret; | ||
711 | } | ||
712 | |||
713 | static int dsa_resume(struct device *d) | ||
714 | { | ||
715 | struct platform_device *pdev = to_platform_device(d); | ||
716 | struct dsa_switch_tree *dst = platform_get_drvdata(pdev); | ||
717 | int i, ret = 0; | ||
718 | |||
719 | for (i = 0; i < dst->pd->nr_chips; i++) { | ||
720 | struct dsa_switch *ds = dst->ds[i]; | ||
721 | |||
722 | if (ds != NULL) | ||
723 | ret = dsa_switch_resume(ds); | ||
724 | } | ||
725 | |||
726 | return ret; | ||
727 | } | ||
728 | #endif | ||
729 | |||
730 | static SIMPLE_DEV_PM_OPS(dsa_pm_ops, dsa_suspend, dsa_resume); | ||
731 | |||
653 | static const struct of_device_id dsa_of_match_table[] = { | 732 | static const struct of_device_id dsa_of_match_table[] = { |
654 | { .compatible = "brcm,bcm7445-switch-v4.0" }, | 733 | { .compatible = "brcm,bcm7445-switch-v4.0" }, |
655 | { .compatible = "marvell,dsa", }, | 734 | { .compatible = "marvell,dsa", }, |
@@ -665,6 +744,7 @@ static struct platform_driver dsa_driver = { | |||
665 | .name = "dsa", | 744 | .name = "dsa", |
666 | .owner = THIS_MODULE, | 745 | .owner = THIS_MODULE, |
667 | .of_match_table = dsa_of_match_table, | 746 | .of_match_table = dsa_of_match_table, |
747 | .pm = &dsa_pm_ops, | ||
668 | }, | 748 | }, |
669 | }; | 749 | }; |
670 | 750 | ||
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index f90899e8ab5a..dc9756d3154c 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h | |||
@@ -56,6 +56,8 @@ void dsa_slave_mii_bus_init(struct dsa_switch *ds); | |||
56 | struct net_device *dsa_slave_create(struct dsa_switch *ds, | 56 | struct net_device *dsa_slave_create(struct dsa_switch *ds, |
57 | struct device *parent, | 57 | struct device *parent, |
58 | int port, char *name); | 58 | int port, char *name); |
59 | int dsa_slave_suspend(struct net_device *slave_dev); | ||
60 | int dsa_slave_resume(struct net_device *slave_dev); | ||
59 | 61 | ||
60 | /* tag_dsa.c */ | 62 | /* tag_dsa.c */ |
61 | extern const struct dsa_device_ops dsa_netdev_ops; | 63 | extern const struct dsa_device_ops dsa_netdev_ops; |
diff --git a/net/dsa/slave.c b/net/dsa/slave.c index a7997265019a..143811ef57ae 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c | |||
@@ -412,6 +412,37 @@ static void dsa_slave_phy_setup(struct dsa_slave_priv *p, | |||
412 | p->phy->addr, p->phy->drv->name); | 412 | p->phy->addr, p->phy->drv->name); |
413 | } | 413 | } |
414 | 414 | ||
415 | int dsa_slave_suspend(struct net_device *slave_dev) | ||
416 | { | ||
417 | struct dsa_slave_priv *p = netdev_priv(slave_dev); | ||
418 | |||
419 | netif_device_detach(slave_dev); | ||
420 | |||
421 | if (p->phy) { | ||
422 | phy_stop(p->phy); | ||
423 | p->old_pause = -1; | ||
424 | p->old_link = -1; | ||
425 | p->old_duplex = -1; | ||
426 | phy_suspend(p->phy); | ||
427 | } | ||
428 | |||
429 | return 0; | ||
430 | } | ||
431 | |||
432 | int dsa_slave_resume(struct net_device *slave_dev) | ||
433 | { | ||
434 | struct dsa_slave_priv *p = netdev_priv(slave_dev); | ||
435 | |||
436 | netif_device_attach(slave_dev); | ||
437 | |||
438 | if (p->phy) { | ||
439 | phy_resume(p->phy); | ||
440 | phy_start(p->phy); | ||
441 | } | ||
442 | |||
443 | return 0; | ||
444 | } | ||
445 | |||
415 | struct net_device * | 446 | struct net_device * |
416 | dsa_slave_create(struct dsa_switch *ds, struct device *parent, | 447 | dsa_slave_create(struct dsa_switch *ds, struct device *parent, |
417 | int port, char *name) | 448 | int port, char *name) |