Linux preempt-rt

Check our new training course

Real-Time Linux with PREEMPT_RT

Check our new training course
with Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/* ieee802154_rf2xx_iface.c - ATMEL RF2XX IEEE 802.15.4 Interface */

/*
 * Copyright (c) 2019-2020 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_info;
	const struct rf2xx_context *ctx = dev->driver_data;

	/* Ensure control lines have correct levels. */
	gpio_pin_set(ctx->reset_gpio, conf->reset.pin, 0);
	gpio_pin_set(ctx->slptr_gpio, conf->slptr.pin, 0);

	/* Wait typical time of timer TR1. */
	k_busy_wait(330);

	gpio_pin_set(ctx->reset_gpio, conf->reset.pin, 1);
	k_busy_wait(10);
	gpio_pin_set(ctx->reset_gpio, conf->reset.pin, 0);
}
void rf2xx_iface_phy_tx_start(struct device *dev)
{
	const struct rf2xx_config *conf = dev->config_info;
	const struct rf2xx_context *ctx = dev->driver_data;

	/* Start TX transmission at rise edge */
	gpio_pin_set(ctx->slptr_gpio, conf->slptr.pin, 1);
	/* 16.125[μs] delay to detect signal */
	k_busy_wait(20);
	/* restore initial pin state */
	gpio_pin_set(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");
}

void rf2xx_iface_sram_read(struct device *dev,
			    u8_t address,
			    u8_t *data,
			    u8_t length)
{
	const struct rf2xx_context *ctx = dev->driver_data;
	u8_t cmd = RF2XX_RF_CMD_SRAM_R;
	u8_t status[2];

	const struct spi_buf tx_buf[2] = {
		{
			.buf = &cmd,
			.len = 1
		},
		{
			.buf = &address,
			.len = 1
		},
	};
	const struct spi_buf_set tx = {
		.buffers = tx_buf,
		.count = 2
	};
	const struct spi_buf rx_buf[2] = {
		{
			.buf = status,
			.len = 2
		},
		{
			.buf = data,
			.len = length
		},
	};
	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_sram_read");
	}

	LOG_DBG("SRAM R: length: %02X, status: %02X", length, status[0]);
	LOG_HEXDUMP_DBG(data, length, "content");
}