1291ab06eSStefan Wahren /*
2291ab06eSStefan Wahren * Copyright (c) 2011, 2012, Qualcomm Atheros Communications Inc.
3291ab06eSStefan Wahren * Copyright (c) 2014, I2SE GmbH
4291ab06eSStefan Wahren *
5291ab06eSStefan Wahren * Permission to use, copy, modify, and/or distribute this software
6291ab06eSStefan Wahren * for any purpose with or without fee is hereby granted, provided
7291ab06eSStefan Wahren * that the above copyright notice and this permission notice appear
8291ab06eSStefan Wahren * in all copies.
9291ab06eSStefan Wahren *
10291ab06eSStefan Wahren * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11291ab06eSStefan Wahren * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12291ab06eSStefan Wahren * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
13291ab06eSStefan Wahren * THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
14291ab06eSStefan Wahren * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15291ab06eSStefan Wahren * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
16291ab06eSStefan Wahren * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
17291ab06eSStefan Wahren * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18291ab06eSStefan Wahren */
19291ab06eSStefan Wahren
20291ab06eSStefan Wahren /* This file contains debugging routines for use in the QCA7K driver.
21291ab06eSStefan Wahren */
22291ab06eSStefan Wahren
23291ab06eSStefan Wahren #include <linux/debugfs.h>
24291ab06eSStefan Wahren #include <linux/ethtool.h>
25291ab06eSStefan Wahren #include <linux/seq_file.h>
26291ab06eSStefan Wahren #include <linux/types.h>
27291ab06eSStefan Wahren
28291ab06eSStefan Wahren #include "qca_7k.h"
29291ab06eSStefan Wahren #include "qca_debug.h"
30291ab06eSStefan Wahren
31291ab06eSStefan Wahren #define QCASPI_MAX_REGS 0x20
32291ab06eSStefan Wahren
3302296b1dSStefan Wahren #define QCASPI_RX_MAX_FRAMES 4
3402296b1dSStefan Wahren
35291ab06eSStefan Wahren static const u16 qcaspi_spi_regs[] = {
36291ab06eSStefan Wahren SPI_REG_BFR_SIZE,
37291ab06eSStefan Wahren SPI_REG_WRBUF_SPC_AVA,
38291ab06eSStefan Wahren SPI_REG_RDBUF_BYTE_AVA,
39291ab06eSStefan Wahren SPI_REG_SPI_CONFIG,
40291ab06eSStefan Wahren SPI_REG_SPI_STATUS,
41291ab06eSStefan Wahren SPI_REG_INTR_CAUSE,
42291ab06eSStefan Wahren SPI_REG_INTR_ENABLE,
43291ab06eSStefan Wahren SPI_REG_RDBUF_WATERMARK,
44291ab06eSStefan Wahren SPI_REG_WRBUF_WATERMARK,
45291ab06eSStefan Wahren SPI_REG_SIGNATURE,
46291ab06eSStefan Wahren SPI_REG_ACTION_CTRL
47291ab06eSStefan Wahren };
48291ab06eSStefan Wahren
49291ab06eSStefan Wahren /* The order of these strings must match the order of the fields in
50291ab06eSStefan Wahren * struct qcaspi_stats
51291ab06eSStefan Wahren * See qca_spi.h
52291ab06eSStefan Wahren */
53291ab06eSStefan Wahren static const char qcaspi_gstrings_stats[][ETH_GSTRING_LEN] = {
54291ab06eSStefan Wahren "Triggered resets",
55291ab06eSStefan Wahren "Device resets",
56291ab06eSStefan Wahren "Reset timeouts",
57291ab06eSStefan Wahren "Read errors",
58291ab06eSStefan Wahren "Write errors",
59291ab06eSStefan Wahren "Read buffer errors",
60291ab06eSStefan Wahren "Write buffer errors",
61291ab06eSStefan Wahren "Out of memory",
62291ab06eSStefan Wahren "Write buffer misses",
63291ab06eSStefan Wahren "Transmit ring full",
64291ab06eSStefan Wahren "SPI errors",
6548c1699eSStefan Wahren "Write verify errors",
66026b907dSStefan Wahren "Buffer available errors",
67a5393567SStefan Wahren "Bad signature",
68291ab06eSStefan Wahren };
69291ab06eSStefan Wahren
70291ab06eSStefan Wahren #ifdef CONFIG_DEBUG_FS
71291ab06eSStefan Wahren
72291ab06eSStefan Wahren static int
qcaspi_info_show(struct seq_file * s,void * what)73291ab06eSStefan Wahren qcaspi_info_show(struct seq_file *s, void *what)
74291ab06eSStefan Wahren {
75291ab06eSStefan Wahren struct qcaspi *qca = s->private;
76291ab06eSStefan Wahren
77291ab06eSStefan Wahren seq_printf(s, "RX buffer size : %lu\n",
78291ab06eSStefan Wahren (unsigned long)qca->buffer_size);
79291ab06eSStefan Wahren
80291ab06eSStefan Wahren seq_puts(s, "TX ring state : ");
81291ab06eSStefan Wahren
82291ab06eSStefan Wahren if (qca->txr.skb[qca->txr.head] == NULL)
83291ab06eSStefan Wahren seq_puts(s, "empty");
84291ab06eSStefan Wahren else if (qca->txr.skb[qca->txr.tail])
85291ab06eSStefan Wahren seq_puts(s, "full");
86291ab06eSStefan Wahren else
87291ab06eSStefan Wahren seq_puts(s, "in use");
88291ab06eSStefan Wahren
89291ab06eSStefan Wahren seq_puts(s, "\n");
90291ab06eSStefan Wahren
91291ab06eSStefan Wahren seq_printf(s, "TX ring size : %u\n",
92291ab06eSStefan Wahren qca->txr.size);
93291ab06eSStefan Wahren
94291ab06eSStefan Wahren seq_printf(s, "Sync state : %u (",
95291ab06eSStefan Wahren (unsigned int)qca->sync);
96291ab06eSStefan Wahren switch (qca->sync) {
97291ab06eSStefan Wahren case QCASPI_SYNC_UNKNOWN:
98291ab06eSStefan Wahren seq_puts(s, "QCASPI_SYNC_UNKNOWN");
99291ab06eSStefan Wahren break;
100291ab06eSStefan Wahren case QCASPI_SYNC_RESET:
101291ab06eSStefan Wahren seq_puts(s, "QCASPI_SYNC_RESET");
102291ab06eSStefan Wahren break;
103291ab06eSStefan Wahren case QCASPI_SYNC_READY:
104291ab06eSStefan Wahren seq_puts(s, "QCASPI_SYNC_READY");
105291ab06eSStefan Wahren break;
106291ab06eSStefan Wahren default:
107291ab06eSStefan Wahren seq_puts(s, "INVALID");
108291ab06eSStefan Wahren break;
109291ab06eSStefan Wahren }
110291ab06eSStefan Wahren seq_puts(s, ")\n");
111291ab06eSStefan Wahren
112291ab06eSStefan Wahren seq_printf(s, "IRQ : %d\n",
113291ab06eSStefan Wahren qca->spi_dev->irq);
114*6b2dfc4fSStefan Wahren seq_printf(s, "INTR : %lx\n",
115*6b2dfc4fSStefan Wahren qca->intr);
116291ab06eSStefan Wahren
117291ab06eSStefan Wahren seq_printf(s, "SPI max speed : %lu\n",
118291ab06eSStefan Wahren (unsigned long)qca->spi_dev->max_speed_hz);
119291ab06eSStefan Wahren seq_printf(s, "SPI mode : %x\n",
120291ab06eSStefan Wahren qca->spi_dev->mode);
121291ab06eSStefan Wahren seq_printf(s, "SPI chip select : %u\n",
12225fd0550SAmit Kumar Mahapatra (unsigned int)spi_get_chipselect(qca->spi_dev, 0));
123291ab06eSStefan Wahren seq_printf(s, "SPI legacy mode : %u\n",
124291ab06eSStefan Wahren (unsigned int)qca->legacy_mode);
125291ab06eSStefan Wahren seq_printf(s, "SPI burst length : %u\n",
126291ab06eSStefan Wahren (unsigned int)qca->burst_len);
127291ab06eSStefan Wahren
128291ab06eSStefan Wahren return 0;
129291ab06eSStefan Wahren }
13025079154SYangtao Li DEFINE_SHOW_ATTRIBUTE(qcaspi_info);
131291ab06eSStefan Wahren
132291ab06eSStefan Wahren void
qcaspi_init_device_debugfs(struct qcaspi * qca)133291ab06eSStefan Wahren qcaspi_init_device_debugfs(struct qcaspi *qca)
134291ab06eSStefan Wahren {
135687236b0SGreg Kroah-Hartman qca->device_root = debugfs_create_dir(dev_name(&qca->net_dev->dev),
136687236b0SGreg Kroah-Hartman NULL);
137291ab06eSStefan Wahren
138687236b0SGreg Kroah-Hartman debugfs_create_file("info", S_IFREG | 0444, qca->device_root, qca,
13925079154SYangtao Li &qcaspi_info_fops);
140291ab06eSStefan Wahren }
141291ab06eSStefan Wahren
142291ab06eSStefan Wahren void
qcaspi_remove_device_debugfs(struct qcaspi * qca)143291ab06eSStefan Wahren qcaspi_remove_device_debugfs(struct qcaspi *qca)
144291ab06eSStefan Wahren {
145291ab06eSStefan Wahren debugfs_remove_recursive(qca->device_root);
146291ab06eSStefan Wahren }
147291ab06eSStefan Wahren
148291ab06eSStefan Wahren #else /* CONFIG_DEBUG_FS */
149291ab06eSStefan Wahren
150291ab06eSStefan Wahren void
qcaspi_init_device_debugfs(struct qcaspi * qca)151291ab06eSStefan Wahren qcaspi_init_device_debugfs(struct qcaspi *qca)
152291ab06eSStefan Wahren {
153291ab06eSStefan Wahren }
154291ab06eSStefan Wahren
155291ab06eSStefan Wahren void
qcaspi_remove_device_debugfs(struct qcaspi * qca)156291ab06eSStefan Wahren qcaspi_remove_device_debugfs(struct qcaspi *qca)
157291ab06eSStefan Wahren {
158291ab06eSStefan Wahren }
159291ab06eSStefan Wahren
160291ab06eSStefan Wahren #endif
161291ab06eSStefan Wahren
162291ab06eSStefan Wahren static void
qcaspi_get_drvinfo(struct net_device * dev,struct ethtool_drvinfo * p)163291ab06eSStefan Wahren qcaspi_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *p)
164291ab06eSStefan Wahren {
165291ab06eSStefan Wahren struct qcaspi *qca = netdev_priv(dev);
166291ab06eSStefan Wahren
167f029c781SWolfram Sang strscpy(p->driver, QCASPI_DRV_NAME, sizeof(p->driver));
168f029c781SWolfram Sang strscpy(p->version, QCASPI_DRV_VERSION, sizeof(p->version));
169f029c781SWolfram Sang strscpy(p->fw_version, "QCA7000", sizeof(p->fw_version));
170f029c781SWolfram Sang strscpy(p->bus_info, dev_name(&qca->spi_dev->dev),
171291ab06eSStefan Wahren sizeof(p->bus_info));
172291ab06eSStefan Wahren }
173291ab06eSStefan Wahren
174291ab06eSStefan Wahren static int
qcaspi_get_link_ksettings(struct net_device * dev,struct ethtool_link_ksettings * cmd)1752cf8a897SPhilippe Reynes qcaspi_get_link_ksettings(struct net_device *dev,
1762cf8a897SPhilippe Reynes struct ethtool_link_ksettings *cmd)
177291ab06eSStefan Wahren {
1782cf8a897SPhilippe Reynes ethtool_link_ksettings_zero_link_mode(cmd, supported);
1792cf8a897SPhilippe Reynes ethtool_link_ksettings_add_link_mode(cmd, supported, 10baseT_Half);
1802cf8a897SPhilippe Reynes
1812cf8a897SPhilippe Reynes cmd->base.speed = SPEED_10;
1822cf8a897SPhilippe Reynes cmd->base.duplex = DUPLEX_HALF;
1832cf8a897SPhilippe Reynes cmd->base.port = PORT_OTHER;
1842cf8a897SPhilippe Reynes cmd->base.autoneg = AUTONEG_DISABLE;
185291ab06eSStefan Wahren
186291ab06eSStefan Wahren return 0;
187291ab06eSStefan Wahren }
188291ab06eSStefan Wahren
189291ab06eSStefan Wahren static void
qcaspi_get_ethtool_stats(struct net_device * dev,struct ethtool_stats * estats,u64 * data)190291ab06eSStefan Wahren qcaspi_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *estats, u64 *data)
191291ab06eSStefan Wahren {
192291ab06eSStefan Wahren struct qcaspi *qca = netdev_priv(dev);
193291ab06eSStefan Wahren struct qcaspi_stats *st = &qca->stats;
194291ab06eSStefan Wahren
195291ab06eSStefan Wahren memcpy(data, st, ARRAY_SIZE(qcaspi_gstrings_stats) * sizeof(u64));
196291ab06eSStefan Wahren }
197291ab06eSStefan Wahren
198291ab06eSStefan Wahren static void
qcaspi_get_strings(struct net_device * dev,u32 stringset,u8 * buf)199291ab06eSStefan Wahren qcaspi_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
200291ab06eSStefan Wahren {
201291ab06eSStefan Wahren switch (stringset) {
202291ab06eSStefan Wahren case ETH_SS_STATS:
203291ab06eSStefan Wahren memcpy(buf, &qcaspi_gstrings_stats,
204291ab06eSStefan Wahren sizeof(qcaspi_gstrings_stats));
205291ab06eSStefan Wahren break;
206291ab06eSStefan Wahren default:
207291ab06eSStefan Wahren WARN_ON(1);
208291ab06eSStefan Wahren break;
209291ab06eSStefan Wahren }
210291ab06eSStefan Wahren }
211291ab06eSStefan Wahren
212291ab06eSStefan Wahren static int
qcaspi_get_sset_count(struct net_device * dev,int sset)213291ab06eSStefan Wahren qcaspi_get_sset_count(struct net_device *dev, int sset)
214291ab06eSStefan Wahren {
215291ab06eSStefan Wahren switch (sset) {
216291ab06eSStefan Wahren case ETH_SS_STATS:
217291ab06eSStefan Wahren return ARRAY_SIZE(qcaspi_gstrings_stats);
218291ab06eSStefan Wahren default:
219291ab06eSStefan Wahren return -EINVAL;
220291ab06eSStefan Wahren }
221291ab06eSStefan Wahren }
222291ab06eSStefan Wahren
223291ab06eSStefan Wahren static int
qcaspi_get_regs_len(struct net_device * dev)224291ab06eSStefan Wahren qcaspi_get_regs_len(struct net_device *dev)
225291ab06eSStefan Wahren {
226291ab06eSStefan Wahren return sizeof(u32) * QCASPI_MAX_REGS;
227291ab06eSStefan Wahren }
228291ab06eSStefan Wahren
229291ab06eSStefan Wahren static void
qcaspi_get_regs(struct net_device * dev,struct ethtool_regs * regs,void * p)230291ab06eSStefan Wahren qcaspi_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p)
231291ab06eSStefan Wahren {
232291ab06eSStefan Wahren struct qcaspi *qca = netdev_priv(dev);
233291ab06eSStefan Wahren u32 *regs_buff = p;
234291ab06eSStefan Wahren unsigned int i;
235291ab06eSStefan Wahren
236291ab06eSStefan Wahren regs->version = 1;
237291ab06eSStefan Wahren memset(regs_buff, 0, sizeof(u32) * QCASPI_MAX_REGS);
238291ab06eSStefan Wahren
239291ab06eSStefan Wahren for (i = 0; i < ARRAY_SIZE(qcaspi_spi_regs); i++) {
240291ab06eSStefan Wahren u16 offset, value;
241291ab06eSStefan Wahren
242291ab06eSStefan Wahren qcaspi_read_register(qca, qcaspi_spi_regs[i], &value);
243291ab06eSStefan Wahren offset = qcaspi_spi_regs[i] >> 8;
244291ab06eSStefan Wahren regs_buff[offset] = value;
245291ab06eSStefan Wahren }
246291ab06eSStefan Wahren }
247291ab06eSStefan Wahren
248291ab06eSStefan Wahren static void
qcaspi_get_ringparam(struct net_device * dev,struct ethtool_ringparam * ring,struct kernel_ethtool_ringparam * kernel_ring,struct netlink_ext_ack * extack)24974624944SHao Chen qcaspi_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ring,
25074624944SHao Chen struct kernel_ethtool_ringparam *kernel_ring,
25174624944SHao Chen struct netlink_ext_ack *extack)
252291ab06eSStefan Wahren {
253291ab06eSStefan Wahren struct qcaspi *qca = netdev_priv(dev);
254291ab06eSStefan Wahren
25502296b1dSStefan Wahren ring->rx_max_pending = QCASPI_RX_MAX_FRAMES;
256291ab06eSStefan Wahren ring->tx_max_pending = TX_RING_MAX_LEN;
25702296b1dSStefan Wahren ring->rx_pending = QCASPI_RX_MAX_FRAMES;
258291ab06eSStefan Wahren ring->tx_pending = qca->txr.count;
259291ab06eSStefan Wahren }
260291ab06eSStefan Wahren
261291ab06eSStefan Wahren static int
qcaspi_set_ringparam(struct net_device * dev,struct ethtool_ringparam * ring,struct kernel_ethtool_ringparam * kernel_ring,struct netlink_ext_ack * extack)26274624944SHao Chen qcaspi_set_ringparam(struct net_device *dev, struct ethtool_ringparam *ring,
26374624944SHao Chen struct kernel_ethtool_ringparam *kernel_ring,
26474624944SHao Chen struct netlink_ext_ack *extack)
265291ab06eSStefan Wahren {
266291ab06eSStefan Wahren struct qcaspi *qca = netdev_priv(dev);
267291ab06eSStefan Wahren
26802296b1dSStefan Wahren if (ring->rx_pending != QCASPI_RX_MAX_FRAMES ||
269291ab06eSStefan Wahren (ring->rx_mini_pending) ||
270291ab06eSStefan Wahren (ring->rx_jumbo_pending))
271291ab06eSStefan Wahren return -EINVAL;
272291ab06eSStefan Wahren
27321b9dc81SStefan Wahren if (qca->spi_thread)
27421b9dc81SStefan Wahren kthread_park(qca->spi_thread);
275291ab06eSStefan Wahren
276291ab06eSStefan Wahren qca->txr.count = max_t(u32, ring->tx_pending, TX_RING_MIN_LEN);
277291ab06eSStefan Wahren qca->txr.count = min_t(u16, qca->txr.count, TX_RING_MAX_LEN);
278291ab06eSStefan Wahren
27921b9dc81SStefan Wahren if (qca->spi_thread)
28021b9dc81SStefan Wahren kthread_unpark(qca->spi_thread);
281291ab06eSStefan Wahren
282291ab06eSStefan Wahren return 0;
283291ab06eSStefan Wahren }
284291ab06eSStefan Wahren
285291ab06eSStefan Wahren static const struct ethtool_ops qcaspi_ethtool_ops = {
286291ab06eSStefan Wahren .get_drvinfo = qcaspi_get_drvinfo,
287291ab06eSStefan Wahren .get_link = ethtool_op_get_link,
288291ab06eSStefan Wahren .get_ethtool_stats = qcaspi_get_ethtool_stats,
289291ab06eSStefan Wahren .get_strings = qcaspi_get_strings,
290291ab06eSStefan Wahren .get_sset_count = qcaspi_get_sset_count,
291291ab06eSStefan Wahren .get_regs_len = qcaspi_get_regs_len,
292291ab06eSStefan Wahren .get_regs = qcaspi_get_regs,
293291ab06eSStefan Wahren .get_ringparam = qcaspi_get_ringparam,
294291ab06eSStefan Wahren .set_ringparam = qcaspi_set_ringparam,
2952cf8a897SPhilippe Reynes .get_link_ksettings = qcaspi_get_link_ksettings,
296291ab06eSStefan Wahren };
297291ab06eSStefan Wahren
qcaspi_set_ethtool_ops(struct net_device * dev)298291ab06eSStefan Wahren void qcaspi_set_ethtool_ops(struct net_device *dev)
299291ab06eSStefan Wahren {
300291ab06eSStefan Wahren dev->ethtool_ops = &qcaspi_ethtool_ops;
301291ab06eSStefan Wahren }
302