Commit c10650b6 authored by Claudiu Manoil's avatar Claudiu Manoil Committed by David S. Miller

gianfar: Add missing graceful reset steps and fixes

gfar_halt() and gfar_start() are responsible for stopping
and starting the DMA and the Rx/Tx hw rings. They implement
the support for the "graceful Rx/Tx stop/start" hw procedure,
and also disable/enable eTSEC's hw interrupts in the process.

The GRS/GTS procedure requires however to have the RQUEUE/TQUEUE
registers cleared first and to wait for a period of time for the
current frame to pass through the interface (around ~10ms for a
jumbo frame). Only then may the GTS and GRS bits from DMACTRL be
set to shut down the DMA, and finally the Tx_EN and Rx_EN bits in
MACCFG1 may be cleared to disable the Tx/Rx blocks.

The same register programming order applies to start the Rx/Tx:
enabling the RQUEUE/TQUEUE *before* clearing the GRS/GTS bits.

This is a HW recommendation in order to avoid a possible
controller "lock up" during graceful reset.

Cleanup the gfar_halt()/start() prototypes, to take priv instead
of ndev as their purpose is to operate on HW. Enabling the
RQUEUE/TQUEUE in the hw_init() is not needed anymore since
that's the job of gfar_start().
Signed-off-by: default avatarClaudiu Manoil <claudiu.manoil@freescale.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent efeddce7
...@@ -138,9 +138,7 @@ int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, int rx_work_limit); ...@@ -138,9 +138,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_clean_tx_ring(struct gfar_priv_tx_q *tx_queue);
static void gfar_process_frame(struct net_device *dev, struct sk_buff *skb, static void gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
int amount_pull, struct napi_struct *napi); int amount_pull, struct napi_struct *napi);
void gfar_halt(struct net_device *dev); static void gfar_halt_nodisable(struct gfar_private *priv);
static void gfar_halt_nodisable(struct net_device *dev);
void gfar_start(struct net_device *dev);
static void gfar_clear_exact_match(struct net_device *dev); static void gfar_clear_exact_match(struct net_device *dev);
static void gfar_set_mac_for_addr(struct net_device *dev, int num, static void gfar_set_mac_for_addr(struct net_device *dev, int num,
const u8 *addr); const u8 *addr);
...@@ -1070,10 +1068,6 @@ static void gfar_hw_init(struct gfar_private *priv) ...@@ -1070,10 +1068,6 @@ static void gfar_hw_init(struct gfar_private *priv)
/* Program the interrupt steering regs, only for MG devices */ /* Program the interrupt steering regs, only for MG devices */
if (priv->num_grps > 1) if (priv->num_grps > 1)
gfar_write_isrg(priv); gfar_write_isrg(priv);
/* Enable all Rx/Tx queues after MAC reset */
gfar_write(&regs->rqueue, priv->rqueue);
gfar_write(&regs->tqueue, priv->tqueue);
} }
static void __init gfar_init_addr_hash_table(struct gfar_private *priv) static void __init gfar_init_addr_hash_table(struct gfar_private *priv)
...@@ -1146,7 +1140,7 @@ static int gfar_probe(struct platform_device *ofdev) ...@@ -1146,7 +1140,7 @@ static int gfar_probe(struct platform_device *ofdev)
/* Stop the DMA engine now, in case it was running before /* Stop the DMA engine now, in case it was running before
* (The firmware could have used it, and left it running). * (The firmware could have used it, and left it running).
*/ */
gfar_halt(dev); gfar_halt(priv);
gfar_hw_init(priv); gfar_hw_init(priv);
...@@ -1314,7 +1308,7 @@ static int gfar_suspend(struct device *dev) ...@@ -1314,7 +1308,7 @@ static int gfar_suspend(struct device *dev)
lock_tx_qs(priv); lock_tx_qs(priv);
lock_rx_qs(priv); lock_rx_qs(priv);
gfar_halt_nodisable(ndev); gfar_halt_nodisable(priv);
/* Disable Tx, and Rx if wake-on-LAN is disabled. */ /* Disable Tx, and Rx if wake-on-LAN is disabled. */
tempval = gfar_read(&regs->maccfg1); tempval = gfar_read(&regs->maccfg1);
...@@ -1378,7 +1372,7 @@ static int gfar_resume(struct device *dev) ...@@ -1378,7 +1372,7 @@ static int gfar_resume(struct device *dev)
tempval &= ~MACCFG2_MPEN; tempval &= ~MACCFG2_MPEN;
gfar_write(&regs->maccfg2, tempval); gfar_write(&regs->maccfg2, tempval);
gfar_start(ndev); gfar_start(priv);
unlock_rx_qs(priv); unlock_rx_qs(priv);
unlock_tx_qs(priv); unlock_tx_qs(priv);
...@@ -1410,7 +1404,7 @@ static int gfar_restore(struct device *dev) ...@@ -1410,7 +1404,7 @@ static int gfar_restore(struct device *dev)
init_registers(ndev); init_registers(ndev);
gfar_set_mac_address(ndev); gfar_set_mac_address(ndev);
gfar_init_mac(ndev); gfar_init_mac(ndev);
gfar_start(ndev); gfar_start(priv);
priv->oldlink = 0; priv->oldlink = 0;
priv->oldspeed = 0; priv->oldspeed = 0;
...@@ -1633,9 +1627,8 @@ static int __gfar_is_rx_idle(struct gfar_private *priv) ...@@ -1633,9 +1627,8 @@ static int __gfar_is_rx_idle(struct gfar_private *priv)
} }
/* Halt the receive and transmit queues */ /* Halt the receive and transmit queues */
static void gfar_halt_nodisable(struct net_device *dev) static void gfar_halt_nodisable(struct gfar_private *priv)
{ {
struct gfar_private *priv = netdev_priv(dev);
struct gfar __iomem *regs = priv->gfargrp[0].regs; struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 tempval; u32 tempval;
...@@ -1661,15 +1654,20 @@ static void gfar_halt_nodisable(struct net_device *dev) ...@@ -1661,15 +1654,20 @@ static void gfar_halt_nodisable(struct net_device *dev)
} }
/* Halt the receive and transmit queues */ /* Halt the receive and transmit queues */
void gfar_halt(struct net_device *dev) void gfar_halt(struct gfar_private *priv)
{ {
struct gfar_private *priv = netdev_priv(dev);
struct gfar __iomem *regs = priv->gfargrp[0].regs; struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 tempval; u32 tempval;
gfar_halt_nodisable(dev); /* Dissable the Rx/Tx hw queues */
gfar_write(&regs->rqueue, 0);
gfar_write(&regs->tqueue, 0);
/* Disable Rx and Tx */ mdelay(10);
gfar_halt_nodisable(priv);
/* Disable Rx/Tx DMA */
tempval = gfar_read(&regs->maccfg1); tempval = gfar_read(&regs->maccfg1);
tempval &= ~(MACCFG1_RX_EN | MACCFG1_TX_EN); tempval &= ~(MACCFG1_RX_EN | MACCFG1_TX_EN);
gfar_write(&regs->maccfg1, tempval); gfar_write(&regs->maccfg1, tempval);
...@@ -1696,7 +1694,7 @@ void stop_gfar(struct net_device *dev) ...@@ -1696,7 +1694,7 @@ void stop_gfar(struct net_device *dev)
lock_tx_qs(priv); lock_tx_qs(priv);
lock_rx_qs(priv); lock_rx_qs(priv);
gfar_halt(dev); gfar_halt(priv);
unlock_rx_qs(priv); unlock_rx_qs(priv);
unlock_tx_qs(priv); unlock_tx_qs(priv);
...@@ -1801,17 +1799,15 @@ static void free_skb_resources(struct gfar_private *priv) ...@@ -1801,17 +1799,15 @@ static void free_skb_resources(struct gfar_private *priv)
priv->tx_queue[0]->tx_bd_dma_base); priv->tx_queue[0]->tx_bd_dma_base);
} }
void gfar_start(struct net_device *dev) void gfar_start(struct gfar_private *priv)
{ {
struct gfar_private *priv = netdev_priv(dev);
struct gfar __iomem *regs = priv->gfargrp[0].regs; struct gfar __iomem *regs = priv->gfargrp[0].regs;
u32 tempval; u32 tempval;
int i = 0; int i = 0;
/* Enable Rx and Tx in MACCFG1 */ /* Enable Rx/Tx hw queues */
tempval = gfar_read(&regs->maccfg1); gfar_write(&regs->rqueue, priv->rqueue);
tempval |= (MACCFG1_RX_EN | MACCFG1_TX_EN); gfar_write(&regs->tqueue, priv->tqueue);
gfar_write(&regs->maccfg1, tempval);
/* Initialize DMACTRL to have WWR and WOP */ /* Initialize DMACTRL to have WWR and WOP */
tempval = gfar_read(&regs->dmactrl); tempval = gfar_read(&regs->dmactrl);
...@@ -1830,9 +1826,14 @@ void gfar_start(struct net_device *dev) ...@@ -1830,9 +1826,14 @@ void gfar_start(struct net_device *dev)
gfar_write(&regs->rstat, priv->gfargrp[i].rstat); gfar_write(&regs->rstat, priv->gfargrp[i].rstat);
} }
/* Enable Rx/Tx DMA */
tempval = gfar_read(&regs->maccfg1);
tempval |= (MACCFG1_RX_EN | MACCFG1_TX_EN);
gfar_write(&regs->maccfg1, tempval);
gfar_ints_enable(priv); gfar_ints_enable(priv);
dev->trans_start = jiffies; /* prevent tx timeout */ priv->ndev->trans_start = jiffies; /* prevent tx timeout */
} }
static void gfar_configure_coalescing(struct gfar_private *priv, static void gfar_configure_coalescing(struct gfar_private *priv,
...@@ -1956,7 +1957,7 @@ int startup_gfar(struct net_device *ndev) ...@@ -1956,7 +1957,7 @@ int startup_gfar(struct net_device *ndev)
} }
/* Start the controller */ /* Start the controller */
gfar_start(ndev); gfar_start(priv);
phy_start(priv->phydev); phy_start(priv->phydev);
......
...@@ -1205,7 +1205,8 @@ void unlock_tx_qs(struct gfar_private *priv); ...@@ -1205,7 +1205,8 @@ void unlock_tx_qs(struct gfar_private *priv);
irqreturn_t gfar_receive(int irq, void *dev_id); irqreturn_t gfar_receive(int irq, void *dev_id);
int startup_gfar(struct net_device *dev); int startup_gfar(struct net_device *dev);
void stop_gfar(struct net_device *dev); void stop_gfar(struct net_device *dev);
void gfar_halt(struct net_device *dev); void gfar_halt(struct gfar_private *priv);
void gfar_start(struct gfar_private *priv);
void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev, int enable, void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev, int enable,
u32 regnum, u32 read); u32 regnum, u32 read);
void gfar_configure_coalescing_all(struct gfar_private *priv); void gfar_configure_coalescing_all(struct gfar_private *priv);
......
...@@ -44,7 +44,6 @@ ...@@ -44,7 +44,6 @@
#include "gianfar.h" #include "gianfar.h"
extern void gfar_start(struct net_device *dev);
extern int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue, extern int gfar_clean_rx_ring(struct gfar_priv_rx_q *rx_queue,
int rx_work_limit); int rx_work_limit);
...@@ -504,7 +503,7 @@ static int gfar_sringparam(struct net_device *dev, ...@@ -504,7 +503,7 @@ static int gfar_sringparam(struct net_device *dev,
lock_tx_qs(priv); lock_tx_qs(priv);
lock_rx_qs(priv); lock_rx_qs(priv);
gfar_halt(dev); gfar_halt(priv);
unlock_rx_qs(priv); unlock_rx_qs(priv);
unlock_tx_qs(priv); unlock_tx_qs(priv);
...@@ -627,7 +626,7 @@ int gfar_set_features(struct net_device *dev, netdev_features_t features) ...@@ -627,7 +626,7 @@ int gfar_set_features(struct net_device *dev, netdev_features_t features)
lock_tx_qs(priv); lock_tx_qs(priv);
lock_rx_qs(priv); lock_rx_qs(priv);
gfar_halt(dev); gfar_halt(priv);
unlock_tx_qs(priv); unlock_tx_qs(priv);
unlock_rx_qs(priv); unlock_rx_qs(priv);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment