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 
33291ab06eSStefan Wahren static const u16 qcaspi_spi_regs[] = {
34291ab06eSStefan Wahren 	SPI_REG_BFR_SIZE,
35291ab06eSStefan Wahren 	SPI_REG_WRBUF_SPC_AVA,
36291ab06eSStefan Wahren 	SPI_REG_RDBUF_BYTE_AVA,
37291ab06eSStefan Wahren 	SPI_REG_SPI_CONFIG,
38291ab06eSStefan Wahren 	SPI_REG_SPI_STATUS,
39291ab06eSStefan Wahren 	SPI_REG_INTR_CAUSE,
40291ab06eSStefan Wahren 	SPI_REG_INTR_ENABLE,
41291ab06eSStefan Wahren 	SPI_REG_RDBUF_WATERMARK,
42291ab06eSStefan Wahren 	SPI_REG_WRBUF_WATERMARK,
43291ab06eSStefan Wahren 	SPI_REG_SIGNATURE,
44291ab06eSStefan Wahren 	SPI_REG_ACTION_CTRL
45291ab06eSStefan Wahren };
46291ab06eSStefan Wahren 
47291ab06eSStefan Wahren /* The order of these strings must match the order of the fields in
48291ab06eSStefan Wahren  * struct qcaspi_stats
49291ab06eSStefan Wahren  * See qca_spi.h
50291ab06eSStefan Wahren  */
51291ab06eSStefan Wahren static const char qcaspi_gstrings_stats[][ETH_GSTRING_LEN] = {
52291ab06eSStefan Wahren 	"Triggered resets",
53291ab06eSStefan Wahren 	"Device resets",
54291ab06eSStefan Wahren 	"Reset timeouts",
55291ab06eSStefan Wahren 	"Read errors",
56291ab06eSStefan Wahren 	"Write errors",
57291ab06eSStefan Wahren 	"Read buffer errors",
58291ab06eSStefan Wahren 	"Write buffer errors",
59291ab06eSStefan Wahren 	"Out of memory",
60291ab06eSStefan Wahren 	"Write buffer misses",
61291ab06eSStefan Wahren 	"Transmit ring full",
62291ab06eSStefan Wahren 	"SPI errors",
63291ab06eSStefan Wahren };
64291ab06eSStefan Wahren 
65291ab06eSStefan Wahren #ifdef CONFIG_DEBUG_FS
66291ab06eSStefan Wahren 
67291ab06eSStefan Wahren static int
68291ab06eSStefan Wahren qcaspi_info_show(struct seq_file *s, void *what)
69291ab06eSStefan Wahren {
70291ab06eSStefan Wahren 	struct qcaspi *qca = s->private;
71291ab06eSStefan Wahren 
72291ab06eSStefan Wahren 	seq_printf(s, "RX buffer size   : %lu\n",
73291ab06eSStefan Wahren 		   (unsigned long)qca->buffer_size);
74291ab06eSStefan Wahren 
75291ab06eSStefan Wahren 	seq_puts(s, "TX ring state    : ");
76291ab06eSStefan Wahren 
77291ab06eSStefan Wahren 	if (qca->txr.skb[qca->txr.head] == NULL)
78291ab06eSStefan Wahren 		seq_puts(s, "empty");
79291ab06eSStefan Wahren 	else if (qca->txr.skb[qca->txr.tail])
80291ab06eSStefan Wahren 		seq_puts(s, "full");
81291ab06eSStefan Wahren 	else
82291ab06eSStefan Wahren 		seq_puts(s, "in use");
83291ab06eSStefan Wahren 
84291ab06eSStefan Wahren 	seq_puts(s, "\n");
85291ab06eSStefan Wahren 
86291ab06eSStefan Wahren 	seq_printf(s, "TX ring size     : %u\n",
87291ab06eSStefan Wahren 		   qca->txr.size);
88291ab06eSStefan Wahren 
89291ab06eSStefan Wahren 	seq_printf(s, "Sync state       : %u (",
90291ab06eSStefan Wahren 		   (unsigned int)qca->sync);
91291ab06eSStefan Wahren 	switch (qca->sync) {
92291ab06eSStefan Wahren 	case QCASPI_SYNC_UNKNOWN:
93291ab06eSStefan Wahren 		seq_puts(s, "QCASPI_SYNC_UNKNOWN");
94291ab06eSStefan Wahren 		break;
95291ab06eSStefan Wahren 	case QCASPI_SYNC_RESET:
96291ab06eSStefan Wahren 		seq_puts(s, "QCASPI_SYNC_RESET");
97291ab06eSStefan Wahren 		break;
98291ab06eSStefan Wahren 	case QCASPI_SYNC_READY:
99291ab06eSStefan Wahren 		seq_puts(s, "QCASPI_SYNC_READY");
100291ab06eSStefan Wahren 		break;
101291ab06eSStefan Wahren 	default:
102291ab06eSStefan Wahren 		seq_puts(s, "INVALID");
103291ab06eSStefan Wahren 		break;
104291ab06eSStefan Wahren 	}
105291ab06eSStefan Wahren 	seq_puts(s, ")\n");
106291ab06eSStefan Wahren 
107291ab06eSStefan Wahren 	seq_printf(s, "IRQ              : %d\n",
108291ab06eSStefan Wahren 		   qca->spi_dev->irq);
109291ab06eSStefan Wahren 	seq_printf(s, "INTR REQ         : %u\n",
110291ab06eSStefan Wahren 		   qca->intr_req);
111291ab06eSStefan Wahren 	seq_printf(s, "INTR SVC         : %u\n",
112291ab06eSStefan Wahren 		   qca->intr_svc);
113291ab06eSStefan Wahren 
114291ab06eSStefan Wahren 	seq_printf(s, "SPI max speed    : %lu\n",
115291ab06eSStefan Wahren 		   (unsigned long)qca->spi_dev->max_speed_hz);
116291ab06eSStefan Wahren 	seq_printf(s, "SPI mode         : %x\n",
117291ab06eSStefan Wahren 		   qca->spi_dev->mode);
118291ab06eSStefan Wahren 	seq_printf(s, "SPI chip select  : %u\n",
119291ab06eSStefan Wahren 		   (unsigned int)qca->spi_dev->chip_select);
120291ab06eSStefan Wahren 	seq_printf(s, "SPI legacy mode  : %u\n",
121291ab06eSStefan Wahren 		   (unsigned int)qca->legacy_mode);
122291ab06eSStefan Wahren 	seq_printf(s, "SPI burst length : %u\n",
123291ab06eSStefan Wahren 		   (unsigned int)qca->burst_len);
124291ab06eSStefan Wahren 
125291ab06eSStefan Wahren 	return 0;
126291ab06eSStefan Wahren }
127291ab06eSStefan Wahren 
128291ab06eSStefan Wahren static int
129291ab06eSStefan Wahren qcaspi_info_open(struct inode *inode, struct file *file)
130291ab06eSStefan Wahren {
131291ab06eSStefan Wahren 	return single_open(file, qcaspi_info_show, inode->i_private);
132291ab06eSStefan Wahren }
133291ab06eSStefan Wahren 
134291ab06eSStefan Wahren static const struct file_operations qcaspi_info_ops = {
135291ab06eSStefan Wahren 	.open = qcaspi_info_open,
136291ab06eSStefan Wahren 	.read = seq_read,
137291ab06eSStefan Wahren 	.llseek = seq_lseek,
138291ab06eSStefan Wahren 	.release = single_release,
139291ab06eSStefan Wahren };
140291ab06eSStefan Wahren 
141291ab06eSStefan Wahren void
142291ab06eSStefan Wahren qcaspi_init_device_debugfs(struct qcaspi *qca)
143291ab06eSStefan Wahren {
144291ab06eSStefan Wahren 	struct dentry *device_root;
145291ab06eSStefan Wahren 
146291ab06eSStefan Wahren 	device_root = debugfs_create_dir(dev_name(&qca->net_dev->dev), NULL);
147291ab06eSStefan Wahren 	qca->device_root = device_root;
148291ab06eSStefan Wahren 
149291ab06eSStefan Wahren 	if (IS_ERR(device_root) || !device_root) {
150291ab06eSStefan Wahren 		pr_warn("failed to create debugfs directory for %s\n",
151291ab06eSStefan Wahren 			dev_name(&qca->net_dev->dev));
152291ab06eSStefan Wahren 		return;
153291ab06eSStefan Wahren 	}
154d3757ba4SJoe Perches 	debugfs_create_file("info", S_IFREG | 0444, device_root, qca,
155291ab06eSStefan Wahren 			    &qcaspi_info_ops);
156291ab06eSStefan Wahren }
157291ab06eSStefan Wahren 
158291ab06eSStefan Wahren void
159291ab06eSStefan Wahren qcaspi_remove_device_debugfs(struct qcaspi *qca)
160291ab06eSStefan Wahren {
161291ab06eSStefan Wahren 	debugfs_remove_recursive(qca->device_root);
162291ab06eSStefan Wahren }
163291ab06eSStefan Wahren 
164291ab06eSStefan Wahren #else /* CONFIG_DEBUG_FS */
165291ab06eSStefan Wahren 
166291ab06eSStefan Wahren void
167291ab06eSStefan Wahren qcaspi_init_device_debugfs(struct qcaspi *qca)
168291ab06eSStefan Wahren {
169291ab06eSStefan Wahren }
170291ab06eSStefan Wahren 
171291ab06eSStefan Wahren void
172291ab06eSStefan Wahren qcaspi_remove_device_debugfs(struct qcaspi *qca)
173291ab06eSStefan Wahren {
174291ab06eSStefan Wahren }
175291ab06eSStefan Wahren 
176291ab06eSStefan Wahren #endif
177291ab06eSStefan Wahren 
178291ab06eSStefan Wahren static void
179291ab06eSStefan Wahren qcaspi_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *p)
180291ab06eSStefan Wahren {
181291ab06eSStefan Wahren 	struct qcaspi *qca = netdev_priv(dev);
182291ab06eSStefan Wahren 
183291ab06eSStefan Wahren 	strlcpy(p->driver, QCASPI_DRV_NAME, sizeof(p->driver));
184291ab06eSStefan Wahren 	strlcpy(p->version, QCASPI_DRV_VERSION, sizeof(p->version));
185291ab06eSStefan Wahren 	strlcpy(p->fw_version, "QCA7000", sizeof(p->fw_version));
186291ab06eSStefan Wahren 	strlcpy(p->bus_info, dev_name(&qca->spi_dev->dev),
187291ab06eSStefan Wahren 		sizeof(p->bus_info));
188291ab06eSStefan Wahren }
189291ab06eSStefan Wahren 
190291ab06eSStefan Wahren static int
1912cf8a897SPhilippe Reynes qcaspi_get_link_ksettings(struct net_device *dev,
1922cf8a897SPhilippe Reynes 			  struct ethtool_link_ksettings *cmd)
193291ab06eSStefan Wahren {
1942cf8a897SPhilippe Reynes 	ethtool_link_ksettings_zero_link_mode(cmd, supported);
1952cf8a897SPhilippe Reynes 	ethtool_link_ksettings_add_link_mode(cmd, supported, 10baseT_Half);
1962cf8a897SPhilippe Reynes 
1972cf8a897SPhilippe Reynes 	cmd->base.speed = SPEED_10;
1982cf8a897SPhilippe Reynes 	cmd->base.duplex = DUPLEX_HALF;
1992cf8a897SPhilippe Reynes 	cmd->base.port = PORT_OTHER;
2002cf8a897SPhilippe Reynes 	cmd->base.autoneg = AUTONEG_DISABLE;
201291ab06eSStefan Wahren 
202291ab06eSStefan Wahren 	return 0;
203291ab06eSStefan Wahren }
204291ab06eSStefan Wahren 
205291ab06eSStefan Wahren static void
206291ab06eSStefan Wahren qcaspi_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *estats, u64 *data)
207291ab06eSStefan Wahren {
208291ab06eSStefan Wahren 	struct qcaspi *qca = netdev_priv(dev);
209291ab06eSStefan Wahren 	struct qcaspi_stats *st = &qca->stats;
210291ab06eSStefan Wahren 
211291ab06eSStefan Wahren 	memcpy(data, st, ARRAY_SIZE(qcaspi_gstrings_stats) * sizeof(u64));
212291ab06eSStefan Wahren }
213291ab06eSStefan Wahren 
214291ab06eSStefan Wahren static void
215291ab06eSStefan Wahren qcaspi_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
216291ab06eSStefan Wahren {
217291ab06eSStefan Wahren 	switch (stringset) {
218291ab06eSStefan Wahren 	case ETH_SS_STATS:
219291ab06eSStefan Wahren 		memcpy(buf, &qcaspi_gstrings_stats,
220291ab06eSStefan Wahren 		       sizeof(qcaspi_gstrings_stats));
221291ab06eSStefan Wahren 		break;
222291ab06eSStefan Wahren 	default:
223291ab06eSStefan Wahren 		WARN_ON(1);
224291ab06eSStefan Wahren 		break;
225291ab06eSStefan Wahren 	}
226291ab06eSStefan Wahren }
227291ab06eSStefan Wahren 
228291ab06eSStefan Wahren static int
229291ab06eSStefan Wahren qcaspi_get_sset_count(struct net_device *dev, int sset)
230291ab06eSStefan Wahren {
231291ab06eSStefan Wahren 	switch (sset) {
232291ab06eSStefan Wahren 	case ETH_SS_STATS:
233291ab06eSStefan Wahren 		return ARRAY_SIZE(qcaspi_gstrings_stats);
234291ab06eSStefan Wahren 	default:
235291ab06eSStefan Wahren 		return -EINVAL;
236291ab06eSStefan Wahren 	}
237291ab06eSStefan Wahren }
238291ab06eSStefan Wahren 
239291ab06eSStefan Wahren static int
240291ab06eSStefan Wahren qcaspi_get_regs_len(struct net_device *dev)
241291ab06eSStefan Wahren {
242291ab06eSStefan Wahren 	return sizeof(u32) * QCASPI_MAX_REGS;
243291ab06eSStefan Wahren }
244291ab06eSStefan Wahren 
245291ab06eSStefan Wahren static void
246291ab06eSStefan Wahren qcaspi_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p)
247291ab06eSStefan Wahren {
248291ab06eSStefan Wahren 	struct qcaspi *qca = netdev_priv(dev);
249291ab06eSStefan Wahren 	u32 *regs_buff = p;
250291ab06eSStefan Wahren 	unsigned int i;
251291ab06eSStefan Wahren 
252291ab06eSStefan Wahren 	regs->version = 1;
253291ab06eSStefan Wahren 	memset(regs_buff, 0, sizeof(u32) * QCASPI_MAX_REGS);
254291ab06eSStefan Wahren 
255291ab06eSStefan Wahren 	for (i = 0; i < ARRAY_SIZE(qcaspi_spi_regs); i++) {
256291ab06eSStefan Wahren 		u16 offset, value;
257291ab06eSStefan Wahren 
258291ab06eSStefan Wahren 		qcaspi_read_register(qca, qcaspi_spi_regs[i], &value);
259291ab06eSStefan Wahren 		offset = qcaspi_spi_regs[i] >> 8;
260291ab06eSStefan Wahren 		regs_buff[offset] = value;
261291ab06eSStefan Wahren 	}
262291ab06eSStefan Wahren }
263291ab06eSStefan Wahren 
264291ab06eSStefan Wahren static void
265291ab06eSStefan Wahren qcaspi_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ring)
266291ab06eSStefan Wahren {
267291ab06eSStefan Wahren 	struct qcaspi *qca = netdev_priv(dev);
268291ab06eSStefan Wahren 
269291ab06eSStefan Wahren 	ring->rx_max_pending = 4;
270291ab06eSStefan Wahren 	ring->tx_max_pending = TX_RING_MAX_LEN;
271291ab06eSStefan Wahren 	ring->rx_pending = 4;
272291ab06eSStefan Wahren 	ring->tx_pending = qca->txr.count;
273291ab06eSStefan Wahren }
274291ab06eSStefan Wahren 
275291ab06eSStefan Wahren static int
276291ab06eSStefan Wahren qcaspi_set_ringparam(struct net_device *dev, struct ethtool_ringparam *ring)
277291ab06eSStefan Wahren {
2780324e75dSStefan Wahren 	const struct net_device_ops *ops = dev->netdev_ops;
279291ab06eSStefan Wahren 	struct qcaspi *qca = netdev_priv(dev);
280291ab06eSStefan Wahren 
281291ab06eSStefan Wahren 	if ((ring->rx_pending) ||
282291ab06eSStefan Wahren 	    (ring->rx_mini_pending) ||
283291ab06eSStefan Wahren 	    (ring->rx_jumbo_pending))
284291ab06eSStefan Wahren 		return -EINVAL;
285291ab06eSStefan Wahren 
286291ab06eSStefan Wahren 	if (netif_running(dev))
2870324e75dSStefan Wahren 		ops->ndo_stop(dev);
288291ab06eSStefan Wahren 
289291ab06eSStefan Wahren 	qca->txr.count = max_t(u32, ring->tx_pending, TX_RING_MIN_LEN);
290291ab06eSStefan Wahren 	qca->txr.count = min_t(u16, qca->txr.count, TX_RING_MAX_LEN);
291291ab06eSStefan Wahren 
292291ab06eSStefan Wahren 	if (netif_running(dev))
2930324e75dSStefan Wahren 		ops->ndo_open(dev);
294291ab06eSStefan Wahren 
295291ab06eSStefan Wahren 	return 0;
296291ab06eSStefan Wahren }
297291ab06eSStefan Wahren 
298291ab06eSStefan Wahren static const struct ethtool_ops qcaspi_ethtool_ops = {
299291ab06eSStefan Wahren 	.get_drvinfo = qcaspi_get_drvinfo,
300291ab06eSStefan Wahren 	.get_link = ethtool_op_get_link,
301291ab06eSStefan Wahren 	.get_ethtool_stats = qcaspi_get_ethtool_stats,
302291ab06eSStefan Wahren 	.get_strings = qcaspi_get_strings,
303291ab06eSStefan Wahren 	.get_sset_count = qcaspi_get_sset_count,
304291ab06eSStefan Wahren 	.get_regs_len = qcaspi_get_regs_len,
305291ab06eSStefan Wahren 	.get_regs = qcaspi_get_regs,
306291ab06eSStefan Wahren 	.get_ringparam = qcaspi_get_ringparam,
307291ab06eSStefan Wahren 	.set_ringparam = qcaspi_set_ringparam,
3082cf8a897SPhilippe Reynes 	.get_link_ksettings = qcaspi_get_link_ksettings,
309291ab06eSStefan Wahren };
310291ab06eSStefan Wahren 
311291ab06eSStefan Wahren void qcaspi_set_ethtool_ops(struct net_device *dev)
312291ab06eSStefan Wahren {
313291ab06eSStefan Wahren 	dev->ethtool_ops = &qcaspi_ethtool_ops;
314291ab06eSStefan Wahren }
315