summaryrefslogtreecommitdiff
path: root/drivers/net/ieee802154
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2016-01-20 14:01:31 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2016-01-20 14:01:31 -0300
commitb4b7ff4b08e691656c9d77c758fc355833128ac0 (patch)
tree82fcb00e6b918026dc9f2d1f05ed8eee83874cc0 /drivers/net/ieee802154
parent35acfa0fc609f2a2cd95cef4a6a9c3a5c38f1778 (diff)
Linux-libre 4.4-gnupck-4.4-gnu
Diffstat (limited to 'drivers/net/ieee802154')
-rw-r--r--drivers/net/ieee802154/Kconfig8
-rw-r--r--drivers/net/ieee802154/at86rf230.c370
-rw-r--r--drivers/net/ieee802154/at86rf230.h8
-rw-r--r--drivers/net/ieee802154/atusb.c13
-rw-r--r--drivers/net/ieee802154/cc2520.c1
-rw-r--r--drivers/net/ieee802154/mrf24j40.c1425
6 files changed, 1235 insertions, 590 deletions
diff --git a/drivers/net/ieee802154/Kconfig b/drivers/net/ieee802154/Kconfig
index 1dd5ab8e5..ce5f1a21e 100644
--- a/drivers/net/ieee802154/Kconfig
+++ b/drivers/net/ieee802154/Kconfig
@@ -32,10 +32,18 @@ config IEEE802154_AT86RF230
This driver can also be built as a module. To do so, say M here.
the module will be called 'at86rf230'.
+config IEEE802154_AT86RF230_DEBUGFS
+ depends on IEEE802154_AT86RF230
+ bool "AT86RF230 debugfs interface"
+ depends on DEBUG_FS
+ ---help---
+ This option compiles debugfs code for the at86rf230 driver.
+
config IEEE802154_MRF24J40
tristate "Microchip MRF24J40 transceiver driver"
depends on IEEE802154_DRIVERS && MAC802154
depends on SPI
+ select REGMAP_SPI
---help---
Say Y here to enable the MRF24J20 SPI 802.15.4 wireless
controller.
diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c
index 6422caac8..0fbbba7a0 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>
@@ -80,7 +81,16 @@ struct at86rf230_state_change {
u8 from_state;
u8 to_state;
- bool irq_enable;
+ bool free;
+};
+
+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 {
@@ -95,14 +105,14 @@ struct at86rf230_local {
struct completion state_complete;
struct at86rf230_state_change state;
- struct at86rf230_state_change irq;
-
unsigned long cal_timeout;
bool is_tx;
bool is_tx_from_off;
u8 tx_retry;
struct sk_buff *tx_skb;
struct at86rf230_state_change tx;
+
+ struct at86rf230_trac trac;
};
#define AT86RF2XX_NUMREGS 0x3F
@@ -110,8 +120,7 @@ struct at86rf230_local {
static void
at86rf230_async_state_change(struct at86rf230_local *lp,
struct at86rf230_state_change *ctx,
- const u8 state, void (*complete)(void *context),
- const bool irq_enable);
+ const u8 state, void (*complete)(void *context));
static inline void
at86rf230_sleep(struct at86rf230_local *lp)
@@ -340,8 +349,10 @@ at86rf230_async_error_recover(void *context)
struct at86rf230_local *lp = ctx->lp;
lp->is_tx = 0;
- at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL, false);
+ at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL);
ieee802154_wake_queue(lp->hw);
+ if (ctx->free)
+ kfree(ctx);
}
static inline void
@@ -351,15 +362,14 @@ at86rf230_async_error(struct at86rf230_local *lp,
dev_err(&lp->spi->dev, "spi_async error %d\n", rc);
at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF,
- at86rf230_async_error_recover, false);
+ at86rf230_async_error_recover);
}
/* Generic function to get some register value in async mode */
static void
-at86rf230_async_read_reg(struct at86rf230_local *lp, const u8 reg,
+at86rf230_async_read_reg(struct at86rf230_local *lp, u8 reg,
struct at86rf230_state_change *ctx,
- void (*complete)(void *context),
- const bool irq_enable)
+ void (*complete)(void *context))
{
int rc;
@@ -367,22 +377,24 @@ at86rf230_async_read_reg(struct at86rf230_local *lp, const u8 reg,
tx_buf[0] = (reg & CMD_REG_MASK) | CMD_REG;
ctx->msg.complete = complete;
- ctx->irq_enable = irq_enable;
rc = spi_async(lp->spi, &ctx->msg);
- if (rc) {
- if (irq_enable)
- enable_irq(ctx->irq);
-
+ if (rc)
at86rf230_async_error(lp, ctx, rc);
- }
}
-static inline u8 at86rf230_state_to_force(u8 state)
+static void
+at86rf230_async_write_reg(struct at86rf230_local *lp, u8 reg, u8 val,
+ struct at86rf230_state_change *ctx,
+ void (*complete)(void *context))
{
- if (state == STATE_TX_ON)
- return STATE_FORCE_TX_ON;
- else
- return STATE_FORCE_TRX_OFF;
+ int rc;
+
+ ctx->buf[0] = (reg & CMD_REG_MASK) | CMD_REG | CMD_WRITE;
+ ctx->buf[1] = val;
+ ctx->msg.complete = complete;
+ rc = spi_async(lp->spi, &ctx->msg);
+ if (rc)
+ at86rf230_async_error(lp, ctx, rc);
}
static void
@@ -426,12 +438,11 @@ 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,
- ctx->complete,
- ctx->irq_enable);
+ ctx->complete);
return;
}
}
@@ -452,8 +463,7 @@ static enum hrtimer_restart at86rf230_async_state_timer(struct hrtimer *timer)
struct at86rf230_local *lp = ctx->lp;
at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
- at86rf230_async_state_assert,
- ctx->irq_enable);
+ at86rf230_async_state_assert);
return HRTIMER_NORESTART;
}
@@ -558,14 +568,12 @@ at86rf230_async_state_change_start(void *context)
struct at86rf230_local *lp = ctx->lp;
u8 *buf = ctx->buf;
const u8 trx_state = buf[1] & TRX_STATE_MASK;
- int rc;
/* Check for "possible" STATE_TRANSITION_IN_PROGRESS */
if (trx_state == STATE_TRANSITION_IN_PROGRESS) {
udelay(1);
at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
- at86rf230_async_state_change_start,
- ctx->irq_enable);
+ at86rf230_async_state_change_start);
return;
}
@@ -582,31 +590,20 @@ at86rf230_async_state_change_start(void *context)
/* Going into the next step for a state change which do a timing
* relevant delay.
*/
- buf[0] = (RG_TRX_STATE & CMD_REG_MASK) | CMD_REG | CMD_WRITE;
- buf[1] = ctx->to_state;
- ctx->msg.complete = at86rf230_async_state_delay;
- rc = spi_async(lp->spi, &ctx->msg);
- if (rc) {
- if (ctx->irq_enable)
- enable_irq(ctx->irq);
-
- at86rf230_async_error(lp, ctx, rc);
- }
+ at86rf230_async_write_reg(lp, RG_TRX_STATE, ctx->to_state, ctx,
+ at86rf230_async_state_delay);
}
static void
at86rf230_async_state_change(struct at86rf230_local *lp,
struct at86rf230_state_change *ctx,
- const u8 state, void (*complete)(void *context),
- const bool irq_enable)
+ const u8 state, void (*complete)(void *context))
{
/* Initialization for the state change context */
ctx->to_state = state;
ctx->complete = complete;
- ctx->irq_enable = irq_enable;
at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
- at86rf230_async_state_change_start,
- irq_enable);
+ at86rf230_async_state_change_start);
}
static void
@@ -628,8 +625,7 @@ at86rf230_sync_state_change(struct at86rf230_local *lp, unsigned int state)
unsigned long rc;
at86rf230_async_state_change(lp, &lp->state, state,
- at86rf230_sync_state_change_complete,
- false);
+ at86rf230_sync_state_change_complete);
rc = wait_for_completion_timeout(&lp->state_complete,
msecs_to_jiffies(100));
@@ -647,9 +643,8 @@ at86rf230_tx_complete(void *context)
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
- enable_irq(ctx->irq);
-
ieee802154_xmit_complete(lp->hw, lp->tx_skb, false);
+ kfree(ctx);
}
static void
@@ -659,7 +654,7 @@ at86rf230_tx_on(void *context)
struct at86rf230_local *lp = ctx->lp;
at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON,
- at86rf230_tx_complete, true);
+ at86rf230_tx_complete);
}
static void
@@ -667,28 +662,33 @@ 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, ctx, STATE_TX_ON, at86rf230_tx_on);
}
static void
@@ -696,7 +696,6 @@ at86rf230_rx_read_frame_complete(void *context)
{
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
- u8 rx_local_buf[AT86RF2XX_MAX_BUF];
const u8 *buf = ctx->buf;
struct sk_buff *skb;
u8 len, lqi;
@@ -708,63 +707,68 @@ at86rf230_rx_read_frame_complete(void *context)
}
lqi = buf[2 + len];
- memcpy(rx_local_buf, buf + 2, len);
- ctx->trx.len = 2;
- enable_irq(ctx->irq);
-
skb = dev_alloc_skb(IEEE802154_MTU);
if (!skb) {
dev_vdbg(&lp->spi->dev, "failed to allocate sk_buff\n");
+ kfree(ctx);
return;
}
- memcpy(skb_put(skb, len), rx_local_buf, len);
+ memcpy(skb_put(skb, len), buf + 2, len);
ieee802154_rx_irqsafe(lp->hw, skb, lqi);
+ kfree(ctx);
}
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;
rc = spi_async(lp->spi, &ctx->msg);
if (rc) {
ctx->trx.len = 2;
- enable_irq(ctx->irq);
at86rf230_async_error(lp, ctx, rc);
}
}
static void
-at86rf230_rx_trac_check(void *context)
+at86rf230_irq_trx_end(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);
-}
+ struct at86rf230_state_change *ctx = context;
+ struct at86rf230_local *lp = ctx->lp;
-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, ctx,
+ at86rf230_tx_trac_check);
} else {
- at86rf230_async_read_reg(lp, RG_TRX_STATE, &lp->irq,
- at86rf230_rx_trac_check, true);
+ at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx,
+ at86rf230_rx_trac_check);
}
}
@@ -774,32 +778,59 @@ at86rf230_irq_status(void *context)
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
const u8 *buf = ctx->buf;
- const u8 irq = buf[1];
+ u8 irq = buf[1];
+
+ enable_irq(lp->spi->irq);
if (irq & IRQ_TRX_END) {
- at86rf230_irq_trx_end(lp);
+ at86rf230_irq_trx_end(ctx);
} else {
- enable_irq(ctx->irq);
dev_err(&lp->spi->dev, "not supported irq %02x received\n",
irq);
+ kfree(ctx);
}
}
+static void
+at86rf230_setup_spi_messages(struct at86rf230_local *lp,
+ struct at86rf230_state_change *state)
+{
+ state->lp = lp;
+ state->irq = lp->spi->irq;
+ spi_message_init(&state->msg);
+ state->msg.context = state;
+ state->trx.len = 2;
+ state->trx.tx_buf = state->buf;
+ state->trx.rx_buf = state->buf;
+ spi_message_add_tail(&state->trx, &state->msg);
+ hrtimer_init(&state->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ state->timer.function = at86rf230_async_state_timer;
+}
+
static irqreturn_t at86rf230_isr(int irq, void *data)
{
struct at86rf230_local *lp = data;
- struct at86rf230_state_change *ctx = &lp->irq;
- u8 *buf = ctx->buf;
+ struct at86rf230_state_change *ctx;
int rc;
disable_irq_nosync(irq);
- buf[0] = (RG_IRQ_STATUS & CMD_REG_MASK) | CMD_REG;
+ ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC);
+ if (!ctx) {
+ enable_irq(irq);
+ return IRQ_NONE;
+ }
+
+ at86rf230_setup_spi_messages(lp, ctx);
+ /* tell on error handling to free ctx */
+ ctx->free = true;
+
+ ctx->buf[0] = (RG_IRQ_STATUS & CMD_REG_MASK) | CMD_REG;
ctx->msg.complete = at86rf230_irq_status;
rc = spi_async(lp->spi, &ctx->msg);
if (rc) {
- enable_irq(irq);
at86rf230_async_error(lp, ctx, rc);
+ enable_irq(irq);
return IRQ_NONE;
}
@@ -811,21 +842,14 @@ at86rf230_write_frame_complete(void *context)
{
struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp;
- u8 *buf = ctx->buf;
- int rc;
ctx->trx.len = 2;
- if (gpio_is_valid(lp->slp_tr)) {
+ if (gpio_is_valid(lp->slp_tr))
at86rf230_slp_tr_rising_edge(lp);
- } else {
- buf[0] = (RG_TRX_STATE & CMD_REG_MASK) | CMD_REG | CMD_WRITE;
- buf[1] = STATE_BUSY_TX;
- ctx->msg.complete = NULL;
- rc = spi_async(lp->spi, &ctx->msg);
- if (rc)
- at86rf230_async_error(lp, ctx, rc);
- }
+ else
+ at86rf230_async_write_reg(lp, RG_TRX_STATE, STATE_BUSY_TX, ctx,
+ NULL);
}
static void
@@ -858,7 +882,7 @@ at86rf230_xmit_tx_on(void *context)
struct at86rf230_local *lp = ctx->lp;
at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON,
- at86rf230_write_frame, false);
+ at86rf230_write_frame);
}
static void
@@ -871,12 +895,10 @@ at86rf230_xmit_start(void *context)
if (lp->is_tx_from_off) {
lp->is_tx_from_off = false;
at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON,
- at86rf230_write_frame,
- false);
+ at86rf230_write_frame);
} else {
at86rf230_async_state_change(lp, ctx, STATE_TX_ON,
- at86rf230_xmit_tx_on,
- false);
+ at86rf230_xmit_tx_on);
}
}
@@ -899,7 +921,7 @@ at86rf230_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
if (time_is_before_jiffies(lp->cal_timeout)) {
lp->is_tx_from_off = true;
at86rf230_async_state_change(lp, ctx, STATE_TRX_OFF,
- at86rf230_xmit_start, false);
+ at86rf230_xmit_start);
} else {
at86rf230_xmit_start(ctx);
}
@@ -920,6 +942,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);
@@ -1354,10 +1380,6 @@ static int at86rf230_hw_init(struct at86rf230_local *lp, u8 xtal_trim)
return rc;
irq_type = irq_get_trigger_type(lp->spi->irq);
- 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");
if (irq_type == IRQ_TYPE_EDGE_FALLING ||
irq_type == IRQ_TYPE_LEVEL_LOW)
irq_pol = IRQ_ACTIVE_LOW;
@@ -1583,42 +1605,65 @@ not_supp:
return rc;
}
-static void
-at86rf230_setup_spi_messages(struct at86rf230_local *lp)
+#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;
+
+ seq_printf(file, "SUCCESS:\t\t%8llu\n", lp->trac.success);
+ seq_printf(file, "SUCCESS_DATA_PENDING:\t%8llu\n",
+ lp->trac.success_data_pending);
+ seq_printf(file, "SUCCESS_WAIT_FOR_ACK:\t%8llu\n",
+ lp->trac.success_wait_for_ack);
+ seq_printf(file, "CHANNEL_ACCESS_FAILURE:\t%8llu\n",
+ lp->trac.channel_access_failure);
+ seq_printf(file, "NO_ACK:\t\t\t%8llu\n", lp->trac.no_ack);
+ seq_printf(file, "INVALID:\t\t%8llu\n", lp->trac.invalid);
+ return 0;
+}
+
+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)
{
- lp->state.lp = lp;
- lp->state.irq = lp->spi->irq;
- spi_message_init(&lp->state.msg);
- lp->state.msg.context = &lp->state;
- lp->state.trx.len = 2;
- lp->state.trx.tx_buf = lp->state.buf;
- lp->state.trx.rx_buf = lp->state.buf;
- spi_message_add_tail(&lp->state.trx, &lp->state.msg);
- hrtimer_init(&lp->state.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- lp->state.timer.function = at86rf230_async_state_timer;
-
- lp->irq.lp = lp;
- lp->irq.irq = lp->spi->irq;
- spi_message_init(&lp->irq.msg);
- lp->irq.msg.context = &lp->irq;
- lp->irq.trx.len = 2;
- lp->irq.trx.tx_buf = lp->irq.buf;
- lp->irq.trx.rx_buf = lp->irq.buf;
- spi_message_add_tail(&lp->irq.trx, &lp->irq.msg);
- hrtimer_init(&lp->irq.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- lp->irq.timer.function = at86rf230_async_state_timer;
-
- lp->tx.lp = lp;
- lp->tx.irq = lp->spi->irq;
- spi_message_init(&lp->tx.msg);
- lp->tx.msg.context = &lp->tx;
- lp->tx.trx.len = 2;
- lp->tx.trx.tx_buf = lp->tx.buf;
- lp->tx.trx.rx_buf = lp->tx.buf;
- spi_message_add_tail(&lp->tx.trx, &lp->tx.msg);
- hrtimer_init(&lp->tx.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- lp->tx.timer.function = at86rf230_async_state_timer;
+ 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)
{
@@ -1681,7 +1726,8 @@ static int at86rf230_probe(struct spi_device *spi)
goto free_dev;
}
- at86rf230_setup_spi_messages(lp);
+ at86rf230_setup_spi_messages(lp, &lp->state);
+ at86rf230_setup_spi_messages(lp, &lp->tx);
rc = at86rf230_detect_device(lp);
if (rc < 0)
@@ -1715,12 +1761,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 +1787,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;
@@ -1763,7 +1816,6 @@ static struct spi_driver at86rf230_driver = {
.driver = {
.of_match_table = of_match_ptr(at86rf230_of_match),
.name = "at86rf230",
- .owner = THIS_MODULE,
},
.probe = at86rf230_probe,
.remove = at86rf230_remove,
diff --git a/drivers/net/ieee802154/at86rf230.h b/drivers/net/ieee802154/at86rf230.h
index 1e6d1cc67..fd9c1f467 100644
--- a/drivers/net/ieee802154/at86rf230.h
+++ b/drivers/net/ieee802154/at86rf230.h
@@ -216,5 +216,13 @@
#define STATE_TRANSITION_IN_PROGRESS 0x1F
#define TRX_STATE_MASK (0x1F)
+#define TRAC_MASK(x) ((x & 0xe0) >> 5)
+
+#define TRAC_SUCCESS 0
+#define TRAC_SUCCESS_DATA_PENDING 1
+#define TRAC_SUCCESS_WAIT_FOR_ACK 2
+#define TRAC_CHANNEL_ACCESS_FAILURE 3
+#define TRAC_NO_ACK 5
+#define TRAC_INVALID 7
#endif /* !_AT86RF230_H */
diff --git a/drivers/net/ieee802154/atusb.c b/drivers/net/ieee802154/atusb.c
index 80dfc725b..199a94a9c 100644
--- a/drivers/net/ieee802154/atusb.c
+++ b/drivers/net/ieee802154/atusb.c
@@ -559,6 +559,7 @@ static int atusb_get_and_show_chip(struct atusb *atusb)
{
struct usb_device *usb_dev = atusb->usb_dev;
uint8_t man_id_0, man_id_1, part_num, version_num;
+ const char *chip;
man_id_0 = atusb_read_reg(atusb, RG_MAN_ID_0);
man_id_1 = atusb_read_reg(atusb, RG_MAN_ID_1);
@@ -574,14 +575,22 @@ static int atusb_get_and_show_chip(struct atusb *atusb)
man_id_1, man_id_0);
goto fail;
}
- if (part_num != 3 && part_num != 2) {
+
+ switch (part_num) {
+ case 2:
+ chip = "AT86RF230";
+ break;
+ case 3:
+ chip = "AT86RF231";
+ break;
+ default:
dev_err(&usb_dev->dev,
"unexpected transceiver, part 0x%02x version 0x%02x\n",
part_num, version_num);
goto fail;
}
- dev_info(&usb_dev->dev, "ATUSB: AT86RF231 version %d\n", version_num);
+ dev_info(&usb_dev->dev, "ATUSB: %s version %d\n", chip, version_num);
return 0;
diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c
index c5b54a15f..e65b60591 100644
--- a/drivers/net/ieee802154/cc2520.c
+++ b/drivers/net/ieee802154/cc2520.c
@@ -1152,7 +1152,6 @@ MODULE_DEVICE_TABLE(of, cc2520_of_ids);
static struct spi_driver cc2520_driver = {
.driver = {
.name = "cc2520",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(cc2520_of_ids),
},
.id_table = cc2520_ids,
diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c
index 997724b8e..4cdf51638 100644
--- a/drivers/net/ieee802154/mrf24j40.c
+++ b/drivers/net/ieee802154/mrf24j40.c
@@ -18,51 +18,172 @@
#include <linux/spi/spi.h>
#include <linux/interrupt.h>
#include <linux/module.h>
+#include <linux/regmap.h>
#include <linux/ieee802154.h>
+#include <linux/irq.h>
#include <net/cfg802154.h>
#include <net/mac802154.h>
/* MRF24J40 Short Address Registers */
-#define REG_RXMCR 0x00 /* Receive MAC control */
-#define REG_PANIDL 0x01 /* PAN ID (low) */
-#define REG_PANIDH 0x02 /* PAN ID (high) */
-#define REG_SADRL 0x03 /* Short address (low) */
-#define REG_SADRH 0x04 /* Short address (high) */
-#define REG_EADR0 0x05 /* Long address (low) (high is EADR7) */
-#define REG_TXMCR 0x11 /* Transmit MAC control */
-#define REG_PACON0 0x16 /* Power Amplifier Control */
-#define REG_PACON1 0x17 /* Power Amplifier Control */
-#define REG_PACON2 0x18 /* Power Amplifier Control */
-#define REG_TXNCON 0x1B /* Transmit Normal FIFO Control */
-#define REG_TXSTAT 0x24 /* TX MAC Status Register */
-#define REG_SOFTRST 0x2A /* Soft Reset */
-#define REG_TXSTBL 0x2E /* TX Stabilization */
-#define REG_INTSTAT 0x31 /* Interrupt Status */
-#define REG_INTCON 0x32 /* Interrupt Control */
-#define REG_GPIO 0x33 /* GPIO */
-#define REG_TRISGPIO 0x34 /* GPIO direction */
-#define REG_RFCTL 0x36 /* RF Control Mode Register */
-#define REG_BBREG1 0x39 /* Baseband Registers */
-#define REG_BBREG2 0x3A /* */
-#define REG_BBREG6 0x3E /* */
-#define REG_CCAEDTH 0x3F /* Energy Detection Threshold */
+#define REG_RXMCR 0x00 /* Receive MAC control */
+#define BIT_PROMI BIT(0)
+#define BIT_ERRPKT BIT(1)
+#define BIT_NOACKRSP BIT(5)
+#define BIT_PANCOORD BIT(3)
+
+#define REG_PANIDL 0x01 /* PAN ID (low) */
+#define REG_PANIDH 0x02 /* PAN ID (high) */
+#define REG_SADRL 0x03 /* Short address (low) */
+#define REG_SADRH 0x04 /* Short address (high) */
+#define REG_EADR0 0x05 /* Long address (low) (high is EADR7) */
+#define REG_EADR1 0x06
+#define REG_EADR2 0x07
+#define REG_EADR3 0x08
+#define REG_EADR4 0x09
+#define REG_EADR5 0x0A
+#define REG_EADR6 0x0B
+#define REG_EADR7 0x0C
+#define REG_RXFLUSH 0x0D
+#define REG_ORDER 0x10
+#define REG_TXMCR 0x11 /* Transmit MAC control */
+#define TXMCR_MIN_BE_SHIFT 3
+#define TXMCR_MIN_BE_MASK 0x18
+#define TXMCR_CSMA_RETRIES_SHIFT 0
+#define TXMCR_CSMA_RETRIES_MASK 0x07
+
+#define REG_ACKTMOUT 0x12
+#define REG_ESLOTG1 0x13
+#define REG_SYMTICKL 0x14
+#define REG_SYMTICKH 0x15
+#define REG_PACON0 0x16 /* Power Amplifier Control */
+#define REG_PACON1 0x17 /* Power Amplifier Control */
+#define REG_PACON2 0x18 /* Power Amplifier Control */
+#define REG_TXBCON0 0x1A
+#define REG_TXNCON 0x1B /* Transmit Normal FIFO Control */
+#define BIT_TXNTRIG BIT(0)
+#define BIT_TXNACKREQ BIT(2)
+
+#define REG_TXG1CON 0x1C
+#define REG_TXG2CON 0x1D
+#define REG_ESLOTG23 0x1E
+#define REG_ESLOTG45 0x1F
+#define REG_ESLOTG67 0x20
+#define REG_TXPEND 0x21
+#define REG_WAKECON 0x22
+#define REG_FROMOFFSET 0x23
+#define REG_TXSTAT 0x24 /* TX MAC Status Register */
+#define REG_TXBCON1 0x25
+#define REG_GATECLK 0x26
+#define REG_TXTIME 0x27
+#define REG_HSYMTMRL 0x28
+#define REG_HSYMTMRH 0x29
+#define REG_SOFTRST 0x2A /* Soft Reset */
+#define REG_SECCON0 0x2C
+#define REG_SECCON1 0x2D
+#define REG_TXSTBL 0x2E /* TX Stabilization */
+#define REG_RXSR 0x30
+#define REG_INTSTAT 0x31 /* Interrupt Status */
+#define BIT_TXNIF BIT(0)
+#define BIT_RXIF BIT(3)
+
+#define REG_INTCON 0x32 /* Interrupt Control */
+#define BIT_TXNIE BIT(0)
+#define BIT_RXIE BIT(3)
+
+#define REG_GPIO 0x33 /* GPIO */
+#define REG_TRISGPIO 0x34 /* GPIO direction */
+#define REG_SLPACK 0x35
+#define REG_RFCTL 0x36 /* RF Control Mode Register */
+#define BIT_RFRST BIT(2)
+
+#define REG_SECCR2 0x37
+#define REG_BBREG0 0x38
+#define REG_BBREG1 0x39 /* Baseband Registers */
+#define BIT_RXDECINV BIT(2)
+
+#define REG_BBREG2 0x3A /* */
+#define BBREG2_CCA_MODE_SHIFT 6
+#define BBREG2_CCA_MODE_MASK 0xc0
+
+#define REG_BBREG3 0x3B
+#define REG_BBREG4 0x3C
+#define REG_BBREG6 0x3E /* */
+#define REG_CCAEDTH 0x3F /* Energy Detection Threshold */
/* MRF24J40 Long Address Registers */
-#define REG_RFCON0 0x200 /* RF Control Registers */
-#define REG_RFCON1 0x201
-#define REG_RFCON2 0x202
-#define REG_RFCON3 0x203
-#define REG_RFCON5 0x205
-#define REG_RFCON6 0x206
-#define REG_RFCON7 0x207
-#define REG_RFCON8 0x208
-#define REG_RSSI 0x210
-#define REG_SLPCON0 0x211 /* Sleep Clock Control Registers */
-#define REG_SLPCON1 0x220
-#define REG_WAKETIMEL 0x222 /* Wake-up Time Match Value Low */
-#define REG_WAKETIMEH 0x223 /* Wake-up Time Match Value High */
-#define REG_TESTMODE 0x22F /* Test mode */
-#define REG_RX_FIFO 0x300 /* Receive FIFO */
+#define REG_RFCON0 0x200 /* RF Control Registers */
+#define RFCON0_CH_SHIFT 4
+#define RFCON0_CH_MASK 0xf0
+#define RFOPT_RECOMMEND 3
+
+#define REG_RFCON1 0x201
+#define REG_RFCON2 0x202
+#define REG_RFCON3 0x203
+
+#define TXPWRL_MASK 0xc0
+#define TXPWRL_SHIFT 6
+#define TXPWRL_30 0x3
+#define TXPWRL_20 0x2
+#define TXPWRL_10 0x1
+#define TXPWRL_0 0x0
+
+#define TXPWRS_MASK 0x38
+#define TXPWRS_SHIFT 3
+#define TXPWRS_6_3 0x7
+#define TXPWRS_4_9 0x6
+#define TXPWRS_3_7 0x5
+#define TXPWRS_2_8 0x4
+#define TXPWRS_1_9 0x3
+#define TXPWRS_1_2 0x2
+#define TXPWRS_0_5 0x1
+#define TXPWRS_0 0x0
+
+#define REG_RFCON5 0x205
+#define REG_RFCON6 0x206
+#define REG_RFCON7 0x207
+#define REG_RFCON8 0x208
+#define REG_SLPCAL0 0x209
+#define REG_SLPCAL1 0x20A
+#define REG_SLPCAL2 0x20B
+#define REG_RFSTATE 0x20F
+#define REG_RSSI 0x210
+#define REG_SLPCON0 0x211 /* Sleep Clock Control Registers */
+#define BIT_INTEDGE BIT(1)
+
+#define REG_SLPCON1 0x220
+#define REG_WAKETIMEL 0x222 /* Wake-up Time Match Value Low */
+#define REG_WAKETIMEH 0x223 /* Wake-up Time Match Value High */
+#define REG_REMCNTL 0x224
+#define REG_REMCNTH 0x225
+#define REG_MAINCNT0 0x226
+#define REG_MAINCNT1 0x227
+#define REG_MAINCNT2 0x228
+#define REG_MAINCNT3 0x229
+#define REG_TESTMODE 0x22F /* Test mode */
+#define REG_ASSOEAR0 0x230
+#define REG_ASSOEAR1 0x231
+#define REG_ASSOEAR2 0x232
+#define REG_ASSOEAR3 0x233
+#define REG_ASSOEAR4 0x234
+#define REG_ASSOEAR5 0x235
+#define REG_ASSOEAR6 0x236
+#define REG_ASSOEAR7 0x237
+#define REG_ASSOSAR0 0x238
+#define REG_ASSOSAR1 0x239
+#define REG_UNONCE0 0x240
+#define REG_UNONCE1 0x241
+#define REG_UNONCE2 0x242
+#define REG_UNONCE3 0x243
+#define REG_UNONCE4 0x244
+#define REG_UNONCE5 0x245
+#define REG_UNONCE6 0x246
+#define REG_UNONCE7 0x247
+#define REG_UNONCE8 0x248
+#define REG_UNONCE9 0x249
+#define REG_UNONCE10 0x24A
+#define REG_UNONCE11 0x24B
+#define REG_UNONCE12 0x24C
+#define REG_RX_FIFO 0x300 /* Receive FIFO */
/* Device configuration: Only channels 11-26 on page 0 are supported. */
#define MRF24J40_CHAN_MIN 11
@@ -81,11 +202,52 @@ struct mrf24j40 {
struct spi_device *spi;
struct ieee802154_hw *hw;
- struct mutex buffer_mutex; /* only used to protect buf */
- struct completion tx_complete;
- u8 *buf; /* 3 bytes. Used for SPI single-register transfers. */
+ struct regmap *regmap_short;
+ struct regmap *regmap_long;
+
+ /* for writing txfifo */
+ struct spi_message tx_msg;
+ u8 tx_hdr_buf[2];
+ struct spi_transfer tx_hdr_trx;
+ u8 tx_len_buf[2];
+ struct spi_transfer tx_len_trx;
+ struct spi_transfer tx_buf_trx;
+ struct sk_buff *tx_skb;
+
+ /* post transmit message to send frame out */
+ struct spi_message tx_post_msg;
+ u8 tx_post_buf[2];
+ struct spi_transfer tx_post_trx;
+
+ /* for protect/unprotect/read length rxfifo */
+ struct spi_message rx_msg;
+ u8 rx_buf[3];
+ struct spi_transfer rx_trx;
+
+ /* receive handling */
+ struct spi_message rx_buf_msg;
+ u8 rx_addr_buf[2];
+ struct spi_transfer rx_addr_trx;
+ u8 rx_lqi_buf[2];
+ struct spi_transfer rx_lqi_trx;
+ u8 rx_fifo_buf[RX_FIFO_SIZE];
+ struct spi_transfer rx_fifo_buf_trx;
+
+ /* isr handling for reading intstat */
+ struct spi_message irq_msg;
+ u8 irq_buf[2];
+ struct spi_transfer irq_trx;
};
+/* regmap information for short address register access */
+#define MRF24J40_SHORT_WRITE 0x01
+#define MRF24J40_SHORT_READ 0x00
+#define MRF24J40_SHORT_NUMREGS 0x3F
+
+/* regmap information for long address register access */
+#define MRF24J40_LONG_ACCESS 0x80
+#define MRF24J40_LONG_NUMREGS 0x38F
+
/* Read/Write SPI Commands for Short and Long Address registers. */
#define MRF24J40_READSHORT(reg) ((reg) << 1)
#define MRF24J40_WRITESHORT(reg) ((reg) << 1 | 1)
@@ -97,118 +259,304 @@ struct mrf24j40 {
#define printdev(X) (&X->spi->dev)
-static int write_short_reg(struct mrf24j40 *devrec, u8 reg, u8 value)
+static bool
+mrf24j40_short_reg_writeable(struct device *dev, unsigned int reg)
{
- int ret;
- struct spi_message msg;
- struct spi_transfer xfer = {
- .len = 2,
- .tx_buf = devrec->buf,
- .rx_buf = devrec->buf,
- };
+ switch (reg) {
+ case REG_RXMCR:
+ case REG_PANIDL:
+ case REG_PANIDH:
+ case REG_SADRL:
+ case REG_SADRH:
+ case REG_EADR0:
+ case REG_EADR1:
+ case REG_EADR2:
+ case REG_EADR3:
+ case REG_EADR4:
+ case REG_EADR5:
+ case REG_EADR6:
+ case REG_EADR7:
+ case REG_RXFLUSH:
+ case REG_ORDER:
+ case REG_TXMCR:
+ case REG_ACKTMOUT:
+ case REG_ESLOTG1:
+ case REG_SYMTICKL:
+ case REG_SYMTICKH:
+ case REG_PACON0:
+ case REG_PACON1:
+ case REG_PACON2:
+ case REG_TXBCON0:
+ case REG_TXNCON:
+ case REG_TXG1CON:
+ case REG_TXG2CON:
+ case REG_ESLOTG23:
+ case REG_ESLOTG45:
+ case REG_ESLOTG67:
+ case REG_TXPEND:
+ case REG_WAKECON:
+ case REG_FROMOFFSET:
+ case REG_TXBCON1:
+ case REG_GATECLK:
+ case REG_TXTIME:
+ case REG_HSYMTMRL:
+ case REG_HSYMTMRH:
+ case REG_SOFTRST:
+ case REG_SECCON0:
+ case REG_SECCON1:
+ case REG_TXSTBL:
+ case REG_RXSR:
+ case REG_INTCON:
+ case REG_TRISGPIO:
+ case REG_GPIO:
+ case REG_RFCTL:
+ case REG_SLPACK:
+ case REG_BBREG0:
+ case REG_BBREG1:
+ case REG_BBREG2:
+ case REG_BBREG3:
+ case REG_BBREG4:
+ case REG_BBREG6:
+ case REG_CCAEDTH:
+ return true;
+ default:
+ return false;
+ }
+}
- spi_message_init(&msg);
- spi_message_add_tail(&xfer, &msg);
+static bool
+mrf24j40_short_reg_readable(struct device *dev, unsigned int reg)
+{
+ bool rc;
+
+ /* all writeable are also readable */
+ rc = mrf24j40_short_reg_writeable(dev, reg);
+ if (rc)
+ return rc;
+
+ /* readonly regs */
+ switch (reg) {
+ case REG_TXSTAT:
+ case REG_INTSTAT:
+ return true;
+ default:
+ return false;
+ }
+}
- mutex_lock(&devrec->buffer_mutex);
- devrec->buf[0] = MRF24J40_WRITESHORT(reg);
- devrec->buf[1] = value;
+static bool
+mrf24j40_short_reg_volatile(struct device *dev, unsigned int reg)
+{
+ /* can be changed during runtime */
+ switch (reg) {
+ case REG_TXSTAT:
+ case REG_INTSTAT:
+ case REG_RXFLUSH:
+ case REG_TXNCON:
+ case REG_SOFTRST:
+ case REG_RFCTL:
+ case REG_TXBCON0:
+ case REG_TXG1CON:
+ case REG_TXG2CON:
+ case REG_TXBCON1:
+ case REG_SECCON0:
+ case REG_RXSR:
+ case REG_SLPACK:
+ case REG_SECCR2:
+ case REG_BBREG6:
+ /* use them in spi_async and regmap so it's volatile */
+ case REG_BBREG1:
+ return true;
+ default:
+ return false;
+ }
+}
- ret = spi_sync(devrec->spi, &msg);
- if (ret)
- dev_err(printdev(devrec),
- "SPI write Failed for short register 0x%hhx\n", reg);
+static bool
+mrf24j40_short_reg_precious(struct device *dev, unsigned int reg)
+{
+ /* don't clear irq line on read */
+ switch (reg) {
+ case REG_INTSTAT:
+ return true;
+ default:
+ return false;
+ }
+}
- mutex_unlock(&devrec->buffer_mutex);
- return ret;
+static const struct regmap_config mrf24j40_short_regmap = {
+ .name = "mrf24j40_short",
+ .reg_bits = 7,
+ .val_bits = 8,
+ .pad_bits = 1,
+ .write_flag_mask = MRF24J40_SHORT_WRITE,
+ .read_flag_mask = MRF24J40_SHORT_READ,
+ .cache_type = REGCACHE_RBTREE,
+ .max_register = MRF24J40_SHORT_NUMREGS,
+ .writeable_reg = mrf24j40_short_reg_writeable,
+ .readable_reg = mrf24j40_short_reg_readable,
+ .volatile_reg = mrf24j40_short_reg_volatile,
+ .precious_reg = mrf24j40_short_reg_precious,
+};
+
+static bool
+mrf24j40_long_reg_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_RFCON0:
+ case REG_RFCON1:
+ case REG_RFCON2:
+ case REG_RFCON3:
+ case REG_RFCON5:
+ case REG_RFCON6:
+ case REG_RFCON7:
+ case REG_RFCON8:
+ case REG_SLPCAL2:
+ case REG_SLPCON0:
+ case REG_SLPCON1:
+ case REG_WAKETIMEL:
+ case REG_WAKETIMEH:
+ case REG_REMCNTL:
+ case REG_REMCNTH:
+ case REG_MAINCNT0:
+ case REG_MAINCNT1:
+ case REG_MAINCNT2:
+ case REG_MAINCNT3:
+ case REG_TESTMODE:
+ case REG_ASSOEAR0:
+ case REG_ASSOEAR1:
+ case REG_ASSOEAR2:
+ case REG_ASSOEAR3:
+ case REG_ASSOEAR4:
+ case REG_ASSOEAR5:
+ case REG_ASSOEAR6:
+ case REG_ASSOEAR7:
+ case REG_ASSOSAR0:
+ case REG_ASSOSAR1:
+ case REG_UNONCE0:
+ case REG_UNONCE1:
+ case REG_UNONCE2:
+ case REG_UNONCE3:
+ case REG_UNONCE4:
+ case REG_UNONCE5:
+ case REG_UNONCE6:
+ case REG_UNONCE7:
+ case REG_UNONCE8:
+ case REG_UNONCE9:
+ case REG_UNONCE10:
+ case REG_UNONCE11:
+ case REG_UNONCE12:
+ return true;
+ default:
+ return false;
+ }
}
-static int read_short_reg(struct mrf24j40 *devrec, u8 reg, u8 *val)
+static bool
+mrf24j40_long_reg_readable(struct device *dev, unsigned int reg)
{
- int ret = -1;
- struct spi_message msg;
- struct spi_transfer xfer = {
- .len = 2,
- .tx_buf = devrec->buf,
- .rx_buf = devrec->buf,
- };
+ bool rc;
+
+ /* all writeable are also readable */
+ rc = mrf24j40_long_reg_writeable(dev, reg);
+ if (rc)
+ return rc;
+
+ /* readonly regs */
+ switch (reg) {
+ case REG_SLPCAL0:
+ case REG_SLPCAL1:
+ case REG_RFSTATE:
+ case REG_RSSI:
+ return true;
+ default:
+ return false;
+ }
+}
- spi_message_init(&msg);
- spi_message_add_tail(&xfer, &msg);
+static bool
+mrf24j40_long_reg_volatile(struct device *dev, unsigned int reg)
+{
+ /* can be changed during runtime */
+ switch (reg) {
+ case REG_SLPCAL0:
+ case REG_SLPCAL1:
+ case REG_SLPCAL2:
+ case REG_RFSTATE:
+ case REG_RSSI:
+ case REG_MAINCNT3:
+ return true;
+ default:
+ return false;
+ }
+}
- mutex_lock(&devrec->buffer_mutex);
- devrec->buf[0] = MRF24J40_READSHORT(reg);
- devrec->buf[1] = 0;
+static const struct regmap_config mrf24j40_long_regmap = {
+ .name = "mrf24j40_long",
+ .reg_bits = 11,
+ .val_bits = 8,
+ .pad_bits = 5,
+ .write_flag_mask = MRF24J40_LONG_ACCESS,
+ .read_flag_mask = MRF24J40_LONG_ACCESS,
+ .cache_type = REGCACHE_RBTREE,
+ .max_register = MRF24J40_LONG_NUMREGS,
+ .writeable_reg = mrf24j40_long_reg_writeable,
+ .readable_reg = mrf24j40_long_reg_readable,
+ .volatile_reg = mrf24j40_long_reg_volatile,
+};
- ret = spi_sync(devrec->spi, &msg);
- if (ret)
- dev_err(printdev(devrec),
- "SPI read Failed for short register 0x%hhx\n", reg);
- else
- *val = devrec->buf[1];
+static int mrf24j40_long_regmap_write(void *context, const void *data,
+ size_t count)
+{
+ struct spi_device *spi = context;
+ u8 buf[3];
- mutex_unlock(&devrec->buffer_mutex);
- return ret;
+ if (count > 3)
+ return -EINVAL;
+
+ /* regmap supports read/write mask only in frist byte
+ * long write access need to set the 12th bit, so we
+ * make special handling for write.
+ */
+ memcpy(buf, data, count);
+ buf[1] |= (1 << 4);
+
+ return spi_write(spi, buf, count);
}
-static int read_long_reg(struct mrf24j40 *devrec, u16 reg, u8 *value)
+static int
+mrf24j40_long_regmap_read(void *context, const void *reg, size_t reg_size,
+ void *val, size_t val_size)
{
- int ret;
- u16 cmd;
- struct spi_message msg;
- struct spi_transfer xfer = {
- .len = 3,
- .tx_buf = devrec->buf,
- .rx_buf = devrec->buf,
- };
-
- spi_message_init(&msg);
- spi_message_add_tail(&xfer, &msg);
-
- cmd = MRF24J40_READLONG(reg);
- mutex_lock(&devrec->buffer_mutex);
- devrec->buf[0] = cmd >> 8 & 0xff;
- devrec->buf[1] = cmd & 0xff;
- devrec->buf[2] = 0;
-
- ret = spi_sync(devrec->spi, &msg);
- if (ret)
- dev_err(printdev(devrec),
- "SPI read Failed for long register 0x%hx\n", reg);
- else
- *value = devrec->buf[2];
+ struct spi_device *spi = context;
- mutex_unlock(&devrec->buffer_mutex);
- return ret;
+ return spi_write_then_read(spi, reg, reg_size, val, val_size);
}
-static int write_long_reg(struct mrf24j40 *devrec, u16 reg, u8 val)
+static const struct regmap_bus mrf24j40_long_regmap_bus = {
+ .write = mrf24j40_long_regmap_write,
+ .read = mrf24j40_long_regmap_read,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static void write_tx_buf_complete(void *context)
{
+ struct mrf24j40 *devrec = context;
+ __le16 fc = ieee802154_get_fc_from_skb(devrec->tx_skb);
+ u8 val = BIT_TXNTRIG;
int ret;
- u16 cmd;
- struct spi_message msg;
- struct spi_transfer xfer = {
- .len = 3,
- .tx_buf = devrec->buf,
- .rx_buf = devrec->buf,
- };
- spi_message_init(&msg);
- spi_message_add_tail(&xfer, &msg);
+ if (ieee802154_is_ackreq(fc))
+ val |= BIT_TXNACKREQ;
- cmd = MRF24J40_WRITELONG(reg);
- mutex_lock(&devrec->buffer_mutex);
- devrec->buf[0] = cmd >> 8 & 0xff;
- devrec->buf[1] = cmd & 0xff;
- devrec->buf[2] = val;
+ devrec->tx_post_msg.complete = NULL;
+ devrec->tx_post_buf[0] = MRF24J40_WRITESHORT(REG_TXNCON);
+ devrec->tx_post_buf[1] = val;
- ret = spi_sync(devrec->spi, &msg);
+ ret = spi_async(devrec->spi, &devrec->tx_post_msg);
if (ret)
- dev_err(printdev(devrec),
- "SPI write Failed for long register 0x%hx\n", reg);
-
- mutex_unlock(&devrec->buffer_mutex);
- return ret;
+ dev_err(printdev(devrec), "SPI write Failed for transmit buf\n");
}
/* This function relies on an undocumented write method. Once a write command
@@ -217,22 +565,8 @@ static int write_long_reg(struct mrf24j40 *devrec, u16 reg, u8 val)
static int write_tx_buf(struct mrf24j40 *devrec, u16 reg,
const u8 *data, size_t length)
{
- int ret;
u16 cmd;
- u8 lengths[2];
- struct spi_message msg;
- struct spi_transfer addr_xfer = {
- .len = 2,
- .tx_buf = devrec->buf,
- };
- struct spi_transfer lengths_xfer = {
- .len = 2,
- .tx_buf = &lengths, /* TODO: Is DMA really required for SPI? */
- };
- struct spi_transfer data_xfer = {
- .len = length,
- .tx_buf = data,
- };
+ int ret;
/* Range check the length. 2 bytes are used for the length fields.*/
if (length > TX_FIFO_SIZE-2) {
@@ -240,147 +574,29 @@ static int write_tx_buf(struct mrf24j40 *devrec, u16 reg,
length = TX_FIFO_SIZE-2;
}
- spi_message_init(&msg);
- spi_message_add_tail(&addr_xfer, &msg);
- spi_message_add_tail(&lengths_xfer, &msg);
- spi_message_add_tail(&data_xfer, &msg);
-
cmd = MRF24J40_WRITELONG(reg);
- mutex_lock(&devrec->buffer_mutex);
- devrec->buf[0] = cmd >> 8 & 0xff;
- devrec->buf[1] = cmd & 0xff;
- lengths[0] = 0x0; /* Header Length. Set to 0 for now. TODO */
- lengths[1] = length; /* Total length */
-
- ret = spi_sync(devrec->spi, &msg);
+ devrec->tx_hdr_buf[0] = cmd >> 8 & 0xff;
+ devrec->tx_hdr_buf[1] = cmd & 0xff;
+ devrec->tx_len_buf[0] = 0x0; /* Header Length. Set to 0 for now. TODO */
+ devrec->tx_len_buf[1] = length; /* Total length */
+ devrec->tx_buf_trx.tx_buf = data;
+ devrec->tx_buf_trx.len = length;
+
+ ret = spi_async(devrec->spi, &devrec->tx_msg);
if (ret)
dev_err(printdev(devrec), "SPI write Failed for TX buf\n");
- mutex_unlock(&devrec->buffer_mutex);
- return ret;
-}
-
-static int mrf24j40_read_rx_buf(struct mrf24j40 *devrec,
- u8 *data, u8 *len, u8 *lqi)
-{
- u8 rx_len;
- u8 addr[2];
- u8 lqi_rssi[2];
- u16 cmd;
- int ret;
- struct spi_message msg;
- struct spi_transfer addr_xfer = {
- .len = 2,
- .tx_buf = &addr,
- };
- struct spi_transfer data_xfer = {
- .len = 0x0, /* set below */
- .rx_buf = data,
- };
- struct spi_transfer status_xfer = {
- .len = 2,
- .rx_buf = &lqi_rssi,
- };
-
- /* Get the length of the data in the RX FIFO. The length in this
- * register exclues the 1-byte length field at the beginning. */
- ret = read_long_reg(devrec, REG_RX_FIFO, &rx_len);
- if (ret)
- goto out;
-
- /* Range check the RX FIFO length, accounting for the one-byte
- * length field at the beginning. */
- if (rx_len > RX_FIFO_SIZE-1) {
- dev_err(printdev(devrec), "Invalid length read from device. Performing short read.\n");
- rx_len = RX_FIFO_SIZE-1;
- }
-
- if (rx_len > *len) {
- /* Passed in buffer wasn't big enough. Should never happen. */
- dev_err(printdev(devrec), "Buffer not big enough. Performing short read\n");
- rx_len = *len;
- }
-
- /* Set up the commands to read the data. */
- cmd = MRF24J40_READLONG(REG_RX_FIFO+1);
- addr[0] = cmd >> 8 & 0xff;
- addr[1] = cmd & 0xff;
- data_xfer.len = rx_len;
-
- spi_message_init(&msg);
- spi_message_add_tail(&addr_xfer, &msg);
- spi_message_add_tail(&data_xfer, &msg);
- spi_message_add_tail(&status_xfer, &msg);
-
- ret = spi_sync(devrec->spi, &msg);
- if (ret) {
- dev_err(printdev(devrec), "SPI RX Buffer Read Failed.\n");
- goto out;
- }
-
- *lqi = lqi_rssi[0];
- *len = rx_len;
-
-#ifdef DEBUG
- print_hex_dump(KERN_DEBUG, "mrf24j40 rx: ",
- DUMP_PREFIX_OFFSET, 16, 1, data, *len, 0);
- pr_debug("mrf24j40 rx: lqi: %02hhx rssi: %02hhx\n",
- lqi_rssi[0], lqi_rssi[1]);
-#endif
-
-out:
return ret;
}
static int mrf24j40_tx(struct ieee802154_hw *hw, struct sk_buff *skb)
{
struct mrf24j40 *devrec = hw->priv;
- u8 val;
- int ret = 0;
dev_dbg(printdev(devrec), "tx packet of %d bytes\n", skb->len);
+ devrec->tx_skb = skb;
- ret = write_tx_buf(devrec, 0x000, skb->data, skb->len);
- if (ret)
- goto err;
-
- reinit_completion(&devrec->tx_complete);
-
- /* Set TXNTRIG bit of TXNCON to send packet */
- ret = read_short_reg(devrec, REG_TXNCON, &val);
- if (ret)
- goto err;
- val |= 0x1;
- /* Set TXNACKREQ if the ACK bit is set in the packet. */
- if (skb->data[0] & IEEE802154_FC_ACK_REQ)
- val |= 0x4;
- write_short_reg(devrec, REG_TXNCON, val);
-
- /* Wait for the device to send the TX complete interrupt. */
- ret = wait_for_completion_interruptible_timeout(
- &devrec->tx_complete,
- 5 * HZ);
- if (ret == -ERESTARTSYS)
- goto err;
- if (ret == 0) {
- dev_warn(printdev(devrec), "Timeout waiting for TX interrupt\n");
- ret = -ETIMEDOUT;
- goto err;
- }
-
- /* Check for send error from the device. */
- ret = read_short_reg(devrec, REG_TXSTAT, &val);
- if (ret)
- goto err;
- if (val & 0x1) {
- dev_dbg(printdev(devrec), "Error Sending. Retry count exceeded\n");
- ret = -ECOMM; /* TODO: Better error code ? */
- } else
- dev_dbg(printdev(devrec), "Packet Sent\n");
-
-err:
-
- return ret;
+ return write_tx_buf(devrec, 0x000, skb->data, skb->len);
}
static int mrf24j40_ed(struct ieee802154_hw *hw, u8 *level)
@@ -394,33 +610,23 @@ static int mrf24j40_ed(struct ieee802154_hw *hw, u8 *level)
static int mrf24j40_start(struct ieee802154_hw *hw)
{
struct mrf24j40 *devrec = hw->priv;
- u8 val;
- int ret;
dev_dbg(printdev(devrec), "start\n");
- ret = read_short_reg(devrec, REG_INTCON, &val);
- if (ret)
- return ret;
- val &= ~(0x1|0x8); /* Clear TXNIE and RXIE. Enable interrupts */
- write_short_reg(devrec, REG_INTCON, val);
-
- return 0;
+ /* Clear TXNIE and RXIE. Enable interrupts */
+ return regmap_update_bits(devrec->regmap_short, REG_INTCON,
+ BIT_TXNIE | BIT_RXIE, 0);
}
static void mrf24j40_stop(struct ieee802154_hw *hw)
{
struct mrf24j40 *devrec = hw->priv;
- u8 val;
- int ret;
dev_dbg(printdev(devrec), "stop\n");
- ret = read_short_reg(devrec, REG_INTCON, &val);
- if (ret)
- return;
- val |= 0x1|0x8; /* Set TXNIE and RXIE. Disable Interrupts */
- write_short_reg(devrec, REG_INTCON, val);
+ /* Set TXNIE and RXIE. Disable Interrupts */
+ regmap_update_bits(devrec->regmap_short, REG_INTCON,
+ BIT_TXNIE | BIT_TXNIE, BIT_TXNIE | BIT_TXNIE);
}
static int mrf24j40_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
@@ -436,21 +642,23 @@ static int mrf24j40_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
WARN_ON(channel > MRF24J40_CHAN_MAX);
/* Set Channel TODO */
- val = (channel-11) << 4 | 0x03;
- write_long_reg(devrec, REG_RFCON0, val);
+ val = (channel - 11) << RFCON0_CH_SHIFT | RFOPT_RECOMMEND;
+ ret = regmap_update_bits(devrec->regmap_long, REG_RFCON0,
+ RFCON0_CH_MASK, val);
+ if (ret)
+ return ret;
/* RF Reset */
- ret = read_short_reg(devrec, REG_RFCTL, &val);
+ ret = regmap_update_bits(devrec->regmap_short, REG_RFCTL, BIT_RFRST,
+ BIT_RFRST);
if (ret)
return ret;
- val |= 0x04;
- write_short_reg(devrec, REG_RFCTL, val);
- val &= ~0x04;
- write_short_reg(devrec, REG_RFCTL, val);
- udelay(SET_CHANNEL_DELAY_US); /* per datasheet */
+ ret = regmap_update_bits(devrec->regmap_short, REG_RFCTL, BIT_RFRST, 0);
+ if (!ret)
+ udelay(SET_CHANNEL_DELAY_US); /* per datasheet */
- return 0;
+ return ret;
}
static int mrf24j40_filter(struct ieee802154_hw *hw,
@@ -468,8 +676,8 @@ static int mrf24j40_filter(struct ieee802154_hw *hw,
addrh = le16_to_cpu(filt->short_addr) >> 8 & 0xff;
addrl = le16_to_cpu(filt->short_addr) & 0xff;
- write_short_reg(devrec, REG_SADRH, addrh);
- write_short_reg(devrec, REG_SADRL, addrl);
+ regmap_write(devrec->regmap_short, REG_SADRH, addrh);
+ regmap_write(devrec->regmap_short, REG_SADRL, addrl);
dev_dbg(printdev(devrec),
"Set short addr to %04hx\n", filt->short_addr);
}
@@ -480,7 +688,8 @@ static int mrf24j40_filter(struct ieee802154_hw *hw,
memcpy(addr, &filt->ieee_addr, 8);
for (i = 0; i < 8; i++)
- write_short_reg(devrec, REG_EADR0 + i, addr[i]);
+ regmap_write(devrec->regmap_short, REG_EADR0 + i,
+ addr[i]);
#ifdef DEBUG
pr_debug("Set long addr to: ");
@@ -496,8 +705,8 @@ static int mrf24j40_filter(struct ieee802154_hw *hw,
panidh = le16_to_cpu(filt->pan_id) >> 8 & 0xff;
panidl = le16_to_cpu(filt->pan_id) & 0xff;
- write_short_reg(devrec, REG_PANIDH, panidh);
- write_short_reg(devrec, REG_PANIDL, panidl);
+ regmap_write(devrec->regmap_short, REG_PANIDH, panidh);
+ regmap_write(devrec->regmap_short, REG_PANIDL, panidl);
dev_dbg(printdev(devrec), "Set PANID to %04hx\n", filt->pan_id);
}
@@ -507,14 +716,14 @@ static int mrf24j40_filter(struct ieee802154_hw *hw,
u8 val;
int ret;
- ret = read_short_reg(devrec, REG_RXMCR, &val);
- if (ret)
- return ret;
if (filt->pan_coord)
- val |= 0x8;
+ val = BIT_PANCOORD;
else
- val &= ~0x8;
- write_short_reg(devrec, REG_RXMCR, val);
+ val = 0;
+ ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR,
+ BIT_PANCOORD, val);
+ if (ret)
+ return ret;
/* REG_SLOTTED is maintained as default (unslotted/CSMA-CA).
* REG_ORDER is maintained as default (no beacon/superframe).
@@ -527,168 +736,392 @@ static int mrf24j40_filter(struct ieee802154_hw *hw,
return 0;
}
-static int mrf24j40_handle_rx(struct mrf24j40 *devrec)
+static void mrf24j40_handle_rx_read_buf_unlock(struct mrf24j40 *devrec)
{
- u8 len = RX_FIFO_SIZE;
- u8 lqi = 0;
- u8 val;
- int ret = 0;
- int ret2;
- struct sk_buff *skb;
+ int ret;
- /* Turn off reception of packets off the air. This prevents the
- * device from overwriting the buffer while we're reading it. */
- ret = read_short_reg(devrec, REG_BBREG1, &val);
+ /* Turn back on reception of packets off the air. */
+ devrec->rx_msg.complete = NULL;
+ devrec->rx_buf[0] = MRF24J40_WRITESHORT(REG_BBREG1);
+ devrec->rx_buf[1] = 0x00; /* CLR RXDECINV */
+ ret = spi_async(devrec->spi, &devrec->rx_msg);
if (ret)
- goto out;
- val |= 4; /* SET RXDECINV */
- write_short_reg(devrec, REG_BBREG1, val);
+ dev_err(printdev(devrec), "failed to unlock rx buffer\n");
+}
+
+static void mrf24j40_handle_rx_read_buf_complete(void *context)
+{
+ struct mrf24j40 *devrec = context;
+ u8 len = devrec->rx_buf[2];
+ u8 rx_local_buf[RX_FIFO_SIZE];
+ struct sk_buff *skb;
+
+ memcpy(rx_local_buf, devrec->rx_fifo_buf, len);
+ mrf24j40_handle_rx_read_buf_unlock(devrec);
- skb = dev_alloc_skb(len);
+ skb = dev_alloc_skb(IEEE802154_MTU);
if (!skb) {
- ret = -ENOMEM;
- goto out;
+ dev_err(printdev(devrec), "failed to allocate skb\n");
+ return;
}
- ret = mrf24j40_read_rx_buf(devrec, skb_put(skb, len), &len, &lqi);
- if (ret < 0) {
- dev_err(printdev(devrec), "Failure reading RX FIFO\n");
- kfree_skb(skb);
- ret = -EINVAL;
- goto out;
+ memcpy(skb_put(skb, len), rx_local_buf, len);
+ ieee802154_rx_irqsafe(devrec->hw, skb, 0);
+
+#ifdef DEBUG
+ print_hex_dump(KERN_DEBUG, "mrf24j40 rx: ", DUMP_PREFIX_OFFSET, 16, 1,
+ rx_local_buf, len, 0);
+ pr_debug("mrf24j40 rx: lqi: %02hhx rssi: %02hhx\n",
+ devrec->rx_lqi_buf[0], devrec->rx_lqi_buf[1]);
+#endif
+}
+
+static void mrf24j40_handle_rx_read_buf(void *context)
+{
+ struct mrf24j40 *devrec = context;
+ u16 cmd;
+ int ret;
+
+ /* if length is invalid read the full MTU */
+ if (!ieee802154_is_valid_psdu_len(devrec->rx_buf[2]))
+ devrec->rx_buf[2] = IEEE802154_MTU;
+
+ cmd = MRF24J40_READLONG(REG_RX_FIFO + 1);
+ devrec->rx_addr_buf[0] = cmd >> 8 & 0xff;
+ devrec->rx_addr_buf[1] = cmd & 0xff;
+ devrec->rx_fifo_buf_trx.len = devrec->rx_buf[2];
+ ret = spi_async(devrec->spi, &devrec->rx_buf_msg);
+ if (ret) {
+ dev_err(printdev(devrec), "failed to read rx buffer\n");
+ mrf24j40_handle_rx_read_buf_unlock(devrec);
}
+}
- /* Cut off the checksum */
- skb_trim(skb, len-2);
+static void mrf24j40_handle_rx_read_len(void *context)
+{
+ struct mrf24j40 *devrec = context;
+ u16 cmd;
+ int ret;
- /* TODO: Other drivers call ieee20154_rx_irqsafe() here (eg: cc2040,
- * also from a workqueue). I think irqsafe is not necessary here.
- * Can someone confirm? */
- ieee802154_rx_irqsafe(devrec->hw, skb, lqi);
+ /* read the length of received frame */
+ devrec->rx_msg.complete = mrf24j40_handle_rx_read_buf;
+ devrec->rx_trx.len = 3;
+ cmd = MRF24J40_READLONG(REG_RX_FIFO);
+ devrec->rx_buf[0] = cmd >> 8 & 0xff;
+ devrec->rx_buf[1] = cmd & 0xff;
- dev_dbg(printdev(devrec), "RX Handled\n");
+ ret = spi_async(devrec->spi, &devrec->rx_msg);
+ if (ret) {
+ dev_err(printdev(devrec), "failed to read rx buffer length\n");
+ mrf24j40_handle_rx_read_buf_unlock(devrec);
+ }
+}
-out:
- /* Turn back on reception of packets off the air. */
- ret2 = read_short_reg(devrec, REG_BBREG1, &val);
- if (ret2)
- return ret2;
- val &= ~0x4; /* Clear RXDECINV */
- write_short_reg(devrec, REG_BBREG1, val);
+static int mrf24j40_handle_rx(struct mrf24j40 *devrec)
+{
+ /* Turn off reception of packets off the air. This prevents the
+ * device from overwriting the buffer while we're reading it.
+ */
+ devrec->rx_msg.complete = mrf24j40_handle_rx_read_len;
+ devrec->rx_trx.len = 2;
+ devrec->rx_buf[0] = MRF24J40_WRITESHORT(REG_BBREG1);
+ devrec->rx_buf[1] = BIT_RXDECINV; /* SET RXDECINV */
+
+ return spi_async(devrec->spi, &devrec->rx_msg);
+}
+
+static int
+mrf24j40_csma_params(struct ieee802154_hw *hw, u8 min_be, u8 max_be,
+ u8 retries)
+{
+ struct mrf24j40 *devrec = hw->priv;
+ u8 val;
+
+ /* min_be */
+ val = min_be << TXMCR_MIN_BE_SHIFT;
+ /* csma backoffs */
+ val |= retries << TXMCR_CSMA_RETRIES_SHIFT;
+
+ return regmap_update_bits(devrec->regmap_short, REG_TXMCR,
+ TXMCR_MIN_BE_MASK | TXMCR_CSMA_RETRIES_MASK,
+ val);
+}
+
+static int mrf24j40_set_cca_mode(struct ieee802154_hw *hw,
+ const struct wpan_phy_cca *cca)
+{
+ struct mrf24j40 *devrec = hw->priv;
+ u8 val;
+
+ /* mapping 802.15.4 to driver spec */
+ switch (cca->mode) {
+ case NL802154_CCA_ENERGY:
+ val = 2;
+ break;
+ case NL802154_CCA_CARRIER:
+ val = 1;
+ break;
+ case NL802154_CCA_ENERGY_CARRIER:
+ switch (cca->opt) {
+ case NL802154_CCA_OPT_ENERGY_CARRIER_AND:
+ val = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(devrec->regmap_short, REG_BBREG2,
+ BBREG2_CCA_MODE_MASK,
+ val << BBREG2_CCA_MODE_SHIFT);
+}
+
+/* array for representing ed levels */
+static const s32 mrf24j40_ed_levels[] = {
+ -9000, -8900, -8800, -8700, -8600, -8500, -8400, -8300, -8200, -8100,
+ -8000, -7900, -7800, -7700, -7600, -7500, -7400, -7300, -7200, -7100,
+ -7000, -6900, -6800, -6700, -6600, -6500, -6400, -6300, -6200, -6100,
+ -6000, -5900, -5800, -5700, -5600, -5500, -5400, -5300, -5200, -5100,
+ -5000, -4900, -4800, -4700, -4600, -4500, -4400, -4300, -4200, -4100,
+ -4000, -3900, -3800, -3700, -3600, -3500
+};
+
+/* map ed levels to register value */
+static const s32 mrf24j40_ed_levels_map[][2] = {
+ { -9000, 0 }, { -8900, 1 }, { -8800, 2 }, { -8700, 5 }, { -8600, 9 },
+ { -8500, 13 }, { -8400, 18 }, { -8300, 23 }, { -8200, 27 },
+ { -8100, 32 }, { -8000, 37 }, { -7900, 43 }, { -7800, 48 },
+ { -7700, 53 }, { -7600, 58 }, { -7500, 63 }, { -7400, 68 },
+ { -7300, 73 }, { -7200, 78 }, { -7100, 83 }, { -7000, 89 },
+ { -6900, 95 }, { -6800, 100 }, { -6700, 107 }, { -6600, 111 },
+ { -6500, 117 }, { -6400, 121 }, { -6300, 125 }, { -6200, 129 },
+ { -6100, 133 }, { -6000, 138 }, { -5900, 143 }, { -5800, 148 },
+ { -5700, 153 }, { -5600, 159 }, { -5500, 165 }, { -5400, 170 },
+ { -5300, 176 }, { -5200, 183 }, { -5100, 188 }, { -5000, 193 },
+ { -4900, 198 }, { -4800, 203 }, { -4700, 207 }, { -4600, 212 },
+ { -4500, 216 }, { -4400, 221 }, { -4300, 225 }, { -4200, 228 },
+ { -4100, 233 }, { -4000, 239 }, { -3900, 245 }, { -3800, 250 },
+ { -3700, 253 }, { -3600, 254 }, { -3500, 255 },
+};
+
+static int mrf24j40_set_cca_ed_level(struct ieee802154_hw *hw, s32 mbm)
+{
+ struct mrf24j40 *devrec = hw->priv;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mrf24j40_ed_levels_map); i++) {
+ if (mrf24j40_ed_levels_map[i][0] == mbm)
+ return regmap_write(devrec->regmap_short, REG_CCAEDTH,
+ mrf24j40_ed_levels_map[i][1]);
+ }
+
+ return -EINVAL;
+}
+
+static const s32 mrf24j40ma_powers[] = {
+ 0, -50, -120, -190, -280, -370, -490, -630, -1000, -1050, -1120, -1190,
+ -1280, -1370, -1490, -1630, -2000, -2050, -2120, -2190, -2280, -2370,
+ -2490, -2630, -3000, -3050, -3120, -3190, -3280, -3370, -3490, -3630,
+};
+
+static int mrf24j40_set_txpower(struct ieee802154_hw *hw, s32 mbm)
+{
+ struct mrf24j40 *devrec = hw->priv;
+ s32 small_scale;
+ u8 val;
+
+ if (0 >= mbm && mbm > -1000) {
+ val = TXPWRL_0 << TXPWRL_SHIFT;
+ small_scale = mbm;
+ } else if (-1000 >= mbm && mbm > -2000) {
+ val = TXPWRL_10 << TXPWRL_SHIFT;
+ small_scale = mbm + 1000;
+ } else if (-2000 >= mbm && mbm > -3000) {
+ val = TXPWRL_20 << TXPWRL_SHIFT;
+ small_scale = mbm + 2000;
+ } else if (-3000 >= mbm && mbm > -4000) {
+ val = TXPWRL_30 << TXPWRL_SHIFT;
+ small_scale = mbm + 3000;
+ } else {
+ return -EINVAL;
+ }
+
+ switch (small_scale) {
+ case 0:
+ val |= (TXPWRS_0 << TXPWRS_SHIFT);
+ break;
+ case -50:
+ val |= (TXPWRS_0_5 << TXPWRS_SHIFT);
+ break;
+ case -120:
+ val |= (TXPWRS_1_2 << TXPWRS_SHIFT);
+ break;
+ case -190:
+ val |= (TXPWRS_1_9 << TXPWRS_SHIFT);
+ break;
+ case -280:
+ val |= (TXPWRS_2_8 << TXPWRS_SHIFT);
+ break;
+ case -370:
+ val |= (TXPWRS_3_7 << TXPWRS_SHIFT);
+ break;
+ case -490:
+ val |= (TXPWRS_4_9 << TXPWRS_SHIFT);
+ break;
+ case -630:
+ val |= (TXPWRS_6_3 << TXPWRS_SHIFT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(devrec->regmap_long, REG_RFCON3,
+ TXPWRL_MASK | TXPWRS_MASK, val);
+}
+
+static int mrf24j40_set_promiscuous_mode(struct ieee802154_hw *hw, bool on)
+{
+ struct mrf24j40 *devrec = hw->priv;
+ int ret;
+
+ if (on) {
+ /* set PROMI, ERRPKT and NOACKRSP */
+ ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR,
+ BIT_PROMI | BIT_ERRPKT | BIT_NOACKRSP,
+ BIT_PROMI | BIT_ERRPKT | BIT_NOACKRSP);
+ } else {
+ /* clear PROMI, ERRPKT and NOACKRSP */
+ ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR,
+ BIT_PROMI | BIT_ERRPKT | BIT_NOACKRSP,
+ 0);
+ }
return ret;
}
static const struct ieee802154_ops mrf24j40_ops = {
.owner = THIS_MODULE,
- .xmit_sync = mrf24j40_tx,
+ .xmit_async = mrf24j40_tx,
.ed = mrf24j40_ed,
.start = mrf24j40_start,
.stop = mrf24j40_stop,
.set_channel = mrf24j40_set_channel,
.set_hw_addr_filt = mrf24j40_filter,
+ .set_csma_params = mrf24j40_csma_params,
+ .set_cca_mode = mrf24j40_set_cca_mode,
+ .set_cca_ed_level = mrf24j40_set_cca_ed_level,
+ .set_txpower = mrf24j40_set_txpower,
+ .set_promiscuous_mode = mrf24j40_set_promiscuous_mode,
};
-static irqreturn_t mrf24j40_isr(int irq, void *data)
+static void mrf24j40_intstat_complete(void *context)
{
- struct mrf24j40 *devrec = data;
- u8 intstat;
- int ret;
+ struct mrf24j40 *devrec = context;
+ u8 intstat = devrec->irq_buf[1];
- /* Read the interrupt status */
- ret = read_short_reg(devrec, REG_INTSTAT, &intstat);
- if (ret)
- goto out;
+ enable_irq(devrec->spi->irq);
/* Check for TX complete */
- if (intstat & 0x1)
- complete(&devrec->tx_complete);
+ if (intstat & BIT_TXNIF)
+ ieee802154_xmit_complete(devrec->hw, devrec->tx_skb, false);
/* Check for Rx */
- if (intstat & 0x8)
+ if (intstat & BIT_RXIF)
mrf24j40_handle_rx(devrec);
+}
+
+static irqreturn_t mrf24j40_isr(int irq, void *data)
+{
+ struct mrf24j40 *devrec = data;
+ int ret;
+
+ disable_irq_nosync(irq);
+
+ devrec->irq_buf[0] = MRF24J40_READSHORT(REG_INTSTAT);
+ /* Read the interrupt status */
+ ret = spi_async(devrec->spi, &devrec->irq_msg);
+ if (ret) {
+ enable_irq(irq);
+ return IRQ_NONE;
+ }
-out:
return IRQ_HANDLED;
}
static int mrf24j40_hw_init(struct mrf24j40 *devrec)
{
+ u32 irq_type;
int ret;
- u8 val;
/* Initialize the device.
From datasheet section 3.2: Initialization. */
- ret = write_short_reg(devrec, REG_SOFTRST, 0x07);
+ ret = regmap_write(devrec->regmap_short, REG_SOFTRST, 0x07);
if (ret)
goto err_ret;
- ret = write_short_reg(devrec, REG_PACON2, 0x98);
+ ret = regmap_write(devrec->regmap_short, REG_PACON2, 0x98);
if (ret)
goto err_ret;
- ret = write_short_reg(devrec, REG_TXSTBL, 0x95);
+ ret = regmap_write(devrec->regmap_short, REG_TXSTBL, 0x95);
if (ret)
goto err_ret;
- ret = write_long_reg(devrec, REG_RFCON0, 0x03);
+ ret = regmap_write(devrec->regmap_long, REG_RFCON0, 0x03);
if (ret)
goto err_ret;
- ret = write_long_reg(devrec, REG_RFCON1, 0x01);
+ ret = regmap_write(devrec->regmap_long, REG_RFCON1, 0x01);
if (ret)
goto err_ret;
- ret = write_long_reg(devrec, REG_RFCON2, 0x80);
+ ret = regmap_write(devrec->regmap_long, REG_RFCON2, 0x80);
if (ret)
goto err_ret;
- ret = write_long_reg(devrec, REG_RFCON6, 0x90);
+ ret = regmap_write(devrec->regmap_long, REG_RFCON6, 0x90);
if (ret)
goto err_ret;
- ret = write_long_reg(devrec, REG_RFCON7, 0x80);
+ ret = regmap_write(devrec->regmap_long, REG_RFCON7, 0x80);
if (ret)
goto err_ret;
- ret = write_long_reg(devrec, REG_RFCON8, 0x10);
+ ret = regmap_write(devrec->regmap_long, REG_RFCON8, 0x10);
if (ret)
goto err_ret;
- ret = write_long_reg(devrec, REG_SLPCON1, 0x21);
+ ret = regmap_write(devrec->regmap_long, REG_SLPCON1, 0x21);
if (ret)
goto err_ret;
- ret = write_short_reg(devrec, REG_BBREG2, 0x80);
+ ret = regmap_write(devrec->regmap_short, REG_BBREG2, 0x80);
if (ret)
goto err_ret;
- ret = write_short_reg(devrec, REG_CCAEDTH, 0x60);
+ ret = regmap_write(devrec->regmap_short, REG_CCAEDTH, 0x60);
if (ret)
goto err_ret;
- ret = write_short_reg(devrec, REG_BBREG6, 0x40);
+ ret = regmap_write(devrec->regmap_short, REG_BBREG6, 0x40);
if (ret)
goto err_ret;
- ret = write_short_reg(devrec, REG_RFCTL, 0x04);
+ ret = regmap_write(devrec->regmap_short, REG_RFCTL, 0x04);
if (ret)
goto err_ret;
- ret = write_short_reg(devrec, REG_RFCTL, 0x0);
+ ret = regmap_write(devrec->regmap_short, REG_RFCTL, 0x0);
if (ret)
goto err_ret;
udelay(192);
/* Set RX Mode. RXMCR<1:0>: 0x0 normal, 0x1 promisc, 0x2 error */
- ret = read_short_reg(devrec, REG_RXMCR, &val);
- if (ret)
- goto err_ret;
-
- val &= ~0x3; /* Clear RX mode (normal) */
-
- ret = write_short_reg(devrec, REG_RXMCR, val);
+ ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR, 0x03, 0x00);
if (ret)
goto err_ret;
@@ -696,22 +1129,39 @@ static int mrf24j40_hw_init(struct mrf24j40 *devrec)
/* Enable external amplifier.
* From MRF24J40MC datasheet section 1.3: Operation.
*/
- read_long_reg(devrec, REG_TESTMODE, &val);
- val |= 0x7; /* Configure GPIO 0-2 to control amplifier */
- write_long_reg(devrec, REG_TESTMODE, val);
+ regmap_update_bits(devrec->regmap_long, REG_TESTMODE, 0x07,
+ 0x07);
- read_short_reg(devrec, REG_TRISGPIO, &val);
- val |= 0x8; /* Set GPIO3 as output. */
- write_short_reg(devrec, REG_TRISGPIO, val);
+ /* Set GPIO3 as output. */
+ regmap_update_bits(devrec->regmap_short, REG_TRISGPIO, 0x08,
+ 0x08);
- read_short_reg(devrec, REG_GPIO, &val);
- val |= 0x8; /* Set GPIO3 HIGH to enable U5 voltage regulator */
- write_short_reg(devrec, REG_GPIO, val);
+ /* Set GPIO3 HIGH to enable U5 voltage regulator */
+ regmap_update_bits(devrec->regmap_short, REG_GPIO, 0x08, 0x08);
/* Reduce TX pwr to meet FCC requirements.
* From MRF24J40MC datasheet section 3.1.1
*/
- write_long_reg(devrec, REG_RFCON3, 0x28);
+ regmap_write(devrec->regmap_long, REG_RFCON3, 0x28);
+ }
+
+ irq_type = irq_get_trigger_type(devrec->spi->irq);
+ if (irq_type == IRQ_TYPE_EDGE_RISING ||
+ irq_type == IRQ_TYPE_EDGE_FALLING)
+ dev_warn(&devrec->spi->dev,
+ "Using edge triggered irq's are not recommended, because it can cause races and result in a non-functional driver!\n");
+ switch (irq_type) {
+ case IRQ_TYPE_EDGE_RISING:
+ case IRQ_TYPE_LEVEL_HIGH:
+ /* set interrupt polarity to rising */
+ ret = regmap_update_bits(devrec->regmap_long, REG_SLPCON0,
+ BIT_INTEDGE, BIT_INTEDGE);
+ if (ret)
+ goto err_ret;
+ break;
+ default:
+ /* default is falling edge */
+ break;
}
return 0;
@@ -720,67 +1170,178 @@ err_ret:
return ret;
}
-static int mrf24j40_probe(struct spi_device *spi)
+static void
+mrf24j40_setup_tx_spi_messages(struct mrf24j40 *devrec)
{
- int ret = -ENOMEM;
- struct mrf24j40 *devrec;
+ spi_message_init(&devrec->tx_msg);
+ devrec->tx_msg.context = devrec;
+ devrec->tx_msg.complete = write_tx_buf_complete;
+ devrec->tx_hdr_trx.len = 2;
+ devrec->tx_hdr_trx.tx_buf = devrec->tx_hdr_buf;
+ spi_message_add_tail(&devrec->tx_hdr_trx, &devrec->tx_msg);
+ devrec->tx_len_trx.len = 2;
+ devrec->tx_len_trx.tx_buf = devrec->tx_len_buf;
+ spi_message_add_tail(&devrec->tx_len_trx, &devrec->tx_msg);
+ spi_message_add_tail(&devrec->tx_buf_trx, &devrec->tx_msg);
+
+ spi_message_init(&devrec->tx_post_msg);
+ devrec->tx_post_msg.context = devrec;
+ devrec->tx_post_trx.len = 2;
+ devrec->tx_post_trx.tx_buf = devrec->tx_post_buf;
+ spi_message_add_tail(&devrec->tx_post_trx, &devrec->tx_post_msg);
+}
- dev_info(&spi->dev, "probe(). IRQ: %d\n", spi->irq);
+static void
+mrf24j40_setup_rx_spi_messages(struct mrf24j40 *devrec)
+{
+ spi_message_init(&devrec->rx_msg);
+ devrec->rx_msg.context = devrec;
+ devrec->rx_trx.len = 2;
+ devrec->rx_trx.tx_buf = devrec->rx_buf;
+ devrec->rx_trx.rx_buf = devrec->rx_buf;
+ spi_message_add_tail(&devrec->rx_trx, &devrec->rx_msg);
+
+ spi_message_init(&devrec->rx_buf_msg);
+ devrec->rx_buf_msg.context = devrec;
+ devrec->rx_buf_msg.complete = mrf24j40_handle_rx_read_buf_complete;
+ devrec->rx_addr_trx.len = 2;
+ devrec->rx_addr_trx.tx_buf = devrec->rx_addr_buf;
+ spi_message_add_tail(&devrec->rx_addr_trx, &devrec->rx_buf_msg);
+ devrec->rx_fifo_buf_trx.rx_buf = devrec->rx_fifo_buf;
+ spi_message_add_tail(&devrec->rx_fifo_buf_trx, &devrec->rx_buf_msg);
+ devrec->rx_lqi_trx.len = 2;
+ devrec->rx_lqi_trx.rx_buf = devrec->rx_lqi_buf;
+ spi_message_add_tail(&devrec->rx_lqi_trx, &devrec->rx_buf_msg);
+}
- devrec = devm_kzalloc(&spi->dev, sizeof(struct mrf24j40), GFP_KERNEL);
- if (!devrec)
- goto err_ret;
- devrec->buf = devm_kzalloc(&spi->dev, 3, GFP_KERNEL);
- if (!devrec->buf)
- goto err_ret;
+static void
+mrf24j40_setup_irq_spi_messages(struct mrf24j40 *devrec)
+{
+ spi_message_init(&devrec->irq_msg);
+ devrec->irq_msg.context = devrec;
+ devrec->irq_msg.complete = mrf24j40_intstat_complete;
+ devrec->irq_trx.len = 2;
+ devrec->irq_trx.tx_buf = devrec->irq_buf;
+ devrec->irq_trx.rx_buf = devrec->irq_buf;
+ spi_message_add_tail(&devrec->irq_trx, &devrec->irq_msg);
+}
+
+static void mrf24j40_phy_setup(struct mrf24j40 *devrec)
+{
+ ieee802154_random_extended_addr(&devrec->hw->phy->perm_extended_addr);
+ devrec->hw->phy->current_channel = 11;
+
+ /* mrf24j40 supports max_minbe 0 - 3 */
+ devrec->hw->phy->supported.max_minbe = 3;
+ /* datasheet doesn't say anything about max_be, but we have min_be
+ * So we assume the max_be default.
+ */
+ devrec->hw->phy->supported.min_maxbe = 5;
+ devrec->hw->phy->supported.max_maxbe = 5;
+
+ devrec->hw->phy->cca.mode = NL802154_CCA_CARRIER;
+ devrec->hw->phy->supported.cca_modes = BIT(NL802154_CCA_ENERGY) |
+ BIT(NL802154_CCA_CARRIER) |
+ BIT(NL802154_CCA_ENERGY_CARRIER);
+ devrec->hw->phy->supported.cca_opts = BIT(NL802154_CCA_OPT_ENERGY_CARRIER_AND);
+
+ devrec->hw->phy->cca_ed_level = -6900;
+ devrec->hw->phy->supported.cca_ed_levels = mrf24j40_ed_levels;
+ devrec->hw->phy->supported.cca_ed_levels_size = ARRAY_SIZE(mrf24j40_ed_levels);
+
+ switch (spi_get_device_id(devrec->spi)->driver_data) {
+ case MRF24J40:
+ case MRF24J40MA:
+ devrec->hw->phy->supported.tx_powers = mrf24j40ma_powers;
+ devrec->hw->phy->supported.tx_powers_size = ARRAY_SIZE(mrf24j40ma_powers);
+ devrec->hw->phy->flags |= WPAN_PHY_FLAG_TXPOWER;
+ break;
+ default:
+ break;
+ }
+}
- spi->mode = SPI_MODE_0; /* TODO: Is this appropriate for right here? */
- if (spi->max_speed_hz > MAX_SPI_SPEED_HZ)
- spi->max_speed_hz = MAX_SPI_SPEED_HZ;
+static int mrf24j40_probe(struct spi_device *spi)
+{
+ int ret = -ENOMEM, irq_type;
+ struct ieee802154_hw *hw;
+ struct mrf24j40 *devrec;
- mutex_init(&devrec->buffer_mutex);
- init_completion(&devrec->tx_complete);
- devrec->spi = spi;
- spi_set_drvdata(spi, devrec);
+ dev_info(&spi->dev, "probe(). IRQ: %d\n", spi->irq);
/* Register with the 802154 subsystem */
- devrec->hw = ieee802154_alloc_hw(0, &mrf24j40_ops);
- if (!devrec->hw)
+ hw = ieee802154_alloc_hw(sizeof(*devrec), &mrf24j40_ops);
+ if (!hw)
goto err_ret;
- devrec->hw->priv = devrec;
- devrec->hw->parent = &devrec->spi->dev;
+ devrec = hw->priv;
+ devrec->spi = spi;
+ spi_set_drvdata(spi, devrec);
+ devrec->hw = hw;
+ devrec->hw->parent = &spi->dev;
devrec->hw->phy->supported.channels[0] = CHANNEL_MASK;
- devrec->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AFILT;
+ devrec->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT |
+ IEEE802154_HW_CSMA_PARAMS |
+ IEEE802154_HW_PROMISCUOUS;
+
+ devrec->hw->phy->flags = WPAN_PHY_FLAG_CCA_MODE |
+ WPAN_PHY_FLAG_CCA_ED_LEVEL;
+
+ mrf24j40_setup_tx_spi_messages(devrec);
+ mrf24j40_setup_rx_spi_messages(devrec);
+ mrf24j40_setup_irq_spi_messages(devrec);
+
+ devrec->regmap_short = devm_regmap_init_spi(spi,
+ &mrf24j40_short_regmap);
+ if (IS_ERR(devrec->regmap_short)) {
+ ret = PTR_ERR(devrec->regmap_short);
+ dev_err(&spi->dev, "Failed to allocate short register map: %d\n",
+ ret);
+ goto err_register_device;
+ }
- dev_dbg(printdev(devrec), "registered mrf24j40\n");
- ret = ieee802154_register_hw(devrec->hw);
- if (ret)
+ devrec->regmap_long = devm_regmap_init(&spi->dev,
+ &mrf24j40_long_regmap_bus,
+ spi, &mrf24j40_long_regmap);
+ if (IS_ERR(devrec->regmap_long)) {
+ ret = PTR_ERR(devrec->regmap_long);
+ dev_err(&spi->dev, "Failed to allocate long register map: %d\n",
+ ret);
goto err_register_device;
+ }
+
+ if (spi->max_speed_hz > MAX_SPI_SPEED_HZ) {
+ dev_warn(&spi->dev, "spi clock above possible maximum: %d",
+ MAX_SPI_SPEED_HZ);
+ return -EINVAL;
+ }
ret = mrf24j40_hw_init(devrec);
if (ret)
- goto err_hw_init;
+ goto err_register_device;
- ret = devm_request_threaded_irq(&spi->dev,
- spi->irq,
- NULL,
- mrf24j40_isr,
- IRQF_TRIGGER_LOW|IRQF_ONESHOT,
- dev_name(&spi->dev),
- devrec);
+ mrf24j40_phy_setup(devrec);
+ /* request IRQF_TRIGGER_LOW as fallback default */
+ irq_type = irq_get_trigger_type(spi->irq);
+ if (!irq_type)
+ irq_type = IRQF_TRIGGER_LOW;
+
+ ret = devm_request_irq(&spi->dev, spi->irq, mrf24j40_isr,
+ irq_type, dev_name(&spi->dev), devrec);
if (ret) {
dev_err(printdev(devrec), "Unable to get IRQ");
- goto err_irq;
+ goto err_register_device;
}
+ dev_dbg(printdev(devrec), "registered mrf24j40\n");
+ ret = ieee802154_register_hw(devrec->hw);
+ if (ret)
+ goto err_register_device;
+
return 0;
-err_irq:
-err_hw_init:
- ieee802154_unregister_hw(devrec->hw);
err_register_device:
ieee802154_free_hw(devrec->hw);
err_ret:
@@ -801,6 +1362,14 @@ static int mrf24j40_remove(struct spi_device *spi)
return 0;
}
+static const struct of_device_id mrf24j40_of_match[] = {
+ { .compatible = "microchip,mrf24j40", .data = (void *)MRF24J40 },
+ { .compatible = "microchip,mrf24j40ma", .data = (void *)MRF24J40MA },
+ { .compatible = "microchip,mrf24j40mc", .data = (void *)MRF24J40MC },
+ { },
+};
+MODULE_DEVICE_TABLE(of, mrf24j40_of_match);
+
static const struct spi_device_id mrf24j40_ids[] = {
{ "mrf24j40", MRF24J40 },
{ "mrf24j40ma", MRF24J40MA },
@@ -811,8 +1380,8 @@ MODULE_DEVICE_TABLE(spi, mrf24j40_ids);
static struct spi_driver mrf24j40_driver = {
.driver = {
+ .of_match_table = of_match_ptr(mrf24j40_of_match),
.name = "mrf24j40",
- .owner = THIS_MODULE,
},
.id_table = mrf24j40_ids,
.probe = mrf24j40_probe,