zephyr/drivers/ieee802154/ieee802154_rf2xx_iface.c
Gerson Fernando Budke 67289d07fe drivers: ieee802154: rf2xx: Add initial driver
Add initial Atmel at86rf2xx transceiver driver. This driver uses device
tree to configure the physical interface. The driver had capability to
use multiple transceiver and systems with multiple bands can be used.
With this, 2.4GHz ISM and Sub-Giga can be used simultaneous.

Below a valid DT example. This samples assume same SPI port with two
transceivers.

&spi0 {
    status = "okay";
    label = "SPI_RF2XX";
    cs-gpios = <&porta 31 0 &porta 30 0>;

    rf2xx@0 {
        compatible = "atmel,rf2xx";
        reg = <0x0>;
        label = "RF2XX_0";
        spi-max-frequency = <7800000>;
        irq-gpios = <&portb 2 0>;
        reset-gpios = <&porta 3 0>;
        slptr-gpios = <&portb 3 0>;
        status = "okay";
    };

    rf2xx@1 {
        compatible = "atmel,rf2xx";
        reg = <0x1>;
        label = "RF2XX_1";
        spi-max-frequency = <7800000>;
        irq-gpios = <&portb 4 0>;
        reset-gpios = <&porta 4 0>;
        slptr-gpios = <&portb 4 0>;
        status = "okay";
    };
};

At the moment driver assume two transceiver are enouth for majority of
appications. Sub-Giga band will be enabled in future.

Signed-off-by: Gerson Fernando Budke <nandojve@gmail.com>
2019-12-13 11:23:23 +02:00

251 lines
5.1 KiB
C

/* ieee802154_rf2xx_iface.c - ATMEL RF2XX IEEE 802.15.4 Interface */
/*
* Copyright (c) 2019 Gerson Fernando Budke
*
* SPDX-License-Identifier: Apache-2.0
*/
#define LOG_MODULE_NAME ieee802154_rf2xx_iface
#define LOG_LEVEL CONFIG_IEEE802154_DRIVER_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#include <errno.h>
#include <assert.h>
#include <device.h>
#include <drivers/spi.h>
#include <drivers/gpio.h>
#include "ieee802154_rf2xx.h"
#include "ieee802154_rf2xx_regs.h"
#include "ieee802154_rf2xx_iface.h"
void rf2xx_iface_phy_rst(struct device *dev)
{
const struct rf2xx_config *conf = dev->config->config_info;
const struct rf2xx_context *ctx = dev->driver_data;
/* Ensure control lines have correct levels. */
gpio_pin_write(ctx->reset_gpio, conf->reset.pin, 1);
gpio_pin_write(ctx->slptr_gpio, conf->slptr.pin, 0);
/* Wait typical time of timer TR1. */
k_busy_wait(330);
gpio_pin_write(ctx->reset_gpio, conf->reset.pin, 0);
k_busy_wait(10);
gpio_pin_write(ctx->reset_gpio, conf->reset.pin, 1);
}
void rf2xx_iface_phy_tx_start(struct device *dev)
{
const struct rf2xx_config *conf = dev->config->config_info;
const struct rf2xx_context *ctx = dev->driver_data;
/* Start TX transmission at rise edge */
gpio_pin_write(ctx->slptr_gpio, conf->slptr.pin, 1);
/* 16.125[μs] delay to detect signal */
k_busy_wait(20);
/* restore initial pin state */
gpio_pin_write(ctx->slptr_gpio, conf->slptr.pin, 0);
}
u8_t rf2xx_iface_reg_read(struct device *dev,
u8_t addr)
{
const struct rf2xx_context *ctx = dev->driver_data;
u8_t status;
u8_t regval = 0;
addr |= RF2XX_RF_CMD_REG_R;
const struct spi_buf tx_buf = {
.buf = &addr,
.len = 1
};
const struct spi_buf_set tx = {
.buffers = &tx_buf,
.count = 1
};
const struct spi_buf rx_buf[2] = {
{
.buf = &status,
.len = 1
},
{
.buf = &regval,
.len = 1
},
};
const struct spi_buf_set rx = {
.buffers = rx_buf,
.count = 2
};
if (spi_transceive(ctx->spi, &ctx->spi_cfg, &tx, &rx) != 0) {
LOG_ERR("Failed to exec rf2xx_reg_read CMD at address %d",
addr);
}
LOG_DBG("Read Address: %02X, PhyStatus: %02X, RegVal: %02X",
(addr & ~(RF2XX_RF_CMD_REG_R)), status, regval);
return regval;
}
void rf2xx_iface_reg_write(struct device *dev,
u8_t addr,
u8_t data)
{
const struct rf2xx_context *ctx = dev->driver_data;
u8_t status;
addr |= RF2XX_RF_CMD_REG_W;
const struct spi_buf tx_buf[2] = {
{
.buf = &addr,
.len = 1
},
{
.buf = &data,
.len = 1
}
};
const struct spi_buf_set tx = {
.buffers = tx_buf,
.count = 2
};
const struct spi_buf rx_buf = {
.buf = &status,
.len = 1
};
const struct spi_buf_set rx = {
.buffers = &rx_buf,
.count = 1
};
if (spi_transceive(ctx->spi, &ctx->spi_cfg, &tx, &rx) != 0) {
LOG_ERR("Failed to exec rf2xx_reg_write at address %d",
addr);
}
LOG_DBG("Write Address: %02X, PhyStatus: %02X, RegVal: %02X",
(addr & ~(RF2XX_RF_CMD_REG_W)), status, data);
}
u8_t rf2xx_iface_bit_read(struct device *dev,
u8_t addr,
u8_t mask,
u8_t pos)
{
u8_t ret;
ret = rf2xx_iface_reg_read(dev, addr);
ret &= mask;
ret >>= pos;
return ret;
}
void rf2xx_iface_bit_write(struct device *dev,
u8_t reg_addr,
u8_t mask,
u8_t pos,
u8_t new_value)
{
u8_t current_reg_value;
current_reg_value = rf2xx_iface_reg_read(dev, reg_addr);
current_reg_value &= ~mask;
new_value <<= pos;
new_value &= mask;
new_value |= current_reg_value;
rf2xx_iface_reg_write(dev, reg_addr, new_value);
}
void rf2xx_iface_frame_read(struct device *dev,
u8_t *data,
u8_t length)
{
const struct rf2xx_context *ctx = dev->driver_data;
u8_t cmd = RF2XX_RF_CMD_FRAME_R;
const struct spi_buf tx_buf = {
.buf = &cmd,
.len = 1
};
const struct spi_buf_set tx = {
.buffers = &tx_buf,
.count = 1
};
const struct spi_buf rx_buf = {
.buf = data,
.len = length
};
const struct spi_buf_set rx = {
.buffers = &rx_buf,
.count = 1
};
if (spi_transceive(ctx->spi, &ctx->spi_cfg, &tx, &rx) != 0) {
LOG_ERR("Failed to exec rf2xx_frame_read PHR");
}
LOG_DBG("Frame R: PhyStatus: %02X. length: %02X", data[0], length);
LOG_HEXDUMP_DBG(data + RX2XX_FRAME_HEADER_SIZE, length, "payload");
}
void rf2xx_iface_frame_write(struct device *dev,
u8_t *data,
u8_t length)
{
const struct rf2xx_context *ctx = dev->driver_data;
u8_t cmd = RF2XX_RF_CMD_FRAME_W;
u8_t status;
u8_t phr;
/* Sanity check */
if (length > 125) {
length = 125;
}
phr = length + RX2XX_FRAME_FCS_LENGTH;
const struct spi_buf tx_buf[3] = {
{
.buf = &cmd,
.len = 1
},
{
.buf = &phr, /* PHR */
.len = 1
},
{
.buf = data, /* PSDU */
.len = length
},
};
const struct spi_buf_set tx = {
.buffers = tx_buf,
.count = 3
};
const struct spi_buf rx_buf = {
.buf = &status,
.len = 1
};
const struct spi_buf_set rx = {
.buffers = &rx_buf,
.count = 1
};
if (spi_transceive(ctx->spi, &ctx->spi_cfg, &tx, &rx) != 0) {
LOG_ERR("Failed to exec rf2xx_frame_write");
}
LOG_DBG("Frame W: PhyStatus: %02X. length: %02X", status, length);
LOG_HEXDUMP_DBG(data, length, "payload");
}