summaryrefslogtreecommitdiff
path: root/drivers/net/ieee802154/at86rf230.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ieee802154/at86rf230.c')
-rw-r--r--drivers/net/ieee802154/at86rf230.c195
1 files changed, 148 insertions, 47 deletions
diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c
index 6422caa..b8b0628 100644
--- a/drivers/net/ieee802154/at86rf230.c
+++ b/drivers/net/ieee802154/at86rf230.c
@@ -31,6 +31,7 @@
#include <linux/skbuff.h>
#include <linux/of_gpio.h>
#include <linux/ieee802154.h>
+#include <linux/debugfs.h>
#include <net/mac802154.h>
#include <net/cfg802154.h>
@@ -83,6 +84,15 @@ struct at86rf230_state_change {
bool irq_enable;
};
+struct at86rf230_trac {
+ u64 success;
+ u64 success_data_pending;
+ u64 success_wait_for_ack;
+ u64 channel_access_failure;
+ u64 no_ack;
+ u64 invalid;
+};
+
struct at86rf230_local {
struct spi_device *spi;
@@ -103,6 +113,8 @@ struct at86rf230_local {
u8 tx_retry;
struct sk_buff *tx_skb;
struct at86rf230_state_change tx;
+
+ struct at86rf230_trac trac;
};
#define AT86RF2XX_NUMREGS 0x3F
@@ -377,14 +389,6 @@ at86rf230_async_read_reg(struct at86rf230_local *lp, const u8 reg,
}
}
-static inline u8 at86rf230_state_to_force(u8 state)
-{
- if (state == STATE_TX_ON)
- return STATE_FORCE_TX_ON;
- else
- return STATE_FORCE_TRX_OFF;
-}
-
static void
at86rf230_async_state_assert(void *context)
{
@@ -426,7 +430,7 @@ at86rf230_async_state_assert(void *context)
u8 state = ctx->to_state;
if (lp->tx_retry >= AT86RF2XX_MAX_TX_RETRIES)
- state = at86rf230_state_to_force(state);
+ state = STATE_FORCE_TRX_OFF;
lp->tx_retry++;
at86rf230_async_state_change(lp, ctx, state,
@@ -667,28 +671,34 @@ at86rf230_tx_trac_check(void *context)
{
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
- const u8 *buf = ctx->buf;
- const u8 trac = (buf[1] & 0xe0) >> 5;
- /* If trac status is different than zero we need to do a state change
- * to STATE_FORCE_TRX_OFF then STATE_RX_AACK_ON to recover the
- * transceiver.
- */
- if (trac)
- at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF,
- at86rf230_tx_on, true);
- else
- at86rf230_tx_on(context);
-}
+ if (IS_ENABLED(CONFIG_IEEE802154_AT86RF230_DEBUGFS)) {
+ u8 trac = TRAC_MASK(ctx->buf[1]);
-static void
-at86rf230_tx_trac_status(void *context)
-{
- struct at86rf230_state_change *ctx = context;
- struct at86rf230_local *lp = ctx->lp;
+ switch (trac) {
+ case TRAC_SUCCESS:
+ lp->trac.success++;
+ break;
+ case TRAC_SUCCESS_DATA_PENDING:
+ lp->trac.success_data_pending++;
+ break;
+ case TRAC_CHANNEL_ACCESS_FAILURE:
+ lp->trac.channel_access_failure++;
+ break;
+ case TRAC_NO_ACK:
+ lp->trac.no_ack++;
+ break;
+ case TRAC_INVALID:
+ lp->trac.invalid++;
+ break;
+ default:
+ WARN_ONCE(1, "received tx trac status %d\n", trac);
+ break;
+ }
+ }
- at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx,
- at86rf230_tx_trac_check, true);
+ at86rf230_async_state_change(lp, &lp->irq, STATE_TX_ON,
+ at86rf230_tx_on, true);
}
static void
@@ -723,13 +733,32 @@ at86rf230_rx_read_frame_complete(void *context)
}
static void
-at86rf230_rx_read_frame(void *context)
+at86rf230_rx_trac_check(void *context)
{
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
u8 *buf = ctx->buf;
int rc;
+ if (IS_ENABLED(CONFIG_IEEE802154_AT86RF230_DEBUGFS)) {
+ u8 trac = TRAC_MASK(buf[1]);
+
+ switch (trac) {
+ case TRAC_SUCCESS:
+ lp->trac.success++;
+ break;
+ case TRAC_SUCCESS_WAIT_FOR_ACK:
+ lp->trac.success_wait_for_ack++;
+ break;
+ case TRAC_INVALID:
+ lp->trac.invalid++;
+ break;
+ default:
+ WARN_ONCE(1, "received rx trac status %d\n", trac);
+ break;
+ }
+ }
+
buf[0] = CMD_FB;
ctx->trx.len = AT86RF2XX_MAX_BUF;
ctx->msg.complete = at86rf230_rx_read_frame_complete;
@@ -742,26 +771,12 @@ at86rf230_rx_read_frame(void *context)
}
static void
-at86rf230_rx_trac_check(void *context)
-{
- /* Possible check on trac status here. This could be useful to make
- * some stats why receive is failed. Not used at the moment, but it's
- * maybe timing relevant. Datasheet doesn't say anything about this.
- * The programming guide say do it so.
- */
-
- at86rf230_rx_read_frame(context);
-}
-
-static void
at86rf230_irq_trx_end(struct at86rf230_local *lp)
{
if (lp->is_tx) {
lp->is_tx = 0;
- at86rf230_async_state_change(lp, &lp->irq,
- STATE_FORCE_TX_ON,
- at86rf230_tx_trac_status,
- true);
+ at86rf230_async_read_reg(lp, RG_TRX_STATE, &lp->irq,
+ at86rf230_tx_trac_check, true);
} else {
at86rf230_async_read_reg(lp, RG_TRX_STATE, &lp->irq,
at86rf230_rx_trac_check, true);
@@ -920,6 +935,10 @@ at86rf230_start(struct ieee802154_hw *hw)
{
struct at86rf230_local *lp = hw->priv;
+ /* reset trac stats on start */
+ if (IS_ENABLED(CONFIG_IEEE802154_AT86RF230_DEBUGFS))
+ memset(&lp->trac, 0, sizeof(struct at86rf230_trac));
+
at86rf230_awake(lp);
enable_irq(lp->spi->irq);
@@ -1357,7 +1376,7 @@ static int at86rf230_hw_init(struct at86rf230_local *lp, u8 xtal_trim)
if (irq_type == IRQ_TYPE_EDGE_RISING ||
irq_type == IRQ_TYPE_EDGE_FALLING)
dev_warn(&lp->spi->dev,
- "Using edge triggered irq's are not recommended!\n");
+ "Using edge triggered irq's are not recommended, because it can cause races and result in a non-functional driver!\n");
if (irq_type == IRQ_TYPE_EDGE_FALLING ||
irq_type == IRQ_TYPE_LEVEL_LOW)
irq_pol = IRQ_ACTIVE_LOW;
@@ -1620,6 +1639,81 @@ at86rf230_setup_spi_messages(struct at86rf230_local *lp)
lp->tx.timer.function = at86rf230_async_state_timer;
}
+#ifdef CONFIG_IEEE802154_AT86RF230_DEBUGFS
+static struct dentry *at86rf230_debugfs_root;
+
+static int at86rf230_stats_show(struct seq_file *file, void *offset)
+{
+ struct at86rf230_local *lp = file->private;
+ int ret;
+
+ ret = seq_printf(file, "SUCCESS:\t\t%8llu\n", lp->trac.success);
+ if (ret < 0)
+ return ret;
+
+ ret = seq_printf(file, "SUCCESS_DATA_PENDING:\t%8llu\n",
+ lp->trac.success_data_pending);
+ if (ret < 0)
+ return ret;
+
+ ret = seq_printf(file, "SUCCESS_WAIT_FOR_ACK:\t%8llu\n",
+ lp->trac.success_wait_for_ack);
+ if (ret < 0)
+ return ret;
+
+ ret = seq_printf(file, "CHANNEL_ACCESS_FAILURE:\t%8llu\n",
+ lp->trac.channel_access_failure);
+ if (ret < 0)
+ return ret;
+
+ ret = seq_printf(file, "NO_ACK:\t\t\t%8llu\n", lp->trac.no_ack);
+ if (ret < 0)
+ return ret;
+
+ return seq_printf(file, "INVALID:\t\t%8llu\n", lp->trac.invalid);
+}
+
+static int at86rf230_stats_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, at86rf230_stats_show, inode->i_private);
+}
+
+static const struct file_operations at86rf230_stats_fops = {
+ .open = at86rf230_stats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int at86rf230_debugfs_init(struct at86rf230_local *lp)
+{
+ char debugfs_dir_name[DNAME_INLINE_LEN + 1] = "at86rf230-";
+ struct dentry *stats;
+
+ strncat(debugfs_dir_name, dev_name(&lp->spi->dev), DNAME_INLINE_LEN);
+
+ at86rf230_debugfs_root = debugfs_create_dir(debugfs_dir_name, NULL);
+ if (!at86rf230_debugfs_root)
+ return -ENOMEM;
+
+ stats = debugfs_create_file("trac_stats", S_IRUGO,
+ at86rf230_debugfs_root, lp,
+ &at86rf230_stats_fops);
+ if (!stats)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void at86rf230_debugfs_remove(void)
+{
+ debugfs_remove_recursive(at86rf230_debugfs_root);
+}
+#else
+static int at86rf230_debugfs_init(struct at86rf230_local *lp) { return 0; }
+static void at86rf230_debugfs_remove(void) { }
+#endif
+
static int at86rf230_probe(struct spi_device *spi)
{
struct ieee802154_hw *hw;
@@ -1715,12 +1809,18 @@ static int at86rf230_probe(struct spi_device *spi)
/* going into sleep by default */
at86rf230_sleep(lp);
- rc = ieee802154_register_hw(lp->hw);
+ rc = at86rf230_debugfs_init(lp);
if (rc)
goto free_dev;
+ rc = ieee802154_register_hw(lp->hw);
+ if (rc)
+ goto free_debugfs;
+
return rc;
+free_debugfs:
+ at86rf230_debugfs_remove();
free_dev:
ieee802154_free_hw(lp->hw);
@@ -1735,6 +1835,7 @@ static int at86rf230_remove(struct spi_device *spi)
at86rf230_write_subreg(lp, SR_IRQ_MASK, 0);
ieee802154_unregister_hw(lp->hw);
ieee802154_free_hw(lp->hw);
+ at86rf230_debugfs_remove();
dev_dbg(&spi->dev, "unregistered at86rf230\n");
return 0;