diff options
Diffstat (limited to 'drivers/net/ieee802154/at86rf230.c')
-rw-r--r-- | drivers/net/ieee802154/at86rf230.c | 195 |
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; |