aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMugunthan V N <mugunthanvnm@ti.com>2013-03-11 19:16:37 -0400
committerDavid S. Miller <davem@davemloft.net>2013-03-13 04:38:20 -0400
commitff5b8ef2ef3af0fd7e1cf6c8c1ed9ec5afbda422 (patch)
tree9fd3d6cedfafda4995db746e8d213198a0b4c506
parentd3bb9c58b567d240eaaa2dc8bd778696eaed5fbd (diff)
driver: net: ethernet: cpsw: implement interrupt pacing via ethtool
This patch implements support for interrupt pacing block of CPSW via ethtool Inetrrupt pacing block is common of both the ethernet interface in dual emac mode Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/ti/cpsw.c104
1 files changed, 104 insertions, 0 deletions
diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c
index 83ce890d6e97..d6cf6982904e 100644
--- a/drivers/net/ethernet/ti/cpsw.c
+++ b/drivers/net/ethernet/ti/cpsw.c
@@ -126,6 +126,13 @@ do { \
126#define CPSW_FIFO_DUAL_MAC_MODE (1 << 15) 126#define CPSW_FIFO_DUAL_MAC_MODE (1 << 15)
127#define CPSW_FIFO_RATE_LIMIT_MODE (2 << 15) 127#define CPSW_FIFO_RATE_LIMIT_MODE (2 << 15)
128 128
129#define CPSW_INTPACEEN (0x3f << 16)
130#define CPSW_INTPRESCALE_MASK (0x7FF << 0)
131#define CPSW_CMINTMAX_CNT 63
132#define CPSW_CMINTMIN_CNT 2
133#define CPSW_CMINTMAX_INTVL (1000 / CPSW_CMINTMIN_CNT)
134#define CPSW_CMINTMIN_INTVL ((1000 / CPSW_CMINTMAX_CNT) + 1)
135
129#define cpsw_enable_irq(priv) \ 136#define cpsw_enable_irq(priv) \
130 do { \ 137 do { \
131 u32 i; \ 138 u32 i; \
@@ -164,6 +171,15 @@ struct cpsw_wr_regs {
164 u32 rx_en; 171 u32 rx_en;
165 u32 tx_en; 172 u32 tx_en;
166 u32 misc_en; 173 u32 misc_en;
174 u32 mem_allign1[8];
175 u32 rx_thresh_stat;
176 u32 rx_stat;
177 u32 tx_stat;
178 u32 misc_stat;
179 u32 mem_allign2[8];
180 u32 rx_imax;
181 u32 tx_imax;
182
167}; 183};
168 184
169struct cpsw_ss_regs { 185struct cpsw_ss_regs {
@@ -318,6 +334,8 @@ struct cpsw_priv {
318 struct cpsw_host_regs __iomem *host_port_regs; 334 struct cpsw_host_regs __iomem *host_port_regs;
319 u32 msg_enable; 335 u32 msg_enable;
320 u32 version; 336 u32 version;
337 u32 coal_intvl;
338 u32 bus_freq_mhz;
321 struct net_device_stats stats; 339 struct net_device_stats stats;
322 int rx_packet_max; 340 int rx_packet_max;
323 int host_port; 341 int host_port;
@@ -616,6 +634,77 @@ static void cpsw_adjust_link(struct net_device *ndev)
616 } 634 }
617} 635}
618 636
637static int cpsw_get_coalesce(struct net_device *ndev,
638 struct ethtool_coalesce *coal)
639{
640 struct cpsw_priv *priv = netdev_priv(ndev);
641
642 coal->rx_coalesce_usecs = priv->coal_intvl;
643 return 0;
644}
645
646static int cpsw_set_coalesce(struct net_device *ndev,
647 struct ethtool_coalesce *coal)
648{
649 struct cpsw_priv *priv = netdev_priv(ndev);
650 u32 int_ctrl;
651 u32 num_interrupts = 0;
652 u32 prescale = 0;
653 u32 addnl_dvdr = 1;
654 u32 coal_intvl = 0;
655
656 if (!coal->rx_coalesce_usecs)
657 return -EINVAL;
658
659 coal_intvl = coal->rx_coalesce_usecs;
660
661 int_ctrl = readl(&priv->wr_regs->int_control);
662 prescale = priv->bus_freq_mhz * 4;
663
664 if (coal_intvl < CPSW_CMINTMIN_INTVL)
665 coal_intvl = CPSW_CMINTMIN_INTVL;
666
667 if (coal_intvl > CPSW_CMINTMAX_INTVL) {
668 /* Interrupt pacer works with 4us Pulse, we can
669 * throttle further by dilating the 4us pulse.
670 */
671 addnl_dvdr = CPSW_INTPRESCALE_MASK / prescale;
672
673 if (addnl_dvdr > 1) {
674 prescale *= addnl_dvdr;
675 if (coal_intvl > (CPSW_CMINTMAX_INTVL * addnl_dvdr))
676 coal_intvl = (CPSW_CMINTMAX_INTVL
677 * addnl_dvdr);
678 } else {
679 addnl_dvdr = 1;
680 coal_intvl = CPSW_CMINTMAX_INTVL;
681 }
682 }
683
684 num_interrupts = (1000 * addnl_dvdr) / coal_intvl;
685 writel(num_interrupts, &priv->wr_regs->rx_imax);
686 writel(num_interrupts, &priv->wr_regs->tx_imax);
687
688 int_ctrl |= CPSW_INTPACEEN;
689 int_ctrl &= (~CPSW_INTPRESCALE_MASK);
690 int_ctrl |= (prescale & CPSW_INTPRESCALE_MASK);
691 writel(int_ctrl, &priv->wr_regs->int_control);
692
693 cpsw_notice(priv, timer, "Set coalesce to %d usecs.\n", coal_intvl);
694 if (priv->data.dual_emac) {
695 int i;
696
697 for (i = 0; i < priv->data.slaves; i++) {
698 priv = netdev_priv(priv->slaves[i].ndev);
699 priv->coal_intvl = coal_intvl;
700 }
701 } else {
702 priv->coal_intvl = coal_intvl;
703 }
704
705 return 0;
706}
707
619static inline int __show_stat(char *buf, int maxlen, const char *name, u32 val) 708static inline int __show_stat(char *buf, int maxlen, const char *name, u32 val)
620{ 709{
621 static char *leader = "........................................"; 710 static char *leader = "........................................";
@@ -838,6 +927,14 @@ static int cpsw_ndo_open(struct net_device *ndev)
838 cpsw_info(priv, ifup, "submitted %d rx descriptors\n", i); 927 cpsw_info(priv, ifup, "submitted %d rx descriptors\n", i);
839 } 928 }
840 929
930 /* Enable Interrupt pacing if configured */
931 if (priv->coal_intvl != 0) {
932 struct ethtool_coalesce coal;
933
934 coal.rx_coalesce_usecs = (priv->coal_intvl << 4);
935 cpsw_set_coalesce(ndev, &coal);
936 }
937
841 cpdma_ctlr_start(priv->dma); 938 cpdma_ctlr_start(priv->dma);
842 cpsw_intr_enable(priv); 939 cpsw_intr_enable(priv);
843 napi_enable(&priv->napi); 940 napi_enable(&priv->napi);
@@ -1279,6 +1376,8 @@ static const struct ethtool_ops cpsw_ethtool_ops = {
1279 .get_ts_info = cpsw_get_ts_info, 1376 .get_ts_info = cpsw_get_ts_info,
1280 .get_settings = cpsw_get_settings, 1377 .get_settings = cpsw_get_settings,
1281 .set_settings = cpsw_set_settings, 1378 .set_settings = cpsw_set_settings,
1379 .get_coalesce = cpsw_get_coalesce,
1380 .set_coalesce = cpsw_set_coalesce,
1282}; 1381};
1283 1382
1284static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv, 1383static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv,
@@ -1466,6 +1565,9 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev,
1466 priv_sl2->slaves = priv->slaves; 1565 priv_sl2->slaves = priv->slaves;
1467 priv_sl2->clk = priv->clk; 1566 priv_sl2->clk = priv->clk;
1468 1567
1568 priv_sl2->coal_intvl = 0;
1569 priv_sl2->bus_freq_mhz = priv->bus_freq_mhz;
1570
1469 priv_sl2->cpsw_res = priv->cpsw_res; 1571 priv_sl2->cpsw_res = priv->cpsw_res;
1470 priv_sl2->regs = priv->regs; 1572 priv_sl2->regs = priv->regs;
1471 priv_sl2->host_port = priv->host_port; 1573 priv_sl2->host_port = priv->host_port;
@@ -1575,6 +1677,8 @@ static int cpsw_probe(struct platform_device *pdev)
1575 ret = -ENODEV; 1677 ret = -ENODEV;
1576 goto clean_slave_ret; 1678 goto clean_slave_ret;
1577 } 1679 }
1680 priv->coal_intvl = 0;
1681 priv->bus_freq_mhz = clk_get_rate(priv->clk) / 1000000;
1578 1682
1579 priv->cpsw_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1683 priv->cpsw_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1580 if (!priv->cpsw_res) { 1684 if (!priv->cpsw_res) {