Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/torvalds/linux.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ieee802154')
-rw-r--r--drivers/net/ieee802154/Kconfig12
-rw-r--r--drivers/net/ieee802154/Makefile1
-rw-r--r--drivers/net/ieee802154/at86rf230.c1464
-rw-r--r--drivers/net/ieee802154/cc2520.c1039
-rw-r--r--drivers/net/ieee802154/fakehard.c3
-rw-r--r--drivers/net/ieee802154/mrf24j40.c115
6 files changed, 2067 insertions, 567 deletions
diff --git a/drivers/net/ieee802154/Kconfig b/drivers/net/ieee802154/Kconfig
index 3e89beab64fd..391a916622a9 100644
--- a/drivers/net/ieee802154/Kconfig
+++ b/drivers/net/ieee802154/Kconfig
@@ -34,6 +34,7 @@ config IEEE802154_AT86RF230
depends on IEEE802154_DRIVERS && MAC802154
tristate "AT86RF230/231/233/212 transceiver driver"
depends on SPI
+ select REGMAP_SPI
---help---
Say Y here to enable the at86rf230/231/233/212 SPI 802.15.4 wireless
controller.
@@ -51,3 +52,14 @@ config IEEE802154_MRF24J40
This driver can also be built as a module. To do so, say M here.
the module will be called 'mrf24j40'.
+
+config IEEE802154_CC2520
+ depends on IEEE802154_DRIVERS && MAC802154
+ tristate "CC2520 transceiver driver"
+ depends on SPI
+ ---help---
+ Say Y here to enable the CC2520 SPI 802.15.4 wireless
+ controller.
+
+ This driver can also be built as a module. To do so, say M here.
+ the module will be called 'cc2520'.
diff --git a/drivers/net/ieee802154/Makefile b/drivers/net/ieee802154/Makefile
index abb0c08decb0..655cb95e6e24 100644
--- a/drivers/net/ieee802154/Makefile
+++ b/drivers/net/ieee802154/Makefile
@@ -2,3 +2,4 @@ obj-$(CONFIG_IEEE802154_FAKEHARD) += fakehard.o
obj-$(CONFIG_IEEE802154_FAKELB) += fakelb.o
obj-$(CONFIG_IEEE802154_AT86RF230) += at86rf230.o
obj-$(CONFIG_IEEE802154_MRF24J40) += mrf24j40.o
+obj-$(CONFIG_IEEE802154_CC2520) += cc2520.o
diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c
index 50899416f668..c9d2a752abd7 100644
--- a/drivers/net/ieee802154/at86rf230.c
+++ b/drivers/net/ieee802154/at86rf230.c
@@ -19,6 +19,7 @@
* Written by:
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
* Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
+ * Alexander Aring <aar@pengutronix.de>
*/
#include <linux/kernel.h>
#include <linux/module.h>
@@ -26,43 +27,75 @@
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/delay.h>
-#include <linux/mutex.h>
-#include <linux/workqueue.h>
#include <linux/spinlock.h>
#include <linux/spi/spi.h>
#include <linux/spi/at86rf230.h>
+#include <linux/regmap.h>
#include <linux/skbuff.h>
#include <linux/of_gpio.h>
+#include <net/ieee802154.h>
#include <net/mac802154.h>
#include <net/wpan-phy.h>
-struct at86rf230_local {
- struct spi_device *spi;
+struct at86rf230_local;
+/* at86rf2xx chip depend data.
+ * All timings are in us.
+ */
+struct at86rf2xx_chip_data {
+ u16 t_sleep_cycle;
+ u16 t_channel_switch;
+ u16 t_reset_to_off;
+ u16 t_off_to_aack;
+ u16 t_off_to_tx_on;
+ u16 t_frame;
+ u16 t_p_ack;
+ /* short interframe spacing time */
+ u16 t_sifs;
+ /* long interframe spacing time */
+ u16 t_lifs;
+ /* completion timeout for tx in msecs */
+ u16 t_tx_timeout;
+ int rssi_base_val;
- u8 part;
- u8 vers;
+ int (*set_channel)(struct at86rf230_local *, int, int);
+ int (*get_desense_steps)(struct at86rf230_local *, s32);
+};
- u8 buf[2];
- struct mutex bmux;
+#define AT86RF2XX_MAX_BUF (127 + 3)
- struct work_struct irqwork;
- struct completion tx_complete;
+struct at86rf230_state_change {
+ struct at86rf230_local *lp;
+
+ struct spi_message msg;
+ struct spi_transfer trx;
+ u8 buf[AT86RF2XX_MAX_BUF];
+
+ void (*complete)(void *context);
+ u8 from_state;
+ u8 to_state;
+};
+
+struct at86rf230_local {
+ struct spi_device *spi;
struct ieee802154_dev *dev;
+ struct at86rf2xx_chip_data *data;
+ struct regmap *regmap;
- spinlock_t lock;
- bool irq_busy;
- bool is_tx;
- bool tx_aret;
+ struct completion state_complete;
+ struct at86rf230_state_change state;
- int rssi_base_val;
-};
+ struct at86rf230_state_change irq;
-static bool is_rf212(struct at86rf230_local *local)
-{
- return local->part == 7;
-}
+ bool tx_aret;
+ bool is_tx;
+ /* spinlock for is_tx protection */
+ spinlock_t lock;
+ struct completion tx_complete;
+ struct sk_buff *tx_skb;
+ struct at86rf230_state_change tx;
+};
#define RG_TRX_STATUS (0x01)
#define SR_TRX_STATUS 0x01, 0x1f, 0
@@ -256,344 +289,753 @@ static bool is_rf212(struct at86rf230_local *local)
#define STATE_BUSY_RX_AACK_NOCLK 0x1E
#define STATE_TRANSITION_IN_PROGRESS 0x1F
+#define AT86RF2XX_NUMREGS 0x3F
+
static int
-__at86rf230_detect_device(struct spi_device *spi, u16 *man_id, u8 *part,
- u8 *version)
+at86rf230_async_state_change(struct at86rf230_local *lp,
+ struct at86rf230_state_change *ctx,
+ const u8 state, void (*complete)(void *context));
+
+static inline int
+__at86rf230_write(struct at86rf230_local *lp,
+ unsigned int addr, unsigned int data)
{
- u8 data[4];
- u8 *buf = kmalloc(2, GFP_KERNEL);
- int status;
- struct spi_message msg;
- struct spi_transfer xfer = {
- .len = 2,
- .tx_buf = buf,
- .rx_buf = buf,
- };
- u8 reg;
-
- if (!buf)
- return -ENOMEM;
+ return regmap_write(lp->regmap, addr, data);
+}
- for (reg = RG_PART_NUM; reg <= RG_MAN_ID_1; reg++) {
- buf[0] = (reg & CMD_REG_MASK) | CMD_REG;
- buf[1] = 0xff;
- dev_vdbg(&spi->dev, "buf[0] = %02x\n", buf[0]);
- spi_message_init(&msg);
- spi_message_add_tail(&xfer, &msg);
+static inline int
+__at86rf230_read(struct at86rf230_local *lp,
+ unsigned int addr, unsigned int *data)
+{
+ return regmap_read(lp->regmap, addr, data);
+}
- status = spi_sync(spi, &msg);
- dev_vdbg(&spi->dev, "status = %d\n", status);
- if (msg.status)
- status = msg.status;
+static inline int
+at86rf230_read_subreg(struct at86rf230_local *lp,
+ unsigned int addr, unsigned int mask,
+ unsigned int shift, unsigned int *data)
+{
+ int rc;
- dev_vdbg(&spi->dev, "status = %d\n", status);
- dev_vdbg(&spi->dev, "buf[0] = %02x\n", buf[0]);
- dev_vdbg(&spi->dev, "buf[1] = %02x\n", buf[1]);
+ rc = __at86rf230_read(lp, addr, data);
+ if (rc > 0)
+ *data = (*data & mask) >> shift;
- if (status == 0)
- data[reg - RG_PART_NUM] = buf[1];
- else
- break;
+ return rc;
+}
+
+static inline int
+at86rf230_write_subreg(struct at86rf230_local *lp,
+ unsigned int addr, unsigned int mask,
+ unsigned int shift, unsigned int data)
+{
+ return regmap_update_bits(lp->regmap, addr, mask, data << shift);
+}
+
+static bool
+at86rf230_reg_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RG_TRX_STATE:
+ case RG_TRX_CTRL_0:
+ case RG_TRX_CTRL_1:
+ case RG_PHY_TX_PWR:
+ case RG_PHY_ED_LEVEL:
+ case RG_PHY_CC_CCA:
+ case RG_CCA_THRES:
+ case RG_RX_CTRL:
+ case RG_SFD_VALUE:
+ case RG_TRX_CTRL_2:
+ case RG_ANT_DIV:
+ case RG_IRQ_MASK:
+ case RG_VREG_CTRL:
+ case RG_BATMON:
+ case RG_XOSC_CTRL:
+ case RG_RX_SYN:
+ case RG_XAH_CTRL_1:
+ case RG_FTN_CTRL:
+ case RG_PLL_CF:
+ case RG_PLL_DCU:
+ case RG_SHORT_ADDR_0:
+ case RG_SHORT_ADDR_1:
+ case RG_PAN_ID_0:
+ case RG_PAN_ID_1:
+ case RG_IEEE_ADDR_0:
+ case RG_IEEE_ADDR_1:
+ case RG_IEEE_ADDR_2:
+ case RG_IEEE_ADDR_3:
+ case RG_IEEE_ADDR_4:
+ case RG_IEEE_ADDR_5:
+ case RG_IEEE_ADDR_6:
+ case RG_IEEE_ADDR_7:
+ case RG_XAH_CTRL_0:
+ case RG_CSMA_SEED_0:
+ case RG_CSMA_SEED_1:
+ case RG_CSMA_BE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool
+at86rf230_reg_readable(struct device *dev, unsigned int reg)
+{
+ bool rc;
+
+ /* all writeable are also readable */
+ rc = at86rf230_reg_writeable(dev, reg);
+ if (rc)
+ return rc;
+
+ /* readonly regs */
+ switch (reg) {
+ case RG_TRX_STATUS:
+ case RG_PHY_RSSI:
+ case RG_IRQ_STATUS:
+ case RG_PART_NUM:
+ case RG_VERSION_NUM:
+ case RG_MAN_ID_1:
+ case RG_MAN_ID_0:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool
+at86rf230_reg_volatile(struct device *dev, unsigned int reg)
+{
+ /* can be changed during runtime */
+ switch (reg) {
+ case RG_TRX_STATUS:
+ case RG_TRX_STATE:
+ case RG_PHY_RSSI:
+ case RG_PHY_ED_LEVEL:
+ case RG_IRQ_STATUS:
+ case RG_VREG_CTRL:
+ return true;
+ default:
+ return false;
}
+}
- if (status == 0) {
- *part = data[0];
- *version = data[1];
- *man_id = (data[3] << 8) | data[2];
+static bool
+at86rf230_reg_precious(struct device *dev, unsigned int reg)
+{
+ /* don't clear irq line on read */
+ switch (reg) {
+ case RG_IRQ_STATUS:
+ return true;
+ default:
+ return false;
}
+}
- kfree(buf);
+static struct regmap_config at86rf230_regmap_spi_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .write_flag_mask = CMD_REG | CMD_WRITE,
+ .read_flag_mask = CMD_REG,
+ .cache_type = REGCACHE_RBTREE,
+ .max_register = AT86RF2XX_NUMREGS,
+ .writeable_reg = at86rf230_reg_writeable,
+ .readable_reg = at86rf230_reg_readable,
+ .volatile_reg = at86rf230_reg_volatile,
+ .precious_reg = at86rf230_reg_precious,
+};
- return status;
+static void
+at86rf230_async_error_recover(void *context)
+{
+ struct at86rf230_state_change *ctx = context;
+ struct at86rf230_local *lp = ctx->lp;
+
+ at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL);
}
-static int
-__at86rf230_write(struct at86rf230_local *lp, u8 addr, u8 data)
+static void
+at86rf230_async_error(struct at86rf230_local *lp,
+ struct at86rf230_state_change *ctx, int rc)
{
- u8 *buf = lp->buf;
- int status;
- struct spi_message msg;
- struct spi_transfer xfer = {
- .len = 2,
- .tx_buf = buf,
- };
-
- buf[0] = (addr & CMD_REG_MASK) | CMD_REG | CMD_WRITE;
- buf[1] = data;
- dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]);
- dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]);
- spi_message_init(&msg);
- spi_message_add_tail(&xfer, &msg);
-
- status = spi_sync(lp->spi, &msg);
- dev_vdbg(&lp->spi->dev, "status = %d\n", status);
- if (msg.status)
- status = msg.status;
-
- dev_vdbg(&lp->spi->dev, "status = %d\n", status);
- dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]);
- dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]);
-
- return status;
+ 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);
}
+/* Generic function to get some register value in async mode */
static int
-__at86rf230_read_subreg(struct at86rf230_local *lp,
- u8 addr, u8 mask, int shift, u8 *data)
+at86rf230_async_read_reg(struct at86rf230_local *lp, const u8 reg,
+ struct at86rf230_state_change *ctx,
+ void (*complete)(void *context))
{
- u8 *buf = lp->buf;
- int status;
- struct spi_message msg;
- struct spi_transfer xfer = {
- .len = 2,
- .tx_buf = buf,
- .rx_buf = buf,
- };
-
- buf[0] = (addr & CMD_REG_MASK) | CMD_REG;
- buf[1] = 0xff;
- dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]);
- spi_message_init(&msg);
- spi_message_add_tail(&xfer, &msg);
-
- status = spi_sync(lp->spi, &msg);
- dev_vdbg(&lp->spi->dev, "status = %d\n", status);
- if (msg.status)
- status = msg.status;
-
- dev_vdbg(&lp->spi->dev, "status = %d\n", status);
- dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]);
- dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]);
-
- if (status == 0)
- *data = (buf[1] & mask) >> shift;
-
- return status;
+ u8 *tx_buf = ctx->buf;
+
+ tx_buf[0] = (reg & CMD_REG_MASK) | CMD_REG;
+ ctx->trx.len = 2;
+ ctx->msg.complete = complete;
+ return spi_async(lp->spi, &ctx->msg);
}
-static int
-at86rf230_read_subreg(struct at86rf230_local *lp,
- u8 addr, u8 mask, int shift, u8 *data)
+static void
+at86rf230_async_state_assert(void *context)
{
- int status;
+ struct at86rf230_state_change *ctx = context;
+ struct at86rf230_local *lp = ctx->lp;
+ const u8 *buf = ctx->buf;
+ const u8 trx_state = buf[1] & 0x1f;
+
+ /* Assert state change */
+ if (trx_state != ctx->to_state) {
+ /* Special handling if transceiver state is in
+ * STATE_BUSY_RX_AACK and a SHR was detected.
+ */
+ if (trx_state == STATE_BUSY_RX_AACK) {
+ /* Undocumented race condition. If we send a state
+ * change to STATE_RX_AACK_ON the transceiver could
+ * change his state automatically to STATE_BUSY_RX_AACK
+ * if a SHR was detected. This is not an error, but we
+ * can't assert this.
+ */
+ if (ctx->to_state == STATE_RX_AACK_ON)
+ goto done;
+
+ /* If we change to STATE_TX_ON without forcing and
+ * transceiver state is STATE_BUSY_RX_AACK, we wait
+ * 'tFrame + tPAck' receiving time. In this time the
+ * PDU should be received. If the transceiver is still
+ * in STATE_BUSY_RX_AACK, we run a force state change
+ * to STATE_TX_ON. This is a timeout handling, if the
+ * transceiver stucks in STATE_BUSY_RX_AACK.
+ */
+ if (ctx->to_state == STATE_TX_ON) {
+ at86rf230_async_state_change(lp, ctx,
+ STATE_FORCE_TX_ON,
+ ctx->complete);
+ return;
+ }
+ }
+
- mutex_lock(&lp->bmux);
- status = __at86rf230_read_subreg(lp, addr, mask, shift, data);
- mutex_unlock(&lp->bmux);
+ dev_warn(&lp->spi->dev, "unexcept state change from 0x%02x to 0x%02x. Actual state: 0x%02x\n",
+ ctx->from_state, ctx->to_state, trx_state);
+ }
- return status;
+done:
+ if (ctx->complete)
+ ctx->complete(context);
}
-static int
-at86rf230_write_subreg(struct at86rf230_local *lp,
- u8 addr, u8 mask, int shift, u8 data)
+/* Do state change timing delay. */
+static void
+at86rf230_async_state_delay(void *context)
+{
+ struct at86rf230_state_change *ctx = context;
+ struct at86rf230_local *lp = ctx->lp;
+ struct at86rf2xx_chip_data *c = lp->data;
+ bool force = false;
+ int rc;
+
+ /* The force state changes are will show as normal states in the
+ * state status subregister. We change the to_state to the
+ * corresponding one and remember if it was a force change, this
+ * differs if we do a state change from STATE_BUSY_RX_AACK.
+ */
+ switch (ctx->to_state) {
+ case STATE_FORCE_TX_ON:
+ ctx->to_state = STATE_TX_ON;
+ force = true;
+ break;
+ case STATE_FORCE_TRX_OFF:
+ ctx->to_state = STATE_TRX_OFF;
+ force = true;
+ break;
+ default:
+ break;
+ }
+
+ switch (ctx->from_state) {
+ case STATE_TRX_OFF:
+ switch (ctx->to_state) {
+ case STATE_RX_AACK_ON:
+ usleep_range(c->t_off_to_aack, c->t_off_to_aack + 10);
+ goto change;
+ case STATE_TX_ON:
+ usleep_range(c->t_off_to_tx_on,
+ c->t_off_to_tx_on + 10);
+ goto change;
+ default:
+ break;
+ }
+ break;
+ case STATE_BUSY_RX_AACK:
+ switch (ctx->to_state) {
+ case STATE_TX_ON:
+ /* Wait for worst case receiving time if we
+ * didn't make a force change from BUSY_RX_AACK
+ * to TX_ON.
+ */
+ if (!force) {
+ usleep_range(c->t_frame + c->t_p_ack,
+ c->t_frame + c->t_p_ack + 1000);
+ goto change;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ /* Default value, means RESET state */
+ case STATE_P_ON:
+ switch (ctx->to_state) {
+ case STATE_TRX_OFF:
+ usleep_range(c->t_reset_to_off, c->t_reset_to_off + 10);
+ goto change;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* Default delay is 1us in the most cases */
+ udelay(1);
+
+change:
+ rc = at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
+ at86rf230_async_state_assert);
+ if (rc)
+ dev_err(&lp->spi->dev, "spi_async error %d\n", rc);
+}
+
+static void
+at86rf230_async_state_change_start(void *context)
{
- int status;
- u8 val;
+ struct at86rf230_state_change *ctx = context;
+ struct at86rf230_local *lp = ctx->lp;
+ u8 *buf = ctx->buf;
+ const u8 trx_state = buf[1] & 0x1f;
+ int rc;
- mutex_lock(&lp->bmux);
- status = __at86rf230_read_subreg(lp, addr, 0xff, 0, &val);
- if (status)
- goto out;
+ /* Check for "possible" STATE_TRANSITION_IN_PROGRESS */
+ if (trx_state == STATE_TRANSITION_IN_PROGRESS) {
+ udelay(1);
+ rc = at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
+ at86rf230_async_state_change_start);
+ if (rc)
+ dev_err(&lp->spi->dev, "spi_async error %d\n", rc);
+ return;
+ }
- val &= ~mask;
- val |= (data << shift) & mask;
+ /* Check if we already are in the state which we change in */
+ if (trx_state == ctx->to_state) {
+ if (ctx->complete)
+ ctx->complete(context);
+ return;
+ }
- status = __at86rf230_write(lp, addr, val);
-out:
- mutex_unlock(&lp->bmux);
+ /* Set current state to the context of state change */
+ ctx->from_state = trx_state;
- return status;
+ /* 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->trx.len = 2;
+ ctx->msg.complete = at86rf230_async_state_delay;
+ rc = spi_async(lp->spi, &ctx->msg);
+ if (rc)
+ dev_err(&lp->spi->dev, "spi_async error %d\n", rc);
}
static int
-at86rf230_write_fbuf(struct at86rf230_local *lp, u8 *data, u8 len)
+at86rf230_async_state_change(struct at86rf230_local *lp,
+ struct at86rf230_state_change *ctx,
+ const u8 state, void (*complete)(void *context))
{
- u8 *buf = lp->buf;
- int status;
- struct spi_message msg;
- struct spi_transfer xfer_head = {
- .len = 2,
- .tx_buf = buf,
-
- };
- struct spi_transfer xfer_buf = {
- .len = len,
- .tx_buf = data,
- };
-
- mutex_lock(&lp->bmux);
- buf[0] = CMD_WRITE | CMD_FB;
- buf[1] = len + 2; /* 2 bytes for CRC that isn't written */
-
- dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]);
- dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]);
-
- spi_message_init(&msg);
- spi_message_add_tail(&xfer_head, &msg);
- spi_message_add_tail(&xfer_buf, &msg);
-
- status = spi_sync(lp->spi, &msg);
- dev_vdbg(&lp->spi->dev, "status = %d\n", status);
- if (msg.status)
- status = msg.status;
-
- dev_vdbg(&lp->spi->dev, "status = %d\n", status);
- dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]);
- dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]);
-
- mutex_unlock(&lp->bmux);
- return status;
+ /* Initialization for the state change context */
+ ctx->to_state = state;
+ ctx->complete = complete;
+ return at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
+ at86rf230_async_state_change_start);
}
+static void
+at86rf230_sync_state_change_complete(void *context)
+{
+ struct at86rf230_state_change *ctx = context;
+ struct at86rf230_local *lp = ctx->lp;
+
+ complete(&lp->state_complete);
+}
+
+/* This function do a sync framework above the async state change.
+ * Some callbacks of the IEEE 802.15.4 driver interface need to be
+ * handled synchronously.
+ */
static int
-at86rf230_read_fbuf(struct at86rf230_local *lp, u8 *data, u8 *len, u8 *lqi)
+at86rf230_sync_state_change(struct at86rf230_local *lp, unsigned int state)
{
- u8 *buf = lp->buf;
- int status;
- struct spi_message msg;
- struct spi_transfer xfer_head = {
- .len = 2,
- .tx_buf = buf,
- .rx_buf = buf,
- };
- struct spi_transfer xfer_head1 = {
- .len = 2,
- .tx_buf = buf,
- .rx_buf = buf,
- };
- struct spi_transfer xfer_buf = {
- .len = 0,
- .rx_buf = data,
- };
-
- mutex_lock(&lp->bmux);
+ int rc;
- buf[0] = CMD_FB;
- buf[1] = 0x00;
+ rc = at86rf230_async_state_change(lp, &lp->state, state,
+ at86rf230_sync_state_change_complete);
+ if (rc) {
+ at86rf230_async_error(lp, &lp->state, rc);
+ return rc;
+ }
- spi_message_init(&msg);
- spi_message_add_tail(&xfer_head, &msg);
+ rc = wait_for_completion_timeout(&lp->state_complete,
+ msecs_to_jiffies(100));
+ if (!rc)
+ return -ETIMEDOUT;
- status = spi_sync(lp->spi, &msg);
- dev_vdbg(&lp->spi->dev, "status = %d\n", status);
+ return 0;
+}
- xfer_buf.len = *(buf + 1) + 1;
- *len = buf[1];
+static void
+at86rf230_tx_complete(void *context)
+{
+ struct at86rf230_state_change *ctx = context;
+ struct at86rf230_local *lp = ctx->lp;
- buf[0] = CMD_FB;
- buf[1] = 0x00;
+ complete(&lp->tx_complete);
+}
- spi_message_init(&msg);
- spi_message_add_tail(&xfer_head1, &msg);
- spi_message_add_tail(&xfer_buf, &msg);
+static void
+at86rf230_tx_on(void *context)
+{
+ struct at86rf230_state_change *ctx = context;
+ struct at86rf230_local *lp = ctx->lp;
+ int rc;
+
+ rc = at86rf230_async_state_change(lp, &lp->irq, STATE_RX_AACK_ON,
+ at86rf230_tx_complete);
+ if (rc)
+ at86rf230_async_error(lp, ctx, rc);
+}
- status = spi_sync(lp->spi, &msg);
+static void
+at86rf230_tx_trac_error(void *context)
+{
+ struct at86rf230_state_change *ctx = context;
+ struct at86rf230_local *lp = ctx->lp;
+ int rc;
- if (msg.status)
- status = msg.status;
+ rc = at86rf230_async_state_change(lp, ctx, STATE_TX_ON,
+ at86rf230_tx_on);
+ if (rc)
+ at86rf230_async_error(lp, ctx, rc);
+}
- dev_vdbg(&lp->spi->dev, "status = %d\n", status);
- dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", buf[0]);
- dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", buf[1]);
+static void
+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;
+ int rc;
- if (status) {
- if (lqi && (*len > lp->buf[1]))
- *lqi = data[lp->buf[1]];
+ /* If trac status is different than zero we need to do a state change
+ * to STATE_FORCE_TRX_OFF then STATE_TX_ON to recover the transceiver
+ * state to TX_ON.
+ */
+ if (trac) {
+ rc = at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF,
+ at86rf230_tx_trac_error);
+ if (rc)
+ at86rf230_async_error(lp, ctx, rc);
+ return;
}
- mutex_unlock(&lp->bmux);
- return status;
+ at86rf230_tx_on(context);
}
-static int
-at86rf230_ed(struct ieee802154_dev *dev, u8 *level)
+
+static void
+at86rf230_tx_trac_status(void *context)
{
- might_sleep();
- BUG_ON(!level);
- *level = 0xbe;
- return 0;
+ struct at86rf230_state_change *ctx = context;
+ struct at86rf230_local *lp = ctx->lp;
+ int rc;
+
+ rc = at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx,
+ at86rf230_tx_trac_check);
+ if (rc)
+ at86rf230_async_error(lp, ctx, rc);
+}
+
+static void
+at86rf230_rx(struct at86rf230_local *lp,
+ const u8 *data, u8 len)
+{
+ u8 lqi;
+ struct sk_buff *skb;
+ u8 rx_local_buf[AT86RF2XX_MAX_BUF];
+
+ if (len < 2)
+ return;
+
+ /* read full frame buffer and invalid lqi value to lowest
+ * indicator if frame was is in a corrupted state.
+ */
+ if (len > IEEE802154_MTU) {
+ lqi = 0;
+ len = IEEE802154_MTU;
+ dev_vdbg(&lp->spi->dev, "corrupted frame received\n");
+ } else {
+ lqi = data[len];
+ }
+
+ memcpy(rx_local_buf, data, len);
+ enable_irq(lp->spi->irq);
+
+ skb = alloc_skb(IEEE802154_MTU, GFP_ATOMIC);
+ if (!skb) {
+ dev_vdbg(&lp->spi->dev, "failed to allocate sk_buff\n");
+ return;
+ }
+
+ memcpy(skb_put(skb, len), rx_local_buf, len);
+
+ /* We do not put CRC into the frame */
+ skb_trim(skb, len - 2);
+
+ ieee802154_rx_irqsafe(lp->dev, skb, lqi);
+}
+
+static void
+at86rf230_rx_read_frame_complete(void *context)
+{
+ struct at86rf230_state_change *ctx = context;
+ struct at86rf230_local *lp = ctx->lp;
+ const u8 *buf = lp->irq.buf;
+ const u8 len = buf[1];
+
+ at86rf230_rx(lp, buf + 2, len);
}
static int
-at86rf230_state(struct ieee802154_dev *dev, int state)
+at86rf230_rx_read_frame(struct at86rf230_local *lp)
{
- struct at86rf230_local *lp = dev->priv;
+ u8 *buf = lp->irq.buf;
+
+ buf[0] = CMD_FB;
+ lp->irq.trx.len = AT86RF2XX_MAX_BUF;
+ lp->irq.msg.complete = at86rf230_rx_read_frame_complete;
+ return spi_async(lp->spi, &lp->irq.msg);
+}
+
+static void
+at86rf230_rx_trac_check(void *context)
+{
+ struct at86rf230_state_change *ctx = context;
+ struct at86rf230_local *lp = ctx->lp;
int rc;
- u8 val;
- u8 desired_status;
- might_sleep();
+ /* 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.
+ */
- if (state == STATE_FORCE_TX_ON)
- desired_status = STATE_TX_ON;
- else if (state == STATE_FORCE_TRX_OFF)
- desired_status = STATE_TRX_OFF;
- else
- desired_status = state;
+ rc = at86rf230_rx_read_frame(lp);
+ if (rc) {
+ enable_irq(lp->spi->irq);
+ at86rf230_async_error(lp, ctx, rc);
+ }
+}
+
+static int
+at86rf230_irq_trx_end(struct at86rf230_local *lp)
+{
+ spin_lock(&lp->lock);
+ if (lp->is_tx) {
+ lp->is_tx = 0;
+ spin_unlock(&lp->lock);
+ enable_irq(lp->spi->irq);
+
+ if (lp->tx_aret)
+ return at86rf230_async_state_change(lp, &lp->irq,
+ STATE_FORCE_TX_ON,
+ at86rf230_tx_trac_status);
+ else
+ return at86rf230_async_state_change(lp, &lp->irq,
+ STATE_RX_AACK_ON,
+ at86rf230_tx_complete);
+ } else {
+ spin_unlock(&lp->lock);
+ return at86rf230_async_read_reg(lp, RG_TRX_STATE, &lp->irq,
+ at86rf230_rx_trac_check);
+ }
+}
- do {
- rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &val);
+static void
+at86rf230_irq_status(void *context)
+{
+ struct at86rf230_state_change *ctx = context;
+ struct at86rf230_local *lp = ctx->lp;
+ const u8 *buf = lp->irq.buf;
+ const u8 irq = buf[1];
+ int rc;
+
+ if (irq & IRQ_TRX_END) {
+ rc = at86rf230_irq_trx_end(lp);
if (rc)
- goto err;
- } while (val == STATE_TRANSITION_IN_PROGRESS);
+ at86rf230_async_error(lp, ctx, rc);
+ } else {
+ enable_irq(lp->spi->irq);
+ dev_err(&lp->spi->dev, "not supported irq %02x received\n",
+ irq);
+ }
+}
- if (val == desired_status)
- return 0;
+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;
+ int rc;
- /* state is equal to phy states */
- rc = at86rf230_write_subreg(lp, SR_TRX_CMD, state);
- if (rc)
- goto err;
+ disable_irq_nosync(lp->spi->irq);
- do {
- rc = at86rf230_read_subreg(lp, SR_TRX_STATUS, &val);
- if (rc)
- goto err;
- } while (val == STATE_TRANSITION_IN_PROGRESS);
+ buf[0] = (RG_IRQ_STATUS & CMD_REG_MASK) | CMD_REG;
+ ctx->trx.len = 2;
+ ctx->msg.complete = at86rf230_irq_status;
+ rc = spi_async(lp->spi, &ctx->msg);
+ if (rc) {
+ at86rf230_async_error(lp, ctx, rc);
+ return IRQ_NONE;
+ }
+ return IRQ_HANDLED;
+}
- if (val == desired_status ||
- (desired_status == STATE_RX_ON && val == STATE_BUSY_RX) ||
- (desired_status == STATE_RX_AACK_ON && val == STATE_BUSY_RX_AACK))
- return 0;
+static void
+at86rf230_write_frame_complete(void *context)
+{
+ struct at86rf230_state_change *ctx = context;
+ struct at86rf230_local *lp = ctx->lp;
+ u8 *buf = ctx->buf;
+ int rc;
- pr_err("unexpected state change: %d, asked for %d\n", val, state);
- return -EBUSY;
+ buf[0] = (RG_TRX_STATE & CMD_REG_MASK) | CMD_REG | CMD_WRITE;
+ buf[1] = STATE_BUSY_TX;
+ ctx->trx.len = 2;
+ ctx->msg.complete = NULL;
+ rc = spi_async(lp->spi, &ctx->msg);
+ if (rc)
+ at86rf230_async_error(lp, ctx, rc);
+}
-err:
- pr_err("error: %d\n", rc);
- return rc;
+static void
+at86rf230_write_frame(void *context)
+{
+ struct at86rf230_state_change *ctx = context;
+ struct at86rf230_local *lp = ctx->lp;
+ struct sk_buff *skb = lp->tx_skb;
+ u8 *buf = lp->tx.buf;
+ int rc;
+
+ spin_lock(&lp->lock);
+ lp->is_tx = 1;
+ spin_unlock(&lp->lock);
+
+ buf[0] = CMD_FB | CMD_WRITE;
+ buf[1] = skb->len + 2;
+ memcpy(buf + 2, skb->data, skb->len);
+ lp->tx.trx.len = skb->len + 2;
+ lp->tx.msg.complete = at86rf230_write_frame_complete;
+ rc = spi_async(lp->spi, &lp->tx.msg);
+ if (rc)
+ at86rf230_async_error(lp, ctx, rc);
+}
+
+static void
+at86rf230_xmit_tx_on(void *context)
+{
+ struct at86rf230_state_change *ctx = context;
+ struct at86rf230_local *lp = ctx->lp;
+ int rc;
+
+ rc = at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON,
+ at86rf230_write_frame);
+ if (rc)
+ at86rf230_async_error(lp, ctx, rc);
}
static int
-at86rf230_start(struct ieee802154_dev *dev)
+at86rf230_xmit(struct ieee802154_dev *dev, struct sk_buff *skb)
{
struct at86rf230_local *lp = dev->priv;
- u8 rc;
+ struct at86rf230_state_change *ctx = &lp->tx;
- rc = at86rf230_write_subreg(lp, SR_RX_SAFE_MODE, 1);
- if (rc)
- return rc;
+ void (*tx_complete)(void *context) = at86rf230_write_frame;
+ int rc;
- rc = at86rf230_state(dev, STATE_TX_ON);
- if (rc)
+ lp->tx_skb = skb;
+
+ /* In ARET mode we need to go into STATE_TX_ARET_ON after we
+ * are in STATE_TX_ON. The pfad differs here, so we change
+ * the complete handler.
+ */
+ if (lp->tx_aret)
+ tx_complete = at86rf230_xmit_tx_on;
+
+ rc = at86rf230_async_state_change(lp, ctx, STATE_TX_ON,
+ tx_complete);
+ if (rc) {
+ at86rf230_async_error(lp, ctx, rc);
return rc;
+ }
+ rc = wait_for_completion_interruptible_timeout(&lp->tx_complete,
+ msecs_to_jiffies(lp->data->t_tx_timeout));
+ if (!rc) {
+ at86rf230_async_error(lp, ctx, rc);
+ return -ETIMEDOUT;
+ }
+
+ /* Interfame spacing time, which is phy depend.
+ * TODO
+ * Move this handling in MAC 802.15.4 layer.
+ * This is currently a workaround to avoid fragmenation issues.
+ */
+ if (skb->len > 18)
+ usleep_range(lp->data->t_lifs, lp->data->t_lifs + 10);
+ else
+ usleep_range(lp->data->t_sifs, lp->data->t_sifs + 10);
+
+ return 0;
+}
- return at86rf230_state(dev, STATE_RX_AACK_ON);
+static int
+at86rf230_ed(struct ieee802154_dev *dev, u8 *level)
+{
+ might_sleep();
+ BUG_ON(!level);
+ *level = 0xbe;
+ return 0;
+}
+
+static int
+at86rf230_start(struct ieee802154_dev *dev)
+{
+ return at86rf230_sync_state_change(dev->priv, STATE_RX_AACK_ON);
}
static void
at86rf230_stop(struct ieee802154_dev *dev)
{
- at86rf230_state(dev, STATE_FORCE_TRX_OFF);
+ at86rf230_sync_state_change(dev->priv, STATE_FORCE_TRX_OFF);
}
static int
-at86rf230_set_channel(struct at86rf230_local *lp, int page, int channel)
+at86rf23x_set_channel(struct at86rf230_local *lp, int page, int channel)
{
- lp->rssi_base_val = -91;
-
return at86rf230_write_subreg(lp, SR_CHANNEL, channel);
}
@@ -611,10 +1053,10 @@ at86rf212_set_channel(struct at86rf230_local *lp, int page, int channel)
if (page == 0) {
rc = at86rf230_write_subreg(lp, SR_BPSK_QPSK, 0);
- lp->rssi_base_val = -100;
+ lp->data->rssi_base_val = -100;
} else {
rc = at86rf230_write_subreg(lp, SR_BPSK_QPSK, 1);
- lp->rssi_base_val = -98;
+ lp->data->rssi_base_val = -98;
}
if (rc < 0)
return rc;
@@ -636,14 +1078,13 @@ at86rf230_channel(struct ieee802154_dev *dev, int page, int channel)
return -EINVAL;
}
- if (is_rf212(lp))
- rc = at86rf212_set_channel(lp, page, channel);
- else
- rc = at86rf230_set_channel(lp, page, channel);
+ rc = lp->data->set_channel(lp, page, channel);
if (rc < 0)
return rc;
- msleep(1); /* Wait for PLL */
+ /* Wait for PLL */
+ usleep_range(lp->data->t_channel_switch,
+ lp->data->t_channel_switch + 10);
dev->phy->current_channel = channel;
dev->phy->current_page = page;
@@ -651,92 +1092,6 @@ at86rf230_channel(struct ieee802154_dev *dev, int page, int channel)
}
static int
-at86rf230_xmit(struct ieee802154_dev *dev, struct sk_buff *skb)
-{
- struct at86rf230_local *lp = dev->priv;
- int rc;
- unsigned long flags;
-
- spin_lock_irqsave(&lp->lock, flags);
- if (lp->irq_busy) {
- spin_unlock_irqrestore(&lp->lock, flags);
- return -EBUSY;
- }
- spin_unlock_irqrestore(&lp->lock, flags);
-
- might_sleep();
-
- rc = at86rf230_state(dev, STATE_FORCE_TX_ON);
- if (rc)
- goto err;
-
- spin_lock_irqsave(&lp->lock, flags);
- lp->is_tx = 1;
- reinit_completion(&lp->tx_complete);
- spin_unlock_irqrestore(&lp->lock, flags);
-
- rc = at86rf230_write_fbuf(lp, skb->data, skb->len);
- if (rc)
- goto err_rx;
-
- if (lp->tx_aret) {
- rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_TX_ARET_ON);
- if (rc)
- goto err_rx;
- }
-
- rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_BUSY_TX);
- if (rc)
- goto err_rx;
-
- rc = wait_for_completion_interruptible(&lp->tx_complete);
- if (rc < 0)
- goto err_rx;
-
- return at86rf230_start(dev);
-err_rx:
- at86rf230_start(dev);
-err:
- pr_err("error: %d\n", rc);
-
- spin_lock_irqsave(&lp->lock, flags);
- lp->is_tx = 0;
- spin_unlock_irqrestore(&lp->lock, flags);
-
- return rc;
-}
-
-static int at86rf230_rx(struct at86rf230_local *lp)
-{
- u8 len = 128, lqi = 0;
- struct sk_buff *skb;
-
- skb = alloc_skb(len, GFP_KERNEL);
-
- if (!skb)
- return -ENOMEM;
-
- if (at86rf230_read_fbuf(lp, skb_put(skb, len), &len, &lqi))
- goto err;
-
- if (len < 2)
- goto err;
-
- skb_trim(skb, len - 2); /* We do not put CRC into the frame */
-
- ieee802154_rx_irqsafe(lp->dev, skb, lqi);
-
- dev_dbg(&lp->spi->dev, "READ_FBUF: %d %x\n", len, lqi);
-
- return 0;
-err:
- pr_debug("received frame is too small\n");
-
- kfree_skb(skb);
- return -EINVAL;
-}
-
-static int
at86rf230_set_hw_addr_filt(struct ieee802154_dev *dev,
struct ieee802154_hw_addr_filt *filt,
unsigned long changed)
@@ -784,7 +1139,7 @@ at86rf230_set_hw_addr_filt(struct ieee802154_dev *dev,
}
static int
-at86rf212_set_txpower(struct ieee802154_dev *dev, int db)
+at86rf230_set_txpower(struct ieee802154_dev *dev, int db)
{
struct at86rf230_local *lp = dev->priv;
@@ -803,7 +1158,7 @@ at86rf212_set_txpower(struct ieee802154_dev *dev, int db)
}
static int
-at86rf212_set_lbt(struct ieee802154_dev *dev, bool on)
+at86rf230_set_lbt(struct ieee802154_dev *dev, bool on)
{
struct at86rf230_local *lp = dev->priv;
@@ -811,7 +1166,7 @@ at86rf212_set_lbt(struct ieee802154_dev *dev, bool on)
}
static int
-at86rf212_set_cca_mode(struct ieee802154_dev *dev, u8 mode)
+at86rf230_set_cca_mode(struct ieee802154_dev *dev, u8 mode)
{
struct at86rf230_local *lp = dev->priv;
@@ -819,21 +1174,31 @@ at86rf212_set_cca_mode(struct ieee802154_dev *dev, u8 mode)
}
static int
-at86rf212_set_cca_ed_level(struct ieee802154_dev *dev, s32 level)
+at86rf212_get_desens_steps(struct at86rf230_local *lp, s32 level)
+{
+ return (level - lp->data->rssi_base_val) * 100 / 207;
+}
+
+static int
+at86rf23x_get_desens_steps(struct at86rf230_local *lp, s32 level)
+{
+ return (level - lp->data->rssi_base_val) / 2;
+}
+
+static int
+at86rf230_set_cca_ed_level(struct ieee802154_dev *dev, s32 level)
{
struct at86rf230_local *lp = dev->priv;
- int desens_steps;
- if (level < lp->rssi_base_val || level > 30)
+ if (level < lp->data->rssi_base_val || level > 30)
return -EINVAL;
- desens_steps = (level - lp->rssi_base_val) * 100 / 207;
-
- return at86rf230_write_subreg(lp, SR_CCA_ED_THRES, desens_steps);
+ return at86rf230_write_subreg(lp, SR_CCA_ED_THRES,
+ lp->data->get_desense_steps(lp, level));
}
static int
-at86rf212_set_csma_params(struct ieee802154_dev *dev, u8 min_be, u8 max_be,
+at86rf230_set_csma_params(struct ieee802154_dev *dev, u8 min_be, u8 max_be,
u8 retries)
{
struct at86rf230_local *lp = dev->priv;
@@ -854,7 +1219,7 @@ at86rf212_set_csma_params(struct ieee802154_dev *dev, u8 min_be, u8 max_be,
}
static int
-at86rf212_set_frame_retries(struct ieee802154_dev *dev, s8 retries)
+at86rf230_set_frame_retries(struct ieee802154_dev *dev, s8 retries)
{
struct at86rf230_local *lp = dev->priv;
int rc = 0;
@@ -878,110 +1243,84 @@ static struct ieee802154_ops at86rf230_ops = {
.start = at86rf230_start,
.stop = at86rf230_stop,
.set_hw_addr_filt = at86rf230_set_hw_addr_filt,
+ .set_txpower = at86rf230_set_txpower,
+ .set_lbt = at86rf230_set_lbt,
+ .set_cca_mode = at86rf230_set_cca_mode,
+ .set_cca_ed_level = at86rf230_set_cca_ed_level,
+ .set_csma_params = at86rf230_set_csma_params,
+ .set_frame_retries = at86rf230_set_frame_retries,
};
-static struct ieee802154_ops at86rf212_ops = {
- .owner = THIS_MODULE,
- .xmit = at86rf230_xmit,
- .ed = at86rf230_ed,
- .set_channel = at86rf230_channel,
- .start = at86rf230_start,
- .stop = at86rf230_stop,
- .set_hw_addr_filt = at86rf230_set_hw_addr_filt,
- .set_txpower = at86rf212_set_txpower,
- .set_lbt = at86rf212_set_lbt,
- .set_cca_mode = at86rf212_set_cca_mode,
- .set_cca_ed_level = at86rf212_set_cca_ed_level,
- .set_csma_params = at86rf212_set_csma_params,
- .set_frame_retries = at86rf212_set_frame_retries,
+static struct at86rf2xx_chip_data at86rf233_data = {
+ .t_sleep_cycle = 330,
+ .t_channel_switch = 11,
+ .t_reset_to_off = 26,
+ .t_off_to_aack = 80,
+ .t_off_to_tx_on = 80,
+ .t_frame = 4096,
+ .t_p_ack = 545,
+ .t_sifs = 192,
+ .t_lifs = 480,
+ .t_tx_timeout = 2000,
+ .rssi_base_val = -91,
+ .set_channel = at86rf23x_set_channel,
+ .get_desense_steps = at86rf23x_get_desens_steps
};
-static void at86rf230_irqwork(struct work_struct *work)
-{
- struct at86rf230_local *lp =
- container_of(work, struct at86rf230_local, irqwork);
- u8 status = 0, val;
- int rc;
- unsigned long flags;
-
- rc = at86rf230_read_subreg(lp, RG_IRQ_STATUS, 0xff, 0, &val);
- status |= val;
-
- status &= ~IRQ_PLL_LOCK; /* ignore */
- status &= ~IRQ_RX_START; /* ignore */
- status &= ~IRQ_AMI; /* ignore */
- status &= ~IRQ_TRX_UR; /* FIXME: possibly handle ???*/
-
- if (status & IRQ_TRX_END) {
- status &= ~IRQ_TRX_END;
- spin_lock_irqsave(&lp->lock, flags);
- if (lp->is_tx) {
- lp->is_tx = 0;
- spin_unlock_irqrestore(&lp->lock, flags);
- complete(&lp->tx_complete);
- } else {
- spin_unlock_irqrestore(&lp->lock, flags);
- at86rf230_rx(lp);
- }
- }
-
- spin_lock_irqsave(&lp->lock, flags);
- lp->irq_busy = 0;
- spin_unlock_irqrestore(&lp->lock, flags);
-}
-
-static void at86rf230_irqwork_level(struct work_struct *work)
-{
- struct at86rf230_local *lp =
- container_of(work, struct at86rf230_local, irqwork);
-
- at86rf230_irqwork(work);
-
- enable_irq(lp->spi->irq);
-}
-
-static irqreturn_t at86rf230_isr(int irq, void *data)
-{
- struct at86rf230_local *lp = data;
- unsigned long flags;
-
- spin_lock_irqsave(&lp->lock, flags);
- lp->irq_busy = 1;
- spin_unlock_irqrestore(&lp->lock, flags);
-
- schedule_work(&lp->irqwork);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t at86rf230_isr_level(int irq, void *data)
-{
- disable_irq_nosync(irq);
+static struct at86rf2xx_chip_data at86rf231_data = {
+ .t_sleep_cycle = 330,
+ .t_channel_switch = 24,
+ .t_reset_to_off = 37,
+ .t_off_to_aack = 110,
+ .t_off_to_tx_on = 110,
+ .t_frame = 4096,
+ .t_p_ack = 545,
+ .t_sifs = 192,
+ .t_lifs = 480,
+ .t_tx_timeout = 2000,
+ .rssi_base_val = -91,
+ .set_channel = at86rf23x_set_channel,
+ .get_desense_steps = at86rf23x_get_desens_steps
+};
- return at86rf230_isr(irq, data);
-}
+static struct at86rf2xx_chip_data at86rf212_data = {
+ .t_sleep_cycle = 330,
+ .t_channel_switch = 11,
+ .t_reset_to_off = 26,
+ .t_off_to_aack = 200,
+ .t_off_to_tx_on = 200,
+ .t_frame = 4096,
+ .t_p_ack = 545,
+ .t_sifs = 192,
+ .t_lifs = 480,
+ .t_tx_timeout = 2000,
+ .rssi_base_val = -100,
+ .set_channel = at86rf212_set_channel,
+ .get_desense_steps = at86rf212_get_desens_steps
+};
static int at86rf230_hw_init(struct at86rf230_local *lp)
{
- int rc, irq_pol, irq_type;
- u8 dvdd;
+ int rc, irq_type, irq_pol = IRQ_ACTIVE_HIGH;
+ unsigned int dvdd;
u8 csma_seed[2];
- rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_FORCE_TRX_OFF);
+ rc = at86rf230_sync_state_change(lp, STATE_FORCE_TRX_OFF);
if (rc)
return rc;
irq_type = irq_get_trigger_type(lp->spi->irq);
- /* configure irq polarity, defaults to high active */
- if (irq_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW))
+ if (irq_type == IRQ_TYPE_EDGE_FALLING)
irq_pol = IRQ_ACTIVE_LOW;
- else
- irq_pol = IRQ_ACTIVE_HIGH;
rc = at86rf230_write_subreg(lp, SR_IRQ_POLARITY, irq_pol);
if (rc)
return rc;
+ rc = at86rf230_write_subreg(lp, SR_RX_SAFE_MODE, 1);
+ if (rc)
+ return rc;
+
rc = at86rf230_write_subreg(lp, SR_IRQ_MASK, IRQ_TRX_END);
if (rc)
return rc;
@@ -1004,7 +1343,8 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
if (rc)
return rc;
/* Wait the next SLEEP cycle */
- msleep(100);
+ usleep_range(lp->data->t_sleep_cycle,
+ lp->data->t_sleep_cycle + 100);
rc = at86rf230_read_subreg(lp, SR_DVDD_OK, &dvdd);
if (rc)
@@ -1037,18 +1377,111 @@ done:
return pdata;
}
+static int
+at86rf230_detect_device(struct at86rf230_local *lp)
+{
+ unsigned int part, version, val;
+ u16 man_id = 0;
+ const char *chip;
+ int rc;
+
+ rc = __at86rf230_read(lp, RG_MAN_ID_0, &val);
+ if (rc)
+ return rc;
+ man_id |= val;
+
+ rc = __at86rf230_read(lp, RG_MAN_ID_1, &val);
+ if (rc)
+ return rc;
+ man_id |= (val << 8);
+
+ rc = __at86rf230_read(lp, RG_PART_NUM, &part);
+ if (rc)
+ return rc;
+
+ rc = __at86rf230_read(lp, RG_PART_NUM, &version);
+ if (rc)
+ return rc;
+
+ if (man_id != 0x001f) {
+ dev_err(&lp->spi->dev, "Non-Atmel dev found (MAN_ID %02x %02x)\n",
+ man_id >> 8, man_id & 0xFF);
+ return -EINVAL;
+ }
+
+ lp->dev->extra_tx_headroom = 0;
+ lp->dev->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK |
+ IEEE802154_HW_TXPOWER | IEEE802154_HW_CSMA;
+
+ switch (part) {
+ case 2:
+ chip = "at86rf230";
+ rc = -ENOTSUPP;
+ break;
+ case 3:
+ chip = "at86rf231";
+ lp->data = &at86rf231_data;
+ lp->dev->phy->channels_supported[0] = 0x7FFF800;
+ break;
+ case 7:
+ chip = "at86rf212";
+ if (version == 1) {
+ lp->data = &at86rf212_data;
+ lp->dev->flags |= IEEE802154_HW_LBT;
+ lp->dev->phy->channels_supported[0] = 0x00007FF;
+ lp->dev->phy->channels_supported[2] = 0x00007FF;
+ } else {
+ rc = -ENOTSUPP;
+ }
+ break;
+ case 11:
+ chip = "at86rf233";
+ lp->data = &at86rf233_data;
+ lp->dev->phy->channels_supported[0] = 0x7FFF800;
+ break;
+ default:
+ chip = "unkown";
+ rc = -ENOTSUPP;
+ break;
+ }
+
+ dev_info(&lp->spi->dev, "Detected %s chip version %d\n", chip, version);
+
+ return rc;
+}
+
+static void
+at86rf230_setup_spi_messages(struct at86rf230_local *lp)
+{
+ lp->state.lp = lp;
+ spi_message_init(&lp->state.msg);
+ lp->state.msg.context = &lp->state;
+ 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);
+
+ lp->irq.lp = lp;
+ spi_message_init(&lp->irq.msg);
+ lp->irq.msg.context = &lp->irq;
+ 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);
+
+ lp->tx.lp = lp;
+ spi_message_init(&lp->tx.msg);
+ lp->tx.msg.context = &lp->tx;
+ 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);
+}
+
static int at86rf230_probe(struct spi_device *spi)
{
struct at86rf230_platform_data *pdata;
struct ieee802154_dev *dev;
struct at86rf230_local *lp;
- u16 man_id = 0;
- u8 part = 0, version = 0, status;
- irq_handler_t irq_handler;
- work_func_t irq_worker;
+ unsigned int status;
int rc, irq_type;
- const char *chip;
- struct ieee802154_ops *ops = NULL;
if (!spi->irq) {
dev_err(&spi->dev, "no IRQ specified\n");
@@ -1084,107 +1517,60 @@ static int at86rf230_probe(struct spi_device *spi)
usleep_range(120, 240);
}
- rc = __at86rf230_detect_device(spi, &man_id, &part, &version);
- if (rc < 0)
- return rc;
-
- if (man_id != 0x001f) {
- dev_err(&spi->dev, "Non-Atmel dev found (MAN_ID %02x %02x)\n",
- man_id >> 8, man_id & 0xFF);
- return -EINVAL;
- }
-
- switch (part) {
- case 2:
- chip = "at86rf230";
- /* FIXME: should be easy to support; */
- break;
- case 3:
- chip = "at86rf231";
- ops = &at86rf230_ops;
- break;
- case 7:
- chip = "at86rf212";
- if (version == 1)
- ops = &at86rf212_ops;
- break;
- case 11:
- chip = "at86rf233";
- ops = &at86rf230_ops;
- break;
- default:
- chip = "UNKNOWN";
- break;
- }
-
- dev_info(&spi->dev, "Detected %s chip version %d\n", chip, version);
- if (!ops)
- return -ENOTSUPP;
-
- dev = ieee802154_alloc_device(sizeof(*lp), ops);
+ dev = ieee802154_alloc_device(sizeof(*lp), &at86rf230_ops);
if (!dev)
return -ENOMEM;
lp = dev->priv;
lp->dev = dev;
- lp->part = part;
- lp->vers = version;
-
lp->spi = spi;
-
dev->parent = &spi->dev;
- dev->extra_tx_headroom = 0;
- dev->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK;
- irq_type = irq_get_trigger_type(spi->irq);
- if (!irq_type)
- irq_type = IRQF_TRIGGER_RISING;
- if (irq_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) {
- irq_worker = at86rf230_irqwork;
- irq_handler = at86rf230_isr;
- } else {
- irq_worker = at86rf230_irqwork_level;
- irq_handler = at86rf230_isr_level;
+ lp->regmap = devm_regmap_init_spi(spi, &at86rf230_regmap_spi_config);
+ if (IS_ERR(lp->regmap)) {
+ rc = PTR_ERR(lp->regmap);
+ dev_err(&spi->dev, "Failed to allocate register map: %d\n",
+ rc);
+ goto free_dev;
}
- mutex_init(&lp->bmux);
- INIT_WORK(&lp->irqwork, irq_worker);
+ at86rf230_setup_spi_messages(lp);
+
+ rc = at86rf230_detect_device(lp);
+ if (rc < 0)
+ goto free_dev;
+
spin_lock_init(&lp->lock);
init_completion(&lp->tx_complete);
+ init_completion(&lp->state_complete);
spi_set_drvdata(spi, lp);
- if (is_rf212(lp)) {
- dev->phy->channels_supported[0] = 0x00007FF;
- dev->phy->channels_supported[2] = 0x00007FF;
- } else {
- dev->phy->channels_supported[0] = 0x7FFF800;
- }
-
rc = at86rf230_hw_init(lp);
if (rc)
- goto err_hw_init;
+ goto free_dev;
/* Read irq status register to reset irq line */
rc = at86rf230_read_subreg(lp, RG_IRQ_STATUS, 0xff, 0, &status);
if (rc)
- goto err_hw_init;
+ goto free_dev;
+
+ irq_type = irq_get_trigger_type(spi->irq);
+ if (!irq_type)
+ irq_type = IRQF_TRIGGER_RISING;
- rc = devm_request_irq(&spi->dev, spi->irq, irq_handler,
- IRQF_SHARED | irq_type,
- dev_name(&spi->dev), lp);
+ rc = devm_request_irq(&spi->dev, spi->irq, at86rf230_isr,
+ IRQF_SHARED | irq_type, dev_name(&spi->dev), lp);
if (rc)
- goto err_hw_init;
+ goto free_dev;
rc = ieee802154_register_device(lp->dev);
if (rc)
- goto err_hw_init;
+ goto free_dev;
return rc;
-err_hw_init:
- flush_work(&lp->irqwork);
- mutex_destroy(&lp->bmux);
+free_dev:
ieee802154_free_device(lp->dev);
return rc;
@@ -1197,8 +1583,6 @@ static int at86rf230_remove(struct spi_device *spi)
/* mask all at86rf230 irq's */
at86rf230_write_subreg(lp, SR_IRQ_MASK, 0);
ieee802154_unregister_device(lp->dev);
- flush_work(&lp->irqwork);
- mutex_destroy(&lp->bmux);
ieee802154_free_device(lp->dev);
dev_dbg(&spi->dev, "unregistered at86rf230\n");
diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c
new file mode 100644
index 000000000000..8a5ac7ab2300
--- /dev/null
+++ b/drivers/net/ieee802154/cc2520.c
@@ -0,0 +1,1039 @@
+/* Driver for TI CC2520 802.15.4 Wireless-PAN Networking controller
+ *
+ * Copyright (C) 2014 Varka Bhadram <varkab@cdac.in>
+ * Md.Jamal Mohiuddin <mjmohiuddin@cdac.in>
+ * P Sowjanya <sowjanyap@cdac.in>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/cc2520.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/skbuff.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/of_gpio.h>
+
+#include <net/mac802154.h>
+#include <net/wpan-phy.h>
+#include <net/ieee802154.h>
+
+#define SPI_COMMAND_BUFFER 3
+#define HIGH 1
+#define LOW 0
+#define STATE_IDLE 0
+#define RSSI_VALID 0
+#define RSSI_OFFSET 78
+
+#define CC2520_RAM_SIZE 640
+#define CC2520_FIFO_SIZE 128
+
+#define CC2520RAM_TXFIFO 0x100
+#define CC2520RAM_RXFIFO 0x180
+#define CC2520RAM_IEEEADDR 0x3EA
+#define CC2520RAM_PANID 0x3F2
+#define CC2520RAM_SHORTADDR 0x3F4
+
+#define CC2520_FREG_MASK 0x3F
+
+/* status byte values */
+#define CC2520_STATUS_XOSC32M_STABLE (1 << 7)
+#define CC2520_STATUS_RSSI_VALID (1 << 6)
+#define CC2520_STATUS_TX_UNDERFLOW (1 << 3)
+
+/* IEEE-802.15.4 defined constants (2.4 GHz logical channels) */
+#define CC2520_MINCHANNEL 11
+#define CC2520_MAXCHANNEL 26
+#define CC2520_CHANNEL_SPACING 5
+
+/* command strobes */
+#define CC2520_CMD_SNOP 0x00
+#define CC2520_CMD_IBUFLD 0x02
+#define CC2520_CMD_SIBUFEX 0x03
+#define CC2520_CMD_SSAMPLECCA 0x04
+#define CC2520_CMD_SRES 0x0f
+#define CC2520_CMD_MEMORY_MASK 0x0f
+#define CC2520_CMD_MEMORY_READ 0x10
+#define CC2520_CMD_MEMORY_WRITE 0x20
+#define CC2520_CMD_RXBUF 0x30
+#define CC2520_CMD_RXBUFCP 0x38
+#define CC2520_CMD_RXBUFMOV 0x32
+#define CC2520_CMD_TXBUF 0x3A
+#define CC2520_CMD_TXBUFCP 0x3E
+#define CC2520_CMD_RANDOM 0x3C
+#define CC2520_CMD_SXOSCON 0x40
+#define CC2520_CMD_STXCAL 0x41
+#define CC2520_CMD_SRXON 0x42
+#define CC2520_CMD_STXON 0x43
+#define CC2520_CMD_STXONCCA 0x44
+#define CC2520_CMD_SRFOFF 0x45
+#define CC2520_CMD_SXOSCOFF 0x46
+#define CC2520_CMD_SFLUSHRX 0x47
+#define CC2520_CMD_SFLUSHTX 0x48
+#define CC2520_CMD_SACK 0x49
+#define CC2520_CMD_SACKPEND 0x4A
+#define CC2520_CMD_SNACK 0x4B
+#define CC2520_CMD_SRXMASKBITSET 0x4C
+#define CC2520_CMD_SRXMASKBITCLR 0x4D
+#define CC2520_CMD_RXMASKAND 0x4E
+#define CC2520_CMD_RXMASKOR 0x4F
+#define CC2520_CMD_MEMCP 0x50
+#define CC2520_CMD_MEMCPR 0x52
+#define CC2520_CMD_MEMXCP 0x54
+#define CC2520_CMD_MEMXWR 0x56
+#define CC2520_CMD_BCLR 0x58
+#define CC2520_CMD_BSET 0x59
+#define CC2520_CMD_CTR_UCTR 0x60
+#define CC2520_CMD_CBCMAC 0x64
+#define CC2520_CMD_UCBCMAC 0x66
+#define CC2520_CMD_CCM 0x68
+#define CC2520_CMD_UCCM 0x6A
+#define CC2520_CMD_ECB 0x70
+#define CC2520_CMD_ECBO 0x72
+#define CC2520_CMD_ECBX 0x74
+#define CC2520_CMD_INC 0x78
+#define CC2520_CMD_ABORT 0x7F
+#define CC2520_CMD_REGISTER_READ 0x80
+#define CC2520_CMD_REGISTER_WRITE 0xC0
+
+/* status registers */
+#define CC2520_CHIPID 0x40
+#define CC2520_VERSION 0x42
+#define CC2520_EXTCLOCK 0x44
+#define CC2520_MDMCTRL0 0x46
+#define CC2520_MDMCTRL1 0x47
+#define CC2520_FREQEST 0x48
+#define CC2520_RXCTRL 0x4A
+#define CC2520_FSCTRL 0x4C
+#define CC2520_FSCAL0 0x4E
+#define CC2520_FSCAL1 0x4F
+#define CC2520_FSCAL2 0x50
+#define CC2520_FSCAL3 0x51
+#define CC2520_AGCCTRL0 0x52
+#define CC2520_AGCCTRL1 0x53
+#define CC2520_AGCCTRL2 0x54
+#define CC2520_AGCCTRL3 0x55
+#define CC2520_ADCTEST0 0x56
+#define CC2520_ADCTEST1 0x57
+#define CC2520_ADCTEST2 0x58
+#define CC2520_MDMTEST0 0x5A
+#define CC2520_MDMTEST1 0x5B
+#define CC2520_DACTEST0 0x5C
+#define CC2520_DACTEST1 0x5D
+#define CC2520_ATEST 0x5E
+#define CC2520_DACTEST2 0x5F
+#define CC2520_PTEST0 0x60
+#define CC2520_PTEST1 0x61
+#define CC2520_RESERVED 0x62
+#define CC2520_DPUBIST 0x7A
+#define CC2520_ACTBIST 0x7C
+#define CC2520_RAMBIST 0x7E
+
+/* frame registers */
+#define CC2520_FRMFILT0 0x00
+#define CC2520_FRMFILT1 0x01
+#define CC2520_SRCMATCH 0x02
+#define CC2520_SRCSHORTEN0 0x04
+#define CC2520_SRCSHORTEN1 0x05
+#define CC2520_SRCSHORTEN2 0x06
+#define CC2520_SRCEXTEN0 0x08
+#define CC2520_SRCEXTEN1 0x09
+#define CC2520_SRCEXTEN2 0x0A
+#define CC2520_FRMCTRL0 0x0C
+#define CC2520_FRMCTRL1 0x0D
+#define CC2520_RXENABLE0 0x0E
+#define CC2520_RXENABLE1 0x0F
+#define CC2520_EXCFLAG0 0x10
+#define CC2520_EXCFLAG1 0x11
+#define CC2520_EXCFLAG2 0x12
+#define CC2520_EXCMASKA0 0x14
+#define CC2520_EXCMASKA1 0x15
+#define CC2520_EXCMASKA2 0x16
+#define CC2520_EXCMASKB0 0x18
+#define CC2520_EXCMASKB1 0x19
+#define CC2520_EXCMASKB2 0x1A
+#define CC2520_EXCBINDX0 0x1C
+#define CC2520_EXCBINDX1 0x1D
+#define CC2520_EXCBINDY0 0x1E
+#define CC2520_EXCBINDY1 0x1F
+#define CC2520_GPIOCTRL0 0x20
+#define CC2520_GPIOCTRL1 0x21
+#define CC2520_GPIOCTRL2 0x22
+#define CC2520_GPIOCTRL3 0x23
+#define CC2520_GPIOCTRL4 0x24
+#define CC2520_GPIOCTRL5 0x25
+#define CC2520_GPIOPOLARITY 0x26
+#define CC2520_GPIOCTRL 0x28
+#define CC2520_DPUCON 0x2A
+#define CC2520_DPUSTAT 0x2C
+#define CC2520_FREQCTRL 0x2E
+#define CC2520_FREQTUNE 0x2F
+#define CC2520_TXPOWER 0x30
+#define CC2520_TXCTRL 0x31
+#define CC2520_FSMSTAT0 0x32
+#define CC2520_FSMSTAT1 0x33
+#define CC2520_FIFOPCTRL 0x34
+#define CC2520_FSMCTRL 0x35
+#define CC2520_CCACTRL0 0x36
+#define CC2520_CCACTRL1 0x37
+#define CC2520_RSSI 0x38
+#define CC2520_RSSISTAT 0x39
+#define CC2520_RXFIRST 0x3C
+#define CC2520_RXFIFOCNT 0x3E
+#define CC2520_TXFIFOCNT 0x3F
+
+/* Driver private information */
+struct cc2520_private {
+ struct spi_device *spi; /* SPI device structure */
+ struct ieee802154_dev *dev; /* IEEE-802.15.4 device */
+ u8 *buf; /* SPI TX/Rx data buffer */
+ struct mutex buffer_mutex; /* SPI buffer mutex */
+ bool is_tx; /* Flag for sync b/w Tx and Rx */
+ int fifo_pin; /* FIFO GPIO pin number */
+ struct work_struct fifop_irqwork;/* Workqueue for FIFOP */
+ spinlock_t lock; /* Lock for is_tx*/
+ struct completion tx_complete; /* Work completion for Tx */
+};
+
+/* Generic Functions */
+static int
+cc2520_cmd_strobe(struct cc2520_private *priv, u8 cmd)
+{
+ int ret;
+ u8 status = 0xff;
+ struct spi_message msg;
+ struct spi_transfer xfer = {
+ .len = 0,
+ .tx_buf = priv->buf,
+ .rx_buf = priv->buf,
+ };
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ mutex_lock(&priv->buffer_mutex);
+ priv->buf[xfer.len++] = cmd;
+ dev_vdbg(&priv->spi->dev,
+ "command strobe buf[0] = %02x\n",
+ priv->buf[0]);
+
+ ret = spi_sync(priv->spi, &msg);
+ if (!ret)
+ status = priv->buf[0];
+ dev_vdbg(&priv->spi->dev,
+ "buf[0] = %02x\n", priv->buf[0]);
+ mutex_unlock(&priv->buffer_mutex);
+
+ return ret;
+}
+
+static int
+cc2520_get_status(struct cc2520_private *priv, u8 *status)
+{
+ int ret;
+ struct spi_message msg;
+ struct spi_transfer xfer = {
+ .len = 0,
+ .tx_buf = priv->buf,
+ .rx_buf = priv->buf,
+ };
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ mutex_lock(&priv->buffer_mutex);
+ priv->buf[xfer.len++] = CC2520_CMD_SNOP;
+ dev_vdbg(&priv->spi->dev,
+ "get status command buf[0] = %02x\n", priv->buf[0]);
+
+ ret = spi_sync(priv->spi, &msg);
+ if (!ret)
+ *status = priv->buf[0];
+ dev_vdbg(&priv->spi->dev,
+ "buf[0] = %02x\n", priv->buf[0]);
+ mutex_unlock(&priv->buffer_mutex);
+
+ return ret;
+}
+
+static int
+cc2520_write_register(struct cc2520_private *priv, u8 reg, u8 value)
+{
+ int status;
+ struct spi_message msg;
+ struct spi_transfer xfer = {
+ .len = 0,
+ .tx_buf = priv->buf,
+ .rx_buf = priv->buf,
+ };
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ mutex_lock(&priv->buffer_mutex);
+
+ if (reg <= CC2520_FREG_MASK) {
+ priv->buf[xfer.len++] = CC2520_CMD_REGISTER_WRITE | reg;
+ priv->buf[xfer.len++] = value;
+ } else {
+ priv->buf[xfer.len++] = CC2520_CMD_MEMORY_WRITE;
+ priv->buf[xfer.len++] = reg;
+ priv->buf[xfer.len++] = value;
+ }
+ status = spi_sync(priv->spi, &msg);
+ if (msg.status)
+ status = msg.status;
+
+ mutex_unlock(&priv->buffer_mutex);
+
+ return status;
+}
+
+static int
+cc2520_write_ram(struct cc2520_private *priv, u16 reg, u8 len, u8 *data)
+{
+ int status;
+ struct spi_message msg;
+ struct spi_transfer xfer_head = {
+ .len = 0,
+ .tx_buf = priv->buf,
+ .rx_buf = priv->buf,
+ };
+
+ struct spi_transfer xfer_buf = {
+ .len = len,
+ .tx_buf = data,
+ };
+
+ mutex_lock(&priv->buffer_mutex);
+ priv->buf[xfer_head.len++] = (CC2520_CMD_MEMORY_WRITE |
+ ((reg >> 8) & 0xff));
+ priv->buf[xfer_head.len++] = reg & 0xff;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer_head, &msg);
+ spi_message_add_tail(&xfer_buf, &msg);
+
+ status = spi_sync(priv->spi, &msg);
+ dev_dbg(&priv->spi->dev, "spi status = %d\n", status);
+ if (msg.status)
+ status = msg.status;
+
+ mutex_unlock(&priv->buffer_mutex);
+ return status;
+}
+
+static int
+cc2520_read_register(struct cc2520_private *priv, u8 reg, u8 *data)
+{
+ int status;
+ struct spi_message msg;
+ struct spi_transfer xfer1 = {
+ .len = 0,
+ .tx_buf = priv->buf,
+ .rx_buf = priv->buf,
+ };
+
+ struct spi_transfer xfer2 = {
+ .len = 1,
+ .rx_buf = data,
+ };
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer1, &msg);
+ spi_message_add_tail(&xfer2, &msg);
+
+ mutex_lock(&priv->buffer_mutex);
+ priv->buf[xfer1.len++] = CC2520_CMD_MEMORY_READ;
+ priv->buf[xfer1.len++] = reg;
+
+ status = spi_sync(priv->spi, &msg);
+ dev_dbg(&priv->spi->dev,
+ "spi status = %d\n", status);
+ if (msg.status)
+ status = msg.status;
+
+ mutex_unlock(&priv->buffer_mutex);
+
+ return status;
+}
+
+static int
+cc2520_write_txfifo(struct cc2520_private *priv, u8 *data, u8 len)
+{
+ int status;
+
+ /* length byte must include FCS even
+ * if it is calculated in the hardware
+ */
+ int len_byte = len + 2;
+
+ struct spi_message msg;
+
+ struct spi_transfer xfer_head = {
+ .len = 0,
+ .tx_buf = priv->buf,
+ .rx_buf = priv->buf,
+ };
+ struct spi_transfer xfer_len = {
+ .len = 1,
+ .tx_buf = &len_byte,
+ };
+ struct spi_transfer xfer_buf = {
+ .len = len,
+ .tx_buf = data,
+ };
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer_head, &msg);
+ spi_message_add_tail(&xfer_len, &msg);
+ spi_message_add_tail(&xfer_buf, &msg);
+
+ mutex_lock(&priv->buffer_mutex);
+ priv->buf[xfer_head.len++] = CC2520_CMD_TXBUF;
+ dev_vdbg(&priv->spi->dev,
+ "TX_FIFO cmd buf[0] = %02x\n", priv->buf[0]);
+
+ status = spi_sync(priv->spi, &msg);
+ dev_vdbg(&priv->spi->dev, "status = %d\n", status);
+ if (msg.status)
+ status = msg.status;
+ dev_vdbg(&priv->spi->dev, "status = %d\n", status);
+ dev_vdbg(&priv->spi->dev, "buf[0] = %02x\n", priv->buf[0]);
+ mutex_unlock(&priv->buffer_mutex);
+
+ return status;
+}
+
+static int
+cc2520_read_rxfifo(struct cc2520_private *priv, u8 *data, u8 len, u8 *lqi)
+{
+ int status;
+ struct spi_message msg;
+
+ struct spi_transfer xfer_head = {
+ .len = 0,
+ .tx_buf = priv->buf,
+ .rx_buf = priv->buf,
+ };
+ struct spi_transfer xfer_buf = {
+ .len = len,
+ .rx_buf = data,
+ };
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer_head, &msg);
+ spi_message_add_tail(&xfer_buf, &msg);
+
+ mutex_lock(&priv->buffer_mutex);
+ priv->buf[xfer_head.len++] = CC2520_CMD_RXBUF;
+
+ dev_vdbg(&priv->spi->dev, "read rxfifo buf[0] = %02x\n", priv->buf[0]);
+ dev_vdbg(&priv->spi->dev, "buf[1] = %02x\n", priv->buf[1]);
+
+ status = spi_sync(priv->spi, &msg);
+ dev_vdbg(&priv->spi->dev, "status = %d\n", status);
+ if (msg.status)
+ status = msg.status;
+ dev_vdbg(&priv->spi->dev, "status = %d\n", status);
+ dev_vdbg(&priv->spi->dev,
+ "return status buf[0] = %02x\n", priv->buf[0]);
+ dev_vdbg(&priv->spi->dev, "length buf[1] = %02x\n", priv->buf[1]);
+
+ mutex_unlock(&priv->buffer_mutex);
+
+ return status;
+}
+
+static int cc2520_start(struct ieee802154_dev *dev)
+{
+ return cc2520_cmd_strobe(dev->priv, CC2520_CMD_SRXON);
+}
+
+static void cc2520_stop(struct ieee802154_dev *dev)
+{
+ cc2520_cmd_strobe(dev->priv, CC2520_CMD_SRFOFF);
+}
+
+static int
+cc2520_tx(struct ieee802154_dev *dev, struct sk_buff *skb)
+{
+ struct cc2520_private *priv = dev->priv;
+ unsigned long flags;
+ int rc;
+ u8 status = 0;
+
+ rc = cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHTX);
+ if (rc)
+ goto err_tx;
+
+ rc = cc2520_write_txfifo(priv, skb->data, skb->len);
+ if (rc)
+ goto err_tx;
+
+ rc = cc2520_get_status(priv, &status);
+ if (rc)
+ goto err_tx;
+
+ if (status & CC2520_STATUS_TX_UNDERFLOW) {
+ dev_err(&priv->spi->dev, "cc2520 tx underflow exception\n");
+ goto err_tx;
+ }
+
+ spin_lock_irqsave(&priv->lock, flags);
+ BUG_ON(priv->is_tx);
+ priv->is_tx = 1;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ rc = cc2520_cmd_strobe(priv, CC2520_CMD_STXONCCA);
+ if (rc)
+ goto err;
+
+ rc = wait_for_completion_interruptible(&priv->tx_complete);
+ if (rc < 0)
+ goto err;
+
+ cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHTX);
+ cc2520_cmd_strobe(priv, CC2520_CMD_SRXON);
+
+ return rc;
+err:
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->is_tx = 0;
+ spin_unlock_irqrestore(&priv->lock, flags);
+err_tx:
+ return rc;
+}
+
+
+static int cc2520_rx(struct cc2520_private *priv)
+{
+ u8 len = 0, lqi = 0, bytes = 1;
+ struct sk_buff *skb;
+
+ cc2520_read_rxfifo(priv, &len, bytes, &lqi);
+
+ if (len < 2 || len > IEEE802154_MTU)
+ return -EINVAL;
+
+ skb = alloc_skb(len, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ if (cc2520_read_rxfifo(priv, skb_put(skb, len), len, &lqi)) {
+ dev_dbg(&priv->spi->dev, "frame reception failed\n");
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ skb_trim(skb, skb->len - 2);
+
+ ieee802154_rx_irqsafe(priv->dev, skb, lqi);
+
+ dev_vdbg(&priv->spi->dev, "RXFIFO: %x %x\n", len, lqi);
+
+ return 0;
+}
+
+static int
+cc2520_ed(struct ieee802154_dev *dev, u8 *level)
+{
+ struct cc2520_private *priv = dev->priv;
+ u8 status = 0xff;
+ u8 rssi;
+ int ret;
+
+ ret = cc2520_read_register(priv , CC2520_RSSISTAT, &status);
+ if (ret)
+ return ret;
+
+ if (status != RSSI_VALID)
+ return -EINVAL;
+
+ ret = cc2520_read_register(priv , CC2520_RSSI, &rssi);
+ if (ret)
+ return ret;
+
+ /* level = RSSI(rssi) - OFFSET [dBm] : offset is 76dBm */
+ *level = rssi - RSSI_OFFSET;
+
+ return 0;
+}
+
+static int
+cc2520_set_channel(struct ieee802154_dev *dev, int page, int channel)
+{
+ struct cc2520_private *priv = dev->priv;
+ int ret;
+
+ might_sleep();
+ dev_dbg(&priv->spi->dev, "trying to set channel\n");
+
+ BUG_ON(page != 0);
+ BUG_ON(channel < CC2520_MINCHANNEL);
+ BUG_ON(channel > CC2520_MAXCHANNEL);
+
+ ret = cc2520_write_register(priv, CC2520_FREQCTRL,
+ 11 + 5*(channel - 11));
+
+ return ret;
+}
+
+static int
+cc2520_filter(struct ieee802154_dev *dev,
+ struct ieee802154_hw_addr_filt *filt, unsigned long changed)
+{
+ struct cc2520_private *priv = dev->priv;
+
+ if (changed & IEEE802515_AFILT_PANID_CHANGED) {
+ u16 panid = le16_to_cpu(filt->pan_id);
+
+ dev_vdbg(&priv->spi->dev,
+ "cc2520_filter called for pan id\n");
+ cc2520_write_ram(priv, CC2520RAM_PANID,
+ sizeof(panid), (u8 *)&panid);
+ }
+
+ if (changed & IEEE802515_AFILT_IEEEADDR_CHANGED) {
+ dev_vdbg(&priv->spi->dev,
+ "cc2520_filter called for IEEE addr\n");
+ cc2520_write_ram(priv, CC2520RAM_IEEEADDR,
+ sizeof(filt->ieee_addr),
+ (u8 *)&filt->ieee_addr);
+ }
+
+ if (changed & IEEE802515_AFILT_SADDR_CHANGED) {
+ u16 addr = le16_to_cpu(filt->short_addr);
+
+ dev_vdbg(&priv->spi->dev,
+ "cc2520_filter called for saddr\n");
+ cc2520_write_ram(priv, CC2520RAM_SHORTADDR,
+ sizeof(addr), (u8 *)&addr);
+ }
+
+ if (changed & IEEE802515_AFILT_PANC_CHANGED) {
+ dev_vdbg(&priv->spi->dev,
+ "cc2520_filter called for panc change\n");
+ if (filt->pan_coord)
+ cc2520_write_register(priv, CC2520_FRMFILT0, 0x02);
+ else
+ cc2520_write_register(priv, CC2520_FRMFILT0, 0x00);
+ }
+
+ return 0;
+}
+
+static struct ieee802154_ops cc2520_ops = {
+ .owner = THIS_MODULE,
+ .start = cc2520_start,
+ .stop = cc2520_stop,
+ .xmit = cc2520_tx,
+ .ed = cc2520_ed,
+ .set_channel = cc2520_set_channel,
+ .set_hw_addr_filt = cc2520_filter,
+};
+
+static int cc2520_register(struct cc2520_private *priv)
+{
+ int ret = -ENOMEM;
+
+ priv->dev = ieee802154_alloc_device(sizeof(*priv), &cc2520_ops);
+ if (!priv->dev)
+ goto err_ret;
+
+ priv->dev->priv = priv;
+ priv->dev->parent = &priv->spi->dev;
+ priv->dev->extra_tx_headroom = 0;
+
+ /* We do support only 2.4 Ghz */
+ priv->dev->phy->channels_supported[0] = 0x7FFF800;
+ priv->dev->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK;
+
+ dev_vdbg(&priv->spi->dev, "registered cc2520\n");
+ ret = ieee802154_register_device(priv->dev);
+ if (ret)
+ goto err_free_device;
+
+ return 0;
+
+err_free_device:
+ ieee802154_free_device(priv->dev);
+err_ret:
+ return ret;
+}
+
+static void cc2520_fifop_irqwork(struct work_struct *work)
+{
+ struct cc2520_private *priv
+ = container_of(work, struct cc2520_private, fifop_irqwork);
+
+ dev_dbg(&priv->spi->dev, "fifop interrupt received\n");
+
+ if (gpio_get_value(priv->fifo_pin))
+ cc2520_rx(priv);
+ else
+ dev_dbg(&priv->spi->dev, "rxfifo overflow\n");
+
+ cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHRX);
+ cc2520_cmd_strobe(priv, CC2520_CMD_SFLUSHRX);
+}
+
+static irqreturn_t cc2520_fifop_isr(int irq, void *data)
+{
+ struct cc2520_private *priv = data;
+
+ schedule_work(&priv->fifop_irqwork);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cc2520_sfd_isr(int irq, void *data)
+{
+ struct cc2520_private *priv = data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (priv->is_tx) {
+ priv->is_tx = 0;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ dev_dbg(&priv->spi->dev, "SFD for TX\n");
+ complete(&priv->tx_complete);
+ } else {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ dev_dbg(&priv->spi->dev, "SFD for RX\n");
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int cc2520_hw_init(struct cc2520_private *priv)
+{
+ u8 status = 0, state = 0xff;
+ int ret;
+ int timeout = 100;
+
+ ret = cc2520_read_register(priv, CC2520_FSMSTAT1, &state);
+ if (ret)
+ goto err_ret;
+
+ if (state != STATE_IDLE)
+ return -EINVAL;
+
+ do {
+ ret = cc2520_get_status(priv, &status);
+ if (ret)
+ goto err_ret;
+
+ if (timeout-- <= 0) {
+ dev_err(&priv->spi->dev, "oscillator start failed!\n");
+ return ret;
+ }
+ udelay(1);
+ } while (!(status & CC2520_STATUS_XOSC32M_STABLE));
+
+ dev_vdbg(&priv->spi->dev, "oscillator brought up\n");
+
+ /* Registers default value: section 28.1 in Datasheet */
+ ret = cc2520_write_register(priv, CC2520_TXPOWER, 0xF7);
+ if (ret)
+ goto err_ret;
+
+ ret = cc2520_write_register(priv, CC2520_CCACTRL0, 0x1A);
+ if (ret)
+ goto err_ret;
+
+ ret = cc2520_write_register(priv, CC2520_MDMCTRL0, 0x85);
+ if (ret)
+ goto err_ret;
+
+ ret = cc2520_write_register(priv, CC2520_MDMCTRL1, 0x14);
+ if (ret)
+ goto err_ret;
+
+ ret = cc2520_write_register(priv, CC2520_RXCTRL, 0x3f);
+ if (ret)
+ goto err_ret;
+
+ ret = cc2520_write_register(priv, CC2520_FSCTRL, 0x5a);
+ if (ret)
+ goto err_ret;
+
+ ret = cc2520_write_register(priv, CC2520_FSCAL1, 0x2b);
+ if (ret)
+ goto err_ret;
+
+ ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x11);
+ if (ret)
+ goto err_ret;
+
+ ret = cc2520_write_register(priv, CC2520_ADCTEST0, 0x10);
+ if (ret)
+ goto err_ret;
+
+ ret = cc2520_write_register(priv, CC2520_ADCTEST1, 0x0e);
+ if (ret)
+ goto err_ret;
+
+ ret = cc2520_write_register(priv, CC2520_ADCTEST2, 0x03);
+ if (ret)
+ goto err_ret;
+
+ ret = cc2520_write_register(priv, CC2520_FRMCTRL0, 0x60);
+ if (ret)
+ goto err_ret;
+
+ ret = cc2520_write_register(priv, CC2520_FRMCTRL1, 0x03);
+ if (ret)
+ goto err_ret;
+
+ ret = cc2520_write_register(priv, CC2520_FRMFILT0, 0x00);
+ if (ret)
+ goto err_ret;
+
+ ret = cc2520_write_register(priv, CC2520_FIFOPCTRL, 127);
+ if (ret)
+ goto err_ret;
+
+ return 0;
+
+err_ret:
+ return ret;
+}
+
+static struct cc2520_platform_data *
+cc2520_get_platform_data(struct spi_device *spi)
+{
+ struct cc2520_platform_data *pdata;
+ struct device_node *np = spi->dev.of_node;
+ struct cc2520_private *priv = spi_get_drvdata(spi);
+
+ if (!np)
+ return spi->dev.platform_data;
+
+ pdata = devm_kzalloc(&spi->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ goto done;
+
+ pdata->fifo = of_get_named_gpio(np, "fifo-gpio", 0);
+ priv->fifo_pin = pdata->fifo;
+
+ pdata->fifop = of_get_named_gpio(np, "fifop-gpio", 0);
+
+ pdata->sfd = of_get_named_gpio(np, "sfd-gpio", 0);
+ pdata->cca = of_get_named_gpio(np, "cca-gpio", 0);
+ pdata->vreg = of_get_named_gpio(np, "vreg-gpio", 0);
+ pdata->reset = of_get_named_gpio(np, "reset-gpio", 0);
+
+ spi->dev.platform_data = pdata;
+
+done:
+ return pdata;
+}
+
+static int cc2520_probe(struct spi_device *spi)
+{
+ struct cc2520_private *priv;
+ struct pinctrl *pinctrl;
+ struct cc2520_platform_data *pdata;
+ int ret;
+
+ priv = devm_kzalloc(&spi->dev,
+ sizeof(struct cc2520_private), GFP_KERNEL);
+ if (!priv) {
+ ret = -ENOMEM;
+ goto err_ret;
+ }
+
+ spi_set_drvdata(spi, priv);
+
+ pinctrl = devm_pinctrl_get_select_default(&spi->dev);
+ if (IS_ERR(pinctrl))
+ dev_warn(&spi->dev,
+ "pinctrl pins are not configured");
+
+ pdata = cc2520_get_platform_data(spi);
+ if (!pdata) {
+ dev_err(&spi->dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ priv->spi = spi;
+
+ priv->buf = devm_kzalloc(&spi->dev,
+ SPI_COMMAND_BUFFER, GFP_KERNEL);
+ if (!priv->buf) {
+ ret = -ENOMEM;
+ goto err_ret;
+ }
+
+ mutex_init(&priv->buffer_mutex);
+ INIT_WORK(&priv->fifop_irqwork, cc2520_fifop_irqwork);
+ spin_lock_init(&priv->lock);
+ init_completion(&priv->tx_complete);
+
+ /* Request all the gpio's */
+ if (!gpio_is_valid(pdata->fifo)) {
+ dev_err(&spi->dev, "fifo gpio is not valid\n");
+ ret = -EINVAL;
+ goto err_hw_init;
+ }
+
+ ret = devm_gpio_request_one(&spi->dev, pdata->fifo,
+ GPIOF_IN, "fifo");
+ if (ret)
+ goto err_hw_init;
+
+ if (!gpio_is_valid(pdata->cca)) {
+ dev_err(&spi->dev, "cca gpio is not valid\n");
+ ret = -EINVAL;
+ goto err_hw_init;
+ }
+
+ ret = devm_gpio_request_one(&spi->dev, pdata->cca,
+ GPIOF_IN, "cca");
+ if (ret)
+ goto err_hw_init;
+
+ if (!gpio_is_valid(pdata->fifop)) {
+ dev_err(&spi->dev, "fifop gpio is not valid\n");
+ ret = -EINVAL;
+ goto err_hw_init;
+ }
+
+ ret = devm_gpio_request_one(&spi->dev, pdata->fifop,
+ GPIOF_IN, "fifop");
+ if (ret)
+ goto err_hw_init;
+
+ if (!gpio_is_valid(pdata->sfd)) {
+ dev_err(&spi->dev, "sfd gpio is not valid\n");
+ ret = -EINVAL;
+ goto err_hw_init;
+ }
+
+ ret = devm_gpio_request_one(&spi->dev, pdata->sfd,
+ GPIOF_IN, "sfd");
+ if (ret)
+ goto err_hw_init;
+
+ if (!gpio_is_valid(pdata->reset)) {
+ dev_err(&spi->dev, "reset gpio is not valid\n");
+ ret = -EINVAL;
+ goto err_hw_init;
+ }
+
+ ret = devm_gpio_request_one(&spi->dev, pdata->reset,
+ GPIOF_OUT_INIT_LOW, "reset");
+ if (ret)
+ goto err_hw_init;
+
+ if (!gpio_is_valid(pdata->vreg)) {
+ dev_err(&spi->dev, "vreg gpio is not valid\n");
+ ret = -EINVAL;
+ goto err_hw_init;
+ }
+
+ ret = devm_gpio_request_one(&spi->dev, pdata->vreg,
+ GPIOF_OUT_INIT_LOW, "vreg");
+ if (ret)
+ goto err_hw_init;
+
+
+ gpio_set_value(pdata->vreg, HIGH);
+ usleep_range(100, 150);
+
+ gpio_set_value(pdata->reset, HIGH);
+ usleep_range(200, 250);
+
+ ret = cc2520_hw_init(priv);
+ if (ret)
+ goto err_hw_init;
+
+ /* Set up fifop interrupt */
+ ret = devm_request_irq(&spi->dev,
+ gpio_to_irq(pdata->fifop),
+ cc2520_fifop_isr,
+ IRQF_TRIGGER_RISING,
+ dev_name(&spi->dev),
+ priv);
+ if (ret) {
+ dev_err(&spi->dev, "could not get fifop irq\n");
+ goto err_hw_init;
+ }
+
+ /* Set up sfd interrupt */
+ ret = devm_request_irq(&spi->dev,
+ gpio_to_irq(pdata->sfd),
+ cc2520_sfd_isr,
+ IRQF_TRIGGER_FALLING,
+ dev_name(&spi->dev),
+ priv);
+ if (ret) {
+ dev_err(&spi->dev, "could not get sfd irq\n");
+ goto err_hw_init;
+ }
+
+ ret = cc2520_register(priv);
+ if (ret)
+ goto err_hw_init;
+
+ return 0;
+
+err_hw_init:
+ mutex_destroy(&priv->buffer_mutex);
+ flush_work(&priv->fifop_irqwork);
+
+err_ret:
+ return ret;
+}
+
+static int cc2520_remove(struct spi_device *spi)
+{
+ struct cc2520_private *priv = spi_get_drvdata(spi);
+
+ mutex_destroy(&priv->buffer_mutex);
+ flush_work(&priv->fifop_irqwork);
+
+ ieee802154_unregister_device(priv->dev);
+ ieee802154_free_device(priv->dev);
+
+ return 0;
+}
+
+static const struct spi_device_id cc2520_ids[] = {
+ {"cc2520", },
+ {},
+};
+MODULE_DEVICE_TABLE(spi, cc2520_ids);
+
+static const struct of_device_id cc2520_of_ids[] = {
+ {.compatible = "ti,cc2520", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cc2520_of_ids);
+
+/* SPI driver structure */
+static struct spi_driver cc2520_driver = {
+ .driver = {
+ .name = "cc2520",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(cc2520_of_ids),
+ },
+ .id_table = cc2520_ids,
+ .probe = cc2520_probe,
+ .remove = cc2520_remove,
+};
+module_spi_driver(cc2520_driver);
+
+MODULE_AUTHOR("Varka Bhadram <varkab@cdac.in>");
+MODULE_DESCRIPTION("CC2520 Transceiver Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/ieee802154/fakehard.c b/drivers/net/ieee802154/fakehard.c
index 78f18be3bbf2..9ce854f43917 100644
--- a/drivers/net/ieee802154/fakehard.c
+++ b/drivers/net/ieee802154/fakehard.c
@@ -343,7 +343,8 @@ static int ieee802154fake_probe(struct platform_device *pdev)
if (!phy)
return -ENOMEM;
- dev = alloc_netdev(sizeof(struct fakehard_priv), "hardwpan%d", ieee802154_fake_setup);
+ dev = alloc_netdev(sizeof(struct fakehard_priv), "hardwpan%d",
+ NET_NAME_UNKNOWN, ieee802154_fake_setup);
if (!dev) {
wpan_phy_free(phy);
return -ENOMEM;
diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c
index 4048062011ba..9e6a124b13f2 100644
--- a/drivers/net/ieee802154/mrf24j40.c
+++ b/drivers/net/ieee802154/mrf24j40.c
@@ -610,10 +610,95 @@ out:
return IRQ_HANDLED;
}
+static int mrf24j40_hw_init(struct mrf24j40 *devrec)
+{
+ int ret;
+ u8 val;
+
+ /* Initialize the device.
+ From datasheet section 3.2: Initialization. */
+ ret = write_short_reg(devrec, REG_SOFTRST, 0x07);
+ if (ret)
+ goto err_ret;
+
+ ret = write_short_reg(devrec, REG_PACON2, 0x98);
+ if (ret)
+ goto err_ret;
+
+ ret = write_short_reg(devrec, REG_TXSTBL, 0x95);
+ if (ret)
+ goto err_ret;
+
+ ret = write_long_reg(devrec, REG_RFCON0, 0x03);
+ if (ret)
+ goto err_ret;
+
+ ret = write_long_reg(devrec, REG_RFCON1, 0x01);
+ if (ret)
+ goto err_ret;
+
+ ret = write_long_reg(devrec, REG_RFCON2, 0x80);
+ if (ret)
+ goto err_ret;
+
+ ret = write_long_reg(devrec, REG_RFCON6, 0x90);
+ if (ret)
+ goto err_ret;
+
+ ret = write_long_reg(devrec, REG_RFCON7, 0x80);
+ if (ret)
+ goto err_ret;
+
+ ret = write_long_reg(devrec, REG_RFCON8, 0x10);
+ if (ret)
+ goto err_ret;
+
+ ret = write_long_reg(devrec, REG_SLPCON1, 0x21);
+ if (ret)
+ goto err_ret;
+
+ ret = write_short_reg(devrec, REG_BBREG2, 0x80);
+ if (ret)
+ goto err_ret;
+
+ ret = write_short_reg(devrec, REG_CCAEDTH, 0x60);
+ if (ret)
+ goto err_ret;
+
+ ret = write_short_reg(devrec, REG_BBREG6, 0x40);
+ if (ret)
+ goto err_ret;
+
+ ret = write_short_reg(devrec, REG_RFCTL, 0x04);
+ if (ret)
+ goto err_ret;
+
+ ret = write_short_reg(devrec, 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);
+ if (ret)
+ goto err_ret;
+
+ return 0;
+
+err_ret:
+ return ret;
+}
+
static int mrf24j40_probe(struct spi_device *spi)
{
int ret = -ENOMEM;
- u8 val;
struct mrf24j40 *devrec;
printk(KERN_INFO "mrf24j40: probe(). IRQ: %d\n", spi->irq);
@@ -650,31 +735,9 @@ static int mrf24j40_probe(struct spi_device *spi)
if (ret)
goto err_register_device;
- /* Initialize the device.
- From datasheet section 3.2: Initialization. */
- write_short_reg(devrec, REG_SOFTRST, 0x07);
- write_short_reg(devrec, REG_PACON2, 0x98);
- write_short_reg(devrec, REG_TXSTBL, 0x95);
- write_long_reg(devrec, REG_RFCON0, 0x03);
- write_long_reg(devrec, REG_RFCON1, 0x01);
- write_long_reg(devrec, REG_RFCON2, 0x80);
- write_long_reg(devrec, REG_RFCON6, 0x90);
- write_long_reg(devrec, REG_RFCON7, 0x80);
- write_long_reg(devrec, REG_RFCON8, 0x10);
- write_long_reg(devrec, REG_SLPCON1, 0x21);
- write_short_reg(devrec, REG_BBREG2, 0x80);
- write_short_reg(devrec, REG_CCAEDTH, 0x60);
- write_short_reg(devrec, REG_BBREG6, 0x40);
- write_short_reg(devrec, REG_RFCTL, 0x04);
- write_short_reg(devrec, REG_RFCTL, 0x0);
- udelay(192);
-
- /* Set RX Mode. RXMCR<1:0>: 0x0 normal, 0x1 promisc, 0x2 error */
- ret = read_short_reg(devrec, REG_RXMCR, &val);
+ ret = mrf24j40_hw_init(devrec);
if (ret)
- goto err_read_reg;
- val &= ~0x3; /* Clear RX mode (normal) */
- write_short_reg(devrec, REG_RXMCR, val);
+ goto err_hw_init;
ret = devm_request_threaded_irq(&spi->dev,
spi->irq,
@@ -692,7 +755,7 @@ static int mrf24j40_probe(struct spi_device *spi)
return 0;
err_irq:
-err_read_reg:
+err_hw_init:
ieee802154_unregister_device(devrec->dev);
err_register_device:
ieee802154_free_device(devrec->dev);