Commit d87eb12785c14de1586e3bad86ca2c0991300339
Committed by
Kumar Gala
1 parent
7e1cc9c55a
Exists in
master
and in
7 other branches
gianfar: Add magic packet and suspend/resume support.
Signed-off-by: Scott Wood <scottwood@freescale.com> Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Showing 5 changed files with 173 additions and 6 deletions Side-by-side Diff
arch/powerpc/sysdev/fsl_soc.c
... | ... | @@ -352,6 +352,9 @@ |
352 | 352 | else |
353 | 353 | gfar_data.interface = PHY_INTERFACE_MODE_MII; |
354 | 354 | |
355 | + if (of_get_property(np, "fsl,magic-packet", NULL)) | |
356 | + gfar_data.device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET; | |
357 | + | |
355 | 358 | ph = of_get_property(np, "phy-handle", NULL); |
356 | 359 | if (ph == NULL) { |
357 | 360 | u32 *fixed_link; |
drivers/net/gianfar.c
... | ... | @@ -143,6 +143,9 @@ |
143 | 143 | static void gfar_vlan_rx_register(struct net_device *netdev, |
144 | 144 | struct vlan_group *grp); |
145 | 145 | void gfar_halt(struct net_device *dev); |
146 | +#ifdef CONFIG_PM | |
147 | +static void gfar_halt_nodisable(struct net_device *dev); | |
148 | +#endif | |
146 | 149 | void gfar_start(struct net_device *dev); |
147 | 150 | static void gfar_clear_exact_match(struct net_device *dev); |
148 | 151 | static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr); |
... | ... | @@ -216,6 +219,7 @@ |
216 | 219 | |
217 | 220 | spin_lock_init(&priv->txlock); |
218 | 221 | spin_lock_init(&priv->rxlock); |
222 | + spin_lock_init(&priv->bflock); | |
219 | 223 | |
220 | 224 | platform_set_drvdata(pdev, dev); |
221 | 225 | |
222 | 226 | |
... | ... | @@ -393,7 +397,104 @@ |
393 | 397 | return 0; |
394 | 398 | } |
395 | 399 | |
400 | +#ifdef CONFIG_PM | |
401 | +static int gfar_suspend(struct platform_device *pdev, pm_message_t state) | |
402 | +{ | |
403 | + struct net_device *dev = platform_get_drvdata(pdev); | |
404 | + struct gfar_private *priv = netdev_priv(dev); | |
405 | + unsigned long flags; | |
406 | + u32 tempval; | |
396 | 407 | |
408 | + int magic_packet = priv->wol_en && | |
409 | + (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); | |
410 | + | |
411 | + netif_device_detach(dev); | |
412 | + | |
413 | + if (netif_running(dev)) { | |
414 | + spin_lock_irqsave(&priv->txlock, flags); | |
415 | + spin_lock(&priv->rxlock); | |
416 | + | |
417 | + gfar_halt_nodisable(dev); | |
418 | + | |
419 | + /* Disable Tx, and Rx if wake-on-LAN is disabled. */ | |
420 | + tempval = gfar_read(&priv->regs->maccfg1); | |
421 | + | |
422 | + tempval &= ~MACCFG1_TX_EN; | |
423 | + | |
424 | + if (!magic_packet) | |
425 | + tempval &= ~MACCFG1_RX_EN; | |
426 | + | |
427 | + gfar_write(&priv->regs->maccfg1, tempval); | |
428 | + | |
429 | + spin_unlock(&priv->rxlock); | |
430 | + spin_unlock_irqrestore(&priv->txlock, flags); | |
431 | + | |
432 | +#ifdef CONFIG_GFAR_NAPI | |
433 | + napi_disable(&priv->napi); | |
434 | +#endif | |
435 | + | |
436 | + if (magic_packet) { | |
437 | + /* Enable interrupt on Magic Packet */ | |
438 | + gfar_write(&priv->regs->imask, IMASK_MAG); | |
439 | + | |
440 | + /* Enable Magic Packet mode */ | |
441 | + tempval = gfar_read(&priv->regs->maccfg2); | |
442 | + tempval |= MACCFG2_MPEN; | |
443 | + gfar_write(&priv->regs->maccfg2, tempval); | |
444 | + } else { | |
445 | + phy_stop(priv->phydev); | |
446 | + } | |
447 | + } | |
448 | + | |
449 | + return 0; | |
450 | +} | |
451 | + | |
452 | +static int gfar_resume(struct platform_device *pdev) | |
453 | +{ | |
454 | + struct net_device *dev = platform_get_drvdata(pdev); | |
455 | + struct gfar_private *priv = netdev_priv(dev); | |
456 | + unsigned long flags; | |
457 | + u32 tempval; | |
458 | + int magic_packet = priv->wol_en && | |
459 | + (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); | |
460 | + | |
461 | + if (!netif_running(dev)) { | |
462 | + netif_device_attach(dev); | |
463 | + return 0; | |
464 | + } | |
465 | + | |
466 | + if (!magic_packet && priv->phydev) | |
467 | + phy_start(priv->phydev); | |
468 | + | |
469 | + /* Disable Magic Packet mode, in case something | |
470 | + * else woke us up. | |
471 | + */ | |
472 | + | |
473 | + spin_lock_irqsave(&priv->txlock, flags); | |
474 | + spin_lock(&priv->rxlock); | |
475 | + | |
476 | + tempval = gfar_read(&priv->regs->maccfg2); | |
477 | + tempval &= ~MACCFG2_MPEN; | |
478 | + gfar_write(&priv->regs->maccfg2, tempval); | |
479 | + | |
480 | + gfar_start(dev); | |
481 | + | |
482 | + spin_unlock(&priv->rxlock); | |
483 | + spin_unlock_irqrestore(&priv->txlock, flags); | |
484 | + | |
485 | + netif_device_attach(dev); | |
486 | + | |
487 | +#ifdef CONFIG_GFAR_NAPI | |
488 | + napi_enable(&priv->napi); | |
489 | +#endif | |
490 | + | |
491 | + return 0; | |
492 | +} | |
493 | +#else | |
494 | +#define gfar_suspend NULL | |
495 | +#define gfar_resume NULL | |
496 | +#endif | |
497 | + | |
397 | 498 | /* Reads the controller's registers to determine what interface |
398 | 499 | * connects it to the PHY. |
399 | 500 | */ |
400 | 501 | |
... | ... | @@ -549,8 +650,9 @@ |
549 | 650 | } |
550 | 651 | |
551 | 652 | |
653 | +#ifdef CONFIG_PM | |
552 | 654 | /* Halt the receive and transmit queues */ |
553 | -void gfar_halt(struct net_device *dev) | |
655 | +static void gfar_halt_nodisable(struct net_device *dev) | |
554 | 656 | { |
555 | 657 | struct gfar_private *priv = netdev_priv(dev); |
556 | 658 | struct gfar __iomem *regs = priv->regs; |
557 | 659 | |
... | ... | @@ -573,7 +675,16 @@ |
573 | 675 | (IEVENT_GRSC | IEVENT_GTSC))) |
574 | 676 | cpu_relax(); |
575 | 677 | } |
678 | +} | |
679 | +#endif | |
576 | 680 | |
681 | +/* Halt the receive and transmit queues */ | |
682 | +void gfar_halt(struct net_device *dev) | |
683 | +{ | |
684 | + struct gfar_private *priv = netdev_priv(dev); | |
685 | + struct gfar __iomem *regs = priv->regs; | |
686 | + u32 tempval; | |
687 | + | |
577 | 688 | /* Disable Rx and Tx */ |
578 | 689 | tempval = gfar_read(®s->maccfg1); |
579 | 690 | tempval &= ~(MACCFG1_RX_EN | MACCFG1_TX_EN); |
580 | 691 | |
... | ... | @@ -1969,8 +2080,13 @@ |
1969 | 2080 | u32 events = gfar_read(&priv->regs->ievent); |
1970 | 2081 | |
1971 | 2082 | /* Clear IEVENT */ |
1972 | - gfar_write(&priv->regs->ievent, IEVENT_ERR_MASK); | |
2083 | + gfar_write(&priv->regs->ievent, events & IEVENT_ERR_MASK); | |
1973 | 2084 | |
2085 | + /* Magic Packet is not an error. */ | |
2086 | + if ((priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) && | |
2087 | + (events & IEVENT_MAG)) | |
2088 | + events &= ~IEVENT_MAG; | |
2089 | + | |
1974 | 2090 | /* Hmm... */ |
1975 | 2091 | if (netif_msg_rx_err(priv) || netif_msg_tx_err(priv)) |
1976 | 2092 | printk(KERN_DEBUG "%s: error interrupt (ievent=0x%08x imask=0x%08x)\n", |
... | ... | @@ -2042,6 +2158,8 @@ |
2042 | 2158 | static struct platform_driver gfar_driver = { |
2043 | 2159 | .probe = gfar_probe, |
2044 | 2160 | .remove = gfar_remove, |
2161 | + .suspend = gfar_suspend, | |
2162 | + .resume = gfar_resume, | |
2045 | 2163 | .driver = { |
2046 | 2164 | .name = "fsl-gianfar", |
2047 | 2165 | .owner = THIS_MODULE, |
drivers/net/gianfar.h
... | ... | @@ -168,6 +168,7 @@ |
168 | 168 | #define MACCFG2_GMII 0x00000200 |
169 | 169 | #define MACCFG2_HUGEFRAME 0x00000020 |
170 | 170 | #define MACCFG2_LENGTHCHECK 0x00000010 |
171 | +#define MACCFG2_MPEN 0x00000008 | |
171 | 172 | |
172 | 173 | #define ECNTRL_INIT_SETTINGS 0x00001000 |
173 | 174 | #define ECNTRL_TBI_MODE 0x00000020 |
... | ... | @@ -240,6 +241,7 @@ |
240 | 241 | #define IEVENT_CRL 0x00020000 |
241 | 242 | #define IEVENT_XFUN 0x00010000 |
242 | 243 | #define IEVENT_RXB0 0x00008000 |
244 | +#define IEVENT_MAG 0x00000800 | |
243 | 245 | #define IEVENT_GRSC 0x00000100 |
244 | 246 | #define IEVENT_RXF0 0x00000080 |
245 | 247 | #define IEVENT_FIR 0x00000008 |
... | ... | @@ -252,7 +254,8 @@ |
252 | 254 | #define IEVENT_ERR_MASK \ |
253 | 255 | (IEVENT_RXC | IEVENT_BSY | IEVENT_EBERR | IEVENT_MSRO | \ |
254 | 256 | IEVENT_BABT | IEVENT_TXC | IEVENT_TXE | IEVENT_LC \ |
255 | - | IEVENT_CRL | IEVENT_XFUN | IEVENT_DPE | IEVENT_PERR) | |
257 | + | IEVENT_CRL | IEVENT_XFUN | IEVENT_DPE | IEVENT_PERR \ | |
258 | + | IEVENT_MAG) | |
256 | 259 | |
257 | 260 | #define IMASK_INIT_CLEAR 0x00000000 |
258 | 261 | #define IMASK_BABR 0x80000000 |
... | ... | @@ -270,6 +273,7 @@ |
270 | 273 | #define IMASK_CRL 0x00020000 |
271 | 274 | #define IMASK_XFUN 0x00010000 |
272 | 275 | #define IMASK_RXB0 0x00008000 |
276 | +#define IMASK_MAG 0x00000800 | |
273 | 277 | #define IMASK_GTSC 0x00000100 |
274 | 278 | #define IMASK_RXFEN0 0x00000080 |
275 | 279 | #define IMASK_FIR 0x00000008 |
276 | 280 | |
... | ... | @@ -737,10 +741,14 @@ |
737 | 741 | unsigned int fifo_starve; |
738 | 742 | unsigned int fifo_starve_off; |
739 | 743 | |
744 | + /* Bitfield update lock */ | |
745 | + spinlock_t bflock; | |
746 | + | |
740 | 747 | unsigned char vlan_enable:1, |
741 | 748 | rx_csum_enable:1, |
742 | 749 | extended_hash:1, |
743 | - bd_stash_en:1; | |
750 | + bd_stash_en:1, | |
751 | + wol_en:1; /* Wake-on-LAN enabled */ | |
744 | 752 | unsigned short padding; |
745 | 753 | |
746 | 754 | unsigned int interruptTransmit; |
drivers/net/gianfar_ethtool.c
... | ... | @@ -479,14 +479,13 @@ |
479 | 479 | static int gfar_set_rx_csum(struct net_device *dev, uint32_t data) |
480 | 480 | { |
481 | 481 | struct gfar_private *priv = netdev_priv(dev); |
482 | + unsigned long flags; | |
482 | 483 | int err = 0; |
483 | 484 | |
484 | 485 | if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) |
485 | 486 | return -EOPNOTSUPP; |
486 | 487 | |
487 | 488 | if (dev->flags & IFF_UP) { |
488 | - unsigned long flags; | |
489 | - | |
490 | 489 | /* Halt TX and RX, and process the frames which |
491 | 490 | * have already been received */ |
492 | 491 | spin_lock_irqsave(&priv->txlock, flags); |
493 | 492 | |
... | ... | @@ -502,7 +501,9 @@ |
502 | 501 | stop_gfar(dev); |
503 | 502 | } |
504 | 503 | |
504 | + spin_lock_irqsave(&priv->bflock, flags); | |
505 | 505 | priv->rx_csum_enable = data; |
506 | + spin_unlock_irqrestore(&priv->bflock, flags); | |
506 | 507 | |
507 | 508 | if (dev->flags & IFF_UP) |
508 | 509 | err = startup_gfar(dev); |
509 | 510 | |
... | ... | @@ -564,7 +565,39 @@ |
564 | 565 | priv->msg_enable = data; |
565 | 566 | } |
566 | 567 | |
568 | +#ifdef CONFIG_PM | |
569 | +static void gfar_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) | |
570 | +{ | |
571 | + struct gfar_private *priv = netdev_priv(dev); | |
567 | 572 | |
573 | + if (priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) { | |
574 | + wol->supported = WAKE_MAGIC; | |
575 | + wol->wolopts = priv->wol_en ? WAKE_MAGIC : 0; | |
576 | + } else { | |
577 | + wol->supported = wol->wolopts = 0; | |
578 | + } | |
579 | +} | |
580 | + | |
581 | +static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) | |
582 | +{ | |
583 | + struct gfar_private *priv = netdev_priv(dev); | |
584 | + unsigned long flags; | |
585 | + | |
586 | + if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) && | |
587 | + wol->wolopts != 0) | |
588 | + return -EINVAL; | |
589 | + | |
590 | + if (wol->wolopts & ~WAKE_MAGIC) | |
591 | + return -EINVAL; | |
592 | + | |
593 | + spin_lock_irqsave(&priv->bflock, flags); | |
594 | + priv->wol_en = wol->wolopts & WAKE_MAGIC ? 1 : 0; | |
595 | + spin_unlock_irqrestore(&priv->bflock, flags); | |
596 | + | |
597 | + return 0; | |
598 | +} | |
599 | +#endif | |
600 | + | |
568 | 601 | const struct ethtool_ops gfar_ethtool_ops = { |
569 | 602 | .get_settings = gfar_gsettings, |
570 | 603 | .set_settings = gfar_ssettings, |
... | ... | @@ -585,5 +618,9 @@ |
585 | 618 | .set_tx_csum = gfar_set_tx_csum, |
586 | 619 | .get_msglevel = gfar_get_msglevel, |
587 | 620 | .set_msglevel = gfar_set_msglevel, |
621 | +#ifdef CONFIG_PM | |
622 | + .get_wol = gfar_get_wol, | |
623 | + .set_wol = gfar_set_wol, | |
624 | +#endif | |
588 | 625 | }; |
include/linux/fsl_devices.h
... | ... | @@ -69,6 +69,7 @@ |
69 | 69 | #define FSL_GIANFAR_DEV_HAS_VLAN 0x00000020 |
70 | 70 | #define FSL_GIANFAR_DEV_HAS_EXTENDED_HASH 0x00000040 |
71 | 71 | #define FSL_GIANFAR_DEV_HAS_PADDING 0x00000080 |
72 | +#define FSL_GIANFAR_DEV_HAS_MAGIC_PACKET 0x00000100 | |
72 | 73 | |
73 | 74 | /* Flags in gianfar_platform_data */ |
74 | 75 | #define FSL_GIANFAR_BRD_HAS_PHY_INTR 0x00000001 /* set or use a timer */ |