Commit 8fad346f authored by Phoebe Buckheister's avatar Phoebe Buckheister Committed by David S. Miller

ieee802154: add basic support for RF212 to at86rf230 driver

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: default avatarPhoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 92e8c831
...@@ -37,7 +37,6 @@ ...@@ -37,7 +37,6 @@
struct at86rf230_local { struct at86rf230_local {
struct spi_device *spi; struct spi_device *spi;
int rstn, slp_tr, dig2;
u8 part; u8 part;
u8 vers; u8 vers;
...@@ -55,6 +54,11 @@ struct at86rf230_local { ...@@ -55,6 +54,11 @@ struct at86rf230_local {
bool is_tx; bool is_tx;
}; };
static inline int is_rf212(struct at86rf230_local *local)
{
return local->part == 7;
}
#define RG_TRX_STATUS (0x01) #define RG_TRX_STATUS (0x01)
#define SR_TRX_STATUS 0x01, 0x1f, 0 #define SR_TRX_STATUS 0x01, 0x1f, 0
#define SR_RESERVED_01_3 0x01, 0x20, 5 #define SR_RESERVED_01_3 0x01, 0x20, 5
...@@ -100,7 +104,9 @@ struct at86rf230_local { ...@@ -100,7 +104,9 @@ struct at86rf230_local {
#define SR_SFD_VALUE 0x0b, 0xff, 0 #define SR_SFD_VALUE 0x0b, 0xff, 0
#define RG_TRX_CTRL_2 (0x0c) #define RG_TRX_CTRL_2 (0x0c)
#define SR_OQPSK_DATA_RATE 0x0c, 0x03, 0 #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 SR_RX_SAFE_MODE 0x0c, 0x80, 7
#define RG_ANT_DIV (0x0d) #define RG_ANT_DIV (0x0d)
#define SR_ANT_CTRL 0x0d, 0x03, 0 #define SR_ANT_CTRL 0x0d, 0x03, 0
...@@ -243,6 +249,57 @@ struct at86rf230_local { ...@@ -243,6 +249,57 @@ struct at86rf230_local {
#define STATE_BUSY_RX_AACK_NOCLK 0x1E #define STATE_BUSY_RX_AACK_NOCLK 0x1E
#define STATE_TRANSITION_IN_PROGRESS 0x1F #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 static int
__at86rf230_write(struct at86rf230_local *lp, u8 addr, u8 data) __at86rf230_write(struct at86rf230_local *lp, u8 addr, u8 data)
{ {
...@@ -519,6 +576,27 @@ at86rf230_stop(struct ieee802154_dev *dev) ...@@ -519,6 +576,27 @@ at86rf230_stop(struct ieee802154_dev *dev)
at86rf230_state(dev, STATE_FORCE_TRX_OFF); at86rf230_state(dev, STATE_FORCE_TRX_OFF);
} }
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 static int
at86rf230_channel(struct ieee802154_dev *dev, int page, int channel) at86rf230_channel(struct ieee802154_dev *dev, int page, int channel)
{ {
...@@ -527,12 +605,19 @@ at86rf230_channel(struct ieee802154_dev *dev, int page, int channel) ...@@ -527,12 +605,19 @@ at86rf230_channel(struct ieee802154_dev *dev, int page, int channel)
might_sleep(); 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); WARN_ON(1);
return -EINVAL; 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 */ msleep(1); /* Wait for PLL */
dev->phy->current_channel = channel; dev->phy->current_channel = channel;
...@@ -678,6 +763,16 @@ static struct ieee802154_ops at86rf230_ops = { ...@@ -678,6 +763,16 @@ static struct ieee802154_ops at86rf230_ops = {
.set_hw_addr_filt = at86rf230_set_hw_addr_filt, .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) static void at86rf230_irqwork(struct work_struct *work)
{ {
struct at86rf230_local *lp = struct at86rf230_local *lp =
...@@ -759,7 +854,8 @@ static int at86rf230_hw_init(struct at86rf230_local *lp) ...@@ -759,7 +854,8 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
dev_info(&lp->spi->dev, "Status: %02x\n", status); dev_info(&lp->spi->dev, "Status: %02x\n", status);
if (status == STATE_P_ON) { 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) if (rc)
return rc; return rc;
msleep(1); msleep(1);
...@@ -824,26 +920,18 @@ static int at86rf230_hw_init(struct at86rf230_local *lp) ...@@ -824,26 +920,18 @@ static int at86rf230_hw_init(struct at86rf230_local *lp)
return 0; 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) static int at86rf230_probe(struct spi_device *spi)
{ {
struct at86rf230_platform_data *pdata; struct at86rf230_platform_data *pdata;
struct ieee802154_dev *dev; struct ieee802154_dev *dev;
struct at86rf230_local *lp; 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; irq_handler_t irq_handler;
work_func_t irq_worker; work_func_t irq_worker;
int rc, supported = 0; int rc;
const char *chip; const char *chip;
struct ieee802154_ops *ops = NULL;
if (!spi->irq) { if (!spi->irq) {
dev_err(&spi->dev, "no IRQ specified\n"); dev_err(&spi->dev, "no IRQ specified\n");
...@@ -856,116 +944,115 @@ static int at86rf230_probe(struct spi_device *spi) ...@@ -856,116 +944,115 @@ static int at86rf230_probe(struct spi_device *spi)
return -EINVAL; return -EINVAL;
} }
dev = ieee802154_alloc_device(sizeof(*lp), &at86rf230_ops); rc = gpio_request(pdata->rstn, "rstn");
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");
if (rc) if (rc)
goto err_rstn; return rc;
if (gpio_is_valid(lp->slp_tr)) { if (gpio_is_valid(pdata->slp_tr)) {
rc = gpio_request(lp->slp_tr, "slp_tr"); rc = gpio_request(pdata->slp_tr, "slp_tr");
if (rc) if (rc)
goto err_slp_tr; goto err_slp_tr;
} }
rc = gpio_direction_output(lp->rstn, 1); rc = gpio_direction_output(pdata->rstn, 1);
if (rc) if (rc)
goto err_gpio_dir; goto err_gpio_dir;
if (gpio_is_valid(lp->slp_tr)) { if (gpio_is_valid(pdata->slp_tr)) {
rc = gpio_direction_output(lp->slp_tr, 0); rc = gpio_direction_output(pdata->slp_tr, 0);
if (rc) if (rc)
goto err_gpio_dir; goto err_gpio_dir;
} }
/* Reset */ /* Reset */
msleep(1); msleep(1);
gpio_set_value(lp->rstn, 0); gpio_set_value(pdata->rstn, 0);
msleep(1); msleep(1);
gpio_set_value(lp->rstn, 1); gpio_set_value(pdata->rstn, 1);
msleep(1); msleep(1);
rc = at86rf230_read_subreg(lp, SR_MAN_ID_0, &man_id_0); rc = __at86rf230_detect_device(spi, &man_id, &part, &version);
if (rc) if (rc < 0)
goto err_gpio_dir;
rc = at86rf230_read_subreg(lp, SR_MAN_ID_1, &man_id_1);
if (rc)
goto err_gpio_dir; 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", 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; rc = -EINVAL;
goto err_gpio_dir; goto err_gpio_dir;
} }
rc = at86rf230_read_subreg(lp, SR_PART_NUM, &lp->part); switch (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) {
case 2: case 2:
chip = "at86rf230"; chip = "at86rf230";
/* supported = 1; FIXME: should be easy to support; */ /* FIXME: should be easy to support; */
break; break;
case 3: case 3:
chip = "at86rf231"; chip = "at86rf231";
supported = 1; ops = &at86rf230_ops;
break;
case 7:
chip = "at86rf212";
if (version == 1)
ops = &at86rf212_ops;
break; break;
default: default:
chip = "UNKNOWN"; chip = "UNKNOWN";
break; break;
} }
dev_info(&spi->dev, "Detected %s chip version %d\n", chip, lp->vers); dev_info(&spi->dev, "Detected %s chip version %d\n", chip, version);
if (!supported) { if (!ops) {
rc = -ENOTSUPP; rc = -ENOTSUPP;
goto err_gpio_dir; 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); rc = at86rf230_hw_init(lp);
if (rc) if (rc)
goto err_gpio_dir; goto err_hw_init;
rc = request_irq(spi->irq, irq_handler, rc = request_irq(spi->irq, irq_handler,
IRQF_SHARED | pdata->irq_type, IRQF_SHARED | pdata->irq_type,
dev_name(&spi->dev), lp); dev_name(&spi->dev), lp);
if (rc) if (rc)
goto err_gpio_dir; goto err_hw_init;
/* Read irq status register to reset irq line */ /* Read irq status register to reset irq line */
rc = at86rf230_read_subreg(lp, RG_IRQ_STATUS, 0xff, 0, &status); rc = at86rf230_read_subreg(lp, RG_IRQ_STATUS, 0xff, 0, &status);
...@@ -980,30 +1067,33 @@ static int at86rf230_probe(struct spi_device *spi) ...@@ -980,30 +1067,33 @@ static int at86rf230_probe(struct spi_device *spi)
err_irq: err_irq:
free_irq(spi->irq, lp); free_irq(spi->irq, lp);
err_hw_init:
flush_work(&lp->irqwork); flush_work(&lp->irqwork);
err_gpio_dir: spi_set_drvdata(spi, NULL);
if (gpio_is_valid(lp->slp_tr))
gpio_free(lp->slp_tr);
err_slp_tr:
gpio_free(lp->rstn);
err_rstn:
mutex_destroy(&lp->bmux); mutex_destroy(&lp->bmux);
ieee802154_free_device(lp->dev); 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; return rc;
} }
static int at86rf230_remove(struct spi_device *spi) static int at86rf230_remove(struct spi_device *spi)
{ {
struct at86rf230_local *lp = spi_get_drvdata(spi); struct at86rf230_local *lp = spi_get_drvdata(spi);
struct at86rf230_platform_data *pdata = spi->dev.platform_data;
ieee802154_unregister_device(lp->dev); ieee802154_unregister_device(lp->dev);
free_irq(spi->irq, lp); free_irq(spi->irq, lp);
flush_work(&lp->irqwork); flush_work(&lp->irqwork);
if (gpio_is_valid(lp->slp_tr)) if (gpio_is_valid(pdata->slp_tr))
gpio_free(lp->slp_tr); gpio_free(pdata->slp_tr);
gpio_free(lp->rstn); gpio_free(pdata->rstn);
mutex_destroy(&lp->bmux); mutex_destroy(&lp->bmux);
ieee802154_free_device(lp->dev); ieee802154_free_device(lp->dev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment