Add support to show CPSW hardware statistics to user via ethtool get_regs ops so user can find if there were any error reported or the system is over loaded duing hagh data rate transfer. Signed-off-by: Mugunthan V N <mugunthanvnm@xxxxxx> --- drivers/net/ethernet/ti/cpsw.c | 74 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 05a1674..64117f5 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -85,12 +85,14 @@ do { \ #define HOST_PORT_NUM 0 #define SLIVER_SIZE 0x40 +#define CPSW_HW_STATS_LEN 36 #define CPSW1_HOST_PORT_OFFSET 0x028 #define CPSW1_SLAVE_OFFSET 0x050 #define CPSW1_SLAVE_SIZE 0x040 #define CPSW1_CPDMA_OFFSET 0x100 #define CPSW1_STATERAM_OFFSET 0x200 +#define CPSW1_HW_STATS 0x400 #define CPSW1_CPTS_OFFSET 0x500 #define CPSW1_ALE_OFFSET 0x600 #define CPSW1_SLIVER_OFFSET 0x700 @@ -99,6 +101,7 @@ do { \ #define CPSW2_SLAVE_OFFSET 0x200 #define CPSW2_SLAVE_SIZE 0x100 #define CPSW2_CPDMA_OFFSET 0x800 +#define CPSW2_HW_STATS 0x900 #define CPSW2_STATERAM_OFFSET 0xa00 #define CPSW2_CPTS_OFFSET 0xc00 #define CPSW2_ALE_OFFSET 0xd00 @@ -332,6 +335,7 @@ struct cpsw_priv { struct cpsw_platform_data data; struct cpsw_ss_regs __iomem *regs; struct cpsw_wr_regs __iomem *wr_regs; + void __iomem *hw_stats; struct cpsw_host_regs __iomem *host_port_regs; u32 msg_enable; u32 version; @@ -723,6 +727,64 @@ static int cpsw_set_coalesce(struct net_device *ndev, return 0; } +static int cpsw_get_regs_len(struct net_device *ndev) +{ + int len; + + len = CPSW_HW_STATS_LEN * sizeof(u32); + len += 2 * sizeof(struct cpdma_chan_stats); + + return len; +} + +static void cpsw_get_regs(struct net_device *ndev, + struct ethtool_regs *regs, void *p) +{ + struct cpsw_priv *priv = netdev_priv(ndev); + struct cpdma_chan_stats dma_stats; + u32 *reg = p; + int i; + + /* update CPSW IP version */ + regs->version = priv->version; + + /* Packet Tx/Rx statistics */ + for (i = 0; i < CPSW_HW_STATS_LEN; i++) + reg[i] = readl(((u32 *)priv->hw_stats) + i); + + /* Rx DMA statistics */ + cpdma_chan_get_stats(priv->rxch, &dma_stats); + reg[i++] = dma_stats.head_enqueue; + reg[i++] = dma_stats.tail_enqueue; + reg[i++] = dma_stats.pad_enqueue; + reg[i++] = dma_stats.misqueued; + reg[i++] = dma_stats.desc_alloc_fail; + reg[i++] = dma_stats.pad_alloc_fail; + reg[i++] = dma_stats.runt_receive_buff; + reg[i++] = dma_stats.runt_transmit_buff; + reg[i++] = dma_stats.empty_dequeue; + reg[i++] = dma_stats.busy_dequeue; + reg[i++] = dma_stats.good_dequeue; + reg[i++] = dma_stats.requeue; + reg[i++] = dma_stats.teardown_dequeue; + + /* Tx DMA statistics */ + cpdma_chan_get_stats(priv->txch, &dma_stats); + reg[i++] = dma_stats.head_enqueue; + reg[i++] = dma_stats.tail_enqueue; + reg[i++] = dma_stats.pad_enqueue; + reg[i++] = dma_stats.misqueued; + reg[i++] = dma_stats.desc_alloc_fail; + reg[i++] = dma_stats.pad_alloc_fail; + reg[i++] = dma_stats.runt_receive_buff; + reg[i++] = dma_stats.runt_transmit_buff; + reg[i++] = dma_stats.empty_dequeue; + reg[i++] = dma_stats.busy_dequeue; + reg[i++] = dma_stats.good_dequeue; + reg[i++] = dma_stats.requeue; + reg[i++] = dma_stats.teardown_dequeue; +} + static inline int __show_stat(char *buf, int maxlen, const char *name, u32 val) { static char *leader = "........................................"; @@ -1344,9 +1406,10 @@ static void cpsw_get_drvinfo(struct net_device *ndev, { struct cpsw_priv *priv = netdev_priv(ndev); - strlcpy(info->driver, "TI CPSW Driver v1.0", sizeof(info->driver)); + strlcpy(info->driver, "cpsw", sizeof(info->driver)); strlcpy(info->version, "1.0", sizeof(info->version)); strlcpy(info->bus_info, priv->pdev->name, sizeof(info->bus_info)); + info->regdump_len = cpsw_get_regs_len(ndev); } static u32 cpsw_get_msglevel(struct net_device *ndev) @@ -1426,6 +1489,8 @@ static const struct ethtool_ops cpsw_ethtool_ops = { .set_settings = cpsw_set_settings, .get_coalesce = cpsw_get_coalesce, .set_coalesce = cpsw_set_coalesce, + .get_regs_len = cpsw_get_regs_len, + .get_regs = cpsw_get_regs, }; static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv, @@ -1623,6 +1688,7 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev, priv_sl2->host_port = priv->host_port; priv_sl2->host_port_regs = priv->host_port_regs; priv_sl2->wr_regs = priv->wr_regs; + priv_sl2->hw_stats = priv->hw_stats; priv_sl2->dma = priv->dma; priv_sl2->txch = priv->txch; priv_sl2->rxch = priv->rxch; @@ -1780,7 +1846,8 @@ static int cpsw_probe(struct platform_device *pdev) switch (priv->version) { case CPSW_VERSION_1: priv->host_port_regs = ss_regs + CPSW1_HOST_PORT_OFFSET; - priv->cpts->reg = ss_regs + CPSW1_CPTS_OFFSET; + priv->hw_stats = ss_regs + CPSW1_HW_STATS; + priv->cpts->reg = ss_regs + CPSW1_CPTS_OFFSET; dma_params.dmaregs = ss_regs + CPSW1_CPDMA_OFFSET; dma_params.txhdp = ss_regs + CPSW1_STATERAM_OFFSET; ale_params.ale_regs = ss_regs + CPSW1_ALE_OFFSET; @@ -1791,7 +1858,8 @@ static int cpsw_probe(struct platform_device *pdev) break; case CPSW_VERSION_2: priv->host_port_regs = ss_regs + CPSW2_HOST_PORT_OFFSET; - priv->cpts->reg = ss_regs + CPSW2_CPTS_OFFSET; + priv->hw_stats = ss_regs + CPSW2_HW_STATS; + priv->cpts->reg = ss_regs + CPSW2_CPTS_OFFSET; dma_params.dmaregs = ss_regs + CPSW2_CPDMA_OFFSET; dma_params.txhdp = ss_regs + CPSW2_STATERAM_OFFSET; ale_params.ale_regs = ss_regs + CPSW2_ALE_OFFSET; -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html