diff options
| -rw-r--r-- | include/net/dsa.h | 6 | ||||
| -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 |
4 files changed, 119 insertions, 0 deletions
diff --git a/include/net/dsa.h b/include/net/dsa.h index e020b8a12b7d..846dce4abaa5 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h | |||
| @@ -210,6 +210,12 @@ struct dsa_switch_driver { | |||
| 210 | void (*get_ethtool_stats)(struct dsa_switch *ds, | 210 | void (*get_ethtool_stats)(struct dsa_switch *ds, |
| 211 | int port, uint64_t *data); | 211 | int port, uint64_t *data); |
| 212 | int (*get_sset_count)(struct dsa_switch *ds); | 212 | int (*get_sset_count)(struct dsa_switch *ds); |
| 213 | |||
| 214 | /* | ||
| 215 | * Suspend and resume | ||
| 216 | */ | ||
| 217 | int (*suspend)(struct dsa_switch *ds); | ||
| 218 | int (*resume)(struct dsa_switch *ds); | ||
| 213 | }; | 219 | }; |
| 214 | 220 | ||
| 215 | void register_switch_driver(struct dsa_switch_driver *type); | 221 | void register_switch_driver(struct dsa_switch_driver *type); |
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) |
