---
drivers/ieee802154/Kconfig | 4
drivers/ieee802154/Makefile | 1
drivers/ieee802154/adf7242.c | 1034 ++++++++++++++++++++++++++++++++++++++++++
include/linux/spi/adf7242.h | 53 ++
4 files changed, 1092 insertions(+), 0 deletions(-)
create mode 100644 drivers/ieee802154/adf7242.c
create mode 100644 include/linux/spi/adf7242.h
diff --git a/drivers/ieee802154/Kconfig b/drivers/ieee802154/Kconfig
index 2c6f449..bd9e81e 100644
--- a/drivers/ieee802154/Kconfig
+++ b/drivers/ieee802154/Kconfig
@@ -45,4 +45,8 @@ config IEEE802154_CC2420
depends on SPI
depends on IEEE802154_DRIVERS
+config IEEE802154_ADF7242
+ tristate "ADF7242 transceiver driver"
+ depends on IEEE802154_DRIVERS && MAC802154
+ depends on SPI
diff --git a/drivers/ieee802154/Makefile b/drivers/ieee802154/Makefile
index b54a097..cb33815 100644
--- a/drivers/ieee802154/Makefile
+++ b/drivers/ieee802154/Makefile
@@ -3,5 +3,6 @@ obj-$(CONFIG_IEEE802154_FAKELB) += fakelb.o
obj-$(CONFIG_IEEE802154_SERIAL) += serial.o
obj-$(CONFIG_IEEE802154_AT86RF230) += at86rf230.o
obj-$(CONFIG_IEEE802154_CC2420) += cc2420.o
+obj-$(CONFIG_IEEE802154_ADF7242) += adf7242.o
EXTRA_CFLAGS += -DDEBUG -DCONFIG_FFD
diff --git a/drivers/ieee802154/adf7242.c b/drivers/ieee802154/adf7242.c
new file mode 100644
index 0000000..b578a55
--- /dev/null
+++ b/drivers/ieee802154/adf7242.c
@@ -0,0 +1,1034 @@
+/*
+ * Analog Devices ADF7242 Low-Power IEEE 802.15.4 Transceiver
+ *
+ * Copyright 2009-2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/firmware.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/adf7242.h>
+
+#include <net/mac802154.h>
+#include <net/wpan-phy.h>
+
+/*
+ * DEBUG LEVEL
+ * 0 OFF
+ * 1 INFO
+ * 2 INFO + TRACE
+ */
+
+#define ADF_DEBUG 0
+#define DBG(n, args...) do { if (ADF_DEBUG >= (n)) pr_debug(args); } while (0)
+
+#define FIRMWARE "adf7242_firmware.bin"
+#define MAX_POLL_LOOPS 10
+
+/* All Registers */
+
+#define REG_EXT_CTRL 0x100 /* RW External LNA/PA and internal PA control
configuration bits */
+#define REG_TX_FSK_TEST 0x101 /* RW TX FSK test mode configuration */
+#define REG_CCA1 0x105 /* RW RSSI threshold for CCA */
+#define REG_CCA2 0x106 /* RW CCA mode configuration */
+#define REG_BUFFERCFG 0x107 /* RW RX_BUFFER overwrite control */
+#define REG_PKT_CFG 0x108 /* RW FCS evaluation configuration */
+#define REG_DELAYCFG0 0x109 /* RW RC_RX command to SFD or sync word search
delay */
+#define REG_DELAYCFG1 0x10A /* RW RC_TX command to TX state */
+#define REG_DELAYCFG2 0x10B /* RW Mac delay extention */
+#define REG_SYNC_WORD0 0x10C /* RW sync word bits [7:0] of [23:0] */
+#define REG_SYNC_WORD1 0x10D /* RW sync word bits [15:8] of [23:0] */
+#define REG_SYNC_WORD2 0x10E /* RW sync word bits [23:16] of [23:0] */
+#define REG_SYNC_CONFIG 0x10F /* RW sync word configuration */
+#define REG_RC_CFG 0x13E /* RW RX / TX packet configuration */
+#define REG_RC_VAR44 0x13F /* RW RESERVED */
+#define REG_CH_FREQ0 0x300 /* RW Channel Frequency Settings - Low Byte */
+#define REG_CH_FREQ1 0x301 /* RW Channel Frequency Settings - Middle Byte */
+#define REG_CH_FREQ2 0x302 /* RW Channel Frequency Settings - 2 MSBs */
+#define REG_TX_FD 0x304 /* RW TX Frequency Deviation Register */
+#define REG_DM_CFG0 0x305 /* RW RX Discriminator BW Register */
+#define REG_TX_M 0x306 /* RW TX Mode Register */
+#define REG_RX_M 0x307 /* RW RX Mode Register */
+#define REG_RRB 0x30C /* R RSSI Readback Register */
+#define REG_LRB 0x30D /* R Link Quality Readback Register */
+#define REG_DR0 0x30E /* RW bits [15:8] of [15:0] for data rate
setting */
+#define REG_DR1 0x30F /* RW bits [7:0] of [15:0] for data rate
setting */
+#define REG_PRAMPG 0x313 /* RW RESERVED */
+#define REG_TXPB 0x314 /* RW TX Packet Storage Base Address */
+#define REG_RXPB 0x315 /* RW RX Packet Storage Base Address */
+#define REG_TMR_CFG0 0x316 /* RW Wake up Timer Configuration Register - High
Byte */
+#define REG_TMR_CFG1 0x317 /* RW Wake up Timer Configuration Register - Low
Byte */
+#define REG_TMR_RLD0 0x318 /* RW Wake up Timer Value Register - High Byte */
+#define REG_TMR_RLD1 0x319 /* RW Wake up Timer Value Register - Low Byte */
+#define REG_TMR_CTRL 0x31A /* RW Wake up Timer Timeout flag */
+#define REG_PD_AUX 0x31E /* RW Battmon enable */
+#define REG_GP_CFG 0x32C /* RW GPIO Configuration */
+#define REG_GP_OUT 0x32D /* RW GPIO Configuration */
+#define REG_GP_IN 0x32E /* R GPIO Configuration */
+#define REG_SYNT 0x335 /* RW bandwidth calibration timers */
+#define REG_CAL_CFG 0x33D /* RW Calibration Settings */
+#define REG_SYNT_CAL 0x371 /* RW Oscillator and Doubler Configuration */
+#define REG_IIRF_CFG 0x389 /* RW BB Filter Decimation Rate */
+#define REG_CDR_CFG 0x38A /* RW CDR kVCO */
+#define REG_DM_CFG1 0x38B /* RW Postdemodulator Filter */
+#define REG_AGCSTAT 0x38E /* R RXBB Ref Osc Calibration Engine Readback */
+#define REG_RXCAL0 0x395 /* RW RX BB filter tuning, LSB */
+#define REG_RXCAL1 0x396 /* RW RX BB filter tuning, MSB */
+#define REG_RXFE_CFG 0x39B /* RW RXBB Ref Osc & RXFE Calibration */
+#define REG_PA_RR 0x3A7 /* RW Set PA ramp rate */
+#define REG_PA_CFG 0x3A8 /* RW PA enable */
+#define REG_EXTPA_CFG 0x3A9 /* RW External PA BIAS DAC */
+#define REG_EXTPA_MSC 0x3AA /* RW PA Bias Mode */
+#define REG_ADC_RBK 0x3AE /* R Readback temp */
+#define REG_AGC_CFG1 0x3B2 /* RW GC Parameters */
+#define REG_AGC_MAX 0x3B4 /* RW Slew rate */
+#define REG_AGC_CFG2 0x3B6 /* RW RSSI Parameters */
+#define REG_AGC_CFG3 0x3B7 /* RW RSSI Parameters */
+#define REG_AGC_CFG4 0x3B8 /* RW RSSI Parameters */
+#define REG_AGC_CFG5 0x3B9 /* RW RSSI & NDEC Parameters */
+#define REG_AGC_CFG6 0x3BA /* RW NDEC Parameters */
+#define REG_OCL_CFG1 0x3C4 /* RW OCL System Parameters */
+#define REG_IRQ1_EN0 0x3C7 /* RW Interrupt Mask set bits [7:0] of [15:0]
for IRQ1 */
+#define REG_IRQ1_EN1 0x3C8 /* RW Interrupt Mask set bits [15:8] of [15:0]
for IRQ1 */
+#define REG_IRQ2_EN0 0x3C9 /* RW Interrupt Mask set bits [7:0] of [15:0]
for IRQ2 */
+#define REG_IRQ2_EN1 0x3CA /* RW Interrupt Mask set bits [15:8] of [15:0]
for IRQ2 */
+#define REG_IRQ1_SRC0 0x3CB /* RW Interrupt Source bits [7:0] of [15:0] for
IRQ */
+#define REG_IRQ1_SRC1 0x3CC /* RW Interrupt Source bits [15:8] of [15:0] for
IRQ */
+#define REG_OCL_BW0 0x3D2 /* RW OCL System Parameters */
+#define REG_OCL_BW1 0x3D3 /* RW OCL System Parameters */
+#define REG_OCL_BW2 0x3D4 /* RW OCL System Parameters */
+#define REG_OCL_BW3 0x3D5 /* RW OCL System Parameters */
+#define REG_OCL_BW4 0x3D6 /* RW OCL System Parameters */
+#define REG_OCL_BWS 0x3D7 /* RW OCL System Parameters */
+#define REG_OCL_CFG13 0x3E0 /* RW OCL System Parameters */
+#define REG_GP_DRV 0x3E3 /* RW I/O pads Configuration and bg trim */
+#define REG_BM_CFG 0x3E6 /* RW Battery Monitor Threshold Voltage setting */
+#define REG_SFD_15_4 0x3F4 /* RW Option to set non standard SFD */
+#define REG_AFC_CFG 0x3F7 /* RW AFC mode and polarity */
+#define REG_AFC_KI_KP 0x3F8 /* RW AFC ki and kp */
+#define REG_AFC_RANGE 0x3F9 /* RW AFC range */
+#define REG_AFC_READ 0x3FA /* RW Readback frequency error */
+
+#define REG_PAN_ID0 0x112
+#define REG_PAN_ID1 0x113
+#define REG_SHORT_ADDR_0 0x114
+#define REG_SHORT_ADDR_1 0x115
+#define REG_IEEE_ADDR_0 0x116
+#define REG_IEEE_ADDR_1 0x117
+#define REG_IEEE_ADDR_2 0x118
+#define REG_IEEE_ADDR_3 0x119
+#define REG_IEEE_ADDR_4 0x11A
+#define REG_IEEE_ADDR_5 0x11B
+#define REG_IEEE_ADDR_6 0x11C
+#define REG_IEEE_ADDR_7 0x11D
+#define REG_FFILT_CFG 0x11E
+#define REG_AUTO_CFG 0x11F
+#define REG_AUTO_TX1 0x120
+#define REG_AUTO_TX2 0x121
+#define REG_AUTO_STATUS 0x122
+
+/* REG_FFILT_CFG */
+#define ACCEPT_BEACON_FRAMES (1 << 0)
+#define ACCEPT_DATA_FRAMES (1 << 1)
+#define ACCEPT_ACK_FRAMES (1 << 2)
+#define ACCEPT_MACCMD_FRAMES (1 << 3)
+#define ACCEPT_RESERVED_FRAMES (1 << 4)
+#define ACCEPT_ALL_ADDRESS (1 << 5)
+
+/* REG_AUTO_CFG */
+#define AUTO_ACK_FRAMEPEND (1 << 0)
+#define IS_PANCOORD (1 << 1)
+#define RX_AUTO_ACK_EN (1 << 3)
+#define CSMA_CA_RX_TURNAROUND (1 << 4)
+
+/* REG_AUTO_TX1 */
+#define MAX_FRAME_RETRIES(x) ((x) & 0xF)
+#define MAX_CCA_RETRIES(x) (((x) & 0x7) << 4)
+
+/* REG_AUTO_TX2 */
+#define CSMA_MAX_BE(x) ((x) & 0xF)
+#define CSMA_MIN_BE(x) (((x) & 0xF) << 4)
+
+#define CMD_SPI_NOP 0xFF /* No operation. Use for dummy writes */
+#define CMD_SPI_PKT_WR 0x10 /* Write telegram to the Packet RAM
starting from the TX packet base address pointer tx_packet_base */
+#define CMD_SPI_PKT_RD 0x30 /* Read telegram from the Packet RAM
starting from RX packet base address pointer rxpb.rx_packet_base */
+#define CMD_SPI_MEM_WR(x) (0x18 + (x >> 8)) /* Write data to MCR or
Packet RAM sequentially */
+#define CMD_SPI_MEM_RD(x) (0x38 + (x >> 8)) /* Read data from MCR or
Packet RAM sequentially */
+#define CMD_SPI_MEMR_WR(x) (0x08 + (x >> 8)) /* Write data to MCR or
Packet RAM as random block */
+#define CMD_SPI_MEMR_RD(x) (0x28 + (x >> 8)) /* Read data from MCR or
Packet RAM as random block */
+#define CMD_SPI_PRAM_WR 0x1E /* Write data sequentially to
current PRAM page selected */
+#define CMD_RC_SLEEP 0xB1 /* Invoke transition of radio controller
into SLEEP state */
+#define CMD_RC_IDLE 0xB2 /* Invoke transition of radio controller
into IDLE state */
+#define CMD_RC_PHY_RDY 0xB3 /* Invoke transition of radio controller
into PHY_RDY state */
+#define CMD_RC_RX 0xB4 /* Invoke transition of radio controller
into RX state */
+#define CMD_RC_TX 0xB5 /* Invoke transition of radio controller
into TX state */
+#define CMD_RC_MEAS 0xB6 /* Invoke transition of radio controller
into MEAS state */
+#define CMD_RC_CCA 0xB7 /* Invoke Clear channel assessment */
+#define CMD_RC_CSMACA 0xC1 /* initiates CSMA-CA channel access
sequence and frame transmission */
+
+/* STATUS */
+
+#define STAT_SPI_READY (1 << 7)
+#define STAT_IRQ_STATUS (1 << 6)
+#define STAT_RC_READY (1 << 5)
+#define STAT_CCA_RESULT (1 << 4)
+#define RC_STATUS_IDLE 1
+#define RC_STATUS_MEAS 2
+#define RC_STATUS_PHY_RDY 3
+#define RC_STATUS_RX 4
+#define RC_STATUS_TX 5
+#define RC_STATUS_MASK 0xF
+
+/* AUTO_STATUS */
+
+#define SUCCESS 0
+#define SUCCESS_DATPEND 1
+#define FAILURE_CSMACA 2
+#define FAILURE_NOACK 3
+#define AUTO_STATUS_MASK 0x3
+
+#define PRAM_PAGESIZE 256
+
+/* IRQ1 */
+
+#define IRQ_CCA_COMPLETE (1 << 0)
+#define IRQ_SFD_RX (1 << 1)
+#define IRQ_SFD_TX (1 << 2)
+#define IRQ_RX_PKT_RCVD (1 << 3)
+#define IRQ_TX_PKT_SENT (1 << 4)
+#define IRQ_FRAME_VALID (1 << 5)
+#define IRQ_ADDRESS_VALID (1 << 6)
+#define IRQ_CSMA_CA (1 << 7)
+
+#define AUTO_TX_TURNAROUND (1 << 3)
+#define ADDON_EN (1 << 4)
+
+struct adf7242_local {
+ struct spi_device *spi;
+ struct adf7242_platform_data *pdata;
+ struct work_struct irqwork;
+ struct completion tx_complete;
+ struct ieee802154_dev *dev;
+ struct mutex bmux;
+ spinlock_t lock;
+ unsigned irq_disabled:1; /* P: lock */
+ unsigned is_tx:1; /* P: lock */
+ unsigned mode;
+ unsigned tx_irq;
+ int tx_stat;
+ u8 buf[3];
+};
+
+static int adf7242_status(struct adf7242_local *lp, u8 *stat)
+{
+ int status;
+ struct spi_message msg;
+ u8 buf_tx[1], buf_rx[1];
+
+ struct spi_transfer xfer = {
+ .len = 1,
+ .tx_buf = buf_tx,
+ .rx_buf = buf_rx,
+ };
+
+ buf_tx[0] = CMD_SPI_NOP;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ mutex_lock(&lp->bmux);
+ status = spi_sync(lp->spi, &msg);
+ mutex_unlock(&lp->bmux);
+
+ *stat = buf_rx[0];
+
+ return status;
+}
+
+static int adf7242_wait_ready(struct adf7242_local *lp)
+{
+ u8 stat;
+ int cnt = 0;
+
+ DBG(2, "%s :Enter\n", __func__);
+
+ do {
+ adf7242_status(lp, &stat);
+ cnt++;
+ } while (!(stat & STAT_RC_READY) && (cnt < MAX_POLL_LOOPS));
+
+ DBG(2, "%s :Exit loops=%d\n", __func__, cnt);
+
+ return 0;
+}
+
+static int adf7242_wait_status(struct adf7242_local *lp, int status)
+{
+ u8 stat;
+ int cnt = 0;
+
+ DBG(2, "%s :Enter\n", __func__);
+
+ do {
+ adf7242_status(lp, &stat);
+ stat &= RC_STATUS_MASK;
+ cnt++;
+ } while ((stat != status) && (cnt < MAX_POLL_LOOPS));
+
+ DBG(2, "%s :Exit loops=%d\n", __func__, cnt);
+
+ return 0;
+}
+
+static int adf7242_write_fbuf(struct adf7242_local *lp, u8 *data, u8 len)
+{
+ 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,
+ };
+
+ DBG(2, "%s :Enter\n", __func__);
+ adf7242_wait_ready(lp);
+
+ buf[0] = CMD_SPI_PKT_WR;
+ buf[1] = len + 2;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer_head, &msg);
+ spi_message_add_tail(&xfer_buf, &msg);
+
+ mutex_lock(&lp->bmux);
+ status = spi_sync(lp->spi, &msg);
+ mutex_unlock(&lp->bmux);
+
+ DBG(2, "%s :Exit\n", __func__);
+ return status;
+}
+
+static int adf7242_read_fbuf(struct adf7242_local *lp,
+ u8 *data, u8 *len, u8 *lqi)
+{
+ u8 *buf = lp->buf;
+ int status;
+ struct spi_message msg;
+ struct spi_transfer xfer_head = {
+ .len = 3,
+ .tx_buf = buf,
+ .rx_buf = buf,
+
+ };
+ struct spi_transfer xfer_buf = {
+ .len = *len,
+ .rx_buf = data,
+ };
+
+ DBG(2, "%s :Enter\n", __func__);
+ adf7242_wait_ready(lp);
+
+ mutex_lock(&lp->bmux);
+ buf[0] = CMD_SPI_PKT_RD;
+ buf[1] = CMD_SPI_NOP;
+ buf[2] = 0; /* PHR */
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer_head, &msg);
+ spi_message_add_tail(&xfer_buf, &msg);
+
+ status = spi_sync(lp->spi, &msg);
+
+ if (!status) {
+ *lqi = data[buf[2] - 1];
+ *len = buf[2]; /* PHR */
+ }
+
+ mutex_unlock(&lp->bmux);
+ DBG(2, "%s :Exit\n", __func__);
+ return status;
+}
+
+static int adf7242_read_reg(struct adf7242_local *lp,
+ u16 addr, u8 *data)
+{
+ int status;
+ struct spi_message msg;
+ u8 buf_tx[4], buf_rx[4];
+
+ struct spi_transfer xfer = {
+ .len = 4,
+ .tx_buf = buf_tx,
+ .rx_buf = buf_rx,
+ };
+
+ DBG(2, "%s :Enter\n", __func__);
+ adf7242_wait_ready(lp);
+
+ mutex_lock(&lp->bmux);
+ buf_tx[0] = CMD_SPI_MEM_RD(addr);
+ buf_tx[1] = addr;
+ buf_tx[2] = CMD_SPI_NOP;
+ buf_tx[3] = CMD_SPI_NOP;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ status = spi_sync(lp->spi, &msg);
+ if (msg.status)
+ status = msg.status;
+
+ if (!status)
+ *data = buf_rx[3];
+
+ mutex_unlock(&lp->bmux);
+ DBG(2, "%s :Exit\n", __func__);
+
+ return status;
+}
+
+static int adf7242_write_reg(struct adf7242_local *lp,
+ u16 addr, u8 data)
+{
+ int status;
+ struct spi_message msg;
+ u8 buf_tx[4];
+
+ struct spi_transfer xfer = {
+ .len = 3,
+ .tx_buf = buf_tx,
+ };
+ DBG(2, "%s :Enter\n", __func__);
+ adf7242_wait_ready(lp);
+
+ buf_tx[0] = CMD_SPI_MEM_WR(addr);
+ buf_tx[1] = addr;
+ buf_tx[2] = data;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ mutex_lock(&lp->bmux);
+ status = spi_sync(lp->spi, &msg);
+ mutex_unlock(&lp->bmux);
+ DBG(2, "%s :Exit\n", __func__);
+
+ return status;
+}
+
+static int adf7242_cmd(struct adf7242_local *lp, u8 cmd)
+{
+ int status;
+ struct spi_message msg;
+ u8 buf_tx[1];
+
+ struct spi_transfer xfer = {
+ .len = 1,
+ .tx_buf = buf_tx,
+ };
+
+ DBG(2, "%s :Enter CMD=0x%X\n", __func__, cmd);
+ adf7242_wait_ready(lp);
+
+ buf_tx[0] = cmd;
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+
+ mutex_lock(&lp->bmux);
+ status = spi_sync(lp->spi, &msg);
+ mutex_unlock(&lp->bmux);
+ DBG(2, "%s :Exit\n", __func__);
+
+ return status;
+}
+
+static int adf7242_upload_firmware(struct adf7242_local *lp,
+ u8 *data, u16 len)
+{
+ int status, i, page = 0;
+ struct spi_message msg;
+ struct spi_transfer xfer_buf = {};
+ u8 buf[2];
+
+ struct spi_transfer xfer_head = {
+ .len = 2,
+ .tx_buf = buf,
+ };
+
+ buf[0] = CMD_SPI_PRAM_WR;
+ buf[1] = 0;
+
+ for (i = len; i >= 0 ; i -= PRAM_PAGESIZE) {
+ adf7242_write_reg(lp, REG_PRAMPG, page);
+
+ xfer_buf.len = i >= PRAM_PAGESIZE ? PRAM_PAGESIZE : i,
+ xfer_buf.tx_buf = &data[page * PRAM_PAGESIZE],
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer_head, &msg);
+ spi_message_add_tail(&xfer_buf, &msg);
+
+ mutex_lock(&lp->bmux);
+ status = spi_sync(lp->spi, &msg);
+ mutex_unlock(&lp->bmux);
+ page++;
+ }
+
+ return status;
+}
+
+static int adf7242_ed(struct ieee802154_dev *dev, u8 *level)
+{
+ struct adf7242_local *lp = dev->priv;
+ int ret;
+
+ DBG(2, "%s :Enter\n", __func__);
+#if 0
+ adf7242_cmd(lp, CMD_RC_PHY_RDY);
+ adf7242_cmd(lp, CMD_RC_CCA);
+ adf7242_wait_status(lp, RC_STATUS_PHY_RDY);
+#else
+ udelay(128);
+#endif
+ ret = adf7242_read_reg(lp, REG_RRB, level);
+ adf7242_cmd(lp, CMD_RC_RX);
+ DBG(2, "%s :Exit\n", __func__);
+
+ return ret;
+}
+
+static int adf7242_start(struct ieee802154_dev *dev)
+{
+ struct adf7242_local *lp = dev->priv;
+ int ret;
+
+ DBG(2, "%s :Enter\n", __func__);
+ ret = adf7242_cmd(lp, CMD_RC_RX);
+ DBG(2, "%s :Exit\n", __func__);
+
+ return ret;
+}
+
+static void adf7242_stop(struct ieee802154_dev *dev)
+{
+ struct adf7242_local *lp = dev->priv;
+
+ DBG(2, "%s :Enter\n", __func__);
+ adf7242_cmd(lp, CMD_RC_PHY_RDY);
+ DBG(2, "%s :Exit\n", __func__);
+}
+
+static int adf7242_channel(struct ieee802154_dev *dev, int page, int channel)
+{
+ struct adf7242_local *lp = dev->priv;
+ unsigned long freq;
+
+ DBG(2, "%s :Enter\n", __func__);
+ DBG(1, "%s :Channel=%d\n", __func__, channel);
+
+ might_sleep();
+
+ BUG_ON(page != 0);
+ BUG_ON(channel < 11);
+ BUG_ON(channel > 26);
+
+ freq = (2405 + 5 * (channel - 11)) * 100;
+
+ adf7242_cmd(lp, CMD_RC_PHY_RDY);
+
+ adf7242_write_reg(lp, REG_CH_FREQ0, freq);
+ adf7242_write_reg(lp, REG_CH_FREQ1, freq >> 8);
+ adf7242_write_reg(lp, REG_CH_FREQ2, freq >> 16);
+
+ adf7242_cmd(lp, CMD_RC_RX);
+
+ dev->phy->current_channel = channel;
+ DBG(2, "%s :Exit\n", __func__);
+
+ return 0;
+}
+
+static int adf7242_set_hw_addr_filt(struct ieee802154_dev *dev,
+ struct ieee802154_hw_addr_filt *filt,
+ unsigned long changed)
+{
+ struct adf7242_local *lp = dev->priv;
+ u8 reg;
+
+ DBG(2, "%s :Enter\n", __func__);
+ DBG(1, "%s :Changed=0x%lX\n", __func__, changed);
+
+ might_sleep();
+
+ if (changed & IEEE802515_IEEEADDR_CHANGED) {
+ adf7242_write_reg(lp, REG_IEEE_ADDR_0, filt->ieee_addr[7]);
+ adf7242_write_reg(lp, REG_IEEE_ADDR_1, filt->ieee_addr[6]);
+ adf7242_write_reg(lp, REG_IEEE_ADDR_2, filt->ieee_addr[5]);
+ adf7242_write_reg(lp, REG_IEEE_ADDR_3, filt->ieee_addr[4]);
+ adf7242_write_reg(lp, REG_IEEE_ADDR_4, filt->ieee_addr[3]);
+ adf7242_write_reg(lp, REG_IEEE_ADDR_5, filt->ieee_addr[2]);
+ adf7242_write_reg(lp, REG_IEEE_ADDR_6, filt->ieee_addr[1]);
+ adf7242_write_reg(lp, REG_IEEE_ADDR_7, filt->ieee_addr[0]);
+ }
+
+ if (changed & IEEE802515_SADDR_CHANGED) {
+ adf7242_write_reg(lp, REG_SHORT_ADDR_0, filt->short_addr);
+ adf7242_write_reg(lp, REG_SHORT_ADDR_1, filt->short_addr >> 8);
+ }
+
+ if (changed & IEEE802515_PANID_CHANGED) {
+ adf7242_write_reg(lp, REG_PAN_ID0, filt->pan_id);
+ adf7242_write_reg(lp, REG_PAN_ID1, filt->pan_id >> 8);
+ }
+
+ if (changed & IEEE802515_PANC_CHANGED) {
+ adf7242_read_reg(lp, REG_AUTO_CFG, ®);
+ if (filt->pan_coord)
+ reg |= IS_PANCOORD;
+ else
+ reg &= ~IS_PANCOORD;
+ adf7242_write_reg(lp, REG_AUTO_CFG, reg);
+ }
+
+ DBG(2, "%s :Exit\n", __func__);
+ return 0;
+}
+
+static int adf7242_xmit(struct ieee802154_dev *dev, struct sk_buff *skb)
+{
+ struct adf7242_local *lp = dev->priv;
+ int ret;
+ unsigned long flags;
+
+ DBG(2, "%s :Enter\n", __func__);
+
+ spin_lock_irqsave(&lp->lock, flags);
+ BUG_ON(lp->is_tx);
+ lp->is_tx = 1;
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ ret = adf7242_write_fbuf(lp, skb->data, skb->len);
+ if (ret)
+ goto err_rx;
+
+ if (lp->mode & ADF_IEEE802154_AUTO_CSMA_CA) {
+ ret = adf7242_cmd(lp, CMD_RC_PHY_RDY);
+ ret |= adf7242_cmd(lp, CMD_RC_CSMACA);
+ } else {
+ ret = adf7242_cmd(lp, CMD_RC_TX);
+ }
+
+ if (ret)
+ goto err_rx;
+
+ ret = wait_for_completion_interruptible(&lp->tx_complete);
+
+ if (ret < 0)
+ goto err_rx;
+
+ DBG(2, "%s :Exit\n", __func__);
+ return ret;
+
+err_rx:
+ spin_lock_irqsave(&lp->lock, flags);
+ lp->is_tx = 0;
+ spin_unlock_irqrestore(&lp->lock, flags);
+ return ret;
+}
+
+static int adf7242_rx(struct adf7242_local *lp)
+{
+ u8 len = 128;
+ u8 lqi = 0;
+ int ret;
+ struct sk_buff *skb;
+
+ DBG(2, "%s :Enter\n", __func__);
+
+ skb = alloc_skb(len, GFP_KERNEL);
+ if (!skb)
+ return -ENOMEM;
+
+ ret = adf7242_read_fbuf(lp, skb_put(skb, len), &len, &lqi);
+
+ adf7242_cmd(lp, CMD_RC_RX);
+
+ skb_trim(skb, len - 2); /* We do not put RSSI/LQI or CRC into the frame
*/
+
+ if (len < 2) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ ieee802154_rx_irqsafe(lp->dev, skb, lqi);
+
+ DBG(1, "%s: %d %d %x\n", __func__, ret, len, lqi);
+ DBG(2, "%s :Exit\n", __func__);
+
+ return 0;
+}
+
+static struct ieee802154_ops adf7242_ops = {
+ .owner = THIS_MODULE,
+ .xmit = adf7242_xmit,
+ .ed = adf7242_ed,
+ .set_channel = adf7242_channel,
+ .set_hw_addr_filt = adf7242_set_hw_addr_filt,
+ .start = adf7242_start,
+ .stop = adf7242_stop,
+};
+
+static void adf7242_irqwork(struct work_struct *work)
+{
+ struct adf7242_local *lp =
+ container_of(work, struct adf7242_local, irqwork);
+ u8 irq1, auto_stat = 0, stat = 0;
+ int ret;
+ unsigned long flags;
+
+ DBG(2, "%s :Enter\n", __func__);
+
+ ret = adf7242_read_reg(lp, REG_IRQ1_SRC1, &irq1);
+
+ DBG(1, "%s IRQ1 = %X:\n%s%s%s%s%s%s%s%s\n", __func__, irq1,
+ irq1 & IRQ_CCA_COMPLETE ? "IRQ_CCA_COMPLETE\n" : "",
+ irq1 & IRQ_SFD_RX ? "IRQ_SFD_RX\n" : "",
+ irq1 & IRQ_SFD_TX ? "IRQ_SFD_TX\n" : "",
+ irq1 & IRQ_RX_PKT_RCVD ? "IRQ_RX_PKT_RCVD\n" : "",
+ irq1 & IRQ_TX_PKT_SENT ? "IRQ_TX_PKT_SENT\n" : "",
+ irq1 & IRQ_CSMA_CA ? "IRQ_CSMA_CA\n" : "",
+ irq1 & IRQ_FRAME_VALID ? "IRQ_FRAME_VALID\n" : "",
+ irq1 & IRQ_ADDRESS_VALID ? "IRQ_ADDRESS_VALID\n" : "");
+
+ adf7242_status(lp, &stat);
+
+ DBG(1, "%s STATUS = %X:\n%s\n%s%s%s%s%s\n", __func__, stat,
+ stat & STAT_RC_READY ? "RC_READY" : "RC_BUSY",
+ (stat & 0xf) == RC_STATUS_IDLE ? "RC_STATUS_IDLE" : "",
+ (stat & 0xf) == RC_STATUS_MEAS ? "RC_STATUS_MEAS" : "",
+ (stat & 0xf) == RC_STATUS_PHY_RDY ? "RC_STATUS_PHY_RDY" : "",
+ (stat & 0xf) == RC_STATUS_RX ? "RC_STATUS_RX" : "",
+ (stat & 0xf) == RC_STATUS_TX ? "RC_STATUS_TX" : "");
+
+ adf7242_write_reg(lp, REG_IRQ1_SRC1, irq1);
+
+ if (irq1 & IRQ_RX_PKT_RCVD) {
+
+ /* Wait until ACK is processed */
+ if ((lp->mode & ADF_IEEE802154_HW_AACK) &&
+ ((stat & RC_STATUS_MASK) != RC_STATUS_PHY_RDY))
+ adf7242_wait_status(lp, RC_STATUS_PHY_RDY);
+
+ adf7242_rx(lp);
+ }
+
+ if (irq1 & lp->tx_irq) {
+
+ if (lp->mode & ADF_IEEE802154_AUTO_CSMA_CA) {
+ adf7242_read_reg(lp, REG_AUTO_STATUS, &auto_stat);
+ auto_stat &= AUTO_STATUS_MASK;
+
+ DBG(1, "%s AUTO_STATUS = %X:\n%s%s%s%s\n",
+ __func__, auto_stat,
+ auto_stat == SUCCESS ? "SUCCESS" : "",
+ auto_stat == SUCCESS_DATPEND ? "SUCCESS_DATPEND" : "",
+ auto_stat == FAILURE_CSMACA ? "FAILURE_CSMACA" : "",
+ auto_stat == FAILURE_NOACK ? "FAILURE_NOACK" : "");
+
+ /* save CSMA-CA completion status */
+ lp->tx_stat = auto_stat;
+ }
+ spin_lock_irqsave(&lp->lock, flags);
+ if (lp->is_tx) {
+ lp->is_tx = 0;
+ complete(&lp->tx_complete);
+ }
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ /* in case we just received a frame we are already in PHY_RX */
+
+ if (!(irq1 & IRQ_RX_PKT_RCVD))
+ adf7242_cmd(lp, CMD_RC_RX);
+ }
+
+ spin_lock_irqsave(&lp->lock, flags);
+ if (lp->irq_disabled) {
+ lp->irq_disabled = 0;
+ enable_irq(lp->spi->irq);
+ }
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ DBG(2, "%s :Exit\n", __func__);
+}
+
+static irqreturn_t adf7242_isr(int irq, void *data)
+{
+ struct adf7242_local *lp = data;
+
+ DBG(2, "%s :Enter\n", __func__);
+
+ spin_lock(&lp->lock);
+ if (!lp->irq_disabled) {
+ disable_irq_nosync(irq);
+ lp->irq_disabled = 1;
+ }
+ spin_unlock(&lp->lock);
+
+ schedule_work(&lp->irqwork);
+
+ DBG(2, "%s :Exit\n", __func__);
+
+ return IRQ_HANDLED;
+}
+
+
+static int adf7242_hw_init(struct adf7242_local *lp)
+{
+ int ret;
+ const struct firmware *fw;
+
+ DBG(2, "%s :Enter\n", __func__);
+
+ adf7242_cmd(lp, CMD_RC_IDLE);
+
+ if (lp->mode) {
+ /* get ADF7242 addon firmware
+ * build this driver as module
+ * and place under /lib/firmware/adf7242_firmware.bin
+ */
+ ret = request_firmware(&fw, FIRMWARE, &lp->spi->dev);
+ if (ret) {
+ dev_err(&lp->spi->dev,
+ "request_firmware() failed with %i\n", ret);
+ return ret;
+ }
+
+ adf7242_upload_firmware(lp, (u8 *) fw->data, fw->size);
+ release_firmware(fw);
+
+ adf7242_write_reg(lp, REG_FFILT_CFG,
+ ACCEPT_BEACON_FRAMES |
+ ACCEPT_DATA_FRAMES |
+ ACCEPT_ACK_FRAMES |
+ ACCEPT_MACCMD_FRAMES |
+ (lp->mode & ADF_IEEE802154_PROMISCUOUS_MODE ?
+ ACCEPT_ALL_ADDRESS : 0) |
+ ACCEPT_RESERVED_FRAMES);
+
+ adf7242_write_reg(lp, REG_AUTO_TX1,
+ MAX_FRAME_RETRIES(lp->pdata->max_frame_retries) |
+ MAX_CCA_RETRIES(lp->pdata->max_cca_retries));
+
+ adf7242_write_reg(lp, REG_AUTO_TX2,
+ CSMA_MAX_BE(lp->pdata->max_csma_be) |
+ CSMA_MIN_BE(lp->pdata->min_csma_be));
+
+ adf7242_write_reg(lp, REG_AUTO_CFG,
+ (lp->mode & ADF_IEEE802154_HW_AACK ?
+ RX_AUTO_ACK_EN : 0));
+ }
+
+ adf7242_write_reg(lp, REG_PKT_CFG, lp->mode ? ADDON_EN : 0);
+
+ adf7242_write_reg(lp, REG_EXTPA_MSC, 0xF1);
+ adf7242_write_reg(lp, REG_RXFE_CFG, 0x1D);
+ adf7242_write_reg(lp, REG_IRQ1_EN0, 0);
+
+ adf7242_write_reg(lp, REG_IRQ1_EN1, IRQ_RX_PKT_RCVD | lp->tx_irq);
+
+ adf7242_write_reg(lp, REG_IRQ1_SRC1, 0xFF);
+ adf7242_write_reg(lp, REG_IRQ1_SRC0, 0xFF);
+
+ adf7242_cmd(lp, CMD_RC_PHY_RDY);
+
+ DBG(2, "%s :Exit\n", __func__);
+
+ return 0;
+}
+
+static int adf7242_suspend(struct spi_device *spi, pm_message_t message)
+{
+ return 0;
+}
+
+static int adf7242_resume(struct spi_device *spi)
+{
+ return 0;
+}
+
+static ssize_t adf7242_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct adf7242_local *lp = dev_get_drvdata(dev);
+ u8 stat;
+
+ adf7242_status(lp, &stat);
+
+ return sprintf(buf, "STATUS = %X:\n%s\n%s%s%s%s%s\n", stat,
+ stat & STAT_RC_READY ? "RC_READY" : "RC_BUSY",
+ (stat & 0xf) == RC_STATUS_IDLE ? "RC_STATUS_IDLE" : "",
+ (stat & 0xf) == RC_STATUS_MEAS ? "RC_STATUS_MEAS" : "",
+ (stat & 0xf) == RC_STATUS_PHY_RDY ? "RC_STATUS_PHY_RDY" : "",
+ (stat & 0xf) == RC_STATUS_RX ? "RC_STATUS_RX" : "",
+ (stat & 0xf) == RC_STATUS_TX ? "RC_STATUS_TX" : "");
+
+}
+static DEVICE_ATTR(status, 0664, adf7242_show, NULL);
+
+static struct attribute *adf7242_attributes[] = {
+ &dev_attr_status.attr,
+ NULL
+};
+
+static const struct attribute_group adf7242_attr_group = {
+ .attrs = adf7242_attributes,
+};
+
+static int __devinit adf7242_probe(struct spi_device *spi)
+{
+ struct adf7242_platform_data *pdata = spi->dev.platform_data;
+ struct ieee802154_dev *dev;
+ struct adf7242_local *lp;
+ int ret;
+
+ if (!spi->irq) {
+ dev_err(&spi->dev, "no IRQ specified\n");
+ return -EINVAL;
+ }
+
+ if (!pdata) {
+ dev_err(&spi->dev, "no platform data?\n");
+ return -ENODEV;
+ }
+
+ dev = ieee802154_alloc_device(sizeof(*lp), &adf7242_ops);
+ if (!dev)
+ return -ENOMEM;
+
+ lp = dev->priv;
+ lp->dev = dev;
+ lp->spi = spi;
+ lp->pdata = pdata;
+ lp->mode = pdata->mode;
+
+ dev->priv = lp;
+ dev->parent = &spi->dev;
+ dev->extra_tx_headroom = 0;
+ /* We do support only 2.4 Ghz */
+
+ dev->phy->channels_supported[0] = 0x7FFF800;
+
+ if (!lp->mode) {
+ adf7242_ops.set_hw_addr_filt = NULL;
+ lp->tx_irq = IRQ_TX_PKT_SENT;
+ } else {
+ if ((lp->mode & ADF_IEEE802154_PROMISCUOUS_MODE) &&
+ (lp->mode & ADF_IEEE802154_HW_AACK))
+ lp->mode &= ~ADF_IEEE802154_HW_AACK;
+
+ if (lp->mode & ADF_IEEE802154_AUTO_CSMA_CA)
+ lp->tx_irq = IRQ_CSMA_CA;
+ else
+ lp->tx_irq = IRQ_TX_PKT_SENT;
+ }
+
+ dev->flags = IEEE802154_HW_OMIT_CKSUM |
+ (lp->mode & ADF_IEEE802154_HW_AACK ?
+ IEEE802154_HW_AACK : 0);
+
+ mutex_init(&lp->bmux);
+ INIT_WORK(&lp->irqwork, adf7242_irqwork);
+ spin_lock_init(&lp->lock);
+ init_completion(&lp->tx_complete);
+
+ spi_set_drvdata(spi, lp);
+
+ ret = adf7242_hw_init(lp);
+ if (ret)
+ goto err_hw_init;
+
+ ret = request_irq(spi->irq, adf7242_isr, IRQF_TRIGGER_HIGH,
+ dev_name(&spi->dev), lp);
+ if (ret)
+ goto err_hw_init;
+
+ ret = ieee802154_register_device(lp->dev);
+ if (ret)
+ goto err_irq;
+
+ dev_set_drvdata(&spi->dev, lp);
+
+ ret = sysfs_create_group(&spi->dev.kobj, &adf7242_attr_group);
+ if (ret)
+ goto out;
+
+ dev_info(&spi->dev, "mac802154 IRQ-%d registered\n", spi->irq);
+
+ return ret;
+
+out:
+ ieee802154_unregister_device(lp->dev);
+err_irq:
+ free_irq(spi->irq, lp);
+ flush_work(&lp->irqwork);
+err_hw_init:
+ spi_set_drvdata(spi, NULL);
+ mutex_destroy(&lp->bmux);
+ ieee802154_free_device(lp->dev);
+ return ret;
+}
+
+static int __devexit adf7242_remove(struct spi_device *spi)
+{
+ struct adf7242_local *lp = spi_get_drvdata(spi);
+
+ ieee802154_unregister_device(lp->dev);
+ free_irq(spi->irq, lp);
+ flush_work(&lp->irqwork);
+ spi_set_drvdata(spi, NULL);
+ mutex_destroy(&lp->bmux);
+ ieee802154_free_device(lp->dev);
+
+ return 0;
+}
+
+static struct spi_driver adf7242_driver = {
+ .driver = {
+ .name = "adf7242",
+ .owner = THIS_MODULE,
+ },
+ .probe = adf7242_probe,
+ .remove = __devexit_p(adf7242_remove),
+ .suspend = adf7242_suspend,
+ .resume = adf7242_resume,
+};
+
+static int __init adf7242_init(void)
+{
+ return spi_register_driver(&adf7242_driver);
+}
+module_init(adf7242_init);
+
+static void __exit adf7242_exit(void)
+{
+ spi_unregister_driver(&adf7242_driver);
+}
+module_exit(adf7242_exit);
+
+MODULE_AUTHOR("Michael Hennerich <[email protected]>");
+MODULE_DESCRIPTION("ADF7242 Transceiver Driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/spi/adf7242.h b/include/linux/spi/adf7242.h
new file mode 100644
index 0000000..d6f755f
--- /dev/null
+++ b/include/linux/spi/adf7242.h
@@ -0,0 +1,53 @@
+/*
+ * Analog Devices ADF7242 Low-Power IEEE 802.15.4 Transceiver
+ *
+ * Copyright 2009-2010 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __LINUX_SPI_ADF7242_H
+#define __LINUX_SPI_ADF7242_H
+
+struct adf7242_platform_data {
+
+#define ADF_IEEE802154_HW_AACK (1 << 1)
+#define ADF_IEEE802154_AUTO_CSMA_CA (1 << 2)
+#define ADF_IEEE802154_PROMISCUOUS_MODE (1 << 3) /* no address
filtering, turns off HW_AACK */
+
+ int mode;
+
+ /*
+ * Specifies number of attempts to
+ * retransmit unacknowledged
+ * frames while in automatic CSMA-CA
+ * Tx mode.
+ */
+ int max_frame_retries;
+
+ /*
+ * Specifies number of attempts to
+ * repeat CSMA-CA algorithm prior to
+ * cancellation of RC_TX command.
+ * Valid range is 0 to 5;
+ * 7: CSMA-CA algorithm is off
+ */
+ int max_cca_retries;
+
+ /*
+ * Specifies the maximum back-off
+ * exponent used in the CSMA-CA
+ * algorithm; valid range is 4 to 8
+ *
+ */
+ int max_csma_be;
+
+ /*
+ * Specifies the minimum back-off
+ * exponent used in the CSMA-CA
+ * algorithm; valid range is 0 to
+ * csma_max_be
+ */
+ int min_csma_be;
+};
+#endif
------------------------------------------------------------------------------
Colocation vs. Managed Hosting
A question and answer guide to determining the best fit
for your organization - today and in the future.
http://p.sf.net/sfu/internap-sfd2d
_______________________________________________
Linux-zigbee-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linux-zigbee-devel