summaryrefslogtreecommitdiffstats
path: root/drivers/net/gianfar.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/gianfar.c')
-rw-r--r--drivers/net/gianfar.c134
1 files changed, 115 insertions, 19 deletions
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index c6791cd4ee05..fccb7a371cc8 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -85,6 +85,7 @@
#include <linux/net_tstamp.h>
#include <asm/io.h>
+#include <asm/reg.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <linux/module.h>
@@ -381,10 +382,14 @@ static void gfar_init_mac(struct net_device *ndev)
/* Insert receive time stamps into padding alignment bytes */
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER) {
rctrl &= ~RCTRL_PAL_MASK;
- rctrl |= RCTRL_PRSDEP_INIT | RCTRL_TS_ENABLE | RCTRL_PADDING(8);
+ rctrl |= RCTRL_PADDING(8);
priv->padding = 8;
}
+ /* Enable HW time stamping if requested from user space */
+ if (priv->hwts_rx_en)
+ rctrl |= RCTRL_PRSDEP_INIT | RCTRL_TS_ENABLE;
+
/* keep vlan related bits if it's enabled */
if (priv->vlgrp) {
rctrl |= RCTRL_VLEX | RCTRL_PRSDEP_INIT;
@@ -608,7 +613,7 @@ static int gfar_of_init(struct of_device *ofdev, struct net_device **pdev)
int err = 0, i;
struct net_device *dev = NULL;
struct gfar_private *priv = NULL;
- struct device_node *np = ofdev->node;
+ struct device_node *np = ofdev->dev.of_node;
struct device_node *child = NULL;
const u32 *stash;
const u32 *stash_len;
@@ -646,7 +651,7 @@ static int gfar_of_init(struct of_device *ofdev, struct net_device **pdev)
return -ENOMEM;
priv = netdev_priv(dev);
- priv->node = ofdev->node;
+ priv->node = ofdev->dev.of_node;
priv->ndev = dev;
dev->num_tx_queues = num_tx_qs;
@@ -681,8 +686,8 @@ static int gfar_of_init(struct of_device *ofdev, struct net_device **pdev)
priv->rx_queue[i] = NULL;
for (i = 0; i < priv->num_tx_queues; i++) {
- priv->tx_queue[i] = (struct gfar_priv_tx_q *)kzalloc(
- sizeof (struct gfar_priv_tx_q), GFP_KERNEL);
+ priv->tx_queue[i] = kzalloc(sizeof(struct gfar_priv_tx_q),
+ GFP_KERNEL);
if (!priv->tx_queue[i]) {
err = -ENOMEM;
goto tx_alloc_failed;
@@ -694,8 +699,8 @@ static int gfar_of_init(struct of_device *ofdev, struct net_device **pdev)
}
for (i = 0; i < priv->num_rx_queues; i++) {
- priv->rx_queue[i] = (struct gfar_priv_rx_q *)kzalloc(
- sizeof (struct gfar_priv_rx_q), GFP_KERNEL);
+ priv->rx_queue[i] = kzalloc(sizeof(struct gfar_priv_rx_q),
+ GFP_KERNEL);
if (!priv->rx_queue[i]) {
err = -ENOMEM;
goto rx_alloc_failed;
@@ -806,12 +811,20 @@ static int gfar_hwtstamp_ioctl(struct net_device *netdev,
switch (config.rx_filter) {
case HWTSTAMP_FILTER_NONE:
- priv->hwts_rx_en = 0;
+ if (priv->hwts_rx_en) {
+ stop_gfar(netdev);
+ priv->hwts_rx_en = 0;
+ startup_gfar(netdev);
+ }
break;
default:
if (!(priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER))
return -ERANGE;
- priv->hwts_rx_en = 1;
+ if (!priv->hwts_rx_en) {
+ stop_gfar(netdev);
+ priv->hwts_rx_en = 1;
+ startup_gfar(netdev);
+ }
config.rx_filter = HWTSTAMP_FILTER_ALL;
break;
}
@@ -916,6 +929,34 @@ static void gfar_init_filer_table(struct gfar_private *priv)
}
}
+static void gfar_detect_errata(struct gfar_private *priv)
+{
+ struct device *dev = &priv->ofdev->dev;
+ unsigned int pvr = mfspr(SPRN_PVR);
+ unsigned int svr = mfspr(SPRN_SVR);
+ unsigned int mod = (svr >> 16) & 0xfff6; /* w/o E suffix */
+ unsigned int rev = svr & 0xffff;
+
+ /* MPC8313 Rev 2.0 and higher; All MPC837x */
+ if ((pvr == 0x80850010 && mod == 0x80b0 && rev >= 0x0020) ||
+ (pvr == 0x80861010 && (mod & 0xfff9) == 0x80c0))
+ priv->errata |= GFAR_ERRATA_74;
+
+ /* MPC8313 and MPC837x all rev */
+ if ((pvr == 0x80850010 && mod == 0x80b0) ||
+ (pvr == 0x80861010 && (mod & 0xfff9) == 0x80c0))
+ priv->errata |= GFAR_ERRATA_76;
+
+ /* MPC8313 and MPC837x all rev */
+ if ((pvr == 0x80850010 && mod == 0x80b0) ||
+ (pvr == 0x80861010 && (mod & 0xfff9) == 0x80c0))
+ priv->errata |= GFAR_ERRATA_A002;
+
+ if (priv->errata)
+ dev_info(dev, "enabled errata workarounds, flags: 0x%x\n",
+ priv->errata);
+}
+
/* Set up the ethernet device structure, private data,
* and anything else we need before we start */
static int gfar_probe(struct of_device *ofdev,
@@ -939,7 +980,7 @@ static int gfar_probe(struct of_device *ofdev,
priv = netdev_priv(dev);
priv->ndev = dev;
priv->ofdev = ofdev;
- priv->node = ofdev->node;
+ priv->node = ofdev->dev.of_node;
SET_NETDEV_DEV(dev, &ofdev->dev);
spin_lock_init(&priv->bflock);
@@ -948,6 +989,8 @@ static int gfar_probe(struct of_device *ofdev,
dev_set_drvdata(&ofdev->dev, priv);
regs = priv->gfargrp[0].regs;
+ gfar_detect_errata(priv);
+
/* Stop the DMA engine now, in case it was running before */
/* (The firmware could have used it, and left it running). */
gfar_halt(dev);
@@ -962,7 +1005,10 @@ static int gfar_probe(struct of_device *ofdev,
gfar_write(&regs->maccfg1, tempval);
/* Initialize MACCFG2. */
- gfar_write(&regs->maccfg2, MACCFG2_INIT_SETTINGS);
+ tempval = MACCFG2_INIT_SETTINGS;
+ if (gfar_has_errata(priv, GFAR_ERRATA_74))
+ tempval |= MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK;
+ gfar_write(&regs->maccfg2, tempval);
/* Initialize ECNTRL */
gfar_write(&regs->ecntrl, ECNTRL_INIT_SETTINGS);
@@ -1529,6 +1575,29 @@ static void init_registers(struct net_device *dev)
gfar_write(&regs->minflr, MINFLR_INIT_SETTINGS);
}
+static int __gfar_is_rx_idle(struct gfar_private *priv)
+{
+ u32 res;
+
+ /*
+ * Normaly TSEC should not hang on GRS commands, so we should
+ * actually wait for IEVENT_GRSC flag.
+ */
+ if (likely(!gfar_has_errata(priv, GFAR_ERRATA_A002)))
+ return 0;
+
+ /*
+ * Read the eTSEC register at offset 0xD1C. If bits 7-14 are
+ * the same as bits 23-30, the eTSEC Rx is assumed to be idle
+ * and the Rx can be safely reset.
+ */
+ res = gfar_read((void __iomem *)priv->gfargrp[0].regs + 0xd1c);
+ res &= 0x7f807f80;
+ if ((res & 0xffff) == (res >> 16))
+ return 1;
+
+ return 0;
+}
/* Halt the receive and transmit queues */
static void gfar_halt_nodisable(struct net_device *dev)
@@ -1552,12 +1621,18 @@ static void gfar_halt_nodisable(struct net_device *dev)
tempval = gfar_read(&regs->dmactrl);
if ((tempval & (DMACTRL_GRS | DMACTRL_GTS))
!= (DMACTRL_GRS | DMACTRL_GTS)) {
+ int ret;
+
tempval |= (DMACTRL_GRS | DMACTRL_GTS);
gfar_write(&regs->dmactrl, tempval);
- spin_event_timeout(((gfar_read(&regs->ievent) &
- (IEVENT_GRSC | IEVENT_GTSC)) ==
- (IEVENT_GRSC | IEVENT_GTSC)), -1, 0);
+ do {
+ ret = spin_event_timeout(((gfar_read(&regs->ievent) &
+ (IEVENT_GRSC | IEVENT_GTSC)) ==
+ (IEVENT_GRSC | IEVENT_GTSC)), 1000000, 0);
+ if (!ret && !(gfar_read(&regs->ievent) & IEVENT_GRSC))
+ ret = __gfar_is_rx_idle(priv);
+ } while (!ret);
}
}
@@ -1975,6 +2050,20 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
unsigned int nr_frags, nr_txbds, length;
union skb_shared_tx *shtx;
+ /*
+ * TOE=1 frames larger than 2500 bytes may see excess delays
+ * before start of transmission.
+ */
+ if (unlikely(gfar_has_errata(priv, GFAR_ERRATA_76) &&
+ skb->ip_summed == CHECKSUM_PARTIAL &&
+ skb->len > 2500)) {
+ int ret;
+
+ ret = skb_checksum_help(skb);
+ if (ret)
+ return ret;
+ }
+
rq = skb->queue_mapping;
tx_queue = priv->tx_queue[rq];
txq = netdev_get_tx_queue(dev, rq);
@@ -2288,7 +2377,8 @@ static int gfar_change_mtu(struct net_device *dev, int new_mtu)
* to allow huge frames, and to check the length */
tempval = gfar_read(&regs->maccfg2);
- if (priv->rx_buffer_size > DEFAULT_RX_BUFFER_SIZE)
+ if (priv->rx_buffer_size > DEFAULT_RX_BUFFER_SIZE ||
+ gfar_has_errata(priv, GFAR_ERRATA_74))
tempval |= (MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK);
else
tempval &= ~(MACCFG2_HUGEFRAME | MACCFG2_LENGTHCHECK);
@@ -2643,6 +2733,10 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit)
dma_unmap_single(&priv->ofdev->dev, bdp->bufPtr,
priv->rx_buffer_size, DMA_FROM_DEVICE);
+ if (unlikely(!(bdp->status & RXBD_ERR) &&
+ bdp->length > priv->rx_buffer_size))
+ bdp->status = RXBD_LARGE;
+
/* We drop the frame if we failed to allocate a new buffer */
if (unlikely(!newskb || !(bdp->status & RXBD_LAST) ||
bdp->status & RXBD_ERR)) {
@@ -3167,12 +3261,14 @@ MODULE_DEVICE_TABLE(of, gfar_match);
/* Structure for a device driver */
static struct of_platform_driver gfar_driver = {
- .name = "fsl-gianfar",
- .match_table = gfar_match,
-
+ .driver = {
+ .name = "fsl-gianfar",
+ .owner = THIS_MODULE,
+ .pm = GFAR_PM_OPS,
+ .of_match_table = gfar_match,
+ },
.probe = gfar_probe,
.remove = gfar_remove,
- .driver.pm = GFAR_PM_OPS,
};
static int __init gfar_init(void)