summaryrefslogtreecommitdiff
path: root/drivers/net
diff options
context:
space:
mode:
authorchenhui zhao <chenhui.zhao@freescale.com>2012-07-20 02:52:03 (GMT)
committerFleming Andrew-AFLEMING <AFLEMING@freescale.com>2013-04-18 21:22:21 (GMT)
commitc17cf55c878fb8a9ae63ef149b80c2dcf76c2c81 (patch)
tree9e52d4f0cd02820f77fc8eaaa3f55cfd0a8c6612 /drivers/net
parent8bb1ab442de8410cc2f056033690ab7452505a71 (diff)
downloadlinux-fsl-qoriq-c17cf55c878fb8a9ae63ef149b80c2dcf76c2c81.tar.xz
gianfar: add support for wake-on-packet
On certain chip like MPC8536 and P1022, system can be waked up from sleep by user-defined packet and Magic Patcket.(The eTSEC cannot supports both types of wake-up event simultaneously.) This patch implements wake-up on user-defined patcket including ARP request packet and unicast patcket to this station. When entering suspend state, the gianfar driver sets receive queue filer table to filter all of packets except ARP request packet and unicast patcket to this station. The driver temporarily uses the last receive queue to receive the user defined packet. In suspend state, the receive part of eTSEC keeps working. When receiving a user defined packet, it generates an interrupt to wake up the system. The rule of the filer table is as below. if (arp request to local ip address) accept it to the last queue elif (unicast packet to local mac address) accept it to the last queue else reject it endif Note: The local ip/mac address is the ethernet primary IP/MAC address of the station. Do not support multiple IP/MAC addresses. Here is an example of enabling and testing wake up on user defined packet. ifconfig eth0 10.193.20.169 ethtool -s eth0 wol a echo standby > /sys/power/state or echo mem > /sys/power/state Ping from PC host to wake up the station: ping 10.193.20.169 Change-Id: Ib3a29047bb55c770fe8cacae3299a5e5f3420c89 Signed-off-by: Dave Liu <daveliu@freescale.com> Signed-off-by: Jin Qing <b24347@freescale.com> Signed-off-by: Li Yang <leoli@freescale.com> Signed-off-by: Zhao Chenhui <chenhui.zhao@freescale.com> Acked-by: Andy Fleming <afleming@freescale.com> Reviewed-on: http://git.am.freescale.net:8181/1517 Reviewed-by: Manoil Claudiu-B08782 <claudiu.manoil@freescale.com> Reviewed-by: Fleming Andrew-AFLEMING <AFLEMING@freescale.com> Tested-by: Fleming Andrew-AFLEMING <AFLEMING@freescale.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/ethernet/freescale/gianfar.c366
-rw-r--r--drivers/net/ethernet/freescale/gianfar.h28
-rw-r--r--drivers/net/ethernet/freescale/gianfar_ethtool.c55
3 files changed, 356 insertions, 93 deletions
diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index f2f6bce..8b86ee9 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -84,6 +84,8 @@
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/in.h>
+#include <linux/inetdevice.h>
+#include <sysdev/fsl_soc.h>
#include <linux/net_tstamp.h>
#include <asm/io.h>
@@ -150,6 +152,17 @@ static void gfar_clear_exact_match(struct net_device *dev);
static void gfar_set_mac_for_addr(struct net_device *dev, int num,
const u8 *addr);
static int gfar_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static void gfar_schedule_rx_cleanup(struct gfar_priv_grp *grp);
+
+#ifdef CONFIG_PM
+static void gfar_halt_rx(struct net_device *dev);
+static void gfar_rx_start(struct net_device *dev);
+static void gfar_enable_filer(struct net_device *dev);
+static void gfar_disable_filer(struct net_device *dev);
+static void gfar_config_filer_table(struct net_device *dev);
+static void gfar_restore_filer_table(struct net_device *dev);
+static int gfar_get_ip(struct net_device *dev);
+#endif
LIST_HEAD(gfar_recycle_queues);
@@ -839,8 +852,17 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev)
else
priv->interface = PHY_INTERFACE_MODE_MII;
- if (of_get_property(np, "fsl,magic-packet", NULL))
+ priv->wol_opts = 0;
+ priv->wol_supported = 0;
+ if (of_get_property(np, "fsl,magic-packet", NULL)) {
priv->device_flags |= FSL_GIANFAR_DEV_HAS_MAGIC_PACKET;
+ priv->wol_supported |= GIANFAR_WOL_MAGIC;
+ }
+
+ if (of_get_property(np, "fsl,wake-on-filer", NULL)) {
+ priv->wol_supported |= GIANFAR_WOL_ARP;
+ priv->wol_supported |= GIANFAR_WOL_UCAST;
+ }
priv->phy_node = of_parse_phandle(np, "phy-handle", 0);
@@ -1311,9 +1333,9 @@ static int gfar_probe(struct platform_device *ofdev)
goto register_fail;
}
- device_init_wakeup(&dev->dev,
- priv->device_flags &
- FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
+ if (priv->wol_supported) {
+ device_set_wakeup_capable(&ofdev->dev, true);
+ }
/* fill out IRQ number and name fields */
for (i = 0; i < priv->num_grps; i++) {
@@ -1383,102 +1405,215 @@ static int gfar_remove(struct platform_device *ofdev)
}
#ifdef CONFIG_PM
-
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;
- unsigned long flags;
+ int retval;
u32 tempval;
- int magic_packet = priv->wol_en &&
- (priv->device_flags &
- FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
+ if (!priv->wol_opts) {
+ /* Wake-On-Lan is disabled */
+ if (!netif_running(ndev))
+ return 0;
+ gfar_close(ndev);
+ return 0;
+ }
+
+ retval = mpc85xx_pmc_set_wake(dev, true);
+ if (retval)
+ return retval;
netif_device_detach(ndev);
+ disable_napi(priv);
+ gfar_halt(ndev);
- if (netif_running(ndev)) {
+ if (priv->wol_opts & GIANFAR_WOL_MAGIC) {
+ /* Enable Magic Packet mode */
+ tempval = gfar_read(&regs->maccfg2);
+ tempval |= MACCFG2_MPEN;
+ gfar_write(&regs->maccfg2, tempval);
+ }
- local_irq_save(flags);
- lock_tx_qs(priv);
- lock_rx_qs(priv);
+ if (priv->wol_opts & (GIANFAR_WOL_ARP | GIANFAR_WOL_UCAST)) {
+ mpc85xx_pmc_set_lossless_ethernet(1);
+ gfar_disable_filer(ndev);
+ gfar_config_filer_table(ndev);
+ gfar_enable_filer(ndev);
+ }
- gfar_halt_nodisable(ndev);
+ gfar_rx_start(ndev);
+ return 0;
+}
- /* Disable Tx, and Rx if wake-on-LAN is disabled. */
- tempval = gfar_read(&regs->maccfg1);
+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;
+ u32 tempval, i;
- tempval &= ~MACCFG1_TX_EN;
+ if (!priv->wol_opts) {
+ /* Wake-On-Lan is disabled */
+ if (!netif_running(ndev))
+ return 0;
+ gfar_enet_open(ndev);
+ return 0;
+ }
- if (!magic_packet)
- tempval &= ~MACCFG1_RX_EN;
+ mpc85xx_pmc_set_wake(dev, false);
- gfar_write(&regs->maccfg1, tempval);
+ gfar_halt_rx(ndev);
- unlock_rx_qs(priv);
- unlock_tx_qs(priv);
- local_irq_restore(flags);
+ if (priv->wol_opts & GIANFAR_WOL_MAGIC) {
+ /* Disable Magic Packet mode */
+ tempval = gfar_read(&regs->maccfg2);
+ tempval &= ~MACCFG2_MPEN;
+ gfar_write(&regs->maccfg2, tempval);
+ }
- disable_napi(priv);
+ if (priv->wol_opts & (GIANFAR_WOL_ARP | GIANFAR_WOL_UCAST)) {
+ mpc85xx_pmc_set_lossless_ethernet(0);
+ gfar_disable_filer(ndev);
+ gfar_restore_filer_table(ndev);
+ }
- if (magic_packet) {
- /* Enable interrupt on Magic Packet */
- gfar_write(&regs->imask, IMASK_MAG);
+ gfar_start(ndev);
+ enable_napi(priv);
+ netif_device_attach(ndev);
- /* Enable Magic Packet mode */
- tempval = gfar_read(&regs->maccfg2);
- tempval |= MACCFG2_MPEN;
- gfar_write(&regs->maccfg2, tempval);
- } else {
- phy_stop(priv->phydev);
- }
+ if (priv->wol_opts & (GIANFAR_WOL_ARP | GIANFAR_WOL_UCAST)) {
+ /* send requests to process the received packets */
+ for (i = 0; i < priv->num_grps; i++)
+ gfar_schedule_rx_cleanup(&priv->gfargrp[i]);
}
-
return 0;
}
-static int gfar_resume(struct device *dev)
+static void gfar_enable_filer(struct net_device *dev)
{
- struct gfar_private *priv = dev_get_drvdata(dev);
- struct net_device *ndev = priv->ndev;
+ struct gfar_private *priv = netdev_priv(dev);
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);
+ u32 temp;
- if (!netif_running(ndev)) {
- netif_device_attach(ndev);
- return 0;
- }
+ temp = gfar_read(&regs->rctrl);
+ temp &= ~(RCTRL_FSQEN | RCTRL_PRSDEP_MASK);
+ temp |= RCTRL_FILREN | RCTRL_PRSDEP_L2L3;
+ gfar_write(&regs->rctrl, temp);
+}
- if (!magic_packet && priv->phydev)
- phy_start(priv->phydev);
+static void gfar_disable_filer(struct net_device *dev)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+ u32 temp;
- /* Disable Magic Packet mode, in case something
- * else woke us up.
- */
- local_irq_save(flags);
- lock_tx_qs(priv);
- lock_rx_qs(priv);
+ temp = gfar_read(&regs->rctrl);
+ temp &= ~RCTRL_FILREN;
+ gfar_write(&regs->rctrl, temp);
+}
- tempval = gfar_read(&regs->maccfg2);
- tempval &= ~MACCFG2_MPEN;
- gfar_write(&regs->maccfg2, tempval);
+static int gfar_get_ip(struct net_device *dev)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ struct in_device *in_dev;
+ int ret = -ENOENT;
- gfar_start(ndev);
+ in_dev = in_dev_get(dev);
+ if (!in_dev)
+ return ret;
- unlock_rx_qs(priv);
- unlock_tx_qs(priv);
- local_irq_restore(flags);
+ /* Get the primary IP address */
+ for_primary_ifa(in_dev) {
+ priv->ip_addr = ifa->ifa_address;
+ ret = 0;
+ break;
+ } endfor_ifa(in_dev);
- netif_device_attach(ndev);
+ in_dev_put(in_dev);
+ return ret;
+}
- enable_napi(priv);
+static void gfar_restore_filer_table(struct net_device *dev)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ u32 rqfcr, rqfpr;
+ int i;
- return 0;
+ 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);
+ }
+}
+
+static void gfar_config_filer_table(struct net_device *dev)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ u32 dest_mac_addr;
+ u32 rqfcr, rqfpr;
+ unsigned int index;
+ u8 rqfcr_queue;
+
+ if (gfar_get_ip(dev)) {
+ netif_err(priv, wol, dev, "WOL: get the ip address error\n");
+ return;
+ }
+
+ /* init filer table */
+ rqfcr = RQFCR_RJE | RQFCR_CMP_MATCH;
+ rqfpr = 0x0;
+ for (index = 0; index <= MAX_FILER_IDX; index++)
+ gfar_write_filer(priv, index, rqfcr, rqfpr);
+
+ index = 0;
+ /* select a rx queue in group 0 */
+ rqfcr_queue = (u8)find_first_bit(&priv->gfargrp[0].napi_rx->rx_bit_map,
+ priv->num_rx_queues);
+ if (priv->wol_opts & GIANFAR_WOL_ARP) {
+ /* ARP request filer, filling the packet to the last queue */
+ rqfcr = (rqfcr_queue << 10) | RQFCR_AND |
+ RQFCR_CMP_EXACT | RQFCR_PID_MASK;
+ rqfpr = RQFPR_ARQ;
+ gfar_write_filer(priv, index++, rqfcr, rqfpr);
+
+ rqfcr = (rqfcr_queue << 10) | RQFCR_AND |
+ RQFCR_CMP_EXACT | RQFCR_PID_PARSE;
+ rqfpr = RQFPR_ARQ;
+ gfar_write_filer(priv, index++, rqfcr, rqfpr);
+
+ /*
+ * DEST_IP address in ARP packet,
+ * filling it to the last queue.
+ */
+ rqfcr = (rqfcr_queue << 10) | RQFCR_AND |
+ RQFCR_CMP_EXACT | RQFCR_PID_MASK;
+ rqfpr = FPR_FILER_MASK;
+ gfar_write_filer(priv, index++, rqfcr, rqfpr);
+
+ rqfcr = (rqfcr_queue << 10) | RQFCR_GPI |
+ RQFCR_CMP_EXACT | RQFCR_PID_DIA;
+ rqfpr = priv->ip_addr;
+ gfar_write_filer(priv, index++, rqfcr, rqfpr);
+ }
+
+ if (priv->wol_opts & GIANFAR_WOL_UCAST) {
+ /* Unicast packet, filling it to the last queue */
+ dest_mac_addr = (dev->dev_addr[0] << 16) |
+ (dev->dev_addr[1] << 8) | dev->dev_addr[2];
+ rqfcr = (rqfcr_queue << 10) | RQFCR_AND |
+ RQFCR_CMP_EXACT | RQFCR_PID_DAH;
+ rqfpr = dest_mac_addr;
+ gfar_write_filer(priv, index++, rqfcr, rqfpr);
+
+ dest_mac_addr = (dev->dev_addr[3] << 16) |
+ (dev->dev_addr[4] << 8) | dev->dev_addr[5];
+ rqfcr = (rqfcr_queue << 10) | RQFCR_GPI |
+ RQFCR_CMP_EXACT | RQFCR_PID_DAL;
+ rqfpr = dest_mac_addr;
+ gfar_write_filer(priv, index++, rqfcr, rqfpr);
+ }
}
static int gfar_restore(struct device *dev)
@@ -1731,6 +1866,48 @@ static int __gfar_is_rx_idle(struct gfar_private *priv)
return 0;
}
+#ifdef CONFIG_PM
+/* Halt the receive queues */
+static void gfar_halt_rx(struct net_device *dev)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+ u32 tempval;
+ int i = 0;
+
+ for (i = 0; i < priv->num_grps; i++) {
+ regs = priv->gfargrp[i].regs;
+ /* Mask all interrupts */
+ gfar_write(&regs->imask, IMASK_INIT_CLEAR);
+
+ /* Clear all interrupts */
+ gfar_write(&regs->ievent, IEVENT_INIT_CLEAR);
+ }
+
+ regs = priv->gfargrp[0].regs;
+ /* Stop the DMA, and wait for it to stop */
+ tempval = gfar_read(&regs->dmactrl);
+ if ((tempval & DMACTRL_GRS) != DMACTRL_GRS) {
+ int ret;
+
+ tempval |= DMACTRL_GRS;
+ gfar_write(&regs->dmactrl, tempval);
+
+ do {
+ ret = spin_event_timeout(((gfar_read(&regs->ievent) &
+ IEVENT_GRSC) == IEVENT_GRSC), 1000000, 0);
+ if (!ret && !(gfar_read(&regs->ievent) & IEVENT_GRSC))
+ ret = __gfar_is_rx_idle(priv);
+ } while (!ret);
+ }
+
+ /* Disable Rx in MACCFG1 */
+ tempval = gfar_read(&regs->maccfg1);
+ tempval &= ~MACCFG1_RX_EN;
+ gfar_write(&regs->maccfg1, tempval);
+}
+#endif
+
/* Halt the receive and transmit queues */
static void gfar_halt_nodisable(struct net_device *dev)
{
@@ -1963,6 +2140,40 @@ void gfar_start(struct net_device *dev)
dev->trans_start = jiffies; /* prevent tx timeout */
}
+#ifdef CONFIG_PM
+void gfar_rx_start(struct net_device *dev)
+{
+ struct gfar_private *priv = netdev_priv(dev);
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+ u32 tempval;
+ int i = 0;
+
+ /* Enable Rx in MACCFG1 */
+ tempval = gfar_read(&regs->maccfg1);
+ tempval |= MACCFG1_RX_EN;
+ gfar_write(&regs->maccfg1, tempval);
+
+ /* Initialize DMACTRL to have WWR and WOP */
+ tempval = gfar_read(&regs->dmactrl);
+ tempval |= DMACTRL_INIT_SETTINGS;
+ gfar_write(&regs->dmactrl, tempval);
+
+ /* Make sure we aren't stopped */
+ tempval = gfar_read(&regs->dmactrl);
+ tempval &= ~DMACTRL_GRS;
+ gfar_write(&regs->dmactrl, tempval);
+
+ for (i = 0; i < priv->num_grps; i++) {
+ regs = priv->gfargrp[i].regs;
+ /* Clear RHLT, so that the DMA starts polling now */
+ gfar_write(&regs->rstat, priv->gfargrp[i].napi_rx->rstat);
+
+ /* Unmask the interrupts we look for */
+ gfar_write(&regs->imask, IMASK_FGPI | IMASK_MAG);
+ }
+}
+#endif
+
static void gfar_configure_tx_coalescing(struct gfar_private *priv,
unsigned long tx_mask)
{
@@ -2035,7 +2246,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",
@@ -2043,14 +2255,16 @@ static int register_grp_irqs(struct gfar_priv_grp *grp)
goto err_irq_fail;
}
- err = request_irq(gfar_irq(grp, TX)->irq, gfar_transmit, 0,
+ err = request_irq(gfar_irq(grp, TX)->irq, gfar_transmit,
+ IRQF_NO_SUSPEND,
gfar_irq(grp, TX)->name, grp);
if (err < 0) {
netif_err(priv, intr, dev, "Can't get IRQ %d\n",
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",
@@ -2058,7 +2272,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",
@@ -2150,7 +2365,7 @@ static int gfar_enet_open(struct net_device *dev)
netif_tx_start_all_queues(dev);
- device_set_wakeup_enable(&dev->dev, priv->wol_en);
+ device_set_wakeup_enable(&priv->ofdev->dev, (bool)!!priv->wol_opts);
return err;
}
@@ -2963,6 +3178,17 @@ static void gfar_schedule_rx_cleanup(struct gfar_priv_grp *grp)
irqreturn_t gfar_receive(int irq, void *grp_id)
{
+ struct gfar_priv_grp *gfargrp = grp_id;
+ struct gfar __iomem *regs = gfargrp->regs;
+ u32 ievent;
+
+ ievent = gfar_read(&regs->ievent);
+
+ if (unlikely((ievent & IEVENT_FGPI) == IEVENT_FGPI)) {
+ gfar_write(&regs->ievent, ievent & IEVENT_RX_MASK);
+ return IRQ_HANDLED;
+ }
+
gfar_schedule_rx_cleanup((struct gfar_priv_grp *)grp_id);
return IRQ_HANDLED;
}
diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h
index 5c482e9..34078fb 100644
--- a/drivers/net/ethernet/freescale/gianfar.h
+++ b/drivers/net/ethernet/freescale/gianfar.h
@@ -302,6 +302,13 @@ static inline int devfp_register_tx_hook(devfp_hook_t hook)
#define RQUEUE_EN7 0x00000001
#define RQUEUE_EN_ALL 0x000000FF
+/* Wake-On-Lan options */
+#define GIANFAR_WOL_UCAST 0x00000001 /* Unicast wakeup */
+#define GIANFAR_WOL_MCAST 0x00000002 /* Multicast wakeup */
+#define GIANFAR_WOL_BCAST 0x00000004 /* Broadcase wakeup */
+#define GIANFAR_WOL_ARP 0x00000008 /* ARP request wakeup */
+#define GIANFAR_WOL_MAGIC 0x00000010 /* Magic packet wakeup */
+
/* Init to do tx snooping for buffers and descriptors */
#define DMACTRL_INIT_SETTINGS 0x000000c3
#define DMACTRL_GRS 0x00000010
@@ -347,11 +354,15 @@ static inline int devfp_register_tx_hook(devfp_hook_t hook)
#define RCTRL_PAL_MASK 0x001f0000
#define RCTRL_VLEX 0x00002000
#define RCTRL_FILREN 0x00001000
+#define RCTRL_FSQEN 0x00000800
#define RCTRL_GHTX 0x00000400
#define RCTRL_IPCSEN 0x00000200
#define RCTRL_TUCSEN 0x00000100
#define RCTRL_PRSDEP_MASK 0x000000c0
#define RCTRL_PRSDEP_INIT 0x000000c0
+#define RCTRL_PRSDEP_L2 0x00000040
+#define RCTRL_PRSDEP_L2L3 0x00000080
+#define RCTRL_PRSDEP_L2L3L4 0x000000c0
#define RCTRL_PRSFM 0x00000020
#define RCTRL_PROM 0x00000008
#define RCTRL_EMEN 0x00000002
@@ -407,18 +418,20 @@ static inline int devfp_register_tx_hook(devfp_hook_t hook)
#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
#define IEVENT_PERR 0x00000001
-#define IEVENT_RX_MASK (IEVENT_RXB0 | IEVENT_RXF0 | IEVENT_BSY)
+#define IEVENT_RX_MASK (IEVENT_RXB0 | IEVENT_RXF0 | IEVENT_BSY | \
+ IEVENT_FGPI)
#define IEVENT_TX_MASK (IEVENT_TXB | IEVENT_TXF)
#define IEVENT_RTX_MASK (IEVENT_RX_MASK | IEVENT_TX_MASK)
#define IEVENT_ERR_MASK \
-(IEVENT_RXC | IEVENT_BSY | IEVENT_EBERR | IEVENT_MSRO | \
- IEVENT_BABT | IEVENT_TXC | IEVENT_TXE | IEVENT_LC \
- | IEVENT_CRL | IEVENT_XFUN | IEVENT_DPE | IEVENT_PERR \
- | IEVENT_MAG | IEVENT_BABR)
+ (IEVENT_RXC | IEVENT_BSY | IEVENT_EBERR | IEVENT_MSRO | \
+ IEVENT_BABT | IEVENT_TXC | IEVENT_TXE | IEVENT_LC \
+ | IEVENT_CRL | IEVENT_XFUN | IEVENT_DPE | IEVENT_PERR \
+ | IEVENT_MAG | IEVENT_BABR | IEVENT_FIR | IEVENT_FIQ)
#define IMASK_INIT_CLEAR 0x00000000
#define IMASK_BABR 0x80000000
@@ -439,6 +452,7 @@ static inline int devfp_register_tx_hook(devfp_hook_t hook)
#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
@@ -1308,6 +1322,10 @@ struct gfar_private {
struct net_device *recycle_ndev;
struct list_head recycle_node;
+ u32 ip_addr; /* the primary IP address of the device */
+ u32 wol_opts; /* enabled Wake-on-Lan modes */
+ u32 wol_supported; /* supported Wake-on-Lan modes */
+
/* The total tx and rx ring size for the enabled queues */
unsigned int total_tx_ring_size;
unsigned int total_rx_ring_size;
diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c
index 6985fd1..565b26d 100644
--- a/drivers/net/ethernet/freescale/gianfar_ethtool.c
+++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c
@@ -30,6 +30,7 @@
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
+#include <linux/platform_device.h>
#include <asm/io.h>
#include <asm/irq.h>
@@ -599,32 +600,50 @@ 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 = 0;
+ wol->wolopts = 0;
+
+ if (!priv->wol_supported || !device_can_wakeup(&priv->ofdev->dev))
+ return;
+
+ if (priv->wol_supported & GIANFAR_WOL_MAGIC)
+ wol->supported |= WAKE_MAGIC;
+
+ if (priv->wol_supported & GIANFAR_WOL_ARP)
+ wol->supported |= WAKE_ARP;
+
+ if (priv->wol_supported & GIANFAR_WOL_UCAST)
+ wol->supported |= WAKE_UCAST;
+
+ if (priv->wol_opts & GIANFAR_WOL_MAGIC)
+ wol->wolopts |= WAKE_MAGIC;
+
+ if (priv->wol_opts & GIANFAR_WOL_ARP)
+ wol->wolopts |= WAKE_ARP;
+
+ if (priv->wol_opts & GIANFAR_WOL_UCAST)
+ wol->wolopts |= WAKE_UCAST;
}
static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
{
struct gfar_private *priv = netdev_priv(dev);
- unsigned long flags;
-
- if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET) &&
- wol->wolopts != 0)
- return -EINVAL;
-
- if (wol->wolopts & ~WAKE_MAGIC)
- return -EINVAL;
- device_set_wakeup_enable(&dev->dev, wol->wolopts & WAKE_MAGIC);
+ if (!priv->wol_supported || !device_can_wakeup(&priv->ofdev->dev) ||
+ (wol->wolopts & ~(WAKE_MAGIC | WAKE_ARP | WAKE_UCAST)))
+ return -EOPNOTSUPP;
- spin_lock_irqsave(&priv->bflock, flags);
- priv->wol_en = !!device_may_wakeup(&dev->dev);
- spin_unlock_irqrestore(&priv->bflock, flags);
+ priv->wol_opts = 0;
+ if (wol->wolopts & WAKE_MAGIC) {
+ priv->wol_opts |= GIANFAR_WOL_MAGIC;
+ } else {
+ if (wol->wolopts & WAKE_ARP)
+ priv->wol_opts |= GIANFAR_WOL_ARP;
+ if (wol->wolopts & WAKE_UCAST)
+ priv->wol_opts |= GIANFAR_WOL_UCAST;
+ }
+ device_set_wakeup_enable(&priv->ofdev->dev, (bool)!!priv->wol_opts);
return 0;
}
#endif