ar71xx: fix ethernet MAC reset on DMA hang

Fully reset the chip like on a full up/down, but without the PHY
statemachine restart.

Signed-off-by: Felix Fietkau <nbd@openwrt.org>

SVN-Revision: 48228
lede-17.01
Felix Fietkau 2016-01-13 16:22:16 +00:00
parent cf2cf43717
commit 5c5c60fec4
1 changed files with 55 additions and 38 deletions

View File

@ -538,7 +538,8 @@ static void ag71xx_hw_start(struct ag71xx *ag)
netif_wake_queue(ag->dev);
}
void ag71xx_link_adjust(struct ag71xx *ag)
static void
__ag71xx_link_adjust(struct ag71xx *ag, bool update)
{
struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
u32 cfg2;
@ -546,7 +547,7 @@ void ag71xx_link_adjust(struct ag71xx *ag)
u32 fifo5;
u32 fifo3;
if (!ag->link) {
if (!ag->link && update) {
ag71xx_hw_stop(ag);
netif_carrier_off(ag->dev);
if (netif_msg_link(ag))
@ -598,7 +599,7 @@ void ag71xx_link_adjust(struct ag71xx *ag)
ag71xx_wr(ag, AG71XX_REG_FIFO_CFG3, fifo3);
if (pdata->set_speed)
if (update && pdata->set_speed)
pdata->set_speed(ag->speed);
ag71xx_wr(ag, AG71XX_REG_MAC_CFG2, cfg2);
@ -607,7 +608,7 @@ void ag71xx_link_adjust(struct ag71xx *ag)
ag71xx_hw_start(ag);
netif_carrier_on(ag->dev);
if (netif_msg_link(ag))
if (update && netif_msg_link(ag))
pr_info("%s: link up (%sMbps/%s duplex)\n",
ag->dev->name,
ag71xx_speed_str(ag),
@ -631,34 +632,66 @@ void ag71xx_link_adjust(struct ag71xx *ag)
ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL));
}
void ag71xx_link_adjust(struct ag71xx *ag)
{
__ag71xx_link_adjust(ag, true);
}
static int ag71xx_hw_enable(struct ag71xx *ag)
{
int ret;
ret = ag71xx_rings_init(ag);
if (ret)
return ret;
napi_enable(&ag->napi);
ag71xx_wr(ag, AG71XX_REG_TX_DESC, ag->tx_ring.descs_dma);
ag71xx_wr(ag, AG71XX_REG_RX_DESC, ag->rx_ring.descs_dma);
netif_start_queue(ag->dev);
return 0;
}
static void ag71xx_hw_disable(struct ag71xx *ag)
{
unsigned long flags;
spin_lock_irqsave(&ag->lock, flags);
netif_stop_queue(ag->dev);
ag71xx_hw_stop(ag);
ag71xx_dma_reset(ag);
napi_disable(&ag->napi);
del_timer_sync(&ag->oom_timer);
spin_unlock_irqrestore(&ag->lock, flags);
ag71xx_rings_cleanup(ag);
}
static int ag71xx_open(struct net_device *dev)
{
struct ag71xx *ag = netdev_priv(dev);
unsigned int max_frame_len;
int ret;
netif_carrier_off(dev);
max_frame_len = ag71xx_max_frame_len(dev->mtu);
ag->rx_buf_size = max_frame_len + NET_SKB_PAD + NET_IP_ALIGN;
/* setup max frame length */
ag71xx_wr(ag, AG71XX_REG_MAC_MFL, max_frame_len);
ag71xx_hw_set_macaddr(ag, dev->dev_addr);
ret = ag71xx_rings_init(ag);
ret = ag71xx_hw_enable(ag);
if (ret)
goto err;
napi_enable(&ag->napi);
netif_carrier_off(dev);
ag71xx_phy_start(ag);
ag71xx_wr(ag, AG71XX_REG_TX_DESC, ag->tx_ring.descs_dma);
ag71xx_wr(ag, AG71XX_REG_RX_DESC, ag->rx_ring.descs_dma);
ag71xx_hw_set_macaddr(ag, dev->dev_addr);
netif_start_queue(dev);
return 0;
err:
@ -669,24 +702,10 @@ err:
static int ag71xx_stop(struct net_device *dev)
{
struct ag71xx *ag = netdev_priv(dev);
unsigned long flags;
netif_carrier_off(dev);
ag71xx_phy_stop(ag);
spin_lock_irqsave(&ag->lock, flags);
netif_stop_queue(dev);
ag71xx_hw_stop(ag);
ag71xx_dma_reset(ag);
napi_disable(&ag->napi);
del_timer_sync(&ag->oom_timer);
spin_unlock_irqrestore(&ag->lock, flags);
ag71xx_rings_cleanup(ag);
ag71xx_hw_disable(ag);
return 0;
}
@ -870,14 +889,12 @@ static void ag71xx_restart_work_func(struct work_struct *work)
{
struct ag71xx *ag = container_of(work, struct ag71xx, restart_work);
if (ag71xx_get_pdata(ag)->is_ar724x) {
ag->link = 0;
ag71xx_link_adjust(ag);
return;
}
ag71xx_stop(ag->dev);
ag71xx_open(ag->dev);
rtnl_lock();
ag71xx_hw_disable(ag);
ag71xx_hw_enable(ag);
if (ag->link)
__ag71xx_link_adjust(ag, false);
rtnl_unlock();
}
static bool ag71xx_check_dma_stuck(struct ag71xx *ag, unsigned long timestamp)