diff options
Diffstat (limited to 'drivers/net/davinci_emac.c')
| -rw-r--r-- | drivers/net/davinci_emac.c | 200 | 
1 files changed, 163 insertions, 37 deletions
diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c index 08e82b1a0b3..d0824e32206 100644 --- a/drivers/net/davinci_emac.c +++ b/drivers/net/davinci_emac.c @@ -298,6 +298,11 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1";  #define EMAC_CTRL_EWCTL		(0x4)  #define EMAC_CTRL_EWINTTCNT	(0x8) +/* EMAC DM644x control module masks */ +#define EMAC_DM644X_EWINTCNT_MASK	0x1FFFF +#define EMAC_DM644X_INTMIN_INTVL	0x1 +#define EMAC_DM644X_INTMAX_INTVL	(EMAC_DM644X_EWINTCNT_MASK) +  /* EMAC MDIO related */  /* Mask & Control defines */  #define MDIO_CONTROL_CLKDIV	(0xFF) @@ -318,8 +323,20 @@ static const char emac_version_string[] = "TI DaVinci EMAC Linux v6.1";  #define MDIO_CONTROL		(0x04)  /* EMAC DM646X control module registers */ -#define EMAC_DM646X_CMRXINTEN	(0x14) -#define EMAC_DM646X_CMTXINTEN	(0x18) +#define EMAC_DM646X_CMINTCTRL	0x0C +#define EMAC_DM646X_CMRXINTEN	0x14 +#define EMAC_DM646X_CMTXINTEN	0x18 +#define EMAC_DM646X_CMRXINTMAX	0x70 +#define EMAC_DM646X_CMTXINTMAX	0x74 + +/* EMAC DM646X control module masks */ +#define EMAC_DM646X_INTPACEEN		(0x3 << 16) +#define EMAC_DM646X_INTPRESCALE_MASK	(0x7FF << 0) +#define EMAC_DM646X_CMINTMAX_CNT	63 +#define EMAC_DM646X_CMINTMIN_CNT	2 +#define EMAC_DM646X_CMINTMAX_INTVL	(1000 / EMAC_DM646X_CMINTMIN_CNT) +#define EMAC_DM646X_CMINTMIN_INTVL	((1000 / EMAC_DM646X_CMINTMAX_CNT) + 1) +  /* EMAC EOI codes for C0 */  #define EMAC_DM646X_MAC_EOI_C0_RXEN	(0x01) @@ -468,9 +485,10 @@ struct emac_priv {  	u32 duplex; /* Link duplex: 0=Half, 1=Full */  	u32 rx_buf_size;  	u32 isr_count; +	u32 coal_intvl; +	u32 bus_freq_mhz;  	u8 rmii_en;  	u8 version; -	struct net_device_stats net_dev_stats;  	u32 mac_hash1;  	u32 mac_hash2;  	u32 multicast_hash_cnt[EMAC_NUM_MULTICAST_BITS]; @@ -546,9 +564,11 @@ static void emac_dump_regs(struct emac_priv *priv)  	/* Print important registers in EMAC */  	dev_info(emac_dev, "EMAC Basic registers\n"); -	dev_info(emac_dev, "EMAC: EWCTL: %08X, EWINTTCNT: %08X\n", -		emac_ctrl_read(EMAC_CTRL_EWCTL), -		emac_ctrl_read(EMAC_CTRL_EWINTTCNT)); +	if (priv->version == EMAC_VERSION_1) { +		dev_info(emac_dev, "EMAC: EWCTL: %08X, EWINTTCNT: %08X\n", +			emac_ctrl_read(EMAC_CTRL_EWCTL), +			emac_ctrl_read(EMAC_CTRL_EWINTTCNT)); +	}  	dev_info(emac_dev, "EMAC: TXID: %08X %s, RXID: %08X %s\n",  		emac_read(EMAC_TXIDVER),  		((emac_read(EMAC_TXCONTROL)) ? "enabled" : "disabled"), @@ -692,6 +712,103 @@ static int emac_set_settings(struct net_device *ndev, struct ethtool_cmd *ecmd)  }  /** + * emac_get_coalesce : Get interrupt coalesce settings for this device + * @ndev : The DaVinci EMAC network adapter + * @coal : ethtool coalesce settings structure + * + * Fetch the current interrupt coalesce settings + * + */ +static int emac_get_coalesce(struct net_device *ndev, +				struct ethtool_coalesce *coal) +{ +	struct emac_priv *priv = netdev_priv(ndev); + +	coal->rx_coalesce_usecs = priv->coal_intvl; +	return 0; + +} + +/** + * emac_set_coalesce : Set interrupt coalesce settings for this device + * @ndev : The DaVinci EMAC network adapter + * @coal : ethtool coalesce settings structure + * + * Set interrupt coalesce parameters + * + */ +static int emac_set_coalesce(struct net_device *ndev, +				struct ethtool_coalesce *coal) +{ +	struct emac_priv *priv = netdev_priv(ndev); +	u32 int_ctrl, num_interrupts = 0; +	u32 prescale = 0, addnl_dvdr = 1, coal_intvl = 0; + +	if (!coal->rx_coalesce_usecs) +		return -EINVAL; + +	coal_intvl = coal->rx_coalesce_usecs; + +	switch (priv->version) { +	case EMAC_VERSION_2: +		int_ctrl =  emac_ctrl_read(EMAC_DM646X_CMINTCTRL); +		prescale = priv->bus_freq_mhz * 4; + +		if (coal_intvl < EMAC_DM646X_CMINTMIN_INTVL) +			coal_intvl = EMAC_DM646X_CMINTMIN_INTVL; + +		if (coal_intvl > EMAC_DM646X_CMINTMAX_INTVL) { +			/* +			 * Interrupt pacer works with 4us Pulse, we can +			 * throttle further by dilating the 4us pulse. +			 */ +			addnl_dvdr = EMAC_DM646X_INTPRESCALE_MASK / prescale; + +			if (addnl_dvdr > 1) { +				prescale *= addnl_dvdr; +				if (coal_intvl > (EMAC_DM646X_CMINTMAX_INTVL +							* addnl_dvdr)) +					coal_intvl = (EMAC_DM646X_CMINTMAX_INTVL +							* addnl_dvdr); +			} else { +				addnl_dvdr = 1; +				coal_intvl = EMAC_DM646X_CMINTMAX_INTVL; +			} +		} + +		num_interrupts = (1000 * addnl_dvdr) / coal_intvl; + +		int_ctrl |= EMAC_DM646X_INTPACEEN; +		int_ctrl &= (~EMAC_DM646X_INTPRESCALE_MASK); +		int_ctrl |= (prescale & EMAC_DM646X_INTPRESCALE_MASK); +		emac_ctrl_write(EMAC_DM646X_CMINTCTRL, int_ctrl); + +		emac_ctrl_write(EMAC_DM646X_CMRXINTMAX, num_interrupts); +		emac_ctrl_write(EMAC_DM646X_CMTXINTMAX, num_interrupts); + +		break; +	default: +		int_ctrl = emac_ctrl_read(EMAC_CTRL_EWINTTCNT); +		int_ctrl &= (~EMAC_DM644X_EWINTCNT_MASK); +		prescale = coal_intvl * priv->bus_freq_mhz; +		if (prescale > EMAC_DM644X_EWINTCNT_MASK) { +			prescale = EMAC_DM644X_EWINTCNT_MASK; +			coal_intvl = prescale / priv->bus_freq_mhz; +		} +		emac_ctrl_write(EMAC_CTRL_EWINTTCNT, (int_ctrl | prescale)); + +		break; +	} + +	printk(KERN_INFO"Set coalesce to %d usecs.\n", coal_intvl); +	priv->coal_intvl = coal_intvl; + +	return 0; + +} + + +/**   * ethtool_ops: DaVinci EMAC Ethtool structure   *   * Ethtool support for EMAC adapter @@ -702,6 +819,8 @@ static const struct ethtool_ops ethtool_ops = {  	.get_settings = emac_get_settings,  	.set_settings = emac_set_settings,  	.get_link = ethtool_op_get_link, +	.get_coalesce = emac_get_coalesce, +	.set_coalesce =  emac_set_coalesce,  };  /** @@ -1180,16 +1299,17 @@ static int emac_net_tx_complete(struct emac_priv *priv,  				void **net_data_tokens,  				int num_tokens, u32 ch)  { +	struct net_device *ndev = priv->ndev;  	u32 cnt; -	if (unlikely(num_tokens && netif_queue_stopped(priv->ndev))) -		netif_start_queue(priv->ndev); +	if (unlikely(num_tokens && netif_queue_stopped(ndev))) +		netif_start_queue(ndev);  	for (cnt = 0; cnt < num_tokens; cnt++) {  		struct sk_buff *skb = (struct sk_buff *)net_data_tokens[cnt];  		if (skb == NULL)  			continue; -		priv->net_dev_stats.tx_packets++; -		priv->net_dev_stats.tx_bytes += skb->len; +		ndev->stats.tx_packets++; +		ndev->stats.tx_bytes += skb->len;  		dev_kfree_skb_any(skb);  	}  	return 0; @@ -1476,7 +1596,7 @@ static int emac_dev_xmit(struct sk_buff *skb, struct net_device *ndev)  					" err. Out of TX BD's");  			netif_stop_queue(priv->ndev);  		} -		priv->net_dev_stats.tx_dropped++; +		ndev->stats.tx_dropped++;  		return NETDEV_TX_BUSY;  	} @@ -1501,7 +1621,7 @@ static void emac_dev_tx_timeout(struct net_device *ndev)  	if (netif_msg_tx_err(priv))  		dev_err(emac_dev, "DaVinci EMAC: xmit timeout, restarting TX"); -	priv->net_dev_stats.tx_errors++; +	ndev->stats.tx_errors++;  	emac_int_disable(priv);  	emac_stop_txch(priv, EMAC_DEF_TX_CH);  	emac_cleanup_txch(priv, EMAC_DEF_TX_CH); @@ -1926,14 +2046,14 @@ static void emac_addbd_to_rx_queue(struct emac_priv *priv, u32 ch,  static int emac_net_rx_cb(struct emac_priv *priv,  			  struct emac_netpktobj *net_pkt_list)  { -	struct sk_buff *p_skb; -	p_skb = (struct sk_buff *)net_pkt_list->pkt_token; +	struct net_device *ndev = priv->ndev; +	struct sk_buff *p_skb = net_pkt_list->pkt_token;  	/* set length of packet */  	skb_put(p_skb, net_pkt_list->pkt_length);  	p_skb->protocol = eth_type_trans(p_skb, priv->ndev);  	netif_receive_skb(p_skb); -	priv->net_dev_stats.rx_bytes += net_pkt_list->pkt_length; -	priv->net_dev_stats.rx_packets++; +	ndev->stats.rx_bytes += net_pkt_list->pkt_length; +	ndev->stats.rx_packets++;  	return 0;  } @@ -2148,7 +2268,7 @@ static int emac_poll(struct napi_struct *napi, int budget)  	struct net_device *ndev = priv->ndev;  	struct device *emac_dev = &ndev->dev;  	u32 status = 0; -	u32 num_pkts = 0; +	u32 num_tx_pkts = 0, num_rx_pkts = 0;  	/* Check interrupt vectors and call packet processing */  	status = emac_read(EMAC_MACINVECTOR); @@ -2159,27 +2279,19 @@ static int emac_poll(struct napi_struct *napi, int budget)  		mask = EMAC_DM646X_MAC_IN_VECTOR_TX_INT_VEC;  	if (status & mask) { -		num_pkts = emac_tx_bdproc(priv, EMAC_DEF_TX_CH, +		num_tx_pkts = emac_tx_bdproc(priv, EMAC_DEF_TX_CH,  					  EMAC_DEF_TX_MAX_SERVICE);  	} /* TX processing */ -	if (num_pkts) -		return budget; -  	mask = EMAC_DM644X_MAC_IN_VECTOR_RX_INT_VEC;  	if (priv->version == EMAC_VERSION_2)  		mask = EMAC_DM646X_MAC_IN_VECTOR_RX_INT_VEC;  	if (status & mask) { -		num_pkts = emac_rx_bdproc(priv, EMAC_DEF_RX_CH, budget); +		num_rx_pkts = emac_rx_bdproc(priv, EMAC_DEF_RX_CH, budget);  	} /* RX processing */ -	if (num_pkts < budget) { -		napi_complete(napi); -		emac_int_enable(priv); -	} -  	mask = EMAC_DM644X_MAC_IN_VECTOR_HOST_INT;  	if (priv->version == EMAC_VERSION_2)  		mask = EMAC_DM646X_MAC_IN_VECTOR_HOST_INT; @@ -2210,9 +2322,12 @@ static int emac_poll(struct napi_struct *napi, int budget)  				dev_err(emac_dev, "RX Host error %s on ch=%d\n",  					&emac_rxhost_errcodes[cause][0], ch);  		} -	} /* Host error processing */ +	} else if (num_rx_pkts < budget) { +		napi_complete(napi); +		emac_int_enable(priv); +	} -	return num_pkts; +	return num_rx_pkts;  }  #ifdef CONFIG_NET_POLL_CONTROLLER @@ -2437,6 +2552,14 @@ static int emac_dev_open(struct net_device *ndev)  	/* Start/Enable EMAC hardware */  	emac_hw_enable(priv); +	/* Enable Interrupt pacing if configured */ +	if (priv->coal_intvl != 0) { +		struct ethtool_coalesce coal; + +		coal.rx_coalesce_usecs = (priv->coal_intvl << 4); +		emac_set_coalesce(ndev, &coal); +	} +  	/* find the first phy */  	priv->phydev = NULL;  	if (priv->phy_mask) { @@ -2570,39 +2693,39 @@ static struct net_device_stats *emac_dev_getnetstats(struct net_device *ndev)  	else  		stats_clear_mask = 0; -	priv->net_dev_stats.multicast += emac_read(EMAC_RXMCASTFRAMES); +	ndev->stats.multicast += emac_read(EMAC_RXMCASTFRAMES);  	emac_write(EMAC_RXMCASTFRAMES, stats_clear_mask); -	priv->net_dev_stats.collisions += (emac_read(EMAC_TXCOLLISION) + +	ndev->stats.collisions += (emac_read(EMAC_TXCOLLISION) +  					   emac_read(EMAC_TXSINGLECOLL) +  					   emac_read(EMAC_TXMULTICOLL));  	emac_write(EMAC_TXCOLLISION, stats_clear_mask);  	emac_write(EMAC_TXSINGLECOLL, stats_clear_mask);  	emac_write(EMAC_TXMULTICOLL, stats_clear_mask); -	priv->net_dev_stats.rx_length_errors += (emac_read(EMAC_RXOVERSIZED) + +	ndev->stats.rx_length_errors += (emac_read(EMAC_RXOVERSIZED) +  						emac_read(EMAC_RXJABBER) +  						emac_read(EMAC_RXUNDERSIZED));  	emac_write(EMAC_RXOVERSIZED, stats_clear_mask);  	emac_write(EMAC_RXJABBER, stats_clear_mask);  	emac_write(EMAC_RXUNDERSIZED, stats_clear_mask); -	priv->net_dev_stats.rx_over_errors += (emac_read(EMAC_RXSOFOVERRUNS) + +	ndev->stats.rx_over_errors += (emac_read(EMAC_RXSOFOVERRUNS) +  					       emac_read(EMAC_RXMOFOVERRUNS));  	emac_write(EMAC_RXSOFOVERRUNS, stats_clear_mask);  	emac_write(EMAC_RXMOFOVERRUNS, stats_clear_mask); -	priv->net_dev_stats.rx_fifo_errors += emac_read(EMAC_RXDMAOVERRUNS); +	ndev->stats.rx_fifo_errors += emac_read(EMAC_RXDMAOVERRUNS);  	emac_write(EMAC_RXDMAOVERRUNS, stats_clear_mask); -	priv->net_dev_stats.tx_carrier_errors += +	ndev->stats.tx_carrier_errors +=  		emac_read(EMAC_TXCARRIERSENSE);  	emac_write(EMAC_TXCARRIERSENSE, stats_clear_mask); -	priv->net_dev_stats.tx_fifo_errors = emac_read(EMAC_TXUNDERRUN); +	ndev->stats.tx_fifo_errors = emac_read(EMAC_TXUNDERRUN);  	emac_write(EMAC_TXUNDERRUN, stats_clear_mask); -	return &priv->net_dev_stats; +	return &ndev->stats;  }  static const struct net_device_ops emac_netdev_ops = { @@ -2677,6 +2800,9 @@ static int __devinit davinci_emac_probe(struct platform_device *pdev)  	priv->int_enable = pdata->interrupt_enable;  	priv->int_disable = pdata->interrupt_disable; +	priv->coal_intvl = 0; +	priv->bus_freq_mhz = (u32)(emac_bus_frequency / 1000000); +  	emac_dev = &ndev->dev;  	/* Get EMAC platform data */  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  |