diff options
author | Scott Wood <scottwood@freescale.com> | 2008-07-11 19:04:45 -0400 |
---|---|---|
committer | Kumar Gala <galak@kernel.crashing.org> | 2008-07-16 18:57:47 -0400 |
commit | d87eb12785c14de1586e3bad86ca2c0991300339 (patch) | |
tree | 9caa5a958d88910049a03bbfbd1a8a3367b6332b | |
parent | 7e1cc9c55a2a4af62f30fade62fb612a243def39 (diff) |
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>
-rw-r--r-- | arch/powerpc/sysdev/fsl_soc.c | 3 | ||||
-rw-r--r-- | drivers/net/gianfar.c | 122 | ||||
-rw-r--r-- | drivers/net/gianfar.h | 12 | ||||
-rw-r--r-- | drivers/net/gianfar_ethtool.c | 41 | ||||
-rw-r--r-- | include/linux/fsl_devices.h | 1 |
5 files changed, 173 insertions, 6 deletions
diff --git a/arch/powerpc/sysdev/fsl_soc.c b/arch/powerpc/sysdev/fsl_soc.c index ef4cb0d67a72..214388e11807 100644 --- a/arch/powerpc/sysdev/fsl_soc.c +++ b/arch/powerpc/sysdev/fsl_soc.c | |||
@@ -352,6 +352,9 @@ static int __init gfar_of_init(void) | |||
352 | else | 352 | else |
353 | gfar_data.interface = PHY_INTERFACE_MODE_MII; | 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 | ph = of_get_property(np, "phy-handle", NULL); | 358 | ph = of_get_property(np, "phy-handle", NULL); |
356 | if (ph == NULL) { | 359 | if (ph == NULL) { |
357 | u32 *fixed_link; | 360 | u32 *fixed_link; |
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 25bdd0832df5..36f229ff52f2 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c | |||
@@ -143,6 +143,9 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int l | |||
143 | static void gfar_vlan_rx_register(struct net_device *netdev, | 143 | static void gfar_vlan_rx_register(struct net_device *netdev, |
144 | struct vlan_group *grp); | 144 | struct vlan_group *grp); |
145 | void gfar_halt(struct net_device *dev); | 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 | void gfar_start(struct net_device *dev); | 149 | void gfar_start(struct net_device *dev); |
147 | static void gfar_clear_exact_match(struct net_device *dev); | 150 | static void gfar_clear_exact_match(struct net_device *dev); |
148 | static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr); | 151 | static void gfar_set_mac_for_addr(struct net_device *dev, int num, u8 *addr); |
@@ -216,6 +219,7 @@ static int gfar_probe(struct platform_device *pdev) | |||
216 | 219 | ||
217 | spin_lock_init(&priv->txlock); | 220 | spin_lock_init(&priv->txlock); |
218 | spin_lock_init(&priv->rxlock); | 221 | spin_lock_init(&priv->rxlock); |
222 | spin_lock_init(&priv->bflock); | ||
219 | 223 | ||
220 | platform_set_drvdata(pdev, dev); | 224 | platform_set_drvdata(pdev, dev); |
221 | 225 | ||
@@ -393,6 +397,103 @@ static int gfar_remove(struct platform_device *pdev) | |||
393 | return 0; | 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; | ||
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 | ||
396 | 497 | ||
397 | /* Reads the controller's registers to determine what interface | 498 | /* Reads the controller's registers to determine what interface |
398 | * connects it to the PHY. | 499 | * connects it to the PHY. |
@@ -549,8 +650,9 @@ static void init_registers(struct net_device *dev) | |||
549 | } | 650 | } |
550 | 651 | ||
551 | 652 | ||
653 | #ifdef CONFIG_PM | ||
552 | /* Halt the receive and transmit queues */ | 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 | struct gfar_private *priv = netdev_priv(dev); | 657 | struct gfar_private *priv = netdev_priv(dev); |
556 | struct gfar __iomem *regs = priv->regs; | 658 | struct gfar __iomem *regs = priv->regs; |
@@ -573,6 +675,15 @@ void gfar_halt(struct net_device *dev) | |||
573 | (IEVENT_GRSC | IEVENT_GTSC))) | 675 | (IEVENT_GRSC | IEVENT_GTSC))) |
574 | cpu_relax(); | 676 | cpu_relax(); |
575 | } | 677 | } |
678 | } | ||
679 | #endif | ||
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; | ||
576 | 687 | ||
577 | /* Disable Rx and Tx */ | 688 | /* Disable Rx and Tx */ |
578 | tempval = gfar_read(®s->maccfg1); | 689 | tempval = gfar_read(®s->maccfg1); |
@@ -1969,7 +2080,12 @@ static irqreturn_t gfar_error(int irq, void *dev_id) | |||
1969 | u32 events = gfar_read(&priv->regs->ievent); | 2080 | u32 events = gfar_read(&priv->regs->ievent); |
1970 | 2081 | ||
1971 | /* Clear IEVENT */ | 2082 | /* Clear IEVENT */ |
1972 | gfar_write(&priv->regs->ievent, IEVENT_ERR_MASK); | 2083 | gfar_write(&priv->regs->ievent, events & IEVENT_ERR_MASK); |
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; | ||
1973 | 2089 | ||
1974 | /* Hmm... */ | 2090 | /* Hmm... */ |
1975 | if (netif_msg_rx_err(priv) || netif_msg_tx_err(priv)) | 2091 | if (netif_msg_rx_err(priv) || netif_msg_tx_err(priv)) |
@@ -2042,6 +2158,8 @@ MODULE_ALIAS("platform:fsl-gianfar"); | |||
2042 | static struct platform_driver gfar_driver = { | 2158 | static struct platform_driver gfar_driver = { |
2043 | .probe = gfar_probe, | 2159 | .probe = gfar_probe, |
2044 | .remove = gfar_remove, | 2160 | .remove = gfar_remove, |
2161 | .suspend = gfar_suspend, | ||
2162 | .resume = gfar_resume, | ||
2045 | .driver = { | 2163 | .driver = { |
2046 | .name = "fsl-gianfar", | 2164 | .name = "fsl-gianfar", |
2047 | .owner = THIS_MODULE, | 2165 | .owner = THIS_MODULE, |
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h index 27f37c81e52c..5ee518a8782b 100644 --- a/drivers/net/gianfar.h +++ b/drivers/net/gianfar.h | |||
@@ -168,6 +168,7 @@ extern const char gfar_driver_version[]; | |||
168 | #define MACCFG2_GMII 0x00000200 | 168 | #define MACCFG2_GMII 0x00000200 |
169 | #define MACCFG2_HUGEFRAME 0x00000020 | 169 | #define MACCFG2_HUGEFRAME 0x00000020 |
170 | #define MACCFG2_LENGTHCHECK 0x00000010 | 170 | #define MACCFG2_LENGTHCHECK 0x00000010 |
171 | #define MACCFG2_MPEN 0x00000008 | ||
171 | 172 | ||
172 | #define ECNTRL_INIT_SETTINGS 0x00001000 | 173 | #define ECNTRL_INIT_SETTINGS 0x00001000 |
173 | #define ECNTRL_TBI_MODE 0x00000020 | 174 | #define ECNTRL_TBI_MODE 0x00000020 |
@@ -240,6 +241,7 @@ extern const char gfar_driver_version[]; | |||
240 | #define IEVENT_CRL 0x00020000 | 241 | #define IEVENT_CRL 0x00020000 |
241 | #define IEVENT_XFUN 0x00010000 | 242 | #define IEVENT_XFUN 0x00010000 |
242 | #define IEVENT_RXB0 0x00008000 | 243 | #define IEVENT_RXB0 0x00008000 |
244 | #define IEVENT_MAG 0x00000800 | ||
243 | #define IEVENT_GRSC 0x00000100 | 245 | #define IEVENT_GRSC 0x00000100 |
244 | #define IEVENT_RXF0 0x00000080 | 246 | #define IEVENT_RXF0 0x00000080 |
245 | #define IEVENT_FIR 0x00000008 | 247 | #define IEVENT_FIR 0x00000008 |
@@ -252,7 +254,8 @@ extern const char gfar_driver_version[]; | |||
252 | #define IEVENT_ERR_MASK \ | 254 | #define IEVENT_ERR_MASK \ |
253 | (IEVENT_RXC | IEVENT_BSY | IEVENT_EBERR | IEVENT_MSRO | \ | 255 | (IEVENT_RXC | IEVENT_BSY | IEVENT_EBERR | IEVENT_MSRO | \ |
254 | IEVENT_BABT | IEVENT_TXC | IEVENT_TXE | IEVENT_LC \ | 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 | #define IMASK_INIT_CLEAR 0x00000000 | 260 | #define IMASK_INIT_CLEAR 0x00000000 |
258 | #define IMASK_BABR 0x80000000 | 261 | #define IMASK_BABR 0x80000000 |
@@ -270,6 +273,7 @@ extern const char gfar_driver_version[]; | |||
270 | #define IMASK_CRL 0x00020000 | 273 | #define IMASK_CRL 0x00020000 |
271 | #define IMASK_XFUN 0x00010000 | 274 | #define IMASK_XFUN 0x00010000 |
272 | #define IMASK_RXB0 0x00008000 | 275 | #define IMASK_RXB0 0x00008000 |
276 | #define IMASK_MAG 0x00000800 | ||
273 | #define IMASK_GTSC 0x00000100 | 277 | #define IMASK_GTSC 0x00000100 |
274 | #define IMASK_RXFEN0 0x00000080 | 278 | #define IMASK_RXFEN0 0x00000080 |
275 | #define IMASK_FIR 0x00000008 | 279 | #define IMASK_FIR 0x00000008 |
@@ -737,10 +741,14 @@ struct gfar_private { | |||
737 | unsigned int fifo_starve; | 741 | unsigned int fifo_starve; |
738 | unsigned int fifo_starve_off; | 742 | unsigned int fifo_starve_off; |
739 | 743 | ||
744 | /* Bitfield update lock */ | ||
745 | spinlock_t bflock; | ||
746 | |||
740 | unsigned char vlan_enable:1, | 747 | unsigned char vlan_enable:1, |
741 | rx_csum_enable:1, | 748 | rx_csum_enable:1, |
742 | extended_hash:1, | 749 | extended_hash:1, |
743 | bd_stash_en:1; | 750 | bd_stash_en:1, |
751 | wol_en:1; /* Wake-on-LAN enabled */ | ||
744 | unsigned short padding; | 752 | unsigned short padding; |
745 | 753 | ||
746 | unsigned int interruptTransmit; | 754 | unsigned int interruptTransmit; |
diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c index 6007147cc1e9..fb7d3ccc0fdc 100644 --- a/drivers/net/gianfar_ethtool.c +++ b/drivers/net/gianfar_ethtool.c | |||
@@ -479,14 +479,13 @@ static int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rva | |||
479 | static int gfar_set_rx_csum(struct net_device *dev, uint32_t data) | 479 | static int gfar_set_rx_csum(struct net_device *dev, uint32_t data) |
480 | { | 480 | { |
481 | struct gfar_private *priv = netdev_priv(dev); | 481 | struct gfar_private *priv = netdev_priv(dev); |
482 | unsigned long flags; | ||
482 | int err = 0; | 483 | int err = 0; |
483 | 484 | ||
484 | if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) | 485 | if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_CSUM)) |
485 | return -EOPNOTSUPP; | 486 | return -EOPNOTSUPP; |
486 | 487 | ||
487 | if (dev->flags & IFF_UP) { | 488 | if (dev->flags & IFF_UP) { |
488 | unsigned long flags; | ||
489 | |||
490 | /* Halt TX and RX, and process the frames which | 489 | /* Halt TX and RX, and process the frames which |
491 | * have already been received */ | 490 | * have already been received */ |
492 | spin_lock_irqsave(&priv->txlock, flags); | 491 | spin_lock_irqsave(&priv->txlock, flags); |
@@ -502,7 +501,9 @@ static int gfar_set_rx_csum(struct net_device *dev, uint32_t data) | |||
502 | stop_gfar(dev); | 501 | stop_gfar(dev); |
503 | } | 502 | } |
504 | 503 | ||
504 | spin_lock_irqsave(&priv->bflock, flags); | ||
505 | priv->rx_csum_enable = data; | 505 | priv->rx_csum_enable = data; |
506 | spin_unlock_irqrestore(&priv->bflock, flags); | ||
506 | 507 | ||
507 | if (dev->flags & IFF_UP) | 508 | if (dev->flags & IFF_UP) |
508 | err = startup_gfar(dev); | 509 | err = startup_gfar(dev); |
@@ -564,6 +565,38 @@ static void gfar_set_msglevel(struct net_device *dev, uint32_t data) | |||
564 | priv->msg_enable = data; | 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); | ||
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 | ||
567 | 600 | ||
568 | const struct ethtool_ops gfar_ethtool_ops = { | 601 | const struct ethtool_ops gfar_ethtool_ops = { |
569 | .get_settings = gfar_gsettings, | 602 | .get_settings = gfar_gsettings, |
@@ -585,4 +618,8 @@ const struct ethtool_ops gfar_ethtool_ops = { | |||
585 | .set_tx_csum = gfar_set_tx_csum, | 618 | .set_tx_csum = gfar_set_tx_csum, |
586 | .get_msglevel = gfar_get_msglevel, | 619 | .get_msglevel = gfar_get_msglevel, |
587 | .set_msglevel = gfar_set_msglevel, | 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 | }; |
diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index 0472877d7ea8..4e625e0094c8 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h | |||
@@ -69,6 +69,7 @@ struct gianfar_mdio_data { | |||
69 | #define FSL_GIANFAR_DEV_HAS_VLAN 0x00000020 | 69 | #define FSL_GIANFAR_DEV_HAS_VLAN 0x00000020 |
70 | #define FSL_GIANFAR_DEV_HAS_EXTENDED_HASH 0x00000040 | 70 | #define FSL_GIANFAR_DEV_HAS_EXTENDED_HASH 0x00000040 |
71 | #define FSL_GIANFAR_DEV_HAS_PADDING 0x00000080 | 71 | #define FSL_GIANFAR_DEV_HAS_PADDING 0x00000080 |
72 | #define FSL_GIANFAR_DEV_HAS_MAGIC_PACKET 0x00000100 | ||
72 | 73 | ||
73 | /* Flags in gianfar_platform_data */ | 74 | /* Flags in gianfar_platform_data */ |
74 | #define FSL_GIANFAR_BRD_HAS_PHY_INTR 0x00000001 /* set or use a timer */ | 75 | #define FSL_GIANFAR_BRD_HAS_PHY_INTR 0x00000001 /* set or use a timer */ |