1/* 2 * Atheros AR71xx built-in ethernet mac driver 3 * 4 * Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org> 5 * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> 6 * 7 * Based on Atheros' AG7100 driver 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU General Public License version 2 as published 11 * by the Free Software Foundation. 12 */ 13 14#include <linux/debugfs.h> 15 16#include "ag71xx.h" 17 18static struct dentry *ag71xx_debugfs_root; 19 20static int ag71xx_debugfs_generic_open(struct inode *inode, struct file *file) 21{ 22 file->private_data = inode->i_private; 23 return 0; 24} 25 26void ag71xx_debugfs_update_int_stats(struct ag71xx *ag, u32 status) 27{ 28 if (status) 29 ag->debug.int_stats.total++; 30 if (status & AG71XX_INT_TX_PS) 31 ag->debug.int_stats.tx_ps++; 32 if (status & AG71XX_INT_TX_UR) 33 ag->debug.int_stats.tx_ur++; 34 if (status & AG71XX_INT_TX_BE) 35 ag->debug.int_stats.tx_be++; 36 if (status & AG71XX_INT_RX_PR) 37 ag->debug.int_stats.rx_pr++; 38 if (status & AG71XX_INT_RX_OF) 39 ag->debug.int_stats.rx_of++; 40 if (status & AG71XX_INT_RX_BE) 41 ag->debug.int_stats.rx_be++; 42} 43 44static ssize_t read_file_int_stats(struct file *file, char __user *user_buf, 45 size_t count, loff_t *ppos) 46{ 47#define PR_INT_STAT(_label, _field) \ 48 len += snprintf(buf + len, sizeof(buf) - len, \ 49 "%20s: %10lu\n", _label, ag->debug.int_stats._field); 50 51 struct ag71xx *ag = file->private_data; 52 char buf[256]; 53 unsigned int len = 0; 54 55 PR_INT_STAT("TX Packet Sent", tx_ps); 56 PR_INT_STAT("TX Underrun", tx_ur); 57 PR_INT_STAT("TX Bus Error", tx_be); 58 PR_INT_STAT("RX Packet Received", rx_pr); 59 PR_INT_STAT("RX Overflow", rx_of); 60 PR_INT_STAT("RX Bus Error", rx_be); 61 len += snprintf(buf + len, sizeof(buf) - len, "\n"); 62 PR_INT_STAT("Total", total); 63 64 return simple_read_from_buffer(user_buf, count, ppos, buf, len); 65#undef PR_INT_STAT 66} 67 68static const struct file_operations ag71xx_fops_int_stats = { 69 .open = ag71xx_debugfs_generic_open, 70 .read = read_file_int_stats, 71 .owner = THIS_MODULE 72}; 73 74void ag71xx_debugfs_update_napi_stats(struct ag71xx *ag, int rx, int tx) 75{ 76 struct ag71xx_napi_stats *stats = &ag->debug.napi_stats; 77 78 if (rx) { 79 stats->rx_count++; 80 stats->rx_packets += rx; 81 if (rx <= AG71XX_NAPI_WEIGHT) 82 stats->rx[rx]++; 83 if (rx > stats->rx_packets_max) 84 stats->rx_packets_max = rx; 85 } 86 87 if (tx) { 88 stats->tx_count++; 89 stats->tx_packets += tx; 90 if (tx <= AG71XX_NAPI_WEIGHT) 91 stats->tx[tx]++; 92 if (tx > stats->tx_packets_max) 93 stats->tx_packets_max = tx; 94 } 95} 96 97static ssize_t read_file_napi_stats(struct file *file, char __user *user_buf, 98 size_t count, loff_t *ppos) 99{ 100 struct ag71xx *ag = file->private_data; 101 struct ag71xx_napi_stats *stats = &ag->debug.napi_stats; 102 char *buf; 103 unsigned int buflen; 104 unsigned int len = 0; 105 unsigned long rx_avg = 0; 106 unsigned long tx_avg = 0; 107 int ret; 108 int i; 109 110 buflen = 2048; 111 buf = kmalloc(buflen, GFP_KERNEL); 112 if (!buf) 113 return -ENOMEM; 114 115 if (stats->rx_count) 116 rx_avg = stats->rx_packets / stats->rx_count; 117 118 if (stats->tx_count) 119 tx_avg = stats->tx_packets / stats->tx_count; 120 121 len += snprintf(buf + len, buflen - len, "%3s %10s %10s\n", 122 "len", "rx", "tx"); 123 124 for (i = 1; i <= AG71XX_NAPI_WEIGHT; i++) 125 len += snprintf(buf + len, buflen - len, 126 "%3d: %10lu %10lu\n", 127 i, stats->rx[i], stats->tx[i]); 128 129 len += snprintf(buf + len, buflen - len, "\n"); 130 131 len += snprintf(buf + len, buflen - len, "%3s: %10lu %10lu\n", 132 "sum", stats->rx_count, stats->tx_count); 133 len += snprintf(buf + len, buflen - len, "%3s: %10lu %10lu\n", 134 "avg", rx_avg, tx_avg); 135 len += snprintf(buf + len, buflen - len, "%3s: %10lu %10lu\n", 136 "max", stats->rx_packets_max, stats->tx_packets_max); 137 len += snprintf(buf + len, buflen - len, "%3s: %10lu %10lu\n", 138 "pkt", stats->rx_packets, stats->tx_packets); 139 140 ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); 141 kfree(buf); 142 143 return ret; 144} 145 146static const struct file_operations ag71xx_fops_napi_stats = { 147 .open = ag71xx_debugfs_generic_open, 148 .read = read_file_napi_stats, 149 .owner = THIS_MODULE 150}; 151 152#define DESC_PRINT_LEN 64 153 154static ssize_t read_file_ring(struct file *file, char __user *user_buf, 155 size_t count, loff_t *ppos, 156 struct ag71xx *ag, 157 struct ag71xx_ring *ring, 158 unsigned desc_reg) 159{ 160 char *buf; 161 unsigned int buflen; 162 unsigned int len = 0; 163 unsigned long flags; 164 ssize_t ret; 165 int curr; 166 int dirty; 167 u32 desc_hw; 168 int i; 169 170 buflen = (ring->size * DESC_PRINT_LEN); 171 buf = kmalloc(buflen, GFP_KERNEL); 172 if (!buf) 173 return -ENOMEM; 174 175 len += snprintf(buf + len, buflen - len, 176 "Idx ... %-8s %-8s %-8s %-8s . %-10s\n", 177 "desc", "next", "data", "ctrl", "timestamp"); 178 179 spin_lock_irqsave(&ag->lock, flags); 180 181 curr = (ring->curr % ring->size); 182 dirty = (ring->dirty % ring->size); 183 desc_hw = ag71xx_rr(ag, desc_reg); 184 for (i = 0; i < ring->size; i++) { 185 struct ag71xx_buf *ab = &ring->buf[i]; 186 u32 desc_dma = ((u32) ring->descs_dma) + i * ring->desc_size; 187 188 len += snprintf(buf + len, buflen - len, 189 "%3d %c%c%c %08x %08x %08x %08x %c %10lu\n", 190 i, 191 (i == curr) ? 'C' : ' ', 192 (i == dirty) ? 'D' : ' ', 193 (desc_hw == desc_dma) ? 'H' : ' ', 194 desc_dma, 195 ab->desc->next, 196 ab->desc->data, 197 ab->desc->ctrl, 198 (ab->desc->ctrl & DESC_EMPTY) ? 'E' : '*', 199 ab->timestamp); 200 } 201 202 spin_unlock_irqrestore(&ag->lock, flags); 203 204 ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); 205 kfree(buf); 206 207 return ret; 208} 209 210static ssize_t read_file_tx_ring(struct file *file, char __user *user_buf, 211 size_t count, loff_t *ppos) 212{ 213 struct ag71xx *ag = file->private_data; 214 215 return read_file_ring(file, user_buf, count, ppos, ag, &ag->tx_ring, 216 AG71XX_REG_TX_DESC); 217} 218 219static const struct file_operations ag71xx_fops_tx_ring = { 220 .open = ag71xx_debugfs_generic_open, 221 .read = read_file_tx_ring, 222 .owner = THIS_MODULE 223}; 224 225static ssize_t read_file_rx_ring(struct file *file, char __user *user_buf, 226 size_t count, loff_t *ppos) 227{ 228 struct ag71xx *ag = file->private_data; 229 230 return read_file_ring(file, user_buf, count, ppos, ag, &ag->rx_ring, 231 AG71XX_REG_RX_DESC); 232} 233 234static const struct file_operations ag71xx_fops_rx_ring = { 235 .open = ag71xx_debugfs_generic_open, 236 .read = read_file_rx_ring, 237 .owner = THIS_MODULE 238}; 239 240void ag71xx_debugfs_exit(struct ag71xx *ag) 241{ 242 debugfs_remove_recursive(ag->debug.debugfs_dir); 243} 244 245int ag71xx_debugfs_init(struct ag71xx *ag) 246{ 247 ag->debug.debugfs_dir = debugfs_create_dir(ag->dev->name, 248 ag71xx_debugfs_root); 249 if (!ag->debug.debugfs_dir) 250 return -ENOMEM; 251 252 debugfs_create_file("int_stats", S_IRUGO, ag->debug.debugfs_dir, 253 ag, &ag71xx_fops_int_stats); 254 debugfs_create_file("napi_stats", S_IRUGO, ag->debug.debugfs_dir, 255 ag, &ag71xx_fops_napi_stats); 256 debugfs_create_file("tx_ring", S_IRUGO, ag->debug.debugfs_dir, 257 ag, &ag71xx_fops_tx_ring); 258 debugfs_create_file("rx_ring", S_IRUGO, ag->debug.debugfs_dir, 259 ag, &ag71xx_fops_rx_ring); 260 261 return 0; 262} 263 264int ag71xx_debugfs_root_init(void) 265{ 266 if (ag71xx_debugfs_root) 267 return -EBUSY; 268 269 ag71xx_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL); 270 if (!ag71xx_debugfs_root) 271 return -ENOENT; 272 273 return 0; 274} 275 276void ag71xx_debugfs_root_exit(void) 277{ 278 debugfs_remove(ag71xx_debugfs_root); 279 ag71xx_debugfs_root = NULL; 280} 281