diff options
Diffstat (limited to 'drivers/net/ethernet/arc')
| -rw-r--r-- | drivers/net/ethernet/arc/emac.h | 2 | ||||
| -rw-r--r-- | drivers/net/ethernet/arc/emac_main.c | 164 | ||||
| -rw-r--r-- | drivers/net/ethernet/arc/emac_rockchip.c | 13 | 
3 files changed, 154 insertions, 25 deletions
diff --git a/drivers/net/ethernet/arc/emac.h b/drivers/net/ethernet/arc/emac.h index 3c63b16d485f..d9efbc8d783b 100644 --- a/drivers/net/ethernet/arc/emac.h +++ b/drivers/net/ethernet/arc/emac.h @@ -159,6 +159,8 @@ struct arc_emac_priv {  	unsigned int link;  	unsigned int duplex;  	unsigned int speed; + +	unsigned int rx_missed_errors;  };  /** diff --git a/drivers/net/ethernet/arc/emac_main.c b/drivers/net/ethernet/arc/emac_main.c index 3241af1ce718..bd277b0dc615 100644 --- a/drivers/net/ethernet/arc/emac_main.c +++ b/drivers/net/ethernet/arc/emac_main.c @@ -26,6 +26,8 @@  #include "emac.h" +static void arc_emac_restart(struct net_device *ndev); +  /**   * arc_emac_tx_avail - Return the number of available slots in the tx ring.   * @priv: Pointer to ARC EMAC private data structure. @@ -210,39 +212,48 @@ static int arc_emac_rx(struct net_device *ndev, int budget)  			continue;  		} -		pktlen = info & LEN_MASK; -		stats->rx_packets++; -		stats->rx_bytes += pktlen; -		skb = rx_buff->skb; -		skb_put(skb, pktlen); -		skb->dev = ndev; -		skb->protocol = eth_type_trans(skb, ndev); - -		dma_unmap_single(&ndev->dev, dma_unmap_addr(rx_buff, addr), -				 dma_unmap_len(rx_buff, len), DMA_FROM_DEVICE); - -		/* Prepare the BD for next cycle */ -		rx_buff->skb = netdev_alloc_skb_ip_align(ndev, -							 EMAC_BUFFER_SIZE); -		if (unlikely(!rx_buff->skb)) { +		/* Prepare the BD for next cycle. netif_receive_skb() +		 * only if new skb was allocated and mapped to avoid holes +		 * in the RX fifo. +		 */ +		skb = netdev_alloc_skb_ip_align(ndev, EMAC_BUFFER_SIZE); +		if (unlikely(!skb)) { +			if (net_ratelimit()) +				netdev_err(ndev, "cannot allocate skb\n"); +			/* Return ownership to EMAC */ +			rxbd->info = cpu_to_le32(FOR_EMAC | EMAC_BUFFER_SIZE);  			stats->rx_errors++; -			/* Because receive_skb is below, increment rx_dropped */  			stats->rx_dropped++;  			continue;  		} -		/* receive_skb only if new skb was allocated to avoid holes */ -		netif_receive_skb(skb); - -		addr = dma_map_single(&ndev->dev, (void *)rx_buff->skb->data, +		addr = dma_map_single(&ndev->dev, (void *)skb->data,  				      EMAC_BUFFER_SIZE, DMA_FROM_DEVICE);  		if (dma_mapping_error(&ndev->dev, addr)) {  			if (net_ratelimit()) -				netdev_err(ndev, "cannot dma map\n"); -			dev_kfree_skb(rx_buff->skb); +				netdev_err(ndev, "cannot map dma buffer\n"); +			dev_kfree_skb(skb); +			/* Return ownership to EMAC */ +			rxbd->info = cpu_to_le32(FOR_EMAC | EMAC_BUFFER_SIZE);  			stats->rx_errors++; +			stats->rx_dropped++;  			continue;  		} + +		/* unmap previosly mapped skb */ +		dma_unmap_single(&ndev->dev, dma_unmap_addr(rx_buff, addr), +				 dma_unmap_len(rx_buff, len), DMA_FROM_DEVICE); + +		pktlen = info & LEN_MASK; +		stats->rx_packets++; +		stats->rx_bytes += pktlen; +		skb_put(rx_buff->skb, pktlen); +		rx_buff->skb->dev = ndev; +		rx_buff->skb->protocol = eth_type_trans(rx_buff->skb, ndev); + +		netif_receive_skb(rx_buff->skb); + +		rx_buff->skb = skb;  		dma_unmap_addr_set(rx_buff, addr, addr);  		dma_unmap_len_set(rx_buff, len, EMAC_BUFFER_SIZE); @@ -259,6 +270,53 @@ static int arc_emac_rx(struct net_device *ndev, int budget)  }  /** + * arc_emac_rx_miss_handle - handle R_MISS register + * @ndev:	Pointer to the net_device structure. + */ +static void arc_emac_rx_miss_handle(struct net_device *ndev) +{ +	struct arc_emac_priv *priv = netdev_priv(ndev); +	struct net_device_stats *stats = &ndev->stats; +	unsigned int miss; + +	miss = arc_reg_get(priv, R_MISS); +	if (miss) { +		stats->rx_errors += miss; +		stats->rx_missed_errors += miss; +		priv->rx_missed_errors += miss; +	} +} + +/** + * arc_emac_rx_stall_check - check RX stall + * @ndev:	Pointer to the net_device structure. + * @budget:	How many BDs requested to process on 1 call. + * @work_done:	How many BDs processed + * + * Under certain conditions EMAC stop reception of incoming packets and + * continuously increment R_MISS register instead of saving data into + * provided buffer. This function detect that condition and restart + * EMAC. + */ +static void arc_emac_rx_stall_check(struct net_device *ndev, +				    int budget, unsigned int work_done) +{ +	struct arc_emac_priv *priv = netdev_priv(ndev); +	struct arc_emac_bd *rxbd; + +	if (work_done) +		priv->rx_missed_errors = 0; + +	if (priv->rx_missed_errors && budget) { +		rxbd = &priv->rxbd[priv->last_rx_bd]; +		if (le32_to_cpu(rxbd->info) & FOR_EMAC) { +			arc_emac_restart(ndev); +			priv->rx_missed_errors = 0; +		} +	} +} + +/**   * arc_emac_poll - NAPI poll handler.   * @napi:	Pointer to napi_struct structure.   * @budget:	How many BDs to process on 1 call. @@ -272,6 +330,7 @@ static int arc_emac_poll(struct napi_struct *napi, int budget)  	unsigned int work_done;  	arc_emac_tx_clean(ndev); +	arc_emac_rx_miss_handle(ndev);  	work_done = arc_emac_rx(ndev, budget);  	if (work_done < budget) { @@ -279,6 +338,8 @@ static int arc_emac_poll(struct napi_struct *napi, int budget)  		arc_reg_or(priv, R_ENABLE, RXINT_MASK | TXINT_MASK);  	} +	arc_emac_rx_stall_check(ndev, budget, work_done); +  	return work_done;  } @@ -320,6 +381,8 @@ static irqreturn_t arc_emac_intr(int irq, void *dev_instance)  		if (status & MSER_MASK) {  			stats->rx_missed_errors += 0x100;  			stats->rx_errors += 0x100; +			priv->rx_missed_errors += 0x100; +			napi_schedule(&priv->napi);  		}  		if (status & RXCR_MASK) { @@ -732,6 +795,63 @@ static int arc_emac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)  } +/** + * arc_emac_restart - Restart EMAC + * @ndev:	Pointer to net_device structure. + * + * This function do hardware reset of EMAC in order to restore + * network packets reception. + */ +static void arc_emac_restart(struct net_device *ndev) +{ +	struct arc_emac_priv *priv = netdev_priv(ndev); +	struct net_device_stats *stats = &ndev->stats; +	int i; + +	if (net_ratelimit()) +		netdev_warn(ndev, "restarting stalled EMAC\n"); + +	netif_stop_queue(ndev); + +	/* Disable interrupts */ +	arc_reg_clr(priv, R_ENABLE, RXINT_MASK | TXINT_MASK | ERR_MASK); + +	/* Disable EMAC */ +	arc_reg_clr(priv, R_CTRL, EN_MASK); + +	/* Return the sk_buff to system */ +	arc_free_tx_queue(ndev); + +	/* Clean Tx BD's */ +	priv->txbd_curr = 0; +	priv->txbd_dirty = 0; +	memset(priv->txbd, 0, TX_RING_SZ); + +	for (i = 0; i < RX_BD_NUM; i++) { +		struct arc_emac_bd *rxbd = &priv->rxbd[i]; +		unsigned int info = le32_to_cpu(rxbd->info); + +		if (!(info & FOR_EMAC)) { +			stats->rx_errors++; +			stats->rx_dropped++; +		} +		/* Return ownership to EMAC */ +		rxbd->info = cpu_to_le32(FOR_EMAC | EMAC_BUFFER_SIZE); +	} +	priv->last_rx_bd = 0; + +	/* Make sure info is visible to EMAC before enable */ +	wmb(); + +	/* Enable interrupts */ +	arc_reg_set(priv, R_ENABLE, RXINT_MASK | TXINT_MASK | ERR_MASK); + +	/* Enable EMAC */ +	arc_reg_or(priv, R_CTRL, EN_MASK); + +	netif_start_queue(ndev); +} +  static const struct net_device_ops arc_emac_netdev_ops = {  	.ndo_open		= arc_emac_open,  	.ndo_stop		= arc_emac_stop, diff --git a/drivers/net/ethernet/arc/emac_rockchip.c b/drivers/net/ethernet/arc/emac_rockchip.c index e278e3d96ee0..16f9bee992fe 100644 --- a/drivers/net/ethernet/arc/emac_rockchip.c +++ b/drivers/net/ethernet/arc/emac_rockchip.c @@ -199,9 +199,11 @@ static int emac_rockchip_probe(struct platform_device *pdev)  	/* RMII interface needs always a rate of 50MHz */  	err = clk_set_rate(priv->refclk, 50000000); -	if (err) +	if (err) {  		dev_err(dev,  			"failed to change reference clock rate (%d)\n", err); +		goto out_regulator_disable; +	}  	if (priv->soc_data->need_div_macclk) {  		priv->macclk = devm_clk_get(dev, "macclk"); @@ -220,19 +222,24 @@ static int emac_rockchip_probe(struct platform_device *pdev)  		/* RMII TX/RX needs always a rate of 25MHz */  		err = clk_set_rate(priv->macclk, 25000000); -		if (err) +		if (err) {  			dev_err(dev,  				"failed to change mac clock rate (%d)\n", err); +			goto out_clk_disable_macclk; +		}  	}  	err = arc_emac_probe(ndev, interface);  	if (err) {  		dev_err(dev, "failed to probe arc emac (%d)\n", err); -		goto out_regulator_disable; +		goto out_clk_disable_macclk;  	}  	return 0; +out_clk_disable_macclk: +	if (priv->soc_data->need_div_macclk) +		clk_disable_unprepare(priv->macclk);  out_regulator_disable:  	if (priv->regulator)  		regulator_disable(priv->regulator);  | 

