summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet')
-rw-r--r--drivers/net/ethernet/freescale/asf_gianfar.c3
-rwxr-xr-xdrivers/net/ethernet/freescale/fman/src/wrapper/lnxwrp_fm.c2
-rw-r--r--drivers/net/ethernet/freescale/gianfar.c346
-rw-r--r--drivers/net/ethernet/freescale/gianfar.h16
-rw-r--r--drivers/net/ethernet/freescale/gianfar_ethtool.c57
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(&regs->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(&regs->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(&regs->rctrl);
+ temp &= ~(RCTRL_FILREN | RCTRL_PRSDEP_INIT);
+ gfar_write(&regs->rctrl, temp);
+}
+
+static void __gfar_filer_enable(struct gfar_private *priv)
+{
+ struct gfar __iomem *regs = priv->gfargrp[0].regs;
+ u32 temp;
+
+ temp = gfar_read(&regs->rctrl);
+ temp |= RCTRL_FILREN | RCTRL_PRSDEP_INIT;
+ gfar_write(&regs->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(&regs->rqueue, priv->rqueue);
- netif_device_detach(ndev);
+ /* 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);
- 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(&regs->rstat, priv->gfargrp[i].rstat);
+ /* enable the filer general purpose interrupts */
+ gfar_write(&regs->imask, IMASK_FGPI);
+ }
- local_irq_save_nort(flags);
- lock_tx_qs(priv);
+ /* Enable Rx/Tx DMA */
+ tempval = gfar_read(&regs->maccfg1);
+ tempval |= MACCFG1_RX_EN;
+ gfar_write(&regs->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(&regs->maccfg1);
+ /* Dissable the Rx hw queues */
+ gfar_write(&regs->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(&regs->dmactrl);
+ if (!(tempval & DMACTRL_GRS)) {
+ int ret;
- gfar_write(&regs->maccfg1, tempval);
+ tempval |= DMACTRL_GRS;
+ gfar_write(&regs->dmactrl, tempval);
- unlock_tx_qs(priv);
- local_irq_restore_nort(flags);
+ do {
+ ret = spin_event_timeout((gfar_read(&regs->ievent) &
+ IEVENT_GRSC), 1000000, 0);
+ if (!ret && !(gfar_read(&regs->ievent) & IEVENT_GRSC))
+ ret = __gfar_is_rx_idle(priv);
+ } while (!ret);
+ }
- disable_napi(priv);
+ /* Disable Rx DMA */
+ tempval = gfar_read(&regs->maccfg1);
+ tempval &= ~MACCFG1_RX_EN;
+ gfar_write(&regs->maccfg1, tempval);
+}
- if (magic_packet) {
- /* Enable interrupt on Magic Packet */
- gfar_write(&regs->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(&regs->maccfg2);
- tempval |= MACCFG2_MPEN;
- gfar_write(&regs->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(&regs->imask, IMASK_MAG);
+
+ /* Enable Magic Packet mode */
+ tempval = gfar_read(&regs->maccfg2);
+ tempval |= MACCFG2_MPEN;
+ gfar_write(&regs->maccfg2, tempval);
+
+ /* re-enable the Rx block */
+ tempval = gfar_read(&regs->maccfg1);
+ tempval |= MACCFG1_RX_EN;
+ gfar_write(&regs->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(&regs->maccfg2);
+ tempval &= ~MACCFG2_MPEN;
+ gfar_write(&regs->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(&regs->maccfg2);
- tempval &= ~MACCFG2_MPEN;
- gfar_write(&regs->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;
}