summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/freescale/gianfar.c
diff options
context:
space:
mode:
authorScott Wood <scottwood@freescale.com>2014-04-18 21:08:55 (GMT)
committerScott Wood <scottwood@freescale.com>2014-04-18 21:08:55 (GMT)
commitfb4408c608480c14dfeae557b7e7e0e75bc0c0f4 (patch)
tree5ed0e003f6b15e1ce13121e06669f458f1df53df /drivers/net/ethernet/freescale/gianfar.c
parent10667100a057af290ccfdf9c7178ba81867f80e7 (diff)
parentb35a69559c46e066e6f24bb02d5a6090483786e3 (diff)
downloadlinux-fsl-qoriq-fb4408c608480c14dfeae557b7e7e0e75bc0c0f4.tar.xz
Merge branch 'master-tmp' into sdk-v1.6.x
master-tmp is the master branch as of 8b60f5ea90c49344692a70f62cd4aa349de38b48 with the following commits reverted due to excessive conflicts: commit b35a69559c46e066e6f24bb02d5a6090483786e3 Author: Scott Wood <scottwood@freescale.com> Date: Fri Apr 18 15:27:52 2014 -0500 Revert "net: add sysfs helpers for netdev_adjacent logic" This reverts commit 0be682ca768d671c91cfd1379759efcb3b29102a. commit 1c0dc06e47e11bf758f3e84ea90c2178a31dbf0f Author: Scott Wood <scottwood@freescale.com> Date: Fri Apr 18 15:27:47 2014 -0500 Revert "net: rename sysfs symlinks on device name change" This reverts commit 45ce45c69750b93b8262aa66792185bd49150293. Conflicts: drivers/iommu/fsl_pamu.c drivers/net/bonding/bond_3ad.c drivers/net/bonding/bond_sysfs.c drivers/net/bonding/bonding.h drivers/net/ethernet/freescale/gianfar.c Signed-off-by: Scott Wood <scottwood@freescale.com> Conflicts: drivers/iommu/fsl_pamu.c drivers/net/bonding/bond_3ad.c drivers/net/bonding/bond_sysfs.c drivers/net/bonding/bonding.h drivers/net/ethernet/freescale/gianfar.c
Diffstat (limited to 'drivers/net/ethernet/freescale/gianfar.c')
-rw-r--r--drivers/net/ethernet/freescale/gianfar.c346
1 files changed, 280 insertions, 66 deletions
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);