---
 drivers/ieee802154/Kconfig  |    6 
 drivers/ieee802154/Makefile |    1 
 drivers/ieee802154/cc2420.c |  859 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/spi/cc2420.h  |  198 ++++++++++
 4 files changed, 1064 insertions(+), 0 deletions(-)
 create mode 100644 drivers/ieee802154/cc2420.c
 create mode 100644 include/linux/spi/cc2420.h

diff --git a/drivers/ieee802154/Kconfig b/drivers/ieee802154/Kconfig
index a734f21..2c6f449 100644
--- a/drivers/ieee802154/Kconfig
+++ b/drivers/ieee802154/Kconfig
@@ -40,3 +40,9 @@ config IEEE802154_AT86RF230
        tristate "AT86RF230 transceiver driver"
        depends on SPI
 
+config IEEE802154_CC2420
+       tristate "CC2420 driver"
+       depends on SPI
+       depends on  IEEE802154_DRIVERS
+
+
diff --git a/drivers/ieee802154/Makefile b/drivers/ieee802154/Makefile
index 76237f3..b54a097 100644
--- a/drivers/ieee802154/Makefile
+++ b/drivers/ieee802154/Makefile
@@ -2,5 +2,6 @@ obj-$(CONFIG_IEEE802154_FAKEHARD) += fakehard.o
 obj-$(CONFIG_IEEE802154_FAKELB) += fakelb.o
 obj-$(CONFIG_IEEE802154_SERIAL) += serial.o
 obj-$(CONFIG_IEEE802154_AT86RF230) += at86rf230.o
+obj-$(CONFIG_IEEE802154_CC2420) += cc2420.o
 
 EXTRA_CFLAGS += -DDEBUG -DCONFIG_FFD
diff --git a/drivers/ieee802154/cc2420.c b/drivers/ieee802154/cc2420.c
new file mode 100644
index 0000000..50761de
--- /dev/null
+++ b/drivers/ieee802154/cc2420.c
@@ -0,0 +1,859 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Author:     Jonathan Cameron <[email protected]>
+ *
+ * Modified 2010:      xue liu <[email protected]>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/cc2420.h>
+#include <linux/skbuff.h>
+#include <linux/irq.h>
+#include <net/mac802154.h>
+#include <net/wpan-phy.h>
+
+#define CC2420_WRITEREG(x) (x)
+#define CC2420_READREG(x) (0x40 | x)
+#define CC2420_RAMADDR(x) ((x & 0x7F) | 0x80)
+#define CC2420_RAMBANK(x) ((x >> 1) & 0xc0)
+#define CC2420_WRITERAM(x) (x)
+#define CC2420_READRAM(x) (0x20 | x)
+
+#define CC2420_FREQ_MASK               0x3FF
+#define CC2420_ADR_DECODE_MASK 0x0B00
+#define CC2420_FIFOP_THR_MASK  0x003F
+#define CC2420_CRC_MASK                        0x80
+#define CC2420_RSSI_MASK               0x7F
+#define CC2420_FSMSTATE_MASK   0x2F
+
+#define CC2420_MANFIDLOW       0x233D
+#define CC2420_MANFIDHIGH      0x3000 /* my chip appears to version 3 - 
broaden this with testing */
+
+#define RSSI_OFFSET 45
+
+#define STATE_PDOWN 0
+#define STATE_IDLE  1
+#define STATE_RX_CALIBRATE             2
+#define STATE_RX_CALIBRATE2            40
+
+#define STATE_RX_SFD_SEARCH_MIN 3
+#define STATE_RX_SFD_SEARCH_MAX 6
+#define STATE_RX_FRAME                 16
+#define STATE_RX_FRAME2                        40
+#define STATE_RX_WAIT                  14
+#define STATE_RX_OVERFLOW              17
+#define STATE_TX_ACK_CALIBRATE 48
+#define STATE_TX_ACK_PREAMBLE_MIN      49
+#define STATE_TX_ACK_PREAMBLE_MAX      51
+#define STATE_TX_ACK_MIN                       52
+#define STATE_TX_ACK_MAX                       54
+#define STATE_TX_CALIBRATE                     32
+#define STATE_TX_PREAMBLE_MIN          34
+#define STATE_TX_PREAMBLE_MAX          36
+#define STATE_TX_FRAME_MIN                     37
+#define STATE_TX_FRAME_MAX                     39
+#define STATE_TX_UNDERFLOW                     56
+
+struct cc2420_local {
+       struct cc2420_platform_data *pdata;
+       struct spi_device *spi;
+       struct ieee802154_dev *dev;
+       u8 *buf;
+       struct mutex bmux;
+       int fifop_irq;
+       int sfd_irq;
+       struct work_struct fifop_irqwork;
+       struct work_struct sfd_irqwork;
+       spinlock_t lock;
+       unsigned irq_disabled:1;/* P:lock */
+       unsigned is_tx:1;               /* P:lock */
+
+       struct completion tx_complete;
+};
+static int cc2420_get_status(struct cc2420_local *lp, u8 *status)
+{
+       int ret;
+       struct spi_message msg;
+       struct spi_transfer xfer = {
+               .len = 1,
+               .tx_buf = lp->buf,
+               .rx_buf = lp->buf,
+       };
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfer, &msg);
+       mutex_lock(&lp->bmux);
+       lp->buf[0] = CC2420_WRITEREG(CC2420_SNOP);
+       dev_vdbg(&lp->spi->dev, "get status command buf[0] = %02x\n", 
lp->buf[0]);
+       ret = spi_sync(lp->spi, &msg);
+       if (!ret)
+               *status = lp->buf[0];
+       dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", lp->buf[0]);
+       mutex_unlock(&lp->bmux);
+       return ret;
+
+}
+static int cc2420_cmd_strobe(struct cc2420_local *lp,
+                                u8 addr)
+{
+       int ret;
+       u8 status = 0xf;
+       struct spi_message msg;
+       struct spi_transfer xfer = {
+               .len = 1,
+               .tx_buf = lp->buf,
+               .rx_buf = lp->buf,
+       };
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfer, &msg);
+       mutex_lock(&lp->bmux);
+       lp->buf[0] = CC2420_WRITEREG(addr);
+       dev_vdbg(&lp->spi->dev, "cmd strobe buf[0] = %02x\n", lp->buf[0]);
+       ret = spi_sync(lp->spi, &msg);
+       if (!ret)
+               status = lp->buf[0];
+       dev_vdbg(&lp->spi->dev, "buf[0] = %02x\n", lp->buf[0]);
+
+       mutex_unlock(&lp->bmux);
+
+       return ret;
+}
+
+static int cc2420_read_16_bit_reg(struct cc2420_local *lp,
+                                 u8 addr, u16 *data)
+{
+       int ret;
+       struct spi_message msg;
+       struct spi_transfer xfer = {
+               .len = 3,
+               .tx_buf = lp->buf,
+               .rx_buf = lp->buf,
+       };
+
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfer, &msg);
+       mutex_lock(&lp->bmux);
+       lp->buf[0] = CC2420_READREG(addr);
+       dev_vdbg(&lp->spi->dev, "readreg addr buf[0] = %02x\n", lp->buf[0]);
+       ret = spi_sync(lp->spi, &msg);
+       dev_dbg(&lp->spi->dev, "status = %d\n", ret);
+       mutex_unlock(&lp->bmux);
+       dev_dbg(&lp->spi->dev, "buf[0] = %02x\n", lp->buf[0]);
+       dev_dbg(&lp->spi->dev, "buf[1] = %02x\n", lp->buf[1]);
+       dev_dbg(&lp->spi->dev, "buf[2] = %02x\n", lp->buf[2]);
+       if (!ret)
+               *data = ((u16) (lp->buf[1]) << 8) | lp->buf[2];
+       return ret;
+}
+
+static int cc2420_write_16_bit_reg_partial(struct cc2420_local *lp,
+                                          u8 addr, u16 data, u16 mask)
+{
+       int ret;
+       struct spi_message msg;
+       struct spi_transfer xfer = {
+               .len = 3,
+               .tx_buf = lp->buf,
+               .rx_buf = lp->buf,
+       };
+       dev_dbg(&lp->spi->dev, "data = %x\n", data);
+       dev_dbg(&lp->spi->dev, "mask = %x\n", mask);
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfer, &msg);
+       mutex_lock(&lp->bmux);
+       lp->buf[0] = CC2420_READREG(addr);
+       dev_vdbg(&lp->spi->dev, "read addr buf[0] = %02x\n", lp->buf[0]);
+       ret = spi_sync(lp->spi, &msg);
+       if (ret)
+               goto err_ret;
+       dev_dbg(&lp->spi->dev, "read buf[0] = %02x\n", lp->buf[0]);
+       dev_dbg(&lp->spi->dev, "buf[1] = %02x\n", lp->buf[1]);
+       dev_dbg(&lp->spi->dev, "buf[2] = %02x\n", lp->buf[2]);
+
+       lp->buf[0] = CC2420_WRITEREG(addr);
+
+       lp->buf[1] &= ~(mask >> 8);
+       lp->buf[2] &= ~(mask & 0xFF);
+       lp->buf[1] |= (mask >> 8) & (data >> 8);
+       lp->buf[2] |= (mask & 0xFF) & (data & 0xFF);
+       dev_vdbg(&lp->spi->dev, "writereg addr buf[0] = %02x\n", lp->buf[0]);
+       dev_dbg(&lp->spi->dev, "buf[1] = %02x\n", lp->buf[1]);
+       dev_dbg(&lp->spi->dev, "buf[2] = %02x\n", lp->buf[2]);
+       ret = spi_sync(lp->spi, &msg);
+       if (ret)
+               goto err_ret;
+       dev_dbg(&lp->spi->dev, "return status buf[0] = %02x\n", lp->buf[0]);
+       dev_dbg(&lp->spi->dev, "buf[1] = %02x\n", lp->buf[1]);
+       dev_dbg(&lp->spi->dev, "buf[2] = %02x\n", lp->buf[2]);
+
+err_ret:
+       mutex_unlock(&lp->bmux);
+       return ret;
+}
+
+static int cc2420_channel(struct ieee802154_dev *dev, int page, int channel)
+{
+       struct cc2420_local *lp = dev->priv;
+       int ret;
+
+       might_sleep();
+       dev_dbg(&lp->spi->dev, "trying to set channel\n");
+
+       BUG_ON(page != 0);
+       BUG_ON(channel < CC2420_MIN_CHANNEL);
+       BUG_ON(channel > CC2420_MAX_CHANNEL);
+
+       ret = cc2420_write_16_bit_reg_partial(lp, CC2420_FSCTRL, 357 + 
5*(channel - 11), CC2420_FREQ_MASK);
+
+       dev->phy->current_channel = channel;
+       return ret;
+}
+
+static int cc2420_write_ram(struct cc2420_local *lp, u16 addr, u8 len, u8 
*data)
+{
+       int status;
+       struct spi_message msg;
+       struct spi_transfer xfer_head = {
+               .len            = 2,
+               .tx_buf         = lp->buf,
+               .rx_buf         = lp->buf,
+       };
+       struct spi_transfer xfer_buf = {
+               .len            = len,
+               .tx_buf         = data,
+       };
+
+       mutex_lock(&lp->bmux);
+       lp->buf[0] = CC2420_RAMADDR(addr);
+       lp->buf[1] = CC2420_WRITERAM(CC2420_RAMBANK(addr));
+       dev_dbg(&lp->spi->dev, "write ram addr buf[0] = %02x\n", lp->buf[0]);
+       dev_dbg(&lp->spi->dev, "ram bank buf[1] = %02x\n", lp->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_dbg(&lp->spi->dev, "spi status = %d\n", status);
+       if (msg.status)
+               status = msg.status;
+       dev_dbg(&lp->spi->dev, "cc2420 status = %02x\n", lp->buf[0]);
+       dev_dbg(&lp->spi->dev, "buf[1] = %02x\n", lp->buf[1]);
+
+       mutex_unlock(&lp->bmux);
+       return status;
+}
+
+static int cc2420_write_txfifo(struct cc2420_local *lp, u8 *data, u8 len)
+{
+       int status;
+       /* Length byte must include FCS even if calculated in hardware */
+       int len_byte = len + 2;
+       struct spi_message msg;
+       struct spi_transfer xfer_head = {
+               .len            = 1,
+               .tx_buf         = lp->buf,
+               .rx_buf         = lp->buf,
+       };
+       struct spi_transfer xfer_len = {
+               .len            = 1,
+               .tx_buf         = &len_byte,
+       };
+       struct spi_transfer xfer_buf = {
+               .len            = len,
+               .tx_buf         = data,
+       };
+
+       mutex_lock(&lp->bmux);
+       lp->buf[0] = CC2420_WRITEREG(CC2420_TXFIFO);
+       dev_vdbg(&lp->spi->dev, "TX_FIFO addr buf[0] = %02x\n", lp->buf[0]);
+
+       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);
+
+       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", lp->buf[0]);
+
+       mutex_unlock(&lp->bmux);
+       return status;
+}
+
+static int
+cc2420_read_rxfifo(struct cc2420_local *lp, u8 *data, u8 *len, u8 *lqi)
+{
+       int status;
+       struct spi_message msg;
+       struct spi_transfer xfer_head = {
+               .len            = 2,
+               .tx_buf         = lp->buf,
+               .rx_buf         = lp->buf,
+       };
+       struct spi_transfer xfer_buf = {
+               .len            = *len,
+               .rx_buf         = data,
+       };
+
+       mutex_lock(&lp->bmux);
+       lp->buf[0] = CC2420_READREG(CC2420_RXFIFO);
+       lp->buf[1] = 0x00;
+       dev_vdbg(&lp->spi->dev, "read rxfifo buf[0] = %02x\n", lp->buf[0]);
+       dev_vdbg(&lp->spi->dev, "buf[1] = %02x\n", lp->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, "return status buf[0] = %02x\n", lp->buf[0]);
+       dev_vdbg(&lp->spi->dev, "length buf[1] = %02x\n", lp->buf[1]);
+
+       *lqi = data[lp->buf[1] - 1] & 0x7f;
+       *len = lp->buf[1]; /* it should be less than 130 */
+
+       mutex_unlock(&lp->bmux);
+
+       return status;
+}
+
+
+static int cc2420_tx(struct ieee802154_dev *dev, struct sk_buff *skb)
+{
+       struct cc2420_local *lp = dev->priv;
+       int rc;
+       unsigned long flags;
+       u8 status = 0;
+
+       pr_debug("%s\n", __func__);
+
+       might_sleep();
+
+       rc = cc2420_cmd_strobe(lp, CC2420_SFLUSHTX);
+       if (rc)
+               goto err_rx;
+       rc = cc2420_write_txfifo(lp, skb->data, skb->len);
+       if (rc)
+               goto err_rx;
+
+       /* TODO: test CCA pin */
+
+       rc = cc2420_get_status(lp, &status);
+       if (rc)
+               goto err_rx;
+
+       if (status & CC2420_STATUS_TX_UNDERFLOW) {
+               dev_err(&lp->spi->dev, "cc2420 tx underflow!\n");
+               goto err_rx;
+       }
+
+       spin_lock_irqsave(&lp->lock, flags);
+       BUG_ON(lp->is_tx);
+       lp->is_tx = 1;
+       INIT_COMPLETION(lp->tx_complete);
+       spin_unlock_irqrestore(&lp->lock, flags);
+
+       rc = cc2420_cmd_strobe(lp, CC2420_STXONCCA);
+       if (rc)
+               goto err;
+
+       rc = wait_for_completion_interruptible(&lp->tx_complete);
+       if (rc < 0)
+               goto err;
+
+       cc2420_cmd_strobe(lp, CC2420_SFLUSHTX);
+       cc2420_cmd_strobe(lp, CC2420_SRXON);
+
+       return rc;
+
+err:
+       spin_lock_irqsave(&lp->lock, flags);
+       lp->is_tx = 0;
+       spin_unlock_irqrestore(&lp->lock, flags);
+err_rx:
+       cc2420_cmd_strobe(lp, CC2420_SFLUSHTX);
+       cc2420_cmd_strobe(lp, CC2420_SRXON);
+       return rc;
+}
+
+static int cc2420_rx(struct cc2420_local *lp)
+{
+       u8 len = 128;
+       u8 lqi = 0; /* link quality */
+       int rc;
+       struct sk_buff *skb;
+
+       skb = alloc_skb(len, GFP_KERNEL);
+       if (!skb)
+               return -ENOMEM;
+
+       rc = cc2420_read_rxfifo(lp, skb_put(skb, len), &len, &lqi);
+       if (len < 2) {
+               kfree_skb(skb);
+               return -EINVAL;
+       }
+
+       /* Clip last two bytes. When using hardware FCS they get replaced with
+        * correlation value, FCS flag and RSSI value */
+       skb_trim(skb, len-2);
+
+       ieee802154_rx_irqsafe(lp->dev, skb, lqi);
+
+       dev_dbg(&lp->spi->dev, "RXFIFO: %d %d %x\n", rc, len, lqi);
+
+       return 0;
+}
+
+static int
+cc2420_set_hw_addr_filt(struct ieee802154_dev *dev,
+                                               struct ieee802154_hw_addr_filt 
*filt,
+                                               unsigned long changed)
+{
+       struct cc2420_local *lp = dev->priv;
+       u16 reg;
+
+       might_sleep();
+
+       if (changed & IEEE802515_IEEEADDR_CHANGED)
+               cc2420_write_ram(lp, CC2420_RAM_IEEEADR,
+                                                IEEE802154_ALEN,
+                                                filt->ieee_addr);
+
+       if (changed & IEEE802515_SADDR_CHANGED) {
+               u8 short_addr[2];
+               short_addr[0] = filt->short_addr & 0xff;/* LSB */
+               short_addr[1] = filt->short_addr >> 8;  /* MSB */
+               cc2420_write_ram(lp, CC2420_RAM_SHORTADR,
+                                                sizeof(short_addr),
+                                                short_addr);
+       }
+
+       if (changed & IEEE802515_PANID_CHANGED) {
+               u8 panid[2];
+               panid[0] = filt->pan_id & 0xff; /* LSB */
+               panid[1] = filt->pan_id >> 8;   /* MSB */
+               cc2420_write_ram(lp, CC2420_RAM_PANID,
+                                                sizeof(panid),
+                                                panid);
+       }
+
+       if (changed & IEEE802515_PANC_CHANGED) {
+               cc2420_read_16_bit_reg(lp, CC2420_MDMCTRL0, &reg);
+               if (filt->pan_coord)
+                       reg |= 1 << CC2420_MDMCTRL0_PANCRD;
+               else
+                       reg &= ~(1 << CC2420_MDMCTRL0_PANCRD);
+               cc2420_write_16_bit_reg_partial(lp, CC2420_MDMCTRL0,
+                                                                               
reg, 1 << CC2420_MDMCTRL0_PANCRD);
+       }
+
+       return 0;
+}
+
+static int cc2420_ed(struct ieee802154_dev *dev, u8 *level)
+{
+       struct cc2420_local *lp = dev->priv;
+       u16 rssi;
+       int ret;
+       dev_dbg(&lp->spi->dev, "ed called\n");
+
+       ret = cc2420_read_16_bit_reg(lp, CC2420_RSSI, &rssi);
+       if (ret)
+               return ret;
+
+       /* P = RSSI_VAL + RSSI_OFFSET[dBm] */
+       *level = (rssi & CC2420_RSSI_MASK) + RSSI_OFFSET;
+       return ret;
+}
+
+static int cc2420_start(struct ieee802154_dev *dev)
+{
+       return cc2420_cmd_strobe(dev->priv, CC2420_SRXON);
+}
+
+static void cc2420_stop(struct ieee802154_dev *dev)
+{
+       cc2420_cmd_strobe(dev->priv, CC2420_SRFOFF);
+}
+
+static struct ieee802154_ops cc2420_ops = {
+       .owner          = THIS_MODULE,
+       .xmit           = cc2420_tx,
+       .ed             = cc2420_ed,
+       .start          = cc2420_start,
+       .stop           = cc2420_stop,
+       .set_channel = cc2420_channel,
+       .set_hw_addr_filt = cc2420_set_hw_addr_filt,
+};
+
+static int cc2420_register(struct cc2420_local *lp)
+{
+       int ret = -ENOMEM;
+       lp->dev = ieee802154_alloc_device(sizeof(*lp), &cc2420_ops);
+       if (!lp->dev)
+               goto err_ret;
+
+       lp->dev->priv = lp;
+       lp->dev->parent = &lp->spi->dev;
+       //look this up.
+       lp->dev->extra_tx_headroom = 0;
+       //and this
+       //lp->dev->channel_mask = 0x7ff;
+       //and more.
+
+       /* We do support only 2.4 Ghz */
+       lp->dev->phy->channels_supported[0] = 0x7FFF800;
+       lp->dev->flags = IEEE802154_HW_OMIT_CKSUM;
+
+       dev_dbg(&lp->spi->dev, "registered cc2420\n");
+       ret = ieee802154_register_device(lp->dev);
+       if (ret)
+               goto err_free_device;
+
+       return 0;
+err_free_device:
+       ieee802154_free_device(lp->dev);
+err_ret:
+       return ret;
+}
+
+static void cc2420_unregister(struct cc2420_local *lp)
+{
+       ieee802154_unregister_device(lp->dev);
+       //check this is needed
+       ieee802154_free_device(lp->dev);
+}
+
+static irqreturn_t cc2420_isr(int irq, void *data)
+{
+       struct cc2420_local *lp = data;
+
+       spin_lock(&lp->lock);
+       if (!lp->irq_disabled) {
+               disable_irq_nosync(irq);
+               lp->irq_disabled = 1;
+       }
+       spin_unlock(&lp->lock);
+
+       if (irq == lp->sfd_irq)
+               schedule_work(&lp->sfd_irqwork);
+
+       if (irq == lp->fifop_irq)
+               schedule_work(&lp->fifop_irqwork);
+
+       return IRQ_HANDLED;
+}
+
+static void cc2420_fifop_irqwork(struct work_struct *work)
+{
+       struct cc2420_local *lp
+               = container_of(work, struct cc2420_local, fifop_irqwork);
+       unsigned long flags;
+
+       dev_dbg(&lp->spi->dev, "fifop interrupt received\n");
+
+       if (gpio_get_value(lp->pdata->fifo))
+               cc2420_rx(lp);
+       else
+               dev_err(&lp->spi->dev, "rxfifo overflow\n");
+
+       cc2420_cmd_strobe(lp, CC2420_SFLUSHRX);
+       cc2420_cmd_strobe(lp, CC2420_SFLUSHRX);
+
+       spin_lock_irqsave(&lp->lock, flags);
+       if (lp->irq_disabled) {
+               lp->irq_disabled = 0;
+               enable_irq(lp->fifop_irq);
+       }
+       spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+static void cc2420_sfd_irqwork(struct work_struct *work)
+{
+       struct cc2420_local *lp
+               = container_of(work, struct cc2420_local, sfd_irqwork);
+       unsigned long flags;
+
+       dev_dbg(&lp->spi->dev, "sfd interrupt received\n");
+
+       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);
+       }
+
+       spin_lock_irqsave(&lp->lock, flags);
+       if (lp->irq_disabled) {
+               lp->irq_disabled = 0;
+               enable_irq(lp->sfd_irq);
+       }
+       spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+static int cc2420_hw_init(struct cc2420_local *lp)
+{
+       int ret;
+       u16 state;
+       u8 status = 0xff;
+       int timeout = 500; /* 500us delay */
+       ret = cc2420_read_16_bit_reg(lp, CC2420_FSMSTATE, &state);
+       if (ret)
+               goto error_ret;
+       /* reset has occured prior to this, so there should be no other option 
*/
+       if (state != STATE_PDOWN) {
+               ret = -EINVAL;
+               goto error_ret;
+       }
+       ret = cc2420_cmd_strobe(lp, CC2420_SXOSCON);
+       if (ret)
+               goto error_ret;
+
+       do {
+               ret = cc2420_get_status(lp, &status);
+               if (ret)
+                       goto error_ret;
+               if (timeout-- <= 0) {
+                       dev_err(&lp->spi->dev, "oscillator start failed!\n");
+                       return ret;
+               }
+               udelay(1);
+       } while (!(status & CC2420_STATUS_XOSC16M_STABLE));
+
+       dev_info(&lp->spi->dev, "oscillator succesfully brought up\n");
+
+       return 0;
+error_ret:
+       return ret;
+}
+
+static int __devinit cc2420_probe(struct spi_device *spi)
+{
+       int ret;
+       u16 manidl, manidh;
+       struct cc2420_local *lp = kzalloc(sizeof *lp, GFP_KERNEL);
+       if (!lp) {
+               ret = -ENOMEM;
+               goto error_ret;
+       }
+
+       lp->pdata = spi->dev.platform_data;
+       if (!lp->pdata) {
+               dev_err(&spi->dev, "no platform data\n");
+               ret = -EINVAL;
+               goto err_free_local;
+       }
+       spi_set_drvdata(spi, lp);
+       mutex_init(&lp->bmux);
+       INIT_WORK(&lp->fifop_irqwork, cc2420_fifop_irqwork);
+       INIT_WORK(&lp->sfd_irqwork, cc2420_sfd_irqwork);
+       spin_lock_init(&lp->lock);
+       init_completion(&lp->tx_complete);
+
+       lp->spi = spi;
+       lp->buf = kmalloc(3*sizeof *lp->buf, GFP_KERNEL);
+       if (!lp->buf) {
+               ret = -ENOMEM;
+               goto err_free_local;
+       }
+
+       /* Request all the gpio's */
+       ret = gpio_request(lp->pdata->fifo, "fifo");
+       if (ret)
+               goto err_free_buf;
+       ret = gpio_request(lp->pdata->cca, "cca");
+       if (ret)
+               goto err_free_gpio_fifo;
+#if 0
+       /* This is causing problems as fifop is gpio 0 ? */
+       ret = gpio_request(lp->pdata->fifop, "fifop");
+       if (ret)
+               goto err_free_gpio_cca;
+#endif
+       ret = gpio_request(lp->pdata->sfd, "sfd");
+       if (ret)
+               goto err_free_gpio_fifop;
+       ret = gpio_request(lp->pdata->reset, "reset");
+       if (ret)
+               goto err_free_gpio_sfd;
+       ret = gpio_request(lp->pdata->vreg, "vreg");
+       if (ret)
+               goto err_free_gpio_reset;
+       /* Configure the gpios appropriately */
+
+       /* Enable the voltage regulator */
+       ret = gpio_direction_output(lp->pdata->vreg, 1);
+       if (ret)
+               goto err_free_gpio_reset;
+       udelay(600); /* Time for regulator to power up */
+       /* Toggle the reset */
+       ret = gpio_direction_output(lp->pdata->reset, 0);
+       if (ret)
+               goto err_disable_vreg;
+       udelay(10); /* no idea how long this should be? */
+       ret = gpio_direction_output(lp->pdata->reset, 1);
+       if (ret)
+               goto err_disable_vreg;
+       udelay(10);
+
+       ret = gpio_direction_input(lp->pdata->cca);
+       if (ret)
+               goto err_disable_vreg;
+       ret = gpio_direction_input(lp->pdata->fifo);
+       if (ret)
+               goto err_disable_vreg;
+       ret = gpio_direction_input(lp->pdata->fifop);
+       if (ret)
+               goto err_disable_vreg;
+       ret = gpio_direction_input(lp->pdata->sfd);
+       if (ret)
+               goto err_disable_vreg;
+
+
+       /* Check this is actually a cc2420 */
+       ret = cc2420_read_16_bit_reg(lp, CC2420_MANFIDL, &manidl);
+       if (ret)
+               goto err_free_gpio_vreg;
+       ret = cc2420_read_16_bit_reg(lp, CC2420_MANFIDH, &manidh);
+       if (ret)
+               goto err_free_gpio_vreg;
+       if (manidh != CC2420_MANFIDHIGH || manidl != CC2420_MANFIDLOW) {
+               dev_err(&spi->dev, "Incorrect manufacturer id %x%x\n", manidh, 
manidl);
+               ret = -ENODEV;
+               goto err_free_gpio_vreg;
+       }
+       /* TODO: make it more readable */
+       dev_info(&lp->spi->dev, "Found Chipcon CC2420\n");
+       dev_info(&lp->spi->dev, "Manufacturer ID:%x Version:%x Partnum:%x\n",
+                  manidl & 0x0FFF, manidh >> 12, manidl >> 12);
+
+       ret = cc2420_hw_init(lp);
+       if (ret)
+               goto err_disable_vreg;
+
+       lp->fifop_irq = gpio_to_irq(lp->pdata->fifop);
+       lp->sfd_irq = gpio_to_irq(lp->pdata->sfd);
+
+       ret = request_irq(lp->fifop_irq,
+                                         cc2420_isr,
+                                         IRQF_TRIGGER_RISING | IRQF_SHARED,
+                                         dev_name(&spi->dev),
+                                         lp);
+       if (ret) {
+               dev_err(&spi->dev, "could not get fifop irq?\n");
+               goto err_free_fifop_irq;
+       }
+
+       ret = request_irq(lp->sfd_irq,
+                                         cc2420_isr,
+                                         IRQF_TRIGGER_FALLING,
+                                         dev_name(&spi->dev),
+                                         lp);
+       if (ret) {
+               dev_err(&spi->dev, "could not get sfd irq?\n");
+               goto err_free_sfd_irq;
+       }
+
+       dev_info(&lp->spi->dev, "Set fifo threshold to 127\n");
+       cc2420_write_16_bit_reg_partial(lp, CC2420_IOCFG0, 127, 
CC2420_FIFOP_THR_MASK);
+       ret = cc2420_register(lp);
+       if (ret)
+               goto err_free_sfd_irq;
+
+       return 0;
+err_free_sfd_irq:
+       free_irq(lp->sfd_irq, lp);
+err_free_fifop_irq:
+       free_irq(lp->fifop_irq, lp);
+err_disable_vreg:
+       gpio_set_value(lp->pdata->vreg, 0);
+err_free_gpio_vreg:
+       gpio_free(lp->pdata->vreg);
+err_free_gpio_reset:
+       gpio_free(lp->pdata->reset);
+err_free_gpio_sfd:
+       gpio_free(lp->pdata->sfd);
+err_free_gpio_fifop:
+       gpio_free(lp->pdata->fifop);
+//err_free_gpio_cca:
+//     gpio_free(lp->pdata->cca);
+err_free_gpio_fifo:
+       gpio_free(lp->pdata->fifo);
+err_free_buf:
+       kfree(lp->buf);
+err_free_local:
+       kfree(lp);
+error_ret:
+       return ret;
+}
+
+static int __devexit cc2420_remove(struct spi_device *spi)
+{
+       struct cc2420_local *lp = spi_get_drvdata(spi);
+
+       cc2420_unregister(lp);
+       free_irq(lp->fifop_irq, lp);
+       free_irq(lp->sfd_irq, lp);
+       flush_work(&lp->fifop_irqwork);
+       flush_work(&lp->sfd_irqwork);
+       gpio_free(lp->pdata->vreg);
+       gpio_free(lp->pdata->reset);
+       gpio_free(lp->pdata->sfd);
+       gpio_free(lp->pdata->fifop);
+       gpio_free(lp->pdata->cca);
+       gpio_free(lp->pdata->fifo);
+       kfree(lp->buf);
+       kfree(lp);
+
+       return 0;
+}
+
+static struct spi_driver cc2420_driver = {
+       .driver = {
+               .name = "cc2420",
+               .owner = THIS_MODULE,
+       },
+       .probe = cc2420_probe,
+       .remove = __devexit_p(cc2420_remove),
+};
+
+static int __init cc2420_init(void)
+{
+       return spi_register_driver(&cc2420_driver);
+}
+module_init(cc2420_init);
+
+static void __exit cc2420_exit(void)
+{
+       spi_unregister_driver(&cc2420_driver);
+}
+module_exit(cc2420_exit);
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/spi/cc2420.h b/include/linux/spi/cc2420.h
new file mode 100644
index 0000000..5db3d58
--- /dev/null
+++ b/include/linux/spi/cc2420.h
@@ -0,0 +1,198 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Author:     Jonathan Cameron <[email protected]>
+ *
+ * Modified 2010:      xue liu <[email protected]>
+ */
+
+#ifndef __CC2420_H
+#define __CC2420_H
+struct cc2420_platform_data {
+       int fifo;   /* high when bytes in fifo */
+       int cca;    /* clear channel assesment */
+       int fifop;  /* high when no bytes exceeds some threshold */
+       int sfd;    /* high when frame come */
+       int reset;
+       int vreg;   /* voltage regulator enable */
+};
+
+enum {
+  CC2420_MIN_CHANNEL =              11,
+  CC2420_MAX_CHANNEL =              26
+};
+
+/* Command strobes */
+#define CC2420_SNOP            0x00 /* no op */
+#define CC2420_SXOSCON         0x01 /* Enable crystal oscillator */
+#define CC2420_STXCAL          0x02
+#define CC2420_SRXON           0x03
+#define CC2420_STXON           0x04
+#define CC2420_STXONCCA        0x05
+#define CC2420_SRFOFF          0x06
+#define CC2420_SXOSCOFF        0x07
+#define CC2420_SFLUSHRX        0x08
+#define CC2420_SFLUSHTX        0x09
+#define CC2420_SACK            0x0A
+#define CC2420_SACKPEND        0x0B
+#define CC2420_SRXDEC          0x0C
+#define CC2420_STXENC          0x0D
+#define CC2420_SAES            0x0E
+
+/* Control registers */
+#define CC2420_MAIN            0x10
+#define CC2420_MDMCTRL0        0x11
+#define CC2420_MDMCTRL1        0x12
+#define CC2420_RSSI            0x13
+#define CC2420_SYNCWORD        0x14
+#define CC2420_TXCTRL          0x15
+#define CC2420_RXCTRL0         0x16
+#define CC2420_RXCTRL1         0x17
+#define CC2420_FSCTRL          0x18
+#define CC2420_SECCTRL0        0x19
+#define CC2420_SECCTRL1        0x1A
+#define CC2420_BATTMON         0x1B
+#define CC2420_IOCFG0          0x1C
+#define CC2420_IOCFG1          0x1D
+#define CC2420_MANFIDL         0x1E /* manufacturer id low */
+#define CC2420_MANFIDH         0x1F /* manufacturer id high */
+#define CC2420_FSMTC           0x20
+#define CC2420_MANAND          0x21
+#define CC2420_MANOR           0x22
+#define CC2420_AGCCTRL         0x23
+#define CC2420_AGCTST0         0x24
+#define CC2420_AGCTST1         0x25
+#define CC2420_AGCTST2         0x26
+#define CC2420_FSTST0          0x27
+#define CC2420_FSTST1          0x28
+#define CC2420_FSTST2          0x29
+#define CC2420_FSTST3          0x2A
+#define CC2420_RXBPFTST        0x2B
+#define CC2420_FSMSTATE        0x2C /* lowest 6 bytes give current state */
+#define CC2420_ADCTST          0x2D
+#define CC2420_DACTST          0x2E
+#define CC2420_TOPTST          0x2F
+#define CC2420_RESERVED        0x30
+#define CC2420_TXFIFO          0x3E
+#define CC2420_RXFIFO          0x3F
+
+#define CC2420_RAM_SHORTADR    0x16A
+#define CC2420_RAM_PANID       0x168
+#define CC2420_RAM_IEEEADR     0x160
+#define CC2420_RAM_CBCSTATE    0x150
+#define CC2420_RAM_TXNONCE     0x140
+#define CC2420_RAM_KEY1        0x130
+#define CC2420_RAM_SABUF       0x120
+#define CC2420_RAM_RXNONCE     0x110
+#define CC2420_RAM_KEY0        0x100
+#define CC2420_RAM_RXFIFO      0x080
+#define CC2420_RAM_TXFIFO      0x000
+
+/* MDMCTRL0 Register Bit Positions */
+#define CC2420_MDMCTRL0_FRAME        13  /* 0 : reject reserved frame types, 1 
= accept */
+#define CC2420_MDMCTRL0_PANCRD       12  /* 0 : not a PAN coordinator */
+#define CC2420_MDMCTRL0_ADRDECODE    11  /* 1 : enable address decode */
+#define CC2420_MDMCTRL0_CCAHIST      8   /* 3 bits (8,9,10) : CCA hysteris in 
db */
+#define CC2420_MDMCTRL0_CCAMODE      6   /* 2 bits (6,7)    : CCA trigger 
modes */
+#define CC2420_MDMCTRL0_AUTOCRC      5   /* 1 : generate/chk CRC */
+#define CC2420_MDMCTRL0_AUTOACK      4   /* 1 : Ack valid packets */
+#define CC2420_MDMCTRL0_PREAMBL      0   /* 4 bits (0..3): Preamble length */
+
+/* MDMCTRL1 Register Bit Positions */
+#define CC2420_MDMCTRL1_CORRTHRESH   6   /* 5 bits (6..10) : correlator 
threshold */
+#define CC2420_MDMCTRL1_DEMOD_MODE   5   /* 0: lock freq after preamble match, 
1: continous udpate */
+#define CC2420_MDMCTRL1_MODU_MODE    4   /* 0: IEEE 802.15.4 */
+#define CC2420_MDMCTRL1_TX_MODE      2   /* 2 bits (2,3) : 0: use buffered 
TXFIFO */
+#define CC2420_MDMCTRL1_RX_MODE      0   /* 2 bits (0,1) : 0: use buffered 
RXFIFO */
+
+/* RSSI Register Bit Positions */
+#define CC2420_RSSI_CCA_THRESH       8   /* 8 bits (8..15) : 2's compl CCA 
threshold */
+
+/* TXCTRL Register Bit Positions */
+#define CC2420_TXCTRL_BUFCUR         14  /* 2 bits (14,15) : Tx mixer buffer 
bias current */
+#define CC2420_TXCTRL_TURNARND       13  /* wait time after STXON before xmit 
*/
+#define CC2420_TXCTRL_VAR            11  /* 2 bits (11,12) : Varactor array 
settings */
+#define CC2420_TXCTRL_XMITCUR        9   /* 2 bits (9,10)  : Xmit mixer 
currents */
+#define CC2420_TXCTRL_PACUR          6   /* 3 bits (6..8)  : PA current */
+#define CC2420_TXCTRL_PADIFF         5   /* 1: Diff PA, 0: Single ended PA */
+#define CC2420_TXCTRL_PAPWR          0   /* 5 bits (0..4): Output PA level */
+
+/* RXCTRL0 Register Bit Positions */
+#define CC2420_RXCTRL0_BUFCUR        12  /* 2 bits (12,13) : Rx mixer buffer 
bias current */
+#define CC2420_RXCTRL0_HILNAG        10  /* 2 bits (10,11) : High gain, LNA 
current */
+#define CC2420_RXCTRL0_MLNAG          8  /* 2 bits (8,9)   : Med gain, LNA 
current */
+#define CC2420_RXCTRL0_LOLNAG         6  /* 2 bits (6,7)   : Lo gain, LNA 
current */
+#define CC2420_RXCTRL0_HICUR          4  /* 2 bits (4,5)   : Main high LNA 
current */
+#define CC2420_RXCTRL0_MCUR           2  /* 2 bits (2,3)   : Main med  LNA 
current */
+#define CC2420_RXCTRL0_LOCUR          0  /* 2 bits (0,1)   : Main low LNA 
current */
+
+/* RXCTRL1 Register Bit Positions */
+#define CC2420_RXCTRL1_LOCUR         13  /* Ref bias current to Rx bandpass 
filter */
+#define CC2420_RXCTRL1_MIDCUR        12  /* Ref bias current to Rx bandpass 
filter */
+#define CC2420_RXCTRL1_LOLOGAIN      11  /* LAN low gain mode */
+#define CC2420_RXCTRL1_MEDLOGAIN     10  /* LAN low gain mode */
+#define CC2420_RXCTRL1_HIHGM          9  /* Rx mixers, hi gain mode */
+#define CC2420_RXCTRL1_MEDHGM         8  /* Rx mixers, hi gain mode */
+#define CC2420_RXCTRL1_LNACAP         6  /* 2 bits (6,7) Selects LAN varactor 
array setting */
+#define CC2420_RXCTRL1_RMIXT          4  /* 2 bits (4,5) Receiver mixer output 
current */
+#define CC2420_RXCTRL1_RMIXV          2  /* 2 bits (2,3) VCM level, mixer 
feedback */
+#define CC2420_RXCTRL1_RMIXCUR        0  /* 2 bits (0,1) Receiver mixer 
current */
+
+/* FSCTRL Register Bit Positions */
+#define CC2420_FSCTRL_LOCK            14 /* 2 bits (14,15) # of clocks for 
synch */
+#define CC2420_FSCTRL_CALDONE         13 /* Read only, =1 if cal done since 
freq synth turned on */
+#define CC2420_FSCTRL_CALRUNING       12 /* Read only, =1 if cal in progress */
+#define CC2420_FSCTRL_LOCKLEN         11 /* Synch window pulse width */
+#define CC2420_FSCTRL_LOCKSTAT        10 /* Read only, = 1 if freq synthesizer 
is loced */
+#define CC2420_FSCTRL_FREQ             0 /* 10 bits, set operating frequency */
+
+/* SECCTRL0 Register Bit Positions */
+#define CC2420_SECCTRL0_PROTECT        9 /* Protect enable Rx fifo */
+#define CC2420_SECCTRL0_CBCHEAD        8 /* Define 1st byte of CBC-MAC */
+#define CC2420_SECCTRL0_SAKEYSEL       7 /* Stand alone key select */
+#define CC2420_SECCTRL0_TXKEYSEL       6 /* Tx key select */
+#define CC2420_SECCTRL0_RXKEYSEL       5 /* Rx key select */
+#define CC2420_SECCTRL0_SECM           2 /* 2 bits (2..4) # of bytes in 
CBC-MAX auth field */
+#define CC2420_SECCTRL0_SECMODE        0 /* Security mode */
+
+/* SECCTRL1 Register Bit Positions */
+#define CC2420_SECCTRL1_TXL            8 /* 7 bits (8..14) Tx in-line security 
*/
+#define CC2420_SECCTRL1_RXL            0 /* 7 bits (0..7)  Rx in-line security 
*/
+
+/* BATTMON  Register Bit Positions */
+#define CC2420_BATTMON_OK              6 /* Read only, battery voltage OK */
+#define CC2420_BATTMON_EN              5 /* Enable battery monitor */
+#define CC2420_BATTMON_VOLT            0 /* 5 bits (0..4) Battery toggle 
voltage */
+
+/* IOCFG0 Register Bit Positions */
+#define CC2420_IOCFG0_FIFOPOL         10 /* Fifo signal polarity */
+#define CC2420_IOCFG0_FIFOPPOL         9 /* FifoP signal polarity */
+#define CC2420_IOCFG0_SFD              8 /* SFD signal polarity */
+#define CC2420_IOCFG0_CCAPOL           7 /* CCA signal polarity */
+#define CC2420_IOCFG0_FIFOTHR          0 /* 7 bits, (0..6) # of Rx bytes in 
fifo to trg fifop */
+
+/* IOCFG1 Register Bit Positions */
+#define CC2420_IOCFG1_HSSD            10 /* 2 bits (10,11) HSSD module config 
*/
+#define CC2420_IOCFG1_SFDMUX           5 /* 5 bits (5..9)  SFD multiplexer pin 
settings */
+#define CC2420_IOCFG1_CCAMUX           0 /* 5 bits (0..4)  CCA multiplexe pin 
settings */
+
+/* Status byte elements */
+#define CC2420_STATUS_XOSC16M_STABLE   (1 << 6)
+#define CC2420_STATUS_TX_UNDERFLOW     (1 << 5)
+#define CC2420_STATUS_ENC_BUSY         (1 << 4)
+#define CC2420_STATUS_TX_ACTIVE                (1 << 3)
+#define CC2420_STATUS_LOCK             (1 << 2)
+#define CC2420_STATUS_RSSI_VALID       (1 << 1)
+
+#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

Reply via email to