18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci *   Copyright (c) 2011, 2012, Qualcomm Atheros Communications Inc.
38c2ecf20Sopenharmony_ci *   Copyright (c) 2014, I2SE GmbH
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *   Permission to use, copy, modify, and/or distribute this software
68c2ecf20Sopenharmony_ci *   for any purpose with or without fee is hereby granted, provided
78c2ecf20Sopenharmony_ci *   that the above copyright notice and this permission notice appear
88c2ecf20Sopenharmony_ci *   in all copies.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
118c2ecf20Sopenharmony_ci *   WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
128c2ecf20Sopenharmony_ci *   WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
138c2ecf20Sopenharmony_ci *   THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
148c2ecf20Sopenharmony_ci *   CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
158c2ecf20Sopenharmony_ci *   LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
168c2ecf20Sopenharmony_ci *   NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
178c2ecf20Sopenharmony_ci *   CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/*   This file contains debugging routines for use in the QCA7K driver.
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
248c2ecf20Sopenharmony_ci#include <linux/ethtool.h>
258c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
268c2ecf20Sopenharmony_ci#include <linux/types.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include "qca_7k.h"
298c2ecf20Sopenharmony_ci#include "qca_debug.h"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define QCASPI_MAX_REGS 0x20
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define QCASPI_RX_MAX_FRAMES 4
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic const u16 qcaspi_spi_regs[] = {
368c2ecf20Sopenharmony_ci	SPI_REG_BFR_SIZE,
378c2ecf20Sopenharmony_ci	SPI_REG_WRBUF_SPC_AVA,
388c2ecf20Sopenharmony_ci	SPI_REG_RDBUF_BYTE_AVA,
398c2ecf20Sopenharmony_ci	SPI_REG_SPI_CONFIG,
408c2ecf20Sopenharmony_ci	SPI_REG_SPI_STATUS,
418c2ecf20Sopenharmony_ci	SPI_REG_INTR_CAUSE,
428c2ecf20Sopenharmony_ci	SPI_REG_INTR_ENABLE,
438c2ecf20Sopenharmony_ci	SPI_REG_RDBUF_WATERMARK,
448c2ecf20Sopenharmony_ci	SPI_REG_WRBUF_WATERMARK,
458c2ecf20Sopenharmony_ci	SPI_REG_SIGNATURE,
468c2ecf20Sopenharmony_ci	SPI_REG_ACTION_CTRL
478c2ecf20Sopenharmony_ci};
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* The order of these strings must match the order of the fields in
508c2ecf20Sopenharmony_ci * struct qcaspi_stats
518c2ecf20Sopenharmony_ci * See qca_spi.h
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_cistatic const char qcaspi_gstrings_stats[][ETH_GSTRING_LEN] = {
548c2ecf20Sopenharmony_ci	"Triggered resets",
558c2ecf20Sopenharmony_ci	"Device resets",
568c2ecf20Sopenharmony_ci	"Reset timeouts",
578c2ecf20Sopenharmony_ci	"Read errors",
588c2ecf20Sopenharmony_ci	"Write errors",
598c2ecf20Sopenharmony_ci	"Read buffer errors",
608c2ecf20Sopenharmony_ci	"Write buffer errors",
618c2ecf20Sopenharmony_ci	"Out of memory",
628c2ecf20Sopenharmony_ci	"Write buffer misses",
638c2ecf20Sopenharmony_ci	"Transmit ring full",
648c2ecf20Sopenharmony_ci	"SPI errors",
658c2ecf20Sopenharmony_ci	"Write verify errors",
668c2ecf20Sopenharmony_ci	"Buffer available errors",
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic int
728c2ecf20Sopenharmony_ciqcaspi_info_show(struct seq_file *s, void *what)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	struct qcaspi *qca = s->private;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	seq_printf(s, "RX buffer size   : %lu\n",
778c2ecf20Sopenharmony_ci		   (unsigned long)qca->buffer_size);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	seq_puts(s, "TX ring state    : ");
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (qca->txr.skb[qca->txr.head] == NULL)
828c2ecf20Sopenharmony_ci		seq_puts(s, "empty");
838c2ecf20Sopenharmony_ci	else if (qca->txr.skb[qca->txr.tail])
848c2ecf20Sopenharmony_ci		seq_puts(s, "full");
858c2ecf20Sopenharmony_ci	else
868c2ecf20Sopenharmony_ci		seq_puts(s, "in use");
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	seq_puts(s, "\n");
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	seq_printf(s, "TX ring size     : %u\n",
918c2ecf20Sopenharmony_ci		   qca->txr.size);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	seq_printf(s, "Sync state       : %u (",
948c2ecf20Sopenharmony_ci		   (unsigned int)qca->sync);
958c2ecf20Sopenharmony_ci	switch (qca->sync) {
968c2ecf20Sopenharmony_ci	case QCASPI_SYNC_UNKNOWN:
978c2ecf20Sopenharmony_ci		seq_puts(s, "QCASPI_SYNC_UNKNOWN");
988c2ecf20Sopenharmony_ci		break;
998c2ecf20Sopenharmony_ci	case QCASPI_SYNC_RESET:
1008c2ecf20Sopenharmony_ci		seq_puts(s, "QCASPI_SYNC_RESET");
1018c2ecf20Sopenharmony_ci		break;
1028c2ecf20Sopenharmony_ci	case QCASPI_SYNC_READY:
1038c2ecf20Sopenharmony_ci		seq_puts(s, "QCASPI_SYNC_READY");
1048c2ecf20Sopenharmony_ci		break;
1058c2ecf20Sopenharmony_ci	default:
1068c2ecf20Sopenharmony_ci		seq_puts(s, "INVALID");
1078c2ecf20Sopenharmony_ci		break;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci	seq_puts(s, ")\n");
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	seq_printf(s, "IRQ              : %d\n",
1128c2ecf20Sopenharmony_ci		   qca->spi_dev->irq);
1138c2ecf20Sopenharmony_ci	seq_printf(s, "INTR REQ         : %u\n",
1148c2ecf20Sopenharmony_ci		   qca->intr_req);
1158c2ecf20Sopenharmony_ci	seq_printf(s, "INTR SVC         : %u\n",
1168c2ecf20Sopenharmony_ci		   qca->intr_svc);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	seq_printf(s, "SPI max speed    : %lu\n",
1198c2ecf20Sopenharmony_ci		   (unsigned long)qca->spi_dev->max_speed_hz);
1208c2ecf20Sopenharmony_ci	seq_printf(s, "SPI mode         : %x\n",
1218c2ecf20Sopenharmony_ci		   qca->spi_dev->mode);
1228c2ecf20Sopenharmony_ci	seq_printf(s, "SPI chip select  : %u\n",
1238c2ecf20Sopenharmony_ci		   (unsigned int)qca->spi_dev->chip_select);
1248c2ecf20Sopenharmony_ci	seq_printf(s, "SPI legacy mode  : %u\n",
1258c2ecf20Sopenharmony_ci		   (unsigned int)qca->legacy_mode);
1268c2ecf20Sopenharmony_ci	seq_printf(s, "SPI burst length : %u\n",
1278c2ecf20Sopenharmony_ci		   (unsigned int)qca->burst_len);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	return 0;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(qcaspi_info);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_civoid
1348c2ecf20Sopenharmony_ciqcaspi_init_device_debugfs(struct qcaspi *qca)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	qca->device_root = debugfs_create_dir(dev_name(&qca->net_dev->dev),
1378c2ecf20Sopenharmony_ci					      NULL);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	debugfs_create_file("info", S_IFREG | 0444, qca->device_root, qca,
1408c2ecf20Sopenharmony_ci			    &qcaspi_info_fops);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_civoid
1448c2ecf20Sopenharmony_ciqcaspi_remove_device_debugfs(struct qcaspi *qca)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	debugfs_remove_recursive(qca->device_root);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci#else /* CONFIG_DEBUG_FS */
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_civoid
1528c2ecf20Sopenharmony_ciqcaspi_init_device_debugfs(struct qcaspi *qca)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_civoid
1578c2ecf20Sopenharmony_ciqcaspi_remove_device_debugfs(struct qcaspi *qca)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci#endif
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic void
1648c2ecf20Sopenharmony_ciqcaspi_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *p)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct qcaspi *qca = netdev_priv(dev);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	strlcpy(p->driver, QCASPI_DRV_NAME, sizeof(p->driver));
1698c2ecf20Sopenharmony_ci	strlcpy(p->version, QCASPI_DRV_VERSION, sizeof(p->version));
1708c2ecf20Sopenharmony_ci	strlcpy(p->fw_version, "QCA7000", sizeof(p->fw_version));
1718c2ecf20Sopenharmony_ci	strlcpy(p->bus_info, dev_name(&qca->spi_dev->dev),
1728c2ecf20Sopenharmony_ci		sizeof(p->bus_info));
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic int
1768c2ecf20Sopenharmony_ciqcaspi_get_link_ksettings(struct net_device *dev,
1778c2ecf20Sopenharmony_ci			  struct ethtool_link_ksettings *cmd)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	ethtool_link_ksettings_zero_link_mode(cmd, supported);
1808c2ecf20Sopenharmony_ci	ethtool_link_ksettings_add_link_mode(cmd, supported, 10baseT_Half);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	cmd->base.speed = SPEED_10;
1838c2ecf20Sopenharmony_ci	cmd->base.duplex = DUPLEX_HALF;
1848c2ecf20Sopenharmony_ci	cmd->base.port = PORT_OTHER;
1858c2ecf20Sopenharmony_ci	cmd->base.autoneg = AUTONEG_DISABLE;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	return 0;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic void
1918c2ecf20Sopenharmony_ciqcaspi_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *estats, u64 *data)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	struct qcaspi *qca = netdev_priv(dev);
1948c2ecf20Sopenharmony_ci	struct qcaspi_stats *st = &qca->stats;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	memcpy(data, st, ARRAY_SIZE(qcaspi_gstrings_stats) * sizeof(u64));
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic void
2008c2ecf20Sopenharmony_ciqcaspi_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	switch (stringset) {
2038c2ecf20Sopenharmony_ci	case ETH_SS_STATS:
2048c2ecf20Sopenharmony_ci		memcpy(buf, &qcaspi_gstrings_stats,
2058c2ecf20Sopenharmony_ci		       sizeof(qcaspi_gstrings_stats));
2068c2ecf20Sopenharmony_ci		break;
2078c2ecf20Sopenharmony_ci	default:
2088c2ecf20Sopenharmony_ci		WARN_ON(1);
2098c2ecf20Sopenharmony_ci		break;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic int
2148c2ecf20Sopenharmony_ciqcaspi_get_sset_count(struct net_device *dev, int sset)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	switch (sset) {
2178c2ecf20Sopenharmony_ci	case ETH_SS_STATS:
2188c2ecf20Sopenharmony_ci		return ARRAY_SIZE(qcaspi_gstrings_stats);
2198c2ecf20Sopenharmony_ci	default:
2208c2ecf20Sopenharmony_ci		return -EINVAL;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic int
2258c2ecf20Sopenharmony_ciqcaspi_get_regs_len(struct net_device *dev)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	return sizeof(u32) * QCASPI_MAX_REGS;
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic void
2318c2ecf20Sopenharmony_ciqcaspi_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	struct qcaspi *qca = netdev_priv(dev);
2348c2ecf20Sopenharmony_ci	u32 *regs_buff = p;
2358c2ecf20Sopenharmony_ci	unsigned int i;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	regs->version = 1;
2388c2ecf20Sopenharmony_ci	memset(regs_buff, 0, sizeof(u32) * QCASPI_MAX_REGS);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(qcaspi_spi_regs); i++) {
2418c2ecf20Sopenharmony_ci		u16 offset, value;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		qcaspi_read_register(qca, qcaspi_spi_regs[i], &value);
2448c2ecf20Sopenharmony_ci		offset = qcaspi_spi_regs[i] >> 8;
2458c2ecf20Sopenharmony_ci		regs_buff[offset] = value;
2468c2ecf20Sopenharmony_ci	}
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic void
2508c2ecf20Sopenharmony_ciqcaspi_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ring)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	struct qcaspi *qca = netdev_priv(dev);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	ring->rx_max_pending = QCASPI_RX_MAX_FRAMES;
2558c2ecf20Sopenharmony_ci	ring->tx_max_pending = TX_RING_MAX_LEN;
2568c2ecf20Sopenharmony_ci	ring->rx_pending = QCASPI_RX_MAX_FRAMES;
2578c2ecf20Sopenharmony_ci	ring->tx_pending = qca->txr.count;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic int
2618c2ecf20Sopenharmony_ciqcaspi_set_ringparam(struct net_device *dev, struct ethtool_ringparam *ring)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	struct qcaspi *qca = netdev_priv(dev);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	if (ring->rx_pending != QCASPI_RX_MAX_FRAMES ||
2668c2ecf20Sopenharmony_ci	    (ring->rx_mini_pending) ||
2678c2ecf20Sopenharmony_ci	    (ring->rx_jumbo_pending))
2688c2ecf20Sopenharmony_ci		return -EINVAL;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	if (qca->spi_thread)
2718c2ecf20Sopenharmony_ci		kthread_park(qca->spi_thread);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	qca->txr.count = max_t(u32, ring->tx_pending, TX_RING_MIN_LEN);
2748c2ecf20Sopenharmony_ci	qca->txr.count = min_t(u16, qca->txr.count, TX_RING_MAX_LEN);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	if (qca->spi_thread)
2778c2ecf20Sopenharmony_ci		kthread_unpark(qca->spi_thread);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	return 0;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic const struct ethtool_ops qcaspi_ethtool_ops = {
2838c2ecf20Sopenharmony_ci	.get_drvinfo = qcaspi_get_drvinfo,
2848c2ecf20Sopenharmony_ci	.get_link = ethtool_op_get_link,
2858c2ecf20Sopenharmony_ci	.get_ethtool_stats = qcaspi_get_ethtool_stats,
2868c2ecf20Sopenharmony_ci	.get_strings = qcaspi_get_strings,
2878c2ecf20Sopenharmony_ci	.get_sset_count = qcaspi_get_sset_count,
2888c2ecf20Sopenharmony_ci	.get_regs_len = qcaspi_get_regs_len,
2898c2ecf20Sopenharmony_ci	.get_regs = qcaspi_get_regs,
2908c2ecf20Sopenharmony_ci	.get_ringparam = qcaspi_get_ringparam,
2918c2ecf20Sopenharmony_ci	.set_ringparam = qcaspi_set_ringparam,
2928c2ecf20Sopenharmony_ci	.get_link_ksettings = qcaspi_get_link_ksettings,
2938c2ecf20Sopenharmony_ci};
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_civoid qcaspi_set_ethtool_ops(struct net_device *dev)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	dev->ethtool_ops = &qcaspi_ethtool_ops;
2988c2ecf20Sopenharmony_ci}
299