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
|
@ -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)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue