Since the AT86RF2xy chips are mostly compatible, this is only a small
change to the actual driver code. The at86rf230 driver already supports the
RF212 in most places, only three small adjustments are required:

 * force the initial state after P_ON to FORCE_TRX_OFF to work around a
   documented erratum
 * channels_supported depends on the frequency of the transceiver, and
   thus is_rf212
 * do early detection of chip version select an appropriate _ops struct
   based on the chip version.

Signed-off-by: Phoebe Buckheister <phoebe.buckheis...@itwm.fraunhofer.de>
---
 drivers/net/ieee802154/at86rf230.c |  271 ++++++++++++++++++++++++------------
 1 file changed, 182 insertions(+), 89 deletions(-)

diff --git a/drivers/net/ieee802154/at86rf230.c 
b/drivers/net/ieee802154/at86rf230.c
index ab31544..93123ae 100644
--- a/drivers/net/ieee802154/at86rf230.c
+++ b/drivers/net/ieee802154/at86rf230.c
@@ -37,7 +37,6 @@
 
 struct at86rf230_local {
        struct spi_device *spi;
-       int rstn, slp_tr, dig2;
 
        u8 part;
        u8 vers;
@@ -55,6 +54,11 @@ struct at86rf230_local {
        bool is_tx;
 };
 
+static inline int is_rf212(struct at86rf230_local *local)
+{
+       return local->part == 7;
+}
+
 #define        RG_TRX_STATUS   (0x01)
 #define        SR_TRX_STATUS           0x01, 0x1f, 0
 #define        SR_RESERVED_01_3        0x01, 0x20, 5
@@ -100,7 +104,9 @@ struct at86rf230_local {
 #define        SR_SFD_VALUE            0x0b, 0xff, 0
 #define        RG_TRX_CTRL_2   (0x0c)
 #define        SR_OQPSK_DATA_RATE      0x0c, 0x03, 0
-#define        SR_RESERVED_0c_2        0x0c, 0x7c, 2
+#define        SR_SUB_MODE             0x0c, 0x04, 2
+#define        SR_BPSK_QPSK            0x0c, 0x08, 3
+#define        SR_RESERVED_0c_4        0x0c, 0x70, 4
 #define        SR_RX_SAFE_MODE         0x0c, 0x80, 7
 #define        RG_ANT_DIV      (0x0d)
 #define        SR_ANT_CTRL             0x0d, 0x03, 0
@@ -244,6 +250,57 @@ struct at86rf230_local {
 #define STATE_TRANSITION_IN_PROGRESS 0x1F
 
 static int
+__at86rf230_detect_device(struct spi_device *spi, u16 *man_id, u8 *part,
+               u8 *version)
+{
+       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;
+
+       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);
+
+               status = spi_sync(spi, &msg);
+               dev_vdbg(&spi->dev, "status = %d\n", status);
+               if (msg.status)
+                       status = msg.status;
+
+               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]);
+
+               if (status == 0)
+                       data[reg - RG_PART_NUM] = buf[1];
+               else
+                       break;
+       }
+
+       if (status == 0) {
+               *part = data[0];
+               *version = data[1];
+               *man_id = (data[3] << 8) | data[2];
+       }
+
+       kfree(buf);
+
+       return status;
+}
+
+static int
 __at86rf230_write(struct at86rf230_local *lp, u8 addr, u8 data)
 {
        u8 *buf = lp->buf;
@@ -520,6 +577,28 @@ at86rf230_stop(struct ieee802154_dev *dev)
 }
 
 static int
+at86rf230_set_channel(struct at86rf230_local *lp, int page, int channel)
+{
+       return at86rf230_write_subreg(lp, SR_CHANNEL, channel);
+}
+
+static int
+at86rf212_set_channel(struct at86rf230_local *lp, int page, int channel)
+{
+       int rc;
+
+       if (channel == 0)
+               rc = at86rf230_write_subreg(lp, SR_SUB_MODE, 0);
+       else
+               rc = at86rf230_write_subreg(lp, SR_SUB_MODE, 1);
+
+       if (rc < 0)
+               return rc;
+
+       return at86rf230_write_subreg(lp, SR_CHANNEL, channel);
+}
+
+static int
 at86rf230_channel(struct ieee802154_dev *dev, int page, int channel)
 {
        struct at86rf230_local *lp = dev->priv;
@@ -527,12 +606,21 @@ at86rf230_channel(struct ieee802154_dev *dev, int page, 
int channel)
 
        might_sleep();
 
-       if (page != 0 || channel < 11 || channel > 26) {
+       if (page < 0
+               || page > 31
+               || !(lp->dev->phy->channels_supported[page] & BIT(channel))) {
                WARN_ON(1);
                return -EINVAL;
        }
 
-       rc = at86rf230_write_subreg(lp, SR_CHANNEL, channel);
+       if (is_rf212(lp))
+               rc = at86rf212_set_channel(lp, page, channel);
+       else
+               rc = at86rf230_set_channel(lp, page, channel);
+
+       if (rc < 0)
+               return rc;
+
        msleep(1); /* Wait for PLL */
        dev->phy->current_channel = channel;
 
@@ -678,6 +766,16 @@ static struct ieee802154_ops at86rf230_ops = {
        .set_hw_addr_filt = at86rf230_set_hw_addr_filt,
 };
 
+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,
+};
+
 static void at86rf230_irqwork(struct work_struct *work)
 {
        struct at86rf230_local *lp =
@@ -759,7 +857,8 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
 
        dev_info(&lp->spi->dev, "Status: %02x\n", status);
        if (status == STATE_P_ON) {
-               rc = at86rf230_write_subreg(lp, SR_TRX_CMD, STATE_TRX_OFF);
+               rc = at86rf230_write_subreg(lp, SR_TRX_CMD,
+                       STATE_FORCE_TRX_OFF);
                if (rc)
                        return rc;
                msleep(1);
@@ -824,26 +923,18 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
        return 0;
 }
 
-static void at86rf230_fill_data(struct spi_device *spi)
-{
-       struct at86rf230_local *lp = spi_get_drvdata(spi);
-       struct at86rf230_platform_data *pdata = spi->dev.platform_data;
-
-       lp->rstn = pdata->rstn;
-       lp->slp_tr = pdata->slp_tr;
-       lp->dig2 = pdata->dig2;
-}
-
 static int at86rf230_probe(struct spi_device *spi)
 {
        struct at86rf230_platform_data *pdata;
        struct ieee802154_dev *dev;
        struct at86rf230_local *lp;
-       u8 man_id_0, man_id_1, status;
+       u16 man_id = 0;
+       u8 part = 0, version = 0, status;
        irq_handler_t irq_handler;
        work_func_t irq_worker;
-       int rc, supported = 0;
+       int rc;
        const char *chip;
+       struct ieee802154_ops *ops = NULL;
 
        if (!spi->irq) {
                dev_err(&spi->dev, "no IRQ specified\n");
@@ -856,116 +947,115 @@ static int at86rf230_probe(struct spi_device *spi)
                return -EINVAL;
        }
 
-       dev = ieee802154_alloc_device(sizeof(*lp), &at86rf230_ops);
-       if (!dev)
-               return -ENOMEM;
-
-       lp = dev->priv;
-       lp->dev = dev;
-
-       lp->spi = spi;
-
-       dev->parent = &spi->dev;
-       dev->extra_tx_headroom = 0;
-       /* We do support only 2.4 Ghz */
-       dev->phy->channels_supported[0] = 0x7FFF800;
-       dev->flags = IEEE802154_HW_OMIT_CKSUM;
-
-       if (pdata->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;
-       }
-
-       mutex_init(&lp->bmux);
-       INIT_WORK(&lp->irqwork, irq_worker);
-       spin_lock_init(&lp->lock);
-       init_completion(&lp->tx_complete);
-
-       spi_set_drvdata(spi, lp);
-
-       at86rf230_fill_data(spi);
-
-       rc = gpio_request(lp->rstn, "rstn");
+       rc = gpio_request(pdata->rstn, "rstn");
        if (rc)
-               goto err_rstn;
+               return rc;
 
-       if (gpio_is_valid(lp->slp_tr)) {
-               rc = gpio_request(lp->slp_tr, "slp_tr");
+       if (gpio_is_valid(pdata->slp_tr)) {
+               rc = gpio_request(pdata->slp_tr, "slp_tr");
                if (rc)
                        goto err_slp_tr;
        }
 
-       rc = gpio_direction_output(lp->rstn, 1);
+       rc = gpio_direction_output(pdata->rstn, 1);
        if (rc)
                goto err_gpio_dir;
 
-       if (gpio_is_valid(lp->slp_tr)) {
-               rc = gpio_direction_output(lp->slp_tr, 0);
+       if (gpio_is_valid(pdata->slp_tr)) {
+               rc = gpio_direction_output(pdata->slp_tr, 0);
                if (rc)
                        goto err_gpio_dir;
        }
 
        /* Reset */
        msleep(1);
-       gpio_set_value(lp->rstn, 0);
+       gpio_set_value(pdata->rstn, 0);
        msleep(1);
-       gpio_set_value(lp->rstn, 1);
+       gpio_set_value(pdata->rstn, 1);
        msleep(1);
 
-       rc = at86rf230_read_subreg(lp, SR_MAN_ID_0, &man_id_0);
-       if (rc)
-               goto err_gpio_dir;
-       rc = at86rf230_read_subreg(lp, SR_MAN_ID_1, &man_id_1);
-       if (rc)
+       rc = __at86rf230_detect_device(spi, &man_id, &part, &version);
+       if (rc < 0)
                goto err_gpio_dir;
 
-       if (man_id_1 != 0x00 || man_id_0 != 0x1f) {
+       if (man_id != 0x001f) {
                dev_err(&spi->dev, "Non-Atmel dev found (MAN_ID %02x %02x)\n",
-                       man_id_1, man_id_0);
+                       man_id >> 8, man_id & 0xFF);
                rc = -EINVAL;
                goto err_gpio_dir;
        }
 
-       rc = at86rf230_read_subreg(lp, SR_PART_NUM, &lp->part);
-       if (rc)
-               goto err_gpio_dir;
-
-       rc = at86rf230_read_subreg(lp, SR_VERSION_NUM, &lp->vers);
-       if (rc)
-               goto err_gpio_dir;
-
-       switch (lp->part) {
+       switch (part) {
        case 2:
                chip = "at86rf230";
-               /* supported = 1;  FIXME: should be easy to support; */
+               /* FIXME: should be easy to support; */
                break;
        case 3:
                chip = "at86rf231";
-               supported = 1;
+               ops = &at86rf230_ops;
+               break;
+       case 7:
+               chip = "at86rf212";
+               if (version == 1)
+                       ops = &at86rf212_ops;
                break;
        default:
                chip = "UNKNOWN";
                break;
        }
 
-       dev_info(&spi->dev, "Detected %s chip version %d\n", chip, lp->vers);
-       if (!supported) {
+       dev_info(&spi->dev, "Detected %s chip version %d\n", chip, version);
+       if (!ops) {
                rc = -ENOTSUPP;
                goto err_gpio_dir;
        }
 
+       dev = ieee802154_alloc_device(sizeof(*lp), ops);
+       if (!dev) {
+               rc = -ENOMEM;
+               goto err_gpio_dir;
+       }
+
+       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;
+
+       if (pdata->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;
+       }
+
+       mutex_init(&lp->bmux);
+       INIT_WORK(&lp->irqwork, irq_worker);
+       spin_lock_init(&lp->lock);
+       init_completion(&lp->tx_complete);
+
+       spi_set_drvdata(spi, lp);
+
+       if (is_rf212(lp))
+               dev->phy->channels_supported[0] = 0x00007FF;
+       else
+               dev->phy->channels_supported[0] = 0x7FFF800;
+
        rc = at86rf230_hw_init(lp);
        if (rc)
-               goto err_gpio_dir;
+               goto err_hw_init;
 
        rc = request_irq(spi->irq, irq_handler,
                         IRQF_SHARED | pdata->irq_type,
                         dev_name(&spi->dev), lp);
        if (rc)
-               goto err_gpio_dir;
+               goto err_hw_init;
 
        /* Read irq status register to reset irq line */
        rc = at86rf230_read_subreg(lp, RG_IRQ_STATUS, 0xff, 0, &status);
@@ -980,30 +1070,33 @@ static int at86rf230_probe(struct spi_device *spi)
 
 err_irq:
        free_irq(spi->irq, lp);
+err_hw_init:
        flush_work(&lp->irqwork);
-err_gpio_dir:
-       if (gpio_is_valid(lp->slp_tr))
-               gpio_free(lp->slp_tr);
-err_slp_tr:
-       gpio_free(lp->rstn);
-err_rstn:
+       spi_set_drvdata(spi, NULL);
        mutex_destroy(&lp->bmux);
        ieee802154_free_device(lp->dev);
+
+err_gpio_dir:
+       if (gpio_is_valid(pdata->slp_tr))
+               gpio_free(pdata->slp_tr);
+err_slp_tr:
+       gpio_free(pdata->rstn);
        return rc;
 }
 
 static int at86rf230_remove(struct spi_device *spi)
 {
        struct at86rf230_local *lp = spi_get_drvdata(spi);
+       struct at86rf230_platform_data *pdata = spi->dev.platform_data;
 
        ieee802154_unregister_device(lp->dev);
 
        free_irq(spi->irq, lp);
        flush_work(&lp->irqwork);
 
-       if (gpio_is_valid(lp->slp_tr))
-               gpio_free(lp->slp_tr);
-       gpio_free(lp->rstn);
+       if (gpio_is_valid(pdata->slp_tr))
+               gpio_free(pdata->slp_tr);
+       gpio_free(pdata->rstn);
 
        mutex_destroy(&lp->bmux);
        ieee802154_free_device(lp->dev);
-- 
1.7.9.5


------------------------------------------------------------------------------
Android apps run on BlackBerry 10
Introducing the new BlackBerry 10.2.1 Runtime for Android apps.
Now with support for Jelly Bean, Bluetooth, Mapview and more.
Get your Android app in front of a whole new audience.  Start now.
http://pubads.g.doubleclick.net/gampad/clk?id=124407151&iu=/4140/ostg.clktrk
_______________________________________________
Linux-zigbee-devel mailing list
Linux-zigbee-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-zigbee-devel

Reply via email to