diff options
Diffstat (limited to 'drivers/net/ethernet')
-rw-r--r-- | drivers/net/ethernet/freescale/asf_gianfar.c | 3 | ||||
-rwxr-xr-x | drivers/net/ethernet/freescale/fman/src/wrapper/lnxwrp_fm.c | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/gianfar.c | 346 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/gianfar.h | 16 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/gianfar_ethtool.c | 57 |
5 files changed, 337 insertions, 87 deletions
diff --git a/drivers/net/ethernet/freescale/asf_gianfar.c b/drivers/net/ethernet/freescale/asf_gianfar.c index af7f205..5280ed9 100644 --- a/drivers/net/ethernet/freescale/asf_gianfar.c +++ b/drivers/net/ethernet/freescale/asf_gianfar.c @@ -55,7 +55,8 @@ static inline void gfar_asf_reclaim_skb(struct sk_buff *skb) static inline void gfar_recycle_skb(struct sk_buff *skb) { struct sk_buff_head *h = &__get_cpu_var(skb_recycle_list); - int skb_size = SKB_DATA_ALIGN(GFAR_RXB_REC_SZ + NET_SKB_PAD); + int skb_size = SKB_DATA_ALIGN(GFAR_RXB_REC_SZ + NET_SKB_PAD + + EXTRA_HEADROOM); if (skb_queue_len(h) < DEFAULT_RX_RING_SIZE && !skb_cloned(skb) && !skb_is_nonlinear(skb) && diff --git a/drivers/net/ethernet/freescale/fman/src/wrapper/lnxwrp_fm.c b/drivers/net/ethernet/freescale/fman/src/wrapper/lnxwrp_fm.c index 7c09d8b..0da272b 100755 --- a/drivers/net/ethernet/freescale/fman/src/wrapper/lnxwrp_fm.c +++ b/drivers/net/ethernet/freescale/fman/src/wrapper/lnxwrp_fm.c @@ -790,7 +790,7 @@ static t_Error ConfigureFmDev(t_LnxWrpFmDev *p_LnxWrpFmDev) if (unlikely(_errno < 0)) RETURN_ERROR(MAJOR, E_INVALID_STATE, ("can_request_irq() = %d", _errno)); #endif - _errno = devm_request_irq(p_LnxWrpFmDev->dev, p_LnxWrpFmDev->irq, fm_irq, 0, "fman", p_LnxWrpFmDev); + _errno = devm_request_irq(p_LnxWrpFmDev->dev, p_LnxWrpFmDev->irq, fm_irq, IRQF_NO_SUSPEND, "fman", p_LnxWrpFmDev); if (unlikely(_errno < 0)) RETURN_ERROR(MAJOR, E_INVALID_STATE, ("request_irq(%d) = %d", p_LnxWrpFmDev->irq, _errno)); diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 574b87e..933ba9a 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -85,6 +85,9 @@ #include <linux/udp.h> #include <linux/in.h> #include <linux/net_tstamp.h> +#ifdef CONFIG_PM +#include <linux/inetdevice.h> +#endif #include <asm/io.h> #include <asm/reg.h> @@ -146,6 +149,7 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit); static void gfar_clean_tx_ring(struct gfar_priv_tx_q *tx_queue); static void gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int amount_pull, struct napi_struct *napi); +static int __gfar_is_rx_idle(struct gfar_private *priv); static void gfar_halt_nodisable(struct gfar_private *priv); static void gfar_clear_exact_match(struct net_device *dev); static void gfar_set_mac_for_addr(struct net_device *dev, int num, @@ -356,7 +360,7 @@ static void gfar_mac_rx_config(struct gfar_private *priv) u32 rctrl = 0; if (priv->rx_filer_enable) { - rctrl |= RCTRL_FILREN; + rctrl |= RCTRL_FILREN | RCTRL_PRSDEP_INIT; /* Program the RIR0 reg with the required distribution */ if (priv->poll_mode == GFAR_SQ_POLLING) gfar_write(®s->rir0, DEFAULT_2RXQ_RIR0); @@ -381,10 +385,10 @@ static void gfar_mac_rx_config(struct gfar_private *priv) /* Enable HW time stamping if requested from user space */ if (priv->hwts_rx_en) - rctrl |= RCTRL_PRSDEP_INIT | RCTRL_TS_ENABLE; + rctrl |= RCTRL_TS_ENABLE; if (priv->ndev->features & NETIF_F_HW_VLAN_CTAG_RX) - rctrl |= RCTRL_VLEX | RCTRL_PRSDEP_INIT; + rctrl |= RCTRL_VLEX; /* Init rctrl based on our settings */ gfar_write(®s->rctrl, rctrl); @@ -481,6 +485,15 @@ static struct net_device_stats *gfar_get_stats(struct net_device *dev) return &dev->stats; } +int gfar_set_mac_addr(struct net_device *dev, void *p) +{ + eth_mac_addr(dev, p); + + gfar_set_mac_for_addr(dev, 0, dev->dev_addr); + + return 0; +} + static const struct net_device_ops gfar_netdev_ops = { .ndo_open = gfar_enet_open, .ndo_start_xmit = gfar_start_xmit, @@ -491,7 +504,7 @@ static const struct net_device_ops gfar_netdev_ops = { .ndo_tx_timeout = gfar_timeout, .ndo_do_ioctl = gfar_ioctl, .ndo_get_stats = gfar_get_stats, - .ndo_set_mac_address = eth_mac_addr, + .ndo_set_mac_address = gfar_set_mac_addr, .ndo_validate_addr = eth_validate_addr, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = gfar_netpoll, @@ -886,6 +899,9 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) if (of_get_property(np, "fsl,magic-packet", NULL)) priv->device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET; + if (of_get_property(np, "fsl,wake-on-filer", NULL)) + priv->device_flags |= FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER; + priv->phy_node = of_parse_phandle(np, "phy-handle", 0); /* Find the TBI PHY. If it's not there, we don't support SGMII */ @@ -1287,7 +1303,6 @@ static int gfar_probe(struct platform_device *ofdev) priv->dev = &ofdev->dev; SET_NETDEV_DEV(dev, &ofdev->dev); - spin_lock_init(&priv->bflock); INIT_WORK(&priv->reset_task, gfar_reset_task); platform_set_drvdata(ofdev, priv); @@ -1331,6 +1346,8 @@ static int gfar_probe(struct platform_device *ofdev) dev->features |= NETIF_F_HW_VLAN_CTAG_RX; } + dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; + gfar_init_addr_hash_table(priv); /* Insert receive time stamps into padding alignment bytes */ @@ -1379,9 +1396,15 @@ static int gfar_probe(struct platform_device *ofdev) /* Carrier starts down, phylib will bring it up */ netif_carrier_off(dev); - device_init_wakeup(&dev->dev, - priv->device_flags & - FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) + priv->wol_supported |= GFAR_WOL_MAGIC; + + if ((priv->device_flags & FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER) && + priv->rx_filer_enable) + priv->wol_supported |= GFAR_WOL_FILER_UCAST | + GFAR_WOL_FILER_ARP; + + device_set_wakeup_capable(&ofdev->dev, priv->wol_supported); /* fill out IRQ number and name fields */ for (i = 0; i < priv->num_grps; i++) { @@ -1451,56 +1474,247 @@ static int gfar_remove(struct platform_device *ofdev) #ifdef CONFIG_PM -static int gfar_suspend(struct device *dev) +static void __gfar_filer_disable(struct gfar_private *priv) +{ + struct gfar __iomem *regs = priv->gfargrp[0].regs; + u32 temp; + + temp = gfar_read(®s->rctrl); + temp &= ~(RCTRL_FILREN | RCTRL_PRSDEP_INIT); + gfar_write(®s->rctrl, temp); +} + +static void __gfar_filer_enable(struct gfar_private *priv) +{ + struct gfar __iomem *regs = priv->gfargrp[0].regs; + u32 temp; + + temp = gfar_read(®s->rctrl); + temp |= RCTRL_FILREN | RCTRL_PRSDEP_INIT; + gfar_write(®s->rctrl, temp); +} + +/* Get the first IP address on this chain for this interface + * so that we can configure wakeup with WOL for ARP. + */ +static int gfar_get_ip(struct gfar_private *priv, __be32 *ip_addr) +{ + struct in_device *in_dev; + int err = -ENOENT; + + rcu_read_lock(); + in_dev = __in_dev_get_rcu(priv->ndev); + if (in_dev != NULL) { + for_primary_ifa(in_dev) { + *ip_addr = ifa->ifa_address; + err = 0; + break; + } endfor_ifa(in_dev); + } + rcu_read_unlock(); + return err; +} + +static int gfar_filer_config_wol(struct gfar_private *priv) { - struct gfar_private *priv = dev_get_drvdata(dev); struct net_device *ndev = priv->ndev; + u32 rqfcr, rqfpr; + unsigned int i; + u8 rqfcr_queue; + int err = 0; + + __gfar_filer_disable(priv); + + /* init filer table */ + rqfcr = RQFCR_RJE | RQFCR_CMP_MATCH; + rqfpr = 0x0; + for (i = 0; i <= MAX_FILER_IDX; i++) + gfar_write_filer(priv, i, rqfcr, rqfpr); + + i = 0; + /* select a rx queue in group 0 */ + rqfcr_queue = (u8)find_first_bit(&priv->gfargrp[0].rx_bit_map, + priv->num_rx_queues); + + if (priv->wol_opts & GFAR_WOL_FILER_UCAST) { + /* Unicast packet, accept it */ + u32 dest_mac_addr = (ndev->dev_addr[0] << 16) | + (ndev->dev_addr[1] << 8) | + ndev->dev_addr[2]; + + rqfcr = (rqfcr_queue << 10) | RQFCR_AND | + RQFCR_CMP_EXACT | RQFCR_PID_DAH; + rqfpr = dest_mac_addr; + gfar_write_filer(priv, i++, rqfcr, rqfpr); + + dest_mac_addr = (ndev->dev_addr[3] << 16) | + (ndev->dev_addr[4] << 8) | + ndev->dev_addr[5]; + rqfcr = (rqfcr_queue << 10) | RQFCR_GPI | + RQFCR_CMP_EXACT | RQFCR_PID_DAL; + rqfpr = dest_mac_addr; + gfar_write_filer(priv, i++, rqfcr, rqfpr); + } + + if (priv->wol_opts & GFAR_WOL_FILER_ARP) { + /* ARP request packet, accept it */ + __be32 ip_addr; + + err = gfar_get_ip(priv, &ip_addr); + if (err) { + netif_err(priv, wol, ndev, "Failed to get ip addr\n"); + goto out; + } + + rqfcr = (rqfcr_queue << 10) | RQFCR_AND | + RQFCR_CMP_EXACT | RQFCR_PID_MASK; + rqfpr = RQFPR_ARQ; + gfar_write_filer(priv, i++, rqfcr, rqfpr); + + rqfcr = (rqfcr_queue << 10) | RQFCR_AND | + RQFCR_CMP_EXACT | RQFCR_PID_PARSE; + rqfpr = RQFPR_ARQ; + gfar_write_filer(priv, i++, rqfcr, rqfpr); + + /* match DEST_IP address in ARP req packet */ + rqfcr = (rqfcr_queue << 10) | RQFCR_AND | + RQFCR_CMP_EXACT | RQFCR_PID_MASK; + rqfpr = FPR_FILER_MASK; + gfar_write_filer(priv, i++, rqfcr, rqfpr); + + rqfcr = (rqfcr_queue << 10) | RQFCR_GPI | + RQFCR_CMP_EXACT | RQFCR_PID_DIA; + rqfpr = ip_addr; + gfar_write_filer(priv, i++, rqfcr, rqfpr); + } +out: + __gfar_filer_enable(priv); + return err; +} + +static void gfar_filer_restore_table(struct gfar_private *priv) +{ + u32 rqfcr, rqfpr; + unsigned int i; + + __gfar_filer_disable(priv); + + for (i = 0; i <= MAX_FILER_IDX; i++) { + rqfcr = priv->ftp_rqfcr[i]; + rqfpr = priv->ftp_rqfpr[i]; + gfar_write_filer(priv, i, rqfcr, rqfpr); + } + + __gfar_filer_enable(priv); +} + +void gfar_start_wol_filer(struct gfar_private *priv) +{ struct gfar __iomem *regs = priv->gfargrp[0].regs; - unsigned long flags; u32 tempval; + int i = 0; - int magic_packet = priv->wol_en && - (priv->device_flags & - FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + /* Enable Rx hw queues */ + gfar_write(®s->rqueue, priv->rqueue); - netif_device_detach(ndev); + /* Initialize DMACTRL to have WWR and WOP */ + tempval = gfar_read(®s->dmactrl); + tempval |= DMACTRL_INIT_SETTINGS; + gfar_write(®s->dmactrl, tempval); + + /* Make sure we aren't stopped */ + tempval = gfar_read(®s->dmactrl); + tempval &= ~DMACTRL_GRS; + gfar_write(®s->dmactrl, tempval); - if (netif_running(ndev)) { + for (i = 0; i < priv->num_grps; i++) { + regs = priv->gfargrp[i].regs; + /* Clear RHLT, so that the DMA starts polling now */ + gfar_write(®s->rstat, priv->gfargrp[i].rstat); + /* enable the filer general purpose interrupts */ + gfar_write(®s->imask, IMASK_FGPI); + } - local_irq_save_nort(flags); - lock_tx_qs(priv); + /* Enable Rx/Tx DMA */ + tempval = gfar_read(®s->maccfg1); + tempval |= MACCFG1_RX_EN; + gfar_write(®s->maccfg1, tempval); +} - gfar_halt_nodisable(priv); +void gfar_halt_wol_filer(struct gfar_private *priv) +{ + struct gfar __iomem *regs = priv->gfargrp[0].regs; + u32 tempval; - /* Disable Tx, and Rx if wake-on-LAN is disabled. */ - tempval = gfar_read(®s->maccfg1); + /* Dissable the Rx hw queues */ + gfar_write(®s->rqueue, 0); - tempval &= ~MACCFG1_TX_EN; + gfar_ints_disable(priv); - if (!magic_packet) - tempval &= ~MACCFG1_RX_EN; + /* Stop the DMA, and wait for it to stop */ + tempval = gfar_read(®s->dmactrl); + if (!(tempval & DMACTRL_GRS)) { + int ret; - gfar_write(®s->maccfg1, tempval); + tempval |= DMACTRL_GRS; + gfar_write(®s->dmactrl, tempval); - unlock_tx_qs(priv); - local_irq_restore_nort(flags); + do { + ret = spin_event_timeout((gfar_read(®s->ievent) & + IEVENT_GRSC), 1000000, 0); + if (!ret && !(gfar_read(®s->ievent) & IEVENT_GRSC)) + ret = __gfar_is_rx_idle(priv); + } while (!ret); + } - disable_napi(priv); + /* Disable Rx DMA */ + tempval = gfar_read(®s->maccfg1); + tempval &= ~MACCFG1_RX_EN; + gfar_write(®s->maccfg1, tempval); +} - if (magic_packet) { - /* Enable interrupt on Magic Packet */ - gfar_write(®s->imask, IMASK_MAG); +static int gfar_suspend(struct device *dev) +{ + struct gfar_private *priv = dev_get_drvdata(dev); + struct net_device *ndev = priv->ndev; + struct gfar __iomem *regs = priv->gfargrp[0].regs; + u32 tempval; + u16 wol = priv->wol_opts; + int err = 0; - /* Enable Magic Packet mode */ - tempval = gfar_read(®s->maccfg2); - tempval |= MACCFG2_MPEN; - gfar_write(®s->maccfg2, tempval); - } else { - phy_stop(priv->phydev); - } + if (!netif_running(ndev)) + return 0; + + disable_napi(priv); + netif_tx_lock(ndev); + netif_device_detach(ndev); + netif_tx_unlock(ndev); + + gfar_halt(priv); + + if (wol & GFAR_WOL_MAGIC) { + /* Enable interrupt on Magic Packet */ + gfar_write(®s->imask, IMASK_MAG); + + /* Enable Magic Packet mode */ + tempval = gfar_read(®s->maccfg2); + tempval |= MACCFG2_MPEN; + gfar_write(®s->maccfg2, tempval); + + /* re-enable the Rx block */ + tempval = gfar_read(®s->maccfg1); + tempval |= MACCFG1_RX_EN; + gfar_write(®s->maccfg1, tempval); + + } else if (wol & (GFAR_WOL_FILER_UCAST | GFAR_WOL_FILER_ARP)) { + err = gfar_filer_config_wol(priv); + gfar_start_wol_filer(priv); + + } else { + phy_stop(priv->phydev); } - return 0; + return err; } static int gfar_resume(struct device *dev) @@ -1508,37 +1722,29 @@ static int gfar_resume(struct device *dev) struct gfar_private *priv = dev_get_drvdata(dev); struct net_device *ndev = priv->ndev; struct gfar __iomem *regs = priv->gfargrp[0].regs; - unsigned long flags; u32 tempval; - int magic_packet = priv->wol_en && - (priv->device_flags & - FSL_GIANFAR_DEV_HAS_MAGIC_PACKET); + u16 wol = priv->wol_opts; - if (!netif_running(ndev)) { - netif_device_attach(ndev); + if (!netif_running(ndev)) return 0; - } - if (!magic_packet && priv->phydev) - phy_start(priv->phydev); + if (wol & GFAR_WOL_MAGIC) { + /* Disable Magic Packet mode */ + tempval = gfar_read(®s->maccfg2); + tempval &= ~MACCFG2_MPEN; + gfar_write(®s->maccfg2, tempval); - /* Disable Magic Packet mode, in case something - * else woke us up. - */ - local_irq_save_nort(flags); - lock_tx_qs(priv); + } else if (wol & (GFAR_WOL_FILER_UCAST | GFAR_WOL_FILER_ARP)) { + gfar_halt_wol_filer(priv); + gfar_filer_restore_table(priv); - tempval = gfar_read(®s->maccfg2); - tempval &= ~MACCFG2_MPEN; - gfar_write(®s->maccfg2, tempval); + } else { + phy_start(priv->phydev); + } gfar_start(priv); - unlock_tx_qs(priv); - local_irq_restore_nort(flags); - netif_device_attach(ndev); - enable_napi(priv); return 0; @@ -1954,7 +2160,8 @@ static int register_grp_irqs(struct gfar_priv_grp *grp) /* Install our interrupt handlers for Error, * Transmit, and Receive */ - err = request_irq(gfar_irq(grp, ER)->irq, gfar_error, 0, + err = request_irq(gfar_irq(grp, ER)->irq, gfar_error, + IRQF_NO_SUSPEND, gfar_irq(grp, ER)->name, grp); if (err < 0) { netif_err(priv, intr, dev, "Can't get IRQ %d\n", @@ -1969,7 +2176,8 @@ static int register_grp_irqs(struct gfar_priv_grp *grp) gfar_irq(grp, TX)->irq); goto tx_irq_fail; } - err = request_irq(gfar_irq(grp, RX)->irq, gfar_receive, 0, + err = request_irq(gfar_irq(grp, RX)->irq, gfar_receive, + IRQF_NO_SUSPEND, gfar_irq(grp, RX)->name, grp); if (err < 0) { netif_err(priv, intr, dev, "Can't get IRQ %d\n", @@ -1977,7 +2185,8 @@ static int register_grp_irqs(struct gfar_priv_grp *grp) goto rx_irq_fail; } } else { - err = request_irq(gfar_irq(grp, TX)->irq, gfar_interrupt, 0, + err = request_irq(gfar_irq(grp, TX)->irq, gfar_interrupt, + IRQF_NO_SUSPEND, gfar_irq(grp, TX)->name, grp); if (err < 0) { netif_err(priv, intr, dev, "Can't get IRQ %d\n", @@ -2078,8 +2287,6 @@ static int gfar_enet_open(struct net_device *dev) if (err) return err; - device_set_wakeup_enable(&dev->dev, priv->wol_en); - return err; } void inline gfar_tx_vlan(struct sk_buff *skb, struct txfcb *fcb) @@ -2621,7 +2828,14 @@ irqreturn_t gfar_receive(int irq, void *grp_id) { struct gfar_priv_grp *grp = (struct gfar_priv_grp *)grp_id; unsigned long flags; - u32 imask; + u32 imask, ievent; + + ievent = gfar_read(&grp->regs->ievent); + + if (unlikely(ievent & IEVENT_FGPI)) { + gfar_write(&grp->regs->ievent, IEVENT_FGPI); + return IRQ_HANDLED; + } if (likely(napi_schedule_prep(&grp->napi_rx))) { spin_lock_irqsave(&grp->grplock, flags); diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h index 44a32ac..1d1b9b1 100644 --- a/drivers/net/ethernet/freescale/gianfar.h +++ b/drivers/net/ethernet/freescale/gianfar.h @@ -406,6 +406,7 @@ extern const char gfar_driver_version[]; #define IEVENT_MAG 0x00000800 #define IEVENT_GRSC 0x00000100 #define IEVENT_RXF0 0x00000080 +#define IEVENT_FGPI 0x00000010 #define IEVENT_FIR 0x00000008 #define IEVENT_FIQ 0x00000004 #define IEVENT_DPE 0x00000002 @@ -438,6 +439,7 @@ extern const char gfar_driver_version[]; #define IMASK_MAG 0x00000800 #define IMASK_GRSC 0x00000100 #define IMASK_RXFEN0 0x00000080 +#define IMASK_FGPI 0x00000010 #define IMASK_FIR 0x00000008 #define IMASK_FIQ 0x00000004 #define IMASK_DPE 0x00000002 @@ -606,6 +608,10 @@ extern const char gfar_driver_version[]; #define GFAR_INT_NAME_MAX (IFNAMSIZ + 6) /* '_g#_xx' */ +#define GFAR_WOL_MAGIC 0x00000001 +#define GFAR_WOL_FILER_UCAST 0x00000002 +#define GFAR_WOL_FILER_ARP 0x00000004 + struct txbd8 { union { @@ -1056,6 +1062,7 @@ struct gfar { #define FSL_GIANFAR_DEV_HAS_BD_STASHING 0x00000200 #define FSL_GIANFAR_DEV_HAS_BUF_STASHING 0x00000400 #define FSL_GIANFAR_DEV_HAS_TIMER 0x00000800 +#define FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER 0x00001000 #if defined CONFIG_FSL_GIANFAR_1588 #define FSL_GIANFAR_DEV_HAS_TS_TO_BUFFER 0x00001000 @@ -1302,9 +1309,6 @@ struct gfar_private { int oldduplex; int oldlink; - /* Bitfield update lock */ - spinlock_t bflock; - uint32_t msg_enable; struct work_struct reset_task; @@ -1314,8 +1318,6 @@ struct gfar_private { extended_hash:1, bd_stash_en:1, rx_filer_enable:1, - /* Wake-on-LAN enabled */ - wol_en:1, /* Enable priorty based Tx scheduling in Hw */ prio_sched_en:1, /* Flow control flags */ @@ -1344,6 +1346,10 @@ struct gfar_private { u32 __iomem *hash_regs[16]; int hash_width; + /* wake-on-lan settings */ + u16 wol_opts; + u16 wol_supported; + /*Filer table*/ unsigned int ftp_rqfpr[MAX_FILER_IDX + 1]; unsigned int ftp_rqfcr[MAX_FILER_IDX + 1]; diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c index f2dfa10..e2bf5d1 100644 --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c @@ -42,6 +42,9 @@ #include <linux/phy.h> #include <linux/sort.h> #include <linux/if_vlan.h> +#ifdef CONFIG_PM +#include <sysdev/fsl_soc.h> +#endif #include "gianfar.h" @@ -635,31 +638,57 @@ static void gfar_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct gfar_private *priv = netdev_priv(dev); - if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) { - wol->supported = WAKE_MAGIC; - wol->wolopts = priv->wol_en ? WAKE_MAGIC : 0; - } else { - wol->supported = wol->wolopts = 0; - } + wol->supported = wol->wolopts = 0; + + if (priv->wol_supported & GFAR_WOL_MAGIC) + wol->supported |= WAKE_MAGIC; + + if (priv->wol_supported & GFAR_WOL_FILER_UCAST) + wol->supported |= WAKE_UCAST; + + if (priv->wol_supported & GFAR_WOL_FILER_ARP) + wol->supported |= WAKE_ARP; + + if (priv->wol_opts & GFAR_WOL_MAGIC) + wol->wolopts |= WAKE_MAGIC; + + if (priv->wol_opts & GFAR_WOL_FILER_UCAST) + wol->wolopts |= WAKE_UCAST; + + if (priv->wol_opts & GFAR_WOL_FILER_ARP) + wol->wolopts |= WAKE_ARP; } static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct gfar_private *priv = netdev_priv(dev); - unsigned long flags; + int err; + u16 wol_opts = 0; - if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) && - wol->wolopts != 0) + if (!priv->wol_supported && wol->wolopts) return -EINVAL; - if (wol->wolopts & ~WAKE_MAGIC) + if (wol->wolopts & ~(WAKE_MAGIC | WAKE_UCAST | WAKE_ARP)) return -EINVAL; - device_set_wakeup_enable(&dev->dev, wol->wolopts & WAKE_MAGIC); + if (wol->wolopts & WAKE_MAGIC) { + wol_opts |= GFAR_WOL_MAGIC; + } else { + if (wol->wolopts & WAKE_UCAST) + wol_opts |= GFAR_WOL_FILER_UCAST; + if (wol->wolopts & WAKE_ARP) + wol_opts |= GFAR_WOL_FILER_ARP; + } - spin_lock_irqsave(&priv->bflock, flags); - priv->wol_en = !!device_may_wakeup(&dev->dev); - spin_unlock_irqrestore(&priv->bflock, flags); + priv->wol_opts = wol_opts & priv->wol_supported; + + device_set_wakeup_enable(priv->dev, priv->wol_opts); + + err = mpc85xx_pmc_set_wake(priv->dev, priv->wol_opts); + if (err) { + device_set_wakeup_enable(priv->dev, false); + return err; + } return 0; } |