drivers: fxas21002 enablement for SPI and I2C
Added support for fxas21002 sensor over SPI and I2C. Made the fxas driver APIs generic for I2C and SPI. Tested with fxas21002 sensor on RDDRONE. Signed-off-by: Benjamin Perseghetti <bperseghetti@rudislabs.com> Co-authored-by: Sumit Batra <sumit.batra@nxp.com>
This commit is contained in:
parent
a0418f9cf0
commit
3906860a8f
4 changed files with 298 additions and 40 deletions
|
@ -7,7 +7,8 @@ menuconfig FXAS21002
|
|||
bool "FXAS21002 gyroscope driver"
|
||||
default y
|
||||
depends on DT_HAS_NXP_FXAS21002_ENABLED
|
||||
select I2C
|
||||
select I2C if $(dt_compat_on_bus,$(DT_COMPAT_NXP_FXAS21002),i2c)
|
||||
select SPI if $(dt_compat_on_bus,$(DT_COMPAT_NXP_FXAS21002),spi)
|
||||
help
|
||||
Enable driver for the FXAS21002 gyroscope
|
||||
|
||||
|
|
|
@ -18,6 +18,133 @@ static const uint32_t sample_period[] = {
|
|||
1250, 2500, 5000, 10000, 20000, 40000, 80000, 80000
|
||||
};
|
||||
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
|
||||
#define DIR_READ(a) ((a) | BIT(7))
|
||||
#define DIR_WRITE(a) ((a) & 0x7f)
|
||||
|
||||
static int fxas21002_transceive(const struct device *dev,
|
||||
void *data, size_t length)
|
||||
{
|
||||
const struct fxas21002_config *cfg = dev->config;
|
||||
const struct spi_buf buf = { .buf = data, .len = length };
|
||||
const struct spi_buf_set s = { .buffers = &buf, .count = 1 };
|
||||
|
||||
return spi_transceive_dt(&cfg->bus_cfg.spi, &s, &s);
|
||||
}
|
||||
|
||||
int fxas21002_read_spi(const struct device *dev,
|
||||
uint8_t reg,
|
||||
void *data,
|
||||
size_t length)
|
||||
{
|
||||
const struct fxas21002_config *cfg = dev->config;
|
||||
|
||||
/* Reads must clock out a dummy byte after sending the address. */
|
||||
uint8_t reg_buf[2] = { DIR_READ(reg), 0 };
|
||||
const struct spi_buf buf[2] = {
|
||||
{ .buf = reg_buf, .len = 3 },
|
||||
{ .buf = data, .len = length }
|
||||
};
|
||||
const struct spi_buf_set tx = { .buffers = buf, .count = 1 };
|
||||
const struct spi_buf_set rx = { .buffers = buf, .count = 2 };
|
||||
|
||||
return spi_transceive_dt(&cfg->bus_cfg.spi, &tx, &rx);
|
||||
}
|
||||
|
||||
int fxas21002_byte_read_spi(const struct device *dev,
|
||||
uint8_t reg,
|
||||
uint8_t *byte)
|
||||
{
|
||||
/* Reads must clock out a dummy byte after sending the address. */
|
||||
uint8_t data[] = { DIR_READ(reg), 0};
|
||||
int ret;
|
||||
|
||||
ret = fxas21002_transceive(dev, data, sizeof(data));
|
||||
|
||||
*byte = data[1];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int fxas21002_byte_write_spi(const struct device *dev,
|
||||
uint8_t reg,
|
||||
uint8_t byte)
|
||||
{
|
||||
uint8_t data[] = { DIR_WRITE(reg), byte };
|
||||
|
||||
return fxas21002_transceive(dev, data, sizeof(data));
|
||||
}
|
||||
|
||||
int fxas21002_reg_field_update_spi(const struct device *dev,
|
||||
uint8_t reg,
|
||||
uint8_t mask,
|
||||
uint8_t val)
|
||||
{
|
||||
uint8_t old_val;
|
||||
int rc = 0;
|
||||
|
||||
rc = fxas21002_byte_read_spi(dev, reg, &old_val);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return fxas21002_byte_write_spi(dev, reg, (old_val & ~mask) | (val & mask));
|
||||
}
|
||||
|
||||
static const struct fxas21002_io_ops fxas21002_spi_ops = {
|
||||
.read = fxas21002_read_spi,
|
||||
.byte_read = fxas21002_byte_read_spi,
|
||||
.byte_write = fxas21002_byte_write_spi,
|
||||
.reg_field_update = fxas21002_reg_field_update_spi,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
|
||||
int fxas21002_read_i2c(const struct device *dev,
|
||||
uint8_t reg,
|
||||
void *data,
|
||||
size_t length)
|
||||
{
|
||||
const struct fxas21002_config *config = dev->config;
|
||||
|
||||
return i2c_burst_read_dt(&config->bus_cfg.i2c, reg, data, length);
|
||||
}
|
||||
|
||||
int fxas21002_byte_read_i2c(const struct device *dev,
|
||||
uint8_t reg,
|
||||
uint8_t *byte)
|
||||
{
|
||||
const struct fxas21002_config *config = dev->config;
|
||||
|
||||
return i2c_reg_read_byte_dt(&config->bus_cfg.i2c, reg, byte);
|
||||
}
|
||||
|
||||
int fxas21002_byte_write_i2c(const struct device *dev,
|
||||
uint8_t reg,
|
||||
uint8_t byte)
|
||||
{
|
||||
const struct fxas21002_config *config = dev->config;
|
||||
|
||||
return i2c_reg_write_byte_dt(&config->bus_cfg.i2c, reg, byte);
|
||||
}
|
||||
|
||||
int fxas21002_reg_field_update_i2c(const struct device *dev,
|
||||
uint8_t reg,
|
||||
uint8_t mask,
|
||||
uint8_t val)
|
||||
{
|
||||
const struct fxas21002_config *config = dev->config;
|
||||
|
||||
return i2c_reg_update_byte_dt(&config->bus_cfg.i2c, reg, mask, val);
|
||||
}
|
||||
static const struct fxas21002_io_ops fxas21002_i2c_ops = {
|
||||
.read = fxas21002_read_i2c,
|
||||
.byte_read = fxas21002_byte_read_i2c,
|
||||
.byte_write = fxas21002_byte_write_i2c,
|
||||
.reg_field_update = fxas21002_reg_field_update_i2c,
|
||||
};
|
||||
#endif
|
||||
|
||||
static int fxas21002_sample_fetch(const struct device *dev,
|
||||
enum sensor_channel chan)
|
||||
{
|
||||
|
@ -36,7 +163,7 @@ static int fxas21002_sample_fetch(const struct device *dev,
|
|||
k_sem_take(&data->sem, K_FOREVER);
|
||||
|
||||
/* Read all the channels in one I2C transaction. */
|
||||
if (i2c_burst_read_dt(&config->i2c, FXAS21002_REG_OUTXMSB, buffer,
|
||||
if (config->ops->read(dev, FXAS21002_REG_OUTXMSB, buffer,
|
||||
sizeof(buffer))) {
|
||||
LOG_ERR("Could not fetch sample");
|
||||
ret = -EIO;
|
||||
|
@ -139,7 +266,7 @@ int fxas21002_get_power(const struct device *dev, enum fxas21002_power *power)
|
|||
const struct fxas21002_config *config = dev->config;
|
||||
uint8_t val = *power;
|
||||
|
||||
if (i2c_reg_read_byte_dt(&config->i2c, FXAS21002_REG_CTRLREG1, &val)) {
|
||||
if (config->ops->byte_read(dev, FXAS21002_REG_CTRLREG1, &val)) {
|
||||
LOG_ERR("Could not get power setting");
|
||||
return -EIO;
|
||||
}
|
||||
|
@ -153,8 +280,8 @@ int fxas21002_set_power(const struct device *dev, enum fxas21002_power power)
|
|||
{
|
||||
const struct fxas21002_config *config = dev->config;
|
||||
|
||||
return i2c_reg_update_byte_dt(&config->i2c, FXAS21002_REG_CTRLREG1,
|
||||
FXAS21002_CTRLREG1_POWER_MASK, power);
|
||||
return config->ops->reg_field_update(dev, FXAS21002_REG_CTRLREG1,
|
||||
FXAS21002_CTRLREG1_POWER_MASK, power);
|
||||
}
|
||||
|
||||
uint32_t fxas21002_get_transition_time(enum fxas21002_power start,
|
||||
|
@ -188,18 +315,53 @@ static int fxas21002_init(const struct device *dev)
|
|||
struct fxas21002_data *data = dev->data;
|
||||
uint32_t transition_time;
|
||||
uint8_t whoami;
|
||||
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
|
||||
uint8_t ctrlreg1;
|
||||
|
||||
if (!device_is_ready(config->i2c.bus)) {
|
||||
LOG_ERR("I2C bus device not ready");
|
||||
return -ENODEV;
|
||||
if (config->inst_on_bus == FXAS21002_BUS_I2C) {
|
||||
if (!device_is_ready(config->bus_cfg.i2c.bus)) {
|
||||
LOG_ERR("I2C bus device not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
|
||||
if (config->inst_on_bus == FXAS21002_BUS_SPI) {
|
||||
if (!device_is_ready(config->bus_cfg.spi.bus)) {
|
||||
LOG_ERR("SPI bus device not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (config->reset_gpio.port) {
|
||||
/* Pulse RST pin high to perform a hardware reset of
|
||||
* the sensor.
|
||||
*/
|
||||
if (!device_is_ready(config->reset_gpio.port)) {
|
||||
LOG_ERR("GPIO device not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
gpio_pin_configure_dt(&config->reset_gpio, GPIO_OUTPUT_INACTIVE);
|
||||
|
||||
gpio_pin_set_dt(&config->reset_gpio, 1);
|
||||
/* The datasheet does not mention how long to pulse
|
||||
* the RST pin high in order to reset. Stay on the
|
||||
* safe side and pulse for 1 millisecond.
|
||||
*/
|
||||
k_busy_wait(USEC_PER_MSEC);
|
||||
gpio_pin_set_dt(&config->reset_gpio, 0);
|
||||
k_busy_wait(USEC_PER_MSEC);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Read the WHOAMI register to make sure we are talking to FXAS21002
|
||||
* and not some other type of device that happens to have the same I2C
|
||||
* address.
|
||||
*/
|
||||
if (i2c_reg_read_byte_dt(&config->i2c, FXAS21002_REG_WHOAMI, &whoami)) {
|
||||
if (config->ops->byte_read(dev, FXAS21002_REG_WHOAMI, &whoami)) {
|
||||
LOG_ERR("Could not get WHOAMI value");
|
||||
return -EIO;
|
||||
}
|
||||
|
@ -210,34 +372,38 @@ static int fxas21002_init(const struct device *dev)
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
/* Reset the sensor. Upon issuing a software reset command over the I2C
|
||||
* interface, the sensor immediately resets and does not send any
|
||||
* acknowledgment (ACK) of the written byte to the master. Therefore,
|
||||
* do not check the return code of the I2C transaction.
|
||||
*/
|
||||
i2c_reg_write_byte_dt(&config->i2c, FXAS21002_REG_CTRLREG1,
|
||||
FXAS21002_CTRLREG1_RST_MASK);
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
|
||||
if (config->inst_on_bus == FXAS21002_BUS_I2C) {
|
||||
/* Reset the sensor. Upon issuing a software reset command over the I2C
|
||||
* interface, the sensor immediately resets and does not send any
|
||||
* acknowledgment (ACK) of the written byte to the master. Therefore,
|
||||
* do not check the return code of the I2C transaction.
|
||||
*/
|
||||
config->ops->byte_write(dev, FXAS21002_REG_CTRLREG1,
|
||||
FXAS21002_CTRLREG1_RST_MASK);
|
||||
|
||||
/* Wait for the reset sequence to complete */
|
||||
do {
|
||||
if (i2c_reg_read_byte_dt(&config->i2c, FXAS21002_REG_CTRLREG1,
|
||||
&ctrlreg1)) {
|
||||
LOG_ERR("Could not get ctrlreg1 value");
|
||||
return -EIO;
|
||||
}
|
||||
} while (ctrlreg1 & FXAS21002_CTRLREG1_RST_MASK);
|
||||
/* Wait for the reset sequence to complete */
|
||||
do {
|
||||
if (config->ops->byte_read(dev, FXAS21002_REG_CTRLREG1,
|
||||
&ctrlreg1)) {
|
||||
LOG_ERR("Could not get ctrlreg1 value");
|
||||
return -EIO;
|
||||
}
|
||||
} while (ctrlreg1 & FXAS21002_CTRLREG1_RST_MASK);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Set the full-scale range */
|
||||
if (i2c_reg_update_byte_dt(&config->i2c, FXAS21002_REG_CTRLREG0,
|
||||
FXAS21002_CTRLREG0_FS_MASK, config->range)) {
|
||||
if (config->ops->reg_field_update(dev, FXAS21002_REG_CTRLREG0,
|
||||
FXAS21002_CTRLREG0_FS_MASK, config->range)) {
|
||||
LOG_ERR("Could not set range");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Set the output data rate */
|
||||
if (i2c_reg_update_byte_dt(&config->i2c, FXAS21002_REG_CTRLREG1,
|
||||
FXAS21002_CTRLREG1_DR_MASK,
|
||||
config->dr << FXAS21002_CTRLREG1_DR_SHIFT)) {
|
||||
if (config->ops->reg_field_update(dev, FXAS21002_REG_CTRLREG1,
|
||||
FXAS21002_CTRLREG1_DR_MASK,
|
||||
config->dr << FXAS21002_CTRLREG1_DR_SHIFT)) {
|
||||
LOG_ERR("Could not set output data rate");
|
||||
return -EIO;
|
||||
}
|
||||
|
@ -279,11 +445,25 @@ static const struct sensor_driver_api fxas21002_driver_api = {
|
|||
#endif
|
||||
};
|
||||
|
||||
#define FXAS21002_CONFIG_I2C(inst) \
|
||||
.bus_cfg = { .i2c = I2C_DT_SPEC_INST_GET(inst) }, \
|
||||
.ops = &fxas21002_i2c_ops, \
|
||||
.inst_on_bus = FXAS21002_BUS_I2C, \
|
||||
|
||||
#define FXAS21002_CONFIG_SPI(inst) \
|
||||
.bus_cfg = {.spi = SPI_DT_SPEC_INST_GET(inst, \
|
||||
SPI_OP_MODE_MASTER | SPI_WORD_SET(8), 0) }, \
|
||||
.ops = &fxas21002_spi_ops, \
|
||||
.reset_gpio = GPIO_DT_SPEC_INST_GET(inst, reset_gpios), \
|
||||
.inst_on_bus = FXAS21002_BUS_SPI, \
|
||||
|
||||
#define FXAS21002_DEFINE(inst) \
|
||||
static struct fxas21002_data fxas21002_data_##inst; \
|
||||
\
|
||||
static const struct fxas21002_config fxas21002_config_##inst = { \
|
||||
.i2c = I2C_DT_SPEC_INST_GET(inst), \
|
||||
COND_CODE_1(DT_INST_ON_BUS(inst, spi), \
|
||||
(FXAS21002_CONFIG_SPI(inst)), \
|
||||
(FXAS21002_CONFIG_I2C(inst))) \
|
||||
.whoami = CONFIG_FXAS21002_WHOAMI, \
|
||||
.range = CONFIG_FXAS21002_RANGE, \
|
||||
.dr = CONFIG_FXAS21002_DR, \
|
||||
|
@ -296,8 +476,8 @@ static const struct sensor_driver_api fxas21002_driver_api = {
|
|||
}; \
|
||||
\
|
||||
SENSOR_DEVICE_DT_INST_DEFINE(inst, fxas21002_init, NULL, \
|
||||
&fxas21002_data_##inst, &fxas21002_config_##inst, \
|
||||
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
|
||||
&fxas21002_driver_api); \
|
||||
&fxas21002_data_##inst, &fxas21002_config_##inst, \
|
||||
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
|
||||
&fxas21002_driver_api); \
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(FXAS21002_DEFINE)
|
||||
|
|
|
@ -5,10 +5,17 @@
|
|||
*/
|
||||
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#endif
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
|
||||
#include <zephyr/drivers/spi.h>
|
||||
#endif
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#define FXAS21002_BUS_I2C (1<<0)
|
||||
#define FXAS21002_BUS_SPI (1<<1)
|
||||
#define FXAS21002_REG_STATUS 0x00
|
||||
#define FXAS21002_REG_OUTXMSB 0x01
|
||||
#define FXAS21002_REG_INT_SOURCE 0x0b
|
||||
|
@ -57,14 +64,44 @@ enum fxas21002_channel {
|
|||
FXAS21002_CHANNEL_GYRO_Z,
|
||||
};
|
||||
|
||||
struct fxas21002_config {
|
||||
struct fxas21002_io_ops {
|
||||
int (*read)(const struct device *dev,
|
||||
uint8_t reg,
|
||||
void *data,
|
||||
size_t length);
|
||||
int (*byte_read)(const struct device *dev,
|
||||
uint8_t reg,
|
||||
uint8_t *byte);
|
||||
int (*byte_write)(const struct device *dev,
|
||||
uint8_t reg,
|
||||
uint8_t byte);
|
||||
int (*reg_field_update)(const struct device *dev,
|
||||
uint8_t reg,
|
||||
uint8_t mask,
|
||||
uint8_t val);
|
||||
};
|
||||
|
||||
union fxas21002_bus_cfg {
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
|
||||
struct spi_dt_spec spi;
|
||||
#endif
|
||||
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
|
||||
struct i2c_dt_spec i2c;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct fxas21002_config {
|
||||
const union fxas21002_bus_cfg bus_cfg;
|
||||
const struct fxas21002_io_ops *ops;
|
||||
#ifdef CONFIG_FXAS21002_TRIGGER
|
||||
struct gpio_dt_spec int_gpio;
|
||||
#endif
|
||||
struct gpio_dt_spec reset_gpio;
|
||||
uint8_t whoami;
|
||||
enum fxas21002_range range;
|
||||
uint8_t dr;
|
||||
uint8_t inst_on_bus;
|
||||
};
|
||||
|
||||
struct fxas21002_data {
|
||||
|
@ -92,6 +129,44 @@ uint32_t fxas21002_get_transition_time(enum fxas21002_power start,
|
|||
enum fxas21002_power end,
|
||||
uint8_t dr);
|
||||
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
|
||||
int fxas21002_byte_write_spi(const struct device *dev,
|
||||
uint8_t reg,
|
||||
uint8_t byte);
|
||||
|
||||
int fxas21002_byte_read_spi(const struct device *dev,
|
||||
uint8_t reg,
|
||||
uint8_t *byte);
|
||||
|
||||
int fxas21002_reg_field_update_spi(const struct device *dev,
|
||||
uint8_t reg,
|
||||
uint8_t mask,
|
||||
uint8_t val);
|
||||
|
||||
int fxas21002_read_spi(const struct device *dev,
|
||||
uint8_t reg,
|
||||
void *data,
|
||||
size_t length);
|
||||
#endif
|
||||
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
|
||||
int fxas21002_byte_write_i2c(const struct device *dev,
|
||||
uint8_t reg,
|
||||
uint8_t byte);
|
||||
|
||||
int fxas21002_byte_read_i2c(const struct device *dev,
|
||||
uint8_t reg,
|
||||
uint8_t *byte);
|
||||
|
||||
int fxas21002_reg_field_update_i2c(const struct device *dev,
|
||||
uint8_t reg,
|
||||
uint8_t mask,
|
||||
uint8_t val);
|
||||
|
||||
int fxas21002_read_i2c(const struct device *dev,
|
||||
uint8_t reg,
|
||||
void *data,
|
||||
size_t length);
|
||||
#endif
|
||||
#if CONFIG_FXAS21002_TRIGGER
|
||||
int fxas21002_trigger_init(const struct device *dev);
|
||||
int fxas21002_trigger_set(const struct device *dev,
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT nxp_fxas21002
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "fxas21002.h"
|
||||
|
@ -55,8 +57,8 @@ static void fxas21002_handle_int(const struct device *dev)
|
|||
|
||||
k_sem_take(&data->sem, K_FOREVER);
|
||||
|
||||
if (i2c_reg_read_byte_dt(&config->i2c, FXAS21002_REG_INT_SOURCE,
|
||||
&int_source)) {
|
||||
if (config->ops->byte_read(dev, FXAS21002_REG_INT_SOURCE,
|
||||
&int_source)) {
|
||||
LOG_ERR("Could not read interrupt source");
|
||||
int_source = 0U;
|
||||
}
|
||||
|
@ -136,8 +138,8 @@ int fxas21002_trigger_set(const struct device *dev,
|
|||
}
|
||||
|
||||
/* Configure the sensor interrupt */
|
||||
if (i2c_reg_update_byte_dt(&config->i2c, FXAS21002_REG_CTRLREG2, mask,
|
||||
handler ? mask : 0)) {
|
||||
if (config->ops->reg_field_update(dev, FXAS21002_REG_CTRLREG2, mask,
|
||||
handler ? mask : 0)) {
|
||||
LOG_ERR("Could not configure interrupt");
|
||||
ret = -EIO;
|
||||
goto exit;
|
||||
|
@ -188,8 +190,8 @@ int fxas21002_trigger_init(const struct device *dev)
|
|||
ctrl_reg2 |= FXAS21002_CTRLREG2_CFG_DRDY_MASK;
|
||||
#endif
|
||||
|
||||
if (i2c_reg_write_byte_dt(&config->i2c, FXAS21002_REG_CTRLREG2,
|
||||
ctrl_reg2)) {
|
||||
if (config->ops->byte_write(dev, FXAS21002_REG_CTRLREG2,
|
||||
ctrl_reg2)) {
|
||||
LOG_ERR("Could not configure interrupt pin routing");
|
||||
return -EIO;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue