drivers: fxos8700 enablement for SPI and I2C

Added support for fxos8700 sensor over SPI and I2C.
Made the fxos driver APIs generic for I2C and SPI.
Tested with fxos8700 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:
Benjamin Perseghetti 2022-10-28 12:20:04 -04:00 committed by Maureen Helm
parent c6d7ea4939
commit 2870b38379
4 changed files with 305 additions and 71 deletions

View file

@ -7,7 +7,8 @@ menuconfig FXOS8700
bool "FXOS8700 accelerometer/magnetometer driver"
default y
depends on DT_HAS_NXP_FXOS8700_ENABLED
select I2C
select I2C if $(dt_compat_on_bus,$(DT_COMPAT_NXP_FXOS8700),i2c)
select SPI if $(dt_compat_on_bus,$(DT_COMPAT_NXP_FXOS8700),spi)
help
Enable driver for the FXOS8700 accelerometer/magnetometer.
The driver also supports MMA8451Q, MMA8652FC and MMA8653FC

View file

@ -18,6 +18,132 @@ LOG_MODULE_REGISTER(FXOS8700, CONFIG_SENSOR_LOG_LEVEL);
/* Convert the range (8g, 4g, 2g) to the encoded FS register field value */
#define RANGE2FS(x) (__builtin_ctz(x) - 1)
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
#define DIR_READ(a) ((a) & 0x7f)
#define DIR_WRITE(a) ((a) | BIT(7))
#define ADDR_7(a) ((a) & BIT(7))
int fxos8700_transceive(const struct device *dev,
void *data, size_t length)
{
const struct fxos8700_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 fxos8700_read_spi(const struct device *dev,
uint8_t reg,
void *data,
size_t length)
{
const struct fxos8700_config *cfg = dev->config;
/* Reads must clock out a dummy byte after sending the address. */
uint8_t reg_buf[3] = { DIR_READ(reg), ADDR_7(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 fxos8700_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), ADDR_7(reg), 0};
int ret;
ret = fxos8700_transceive(dev, data, sizeof(data));
*byte = data[2];
return ret;
}
int fxos8700_byte_write_spi(const struct device *dev,
uint8_t reg,
uint8_t byte)
{
uint8_t data[] = { DIR_WRITE(reg), ADDR_7(reg), byte };
return fxos8700_transceive(dev, data, sizeof(data));
}
int fxos8700_reg_field_update_spi(const struct device *dev,
uint8_t reg,
uint8_t mask,
uint8_t val)
{
uint8_t old_val;
if (fxos8700_byte_read_spi(dev, reg, &old_val) < 0) {
return -EIO;
}
return fxos8700_byte_write_spi(dev, reg, (old_val & ~mask) | (val & mask));
}
static const struct fxos8700_io_ops fxos8700_spi_ops = {
.read = fxos8700_read_spi,
.byte_read = fxos8700_byte_read_spi,
.byte_write = fxos8700_byte_write_spi,
.reg_field_update = fxos8700_reg_field_update_spi,
};
#endif
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
int fxos8700_read_i2c(const struct device *dev,
uint8_t reg,
void *data,
size_t length)
{
const struct fxos8700_config *config = dev->config;
return i2c_burst_read_dt(&config->bus_cfg.i2c, reg, data, length);
}
int fxos8700_byte_read_i2c(const struct device *dev,
uint8_t reg,
uint8_t *byte)
{
const struct fxos8700_config *config = dev->config;
return i2c_reg_read_byte_dt(&config->bus_cfg.i2c, reg, byte);
}
int fxos8700_byte_write_i2c(const struct device *dev,
uint8_t reg,
uint8_t byte)
{
const struct fxos8700_config *config = dev->config;
return i2c_reg_write_byte_dt(&config->bus_cfg.i2c, reg, byte);
}
int fxos8700_reg_field_update_i2c(const struct device *dev,
uint8_t reg,
uint8_t mask,
uint8_t val)
{
const struct fxos8700_config *config = dev->config;
return i2c_reg_update_byte_dt(&config->bus_cfg.i2c, reg, mask, val);
}
static const struct fxos8700_io_ops fxos8700_i2c_ops = {
.read = fxos8700_read_i2c,
.byte_read = fxos8700_byte_read_i2c,
.byte_write = fxos8700_byte_write_i2c,
.reg_field_update = fxos8700_reg_field_update_i2c,
};
#endif
static int fxos8700_set_odr(const struct device *dev,
const struct sensor_value *val)
{
@ -86,7 +212,7 @@ static int fxos8700_set_odr(const struct device *dev,
}
/* Change the attribute and restore power mode. */
return i2c_reg_update_byte_dt(&config->i2c, FXOS8700_REG_CTRLREG1,
return config->ops->reg_field_update(dev, FXOS8700_REG_CTRLREG1,
FXOS8700_CTRLREG1_DR_MASK | FXOS8700_CTRLREG1_ACTIVE_MASK,
dr | power);
}
@ -106,7 +232,7 @@ static int fxos8700_set_mt_ths(const struct device *dev,
LOG_DBG("Set FF_MT_THS to %d", (uint8_t)ths);
return i2c_reg_update_byte_dt(&config->i2c, FXOS8700_REG_FF_MT_THS,
return config->ops->reg_field_update(dev, FXOS8700_REG_FF_MT_THS,
FXOS8700_FF_MT_THS_MASK, (uint8_t)ths);
#else
return -ENOTSUP;
@ -151,7 +277,7 @@ static int fxos8700_sample_fetch(const struct device *dev,
k_sem_take(&data->sem, K_FOREVER);
/* Read all the channels in one I2C transaction. The number of bytes to
/* Read all the channels in one I2C/SPI transaction. The number of bytes to
* read and the starting register address depend on the mode
* configuration (accel-only, mag-only, or hybrid).
*/
@ -159,8 +285,7 @@ static int fxos8700_sample_fetch(const struct device *dev,
__ASSERT(num_bytes <= sizeof(buffer), "Too many bytes to read");
if (i2c_burst_read_dt(&config->i2c, config->start_addr, buffer,
num_bytes)) {
if (config->ops->read(dev, config->start_addr, buffer, num_bytes)) {
LOG_ERR("Could not fetch sample");
ret = -EIO;
goto exit;
@ -181,7 +306,7 @@ static int fxos8700_sample_fetch(const struct device *dev,
}
#ifdef CONFIG_FXOS8700_TEMP
if (i2c_reg_read_byte_dt(&config->i2c, FXOS8700_REG_TEMP,
if (config->ops->byte_read(dev, FXOS8700_REG_TEMP,
&data->temp)) {
LOG_ERR("Could not fetch temperature");
ret = -EIO;
@ -364,7 +489,7 @@ int fxos8700_get_power(const struct device *dev, enum fxos8700_power *power)
const struct fxos8700_config *config = dev->config;
uint8_t val = *power;
if (i2c_reg_read_byte_dt(&config->i2c, FXOS8700_REG_CTRLREG1, &val)) {
if (config->ops->byte_read(dev, FXOS8700_REG_CTRLREG1, &val)) {
LOG_ERR("Could not get power setting");
return -EIO;
}
@ -378,7 +503,7 @@ int fxos8700_set_power(const struct device *dev, enum fxos8700_power power)
{
const struct fxos8700_config *config = dev->config;
return i2c_reg_update_byte_dt(&config->i2c, FXOS8700_REG_CTRLREG1,
return config->ops->reg_field_update(dev, FXOS8700_REG_CTRLREG1,
FXOS8700_CTRLREG1_ACTIVE_MASK, power);
}
@ -388,10 +513,23 @@ static int fxos8700_init(const struct device *dev)
struct fxos8700_data *data = dev->data;
struct sensor_value odr = {.val1 = 6, .val2 = 250000};
if (!device_is_ready(config->i2c.bus)) {
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
if (config->inst_on_bus == FXOS8700_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 == FXOS8700_BUS_SPI) {
if (!device_is_ready(config->bus_cfg.spi.bus)) {
LOG_ERR("SPI bus device not ready");
return -ENODEV;
}
}
#endif
if (config->reset_gpio.port) {
/* Pulse RST pin high to perform a hardware reset of
@ -420,7 +558,7 @@ static int fxos8700_init(const struct device *dev)
* master. Therefore, do not check the return code of
* the I2C transaction.
*/
i2c_reg_write_byte_dt(&config->i2c, FXOS8700_REG_CTRLREG2,
config->ops->byte_write(dev, FXOS8700_REG_CTRLREG2,
FXOS8700_CTRLREG2_RST_MASK);
}
@ -434,7 +572,7 @@ static int fxos8700_init(const struct device *dev)
* compatible device and not some other type of device that happens to
* have the same I2C address.
*/
if (i2c_reg_read_byte_dt(&config->i2c, FXOS8700_REG_WHOAMI,
if (config->ops->byte_read(dev, FXOS8700_REG_WHOAMI,
&data->whoami)) {
LOG_ERR("Could not get WHOAMI value");
return -EIO;
@ -464,7 +602,7 @@ static int fxos8700_init(const struct device *dev)
return -EIO;
}
if (i2c_reg_update_byte_dt(&config->i2c, FXOS8700_REG_CTRLREG2,
if (config->ops->reg_field_update(dev, FXOS8700_REG_CTRLREG2,
FXOS8700_CTRLREG2_MODS_MASK,
config->power_mode)) {
LOG_ERR("Could not set power scheme");
@ -472,7 +610,7 @@ static int fxos8700_init(const struct device *dev)
}
/* Set the mode (accel-only, mag-only, or hybrid) */
if (i2c_reg_update_byte_dt(&config->i2c, FXOS8700_REG_M_CTRLREG1,
if (config->ops->reg_field_update(dev, FXOS8700_REG_M_CTRLREG1,
FXOS8700_M_CTRLREG1_MODE_MASK,
config->mode)) {
LOG_ERR("Could not set mode");
@ -480,9 +618,9 @@ static int fxos8700_init(const struct device *dev)
}
/* Set hybrid autoincrement so we can read accel and mag channels in
* one I2C transaction.
* one I2C/SPI transaction.
*/
if (i2c_reg_update_byte_dt(&config->i2c, FXOS8700_REG_M_CTRLREG2,
if (config->ops->reg_field_update(dev, FXOS8700_REG_M_CTRLREG2,
FXOS8700_M_CTRLREG2_AUTOINC_MASK,
FXOS8700_M_CTRLREG2_AUTOINC_MASK)) {
LOG_ERR("Could not set hybrid autoincrement");
@ -490,7 +628,7 @@ static int fxos8700_init(const struct device *dev)
}
/* Set the full-scale range */
if (i2c_reg_update_byte_dt(&config->i2c, FXOS8700_REG_XYZ_DATA_CFG,
if (config->ops->reg_field_update(dev, FXOS8700_REG_XYZ_DATA_CFG,
FXOS8700_XYZ_DATA_CFG_FS_MASK,
RANGE2FS(config->range))) {
LOG_ERR("Could not set range");
@ -597,11 +735,29 @@ static const struct sensor_driver_api fxos8700_driver_api = {
(FXOS8700_MAG_VECM_PROPS(n)), \
())
#define FXOS8700_INIT(n) \
static const struct fxos8700_config fxos8700_config_##n = { \
.i2c = I2C_DT_SPEC_INST_GET(n), \
#define FXOS8700_CONFIG_I2C(n) \
.bus_cfg = { .i2c = I2C_DT_SPEC_INST_GET(n) }, \
.ops = &fxos8700_i2c_ops, \
.power_mode = DT_INST_PROP(n, power_mode), \
.range = DT_INST_PROP(n, range), \
.inst_on_bus = FXOS8700_BUS_I2C,
#define FXOS8700_CONFIG_SPI(n) \
.bus_cfg = { .spi = SPI_DT_SPEC_INST_GET(n, \
SPI_OP_MODE_MASTER | SPI_WORD_SET(8), 0) }, \
.ops = &fxos8700_spi_ops, \
.power_mode = DT_INST_PROP(n, power_mode), \
.range = DT_INST_PROP(n, range), \
.inst_on_bus = FXOS8700_BUS_SPI, \
#define FXOS8700_SPI_OPERATION (SPI_WORD_SET(8) | \
SPI_OP_MODE_MASTER) \
#define FXOS8700_INIT(n) \
static const struct fxos8700_config fxos8700_config_##n = { \
COND_CODE_1(DT_INST_ON_BUS(n, spi), \
(FXOS8700_CONFIG_SPI(n)), \
(FXOS8700_CONFIG_I2C(n))) \
FXOS8700_RESET(n) \
FXOS8700_MODE(n) \
FXOS8700_INT(n) \

View file

@ -5,10 +5,18 @@
*/
#include <zephyr/drivers/sensor.h>
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
#include <zephyr/drivers/spi.h>
#endif
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
#include <zephyr/drivers/i2c.h>
#endif
#include <zephyr/drivers/gpio.h>
#include <zephyr/kernel.h>
#define FXOS8700_BUS_I2C (1<<0)
#define FXOS8700_BUS_SPI (1<<1)
#define FXOS8700_REG_STATUS 0x00
#define FXOS8700_REG_OUTXMSB 0x01
#define FXOS8700_REG_INT_SOURCE 0x0c
@ -125,8 +133,36 @@ enum fxos_trigger_type {
FXOS8700_TRIG_M_VECM,
};
struct fxos8700_config {
struct fxos8700_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 fxos8700_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 fxos8700_config {
const union fxos8700_bus_cfg bus_cfg;
const struct fxos8700_io_ops *ops;
#ifdef CONFIG_FXOS8700_TRIGGER
struct gpio_dt_spec int_gpio;
#endif
@ -148,6 +184,7 @@ struct fxos8700_config {
uint8_t mag_vecm_cfg;
uint8_t mag_vecm_ths[2];
#endif
uint8_t inst_on_bus;
};
struct fxos8700_data {
@ -185,6 +222,44 @@ struct fxos8700_data {
int fxos8700_get_power(const struct device *dev, enum fxos8700_power *power);
int fxos8700_set_power(const struct device *dev, enum fxos8700_power power);
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
int fxos8700_byte_write_spi(const struct device *dev,
uint8_t reg,
uint8_t byte);
int fxos8700_byte_read_spi(const struct device *dev,
uint8_t reg,
uint8_t *byte);
int fxos8700_reg_field_update_spi(const struct device *dev,
uint8_t reg,
uint8_t mask,
uint8_t val);
int fxos8700_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 fxos8700_byte_write_i2c(const struct device *dev,
uint8_t reg,
uint8_t byte);
int fxos8700_byte_read_i2c(const struct device *dev,
uint8_t reg,
uint8_t *byte);
int fxos8700_reg_field_update_i2c(const struct device *dev,
uint8_t reg,
uint8_t mask,
uint8_t val);
int fxos8700_read_i2c(const struct device *dev,
uint8_t reg,
void *data,
size_t length);
#endif
#if CONFIG_FXOS8700_TRIGGER
int fxos8700_trigger_init(const struct device *dev);
int fxos8700_trigger_set(const struct device *dev,

View file

@ -5,6 +5,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT nxp_fxos8700
#include "fxos8700.h"
#include <zephyr/logging/log.h>
@ -61,7 +63,7 @@ static int fxos8700_handle_pulse_int(const struct device *dev)
k_sem_take(&data->sem, K_FOREVER);
if (i2c_reg_read_byte_dt(&config->i2c, FXOS8700_REG_PULSE_SRC,
if (config->ops->byte_read(dev, FXOS8700_REG_PULSE_SRC,
&pulse_source)) {
LOG_ERR("Could not read pulse source");
}
@ -98,7 +100,7 @@ static int fxos8700_handle_motion_int(const struct device *dev)
k_sem_take(&data->sem, K_FOREVER);
if (i2c_reg_read_byte_dt(&config->i2c, FXOS8700_REG_FF_MT_SRC,
if (config->ops->byte_read(dev, FXOS8700_REG_FF_MT_SRC,
&motion_source)) {
LOG_ERR("Could not read pulse source");
}
@ -141,7 +143,7 @@ static void fxos8700_handle_int(const struct device *dev)
/* Interrupt status register */
k_sem_take(&data->sem, K_FOREVER);
if (i2c_reg_read_byte_dt(&config->i2c, FXOS8700_REG_INT_SOURCE,
if (config->ops->byte_read(dev, FXOS8700_REG_INT_SOURCE,
&int_source)) {
LOG_ERR("Could not read interrupt source");
int_source = 0U;
@ -166,7 +168,7 @@ static void fxos8700_handle_int(const struct device *dev)
/* Magnetometer interrupt source register */
k_sem_take(&data->sem, K_FOREVER);
if (i2c_reg_read_byte_dt(&config->i2c, FXOS8700_REG_M_INT_SRC,
if (config->ops->byte_read(dev, FXOS8700_REG_M_INT_SRC,
&int_source)) {
LOG_ERR("Could not read magnetometer interrupt source");
int_source = 0U;
@ -265,7 +267,7 @@ int fxos8700_trigger_set(const struct device *dev,
}
/* Configure the sensor interrupt */
if (i2c_reg_update_byte_dt(&config->i2c, FXOS8700_REG_CTRLREG4, mask,
if (config->ops->reg_field_update(dev, FXOS8700_REG_CTRLREG4, mask,
handler ? mask : 0)) {
LOG_ERR("Could not configure interrupt");
ret = -EIO;
@ -290,37 +292,37 @@ static int fxos8700_pulse_init(const struct device *dev)
{
const struct fxos8700_config *config = dev->config;
if (i2c_reg_write_byte_dt(&config->i2c, FXOS8700_REG_PULSE_CFG,
if (config->ops->byte_write(dev, FXOS8700_REG_PULSE_CFG,
config->pulse_cfg)) {
return -EIO;
}
if (i2c_reg_write_byte_dt(&config->i2c, FXOS8700_REG_PULSE_THSX,
if (config->ops->byte_write(dev, FXOS8700_REG_PULSE_THSX,
config->pulse_ths[0])) {
return -EIO;
}
if (i2c_reg_write_byte_dt(&config->i2c, FXOS8700_REG_PULSE_THSY,
if (config->ops->byte_write(dev, FXOS8700_REG_PULSE_THSY,
config->pulse_ths[1])) {
return -EIO;
}
if (i2c_reg_write_byte_dt(&config->i2c, FXOS8700_REG_PULSE_THSZ,
if (config->ops->byte_write(dev, FXOS8700_REG_PULSE_THSZ,
config->pulse_ths[2])) {
return -EIO;
}
if (i2c_reg_write_byte_dt(&config->i2c, FXOS8700_REG_PULSE_TMLT,
if (config->ops->byte_write(dev, FXOS8700_REG_PULSE_TMLT,
config->pulse_tmlt)) {
return -EIO;
}
if (i2c_reg_write_byte_dt(&config->i2c, FXOS8700_REG_PULSE_LTCY,
if (config->ops->byte_write(dev, FXOS8700_REG_PULSE_LTCY,
config->pulse_ltcy)) {
return -EIO;
}
if (i2c_reg_write_byte_dt(&config->i2c, FXOS8700_REG_PULSE_WIND,
if (config->ops->byte_write(dev, FXOS8700_REG_PULSE_WIND,
config->pulse_wind)) {
return -EIO;
}
@ -335,7 +337,7 @@ static int fxos8700_motion_init(const struct device *dev)
const struct fxos8700_config *config = dev->config;
/* Set Mode 4, Motion detection with ELE = 1, OAE = 1 */
if (i2c_reg_write_byte_dt(&config->i2c,
if (config->ops->byte_write(dev,
FXOS8700_REG_FF_MT_CFG,
FXOS8700_FF_MT_CFG_ELE |
FXOS8700_FF_MT_CFG_OAE |
@ -346,7 +348,7 @@ static int fxos8700_motion_init(const struct device *dev)
}
/* Set motion threshold to maximimum */
if (i2c_reg_write_byte_dt(&config->i2c, FXOS8700_REG_FF_MT_THS,
if (config->ops->byte_write(dev, FXOS8700_REG_FF_MT_THS,
FXOS8700_REG_FF_MT_THS)) {
return -EIO;
}
@ -367,7 +369,7 @@ static int fxos8700_m_vecm_init(const struct device *dev)
#endif
/* Set magnetic vector-magnitude function */
if (i2c_reg_write_byte_dt(&config->i2c, FXOS8700_REG_M_VECM_CFG,
if (config->ops->byte_write(dev, FXOS8700_REG_M_VECM_CFG,
m_vecm_cfg)) {
LOG_ERR("Could not set magnetic vector-magnitude function");
return -EIO;
@ -376,13 +378,13 @@ static int fxos8700_m_vecm_init(const struct device *dev)
/* Set magnetic vector-magnitude function threshold values:
* handle both MSB and LSB registers
*/
if (i2c_reg_write_byte_dt(&config->i2c, FXOS8700_REG_M_VECM_THS_MSB,
if (config->ops->byte_write(dev, FXOS8700_REG_M_VECM_THS_MSB,
config->mag_vecm_ths[0])) {
LOG_ERR("Could not set magnetic vector-magnitude function threshold MSB value");
return -EIO;
}
if (i2c_reg_write_byte_dt(&config->i2c, FXOS8700_REG_M_VECM_THS_LSB,
if (config->ops->byte_write(dev, FXOS8700_REG_M_VECM_THS_LSB,
config->mag_vecm_ths[1])) {
LOG_ERR("Could not set magnetic vector-magnitude function threshold LSB value");
return -EIO;
@ -425,7 +427,7 @@ int fxos8700_trigger_init(const struct device *dev)
ctrl_reg5 |= FXOS8700_MOTION_MASK;
#endif
if (i2c_reg_write_byte_dt(&config->i2c, FXOS8700_REG_CTRLREG5,
if (config->ops->byte_write(dev, FXOS8700_REG_CTRLREG5,
ctrl_reg5)) {
LOG_ERR("Could not configure interrupt pin routing");
return -EIO;