|
@@ -39,6 +39,7 @@
|
|
|
#include <linux/workqueue.h>
|
|
|
#include <linux/if_vlan.h>
|
|
|
#include <linux/prefetch.h>
|
|
|
+#include <linux/debugfs.h>
|
|
|
#include <linux/mii.h>
|
|
|
|
|
|
#include <asm/irq.h>
|
|
@@ -1574,13 +1575,13 @@ static void sky2_tx_complete(struct sky2_port *sky2, u16 done)
|
|
|
if (unlikely(netif_msg_tx_done(sky2)))
|
|
|
printk(KERN_DEBUG "%s: tx done %u\n",
|
|
|
dev->name, idx);
|
|
|
+
|
|
|
sky2->net_stats.tx_packets++;
|
|
|
sky2->net_stats.tx_bytes += re->skb->len;
|
|
|
|
|
|
dev_kfree_skb_any(re->skb);
|
|
|
+ sky2->tx_next = RING_NEXT(idx, TX_RING_SIZE);
|
|
|
}
|
|
|
-
|
|
|
- le->opcode = 0; /* paranoia */
|
|
|
}
|
|
|
|
|
|
sky2->tx_cons = idx;
|
|
@@ -3470,6 +3471,195 @@ static const struct ethtool_ops sky2_ethtool_ops = {
|
|
|
.get_perm_addr = ethtool_op_get_perm_addr,
|
|
|
};
|
|
|
|
|
|
+#ifdef CONFIG_SKY2_DEBUG
|
|
|
+
|
|
|
+static struct dentry *sky2_debug;
|
|
|
+
|
|
|
+static int sky2_debug_show(struct seq_file *seq, void *v)
|
|
|
+{
|
|
|
+ struct net_device *dev = seq->private;
|
|
|
+ const struct sky2_port *sky2 = netdev_priv(dev);
|
|
|
+ const struct sky2_hw *hw = sky2->hw;
|
|
|
+ unsigned port = sky2->port;
|
|
|
+ unsigned idx, last;
|
|
|
+ int sop;
|
|
|
+
|
|
|
+ if (!netif_running(dev))
|
|
|
+ return -ENETDOWN;
|
|
|
+
|
|
|
+ seq_printf(seq, "IRQ src=%x mask=%x control=%x\n",
|
|
|
+ sky2_read32(hw, B0_ISRC),
|
|
|
+ sky2_read32(hw, B0_IMSK),
|
|
|
+ sky2_read32(hw, B0_Y2_SP_ICR));
|
|
|
+
|
|
|
+ netif_poll_disable(hw->dev[0]);
|
|
|
+ last = sky2_read16(hw, STAT_PUT_IDX);
|
|
|
+
|
|
|
+ if (hw->st_idx == last)
|
|
|
+ seq_puts(seq, "Status ring (empty)\n");
|
|
|
+ else {
|
|
|
+ seq_puts(seq, "Status ring\n");
|
|
|
+ for (idx = hw->st_idx; idx != last && idx < STATUS_RING_SIZE;
|
|
|
+ idx = RING_NEXT(idx, STATUS_RING_SIZE)) {
|
|
|
+ const struct sky2_status_le *le = hw->st_le + idx;
|
|
|
+ seq_printf(seq, "[%d] %#x %d %#x\n",
|
|
|
+ idx, le->opcode, le->length, le->status);
|
|
|
+ }
|
|
|
+ seq_puts(seq, "\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ seq_printf(seq, "Tx ring pending=%u...%u report=%d done=%d\n",
|
|
|
+ sky2->tx_cons, sky2->tx_prod,
|
|
|
+ sky2_read16(hw, port == 0 ? STAT_TXA1_RIDX : STAT_TXA2_RIDX),
|
|
|
+ sky2_read16(hw, Q_ADDR(txqaddr[port], Q_DONE)));
|
|
|
+
|
|
|
+ /* Dump contents of tx ring */
|
|
|
+ sop = 1;
|
|
|
+ for (idx = sky2->tx_next; idx != sky2->tx_prod && idx < TX_RING_SIZE;
|
|
|
+ idx = RING_NEXT(idx, TX_RING_SIZE)) {
|
|
|
+ const struct sky2_tx_le *le = sky2->tx_le + idx;
|
|
|
+ u32 a = le32_to_cpu(le->addr);
|
|
|
+
|
|
|
+ if (sop)
|
|
|
+ seq_printf(seq, "%u:", idx);
|
|
|
+ sop = 0;
|
|
|
+
|
|
|
+ switch(le->opcode & ~HW_OWNER) {
|
|
|
+ case OP_ADDR64:
|
|
|
+ seq_printf(seq, " %#x:", a);
|
|
|
+ break;
|
|
|
+ case OP_LRGLEN:
|
|
|
+ seq_printf(seq, " mtu=%d", a);
|
|
|
+ break;
|
|
|
+ case OP_VLAN:
|
|
|
+ seq_printf(seq, " vlan=%d", be16_to_cpu(le->length));
|
|
|
+ break;
|
|
|
+ case OP_TCPLISW:
|
|
|
+ seq_printf(seq, " csum=%#x", a);
|
|
|
+ break;
|
|
|
+ case OP_LARGESEND:
|
|
|
+ seq_printf(seq, " tso=%#x(%d)", a, le16_to_cpu(le->length));
|
|
|
+ break;
|
|
|
+ case OP_PACKET:
|
|
|
+ seq_printf(seq, " %#x(%d)", a, le16_to_cpu(le->length));
|
|
|
+ break;
|
|
|
+ case OP_BUFFER:
|
|
|
+ seq_printf(seq, " frag=%#x(%d)", a, le16_to_cpu(le->length));
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ seq_printf(seq, " op=%#x,%#x(%d)", le->opcode,
|
|
|
+ a, le16_to_cpu(le->length));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (le->ctrl & EOP) {
|
|
|
+ seq_putc(seq, '\n');
|
|
|
+ sop = 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ seq_printf(seq, "\nRx ring hw get=%d put=%d last=%d\n",
|
|
|
+ sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_GET_IDX)),
|
|
|
+ last = sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_PUT_IDX)),
|
|
|
+ sky2_read16(hw, Y2_QADDR(rxqaddr[port], PREF_UNIT_LAST_IDX)));
|
|
|
+
|
|
|
+ netif_poll_enable(hw->dev[0]);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int sky2_debug_open(struct inode *inode, struct file *file)
|
|
|
+{
|
|
|
+ return single_open(file, sky2_debug_show, inode->i_private);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct file_operations sky2_debug_fops = {
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .open = sky2_debug_open,
|
|
|
+ .read = seq_read,
|
|
|
+ .llseek = seq_lseek,
|
|
|
+ .release = single_release,
|
|
|
+};
|
|
|
+
|
|
|
+/*
|
|
|
+ * Use network device events to create/remove/rename
|
|
|
+ * debugfs file entries
|
|
|
+ */
|
|
|
+static int sky2_device_event(struct notifier_block *unused,
|
|
|
+ unsigned long event, void *ptr)
|
|
|
+{
|
|
|
+ struct net_device *dev = ptr;
|
|
|
+
|
|
|
+ if (dev->open == sky2_up) {
|
|
|
+ struct sky2_port *sky2 = netdev_priv(dev);
|
|
|
+
|
|
|
+ switch(event) {
|
|
|
+ case NETDEV_CHANGENAME:
|
|
|
+ if (!netif_running(dev))
|
|
|
+ break;
|
|
|
+ /* fallthrough */
|
|
|
+ case NETDEV_DOWN:
|
|
|
+ case NETDEV_GOING_DOWN:
|
|
|
+ if (sky2->debugfs) {
|
|
|
+ printk(KERN_DEBUG PFX "%s: remove debugfs\n",
|
|
|
+ dev->name);
|
|
|
+ debugfs_remove(sky2->debugfs);
|
|
|
+ sky2->debugfs = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (event != NETDEV_CHANGENAME)
|
|
|
+ break;
|
|
|
+ /* fallthrough for changename */
|
|
|
+ case NETDEV_UP:
|
|
|
+ if (sky2_debug) {
|
|
|
+ struct dentry *d;
|
|
|
+ d = debugfs_create_file(dev->name, S_IRUGO,
|
|
|
+ sky2_debug, dev,
|
|
|
+ &sky2_debug_fops);
|
|
|
+ if (d == NULL || IS_ERR(d))
|
|
|
+ printk(KERN_INFO PFX
|
|
|
+ "%s: debugfs create failed\n",
|
|
|
+ dev->name);
|
|
|
+ else
|
|
|
+ sky2->debugfs = d;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return NOTIFY_DONE;
|
|
|
+}
|
|
|
+
|
|
|
+static struct notifier_block sky2_notifier = {
|
|
|
+ .notifier_call = sky2_device_event,
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+static __init void sky2_debug_init(void)
|
|
|
+{
|
|
|
+ struct dentry *ent;
|
|
|
+
|
|
|
+ ent = debugfs_create_dir("sky2", NULL);
|
|
|
+ if (!ent || IS_ERR(ent))
|
|
|
+ return;
|
|
|
+
|
|
|
+ sky2_debug = ent;
|
|
|
+ register_netdevice_notifier(&sky2_notifier);
|
|
|
+}
|
|
|
+
|
|
|
+static __exit void sky2_debug_cleanup(void)
|
|
|
+{
|
|
|
+ if (sky2_debug) {
|
|
|
+ unregister_netdevice_notifier(&sky2_notifier);
|
|
|
+ debugfs_remove(sky2_debug);
|
|
|
+ sky2_debug = NULL;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#else
|
|
|
+#define sky2_debug_init()
|
|
|
+#define sky2_debug_cleanup()
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
/* Initialize network device */
|
|
|
static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw,
|
|
|
unsigned port,
|
|
@@ -3960,12 +4150,14 @@ static struct pci_driver sky2_driver = {
|
|
|
|
|
|
static int __init sky2_init_module(void)
|
|
|
{
|
|
|
+ sky2_debug_init();
|
|
|
return pci_register_driver(&sky2_driver);
|
|
|
}
|
|
|
|
|
|
static void __exit sky2_cleanup_module(void)
|
|
|
{
|
|
|
pci_unregister_driver(&sky2_driver);
|
|
|
+ sky2_debug_cleanup();
|
|
|
}
|
|
|
|
|
|
module_init(sky2_init_module);
|