driver/sensors: lis2dh: Fix I2C and SPI bus communication
Add I2C and SPI bus communication routines in separate files, and register one or the other as read/write callbacks based on bus selection in DTS. This commit is fixing issue #22348 as well, as the SPI part is handling in the proper way the CS GPIO part. Signed-off-by: Armando Visconti <armando.visconti@st.com>
This commit is contained in:
parent
86087f202a
commit
1ebb708910
6 changed files with 428 additions and 254 deletions
|
@ -3,4 +3,6 @@
|
|||
zephyr_library()
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_LIS2DH lis2dh.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_LIS2DH lis2dh_i2c.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_LIS2DH lis2dh_spi.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_LIS2DH_TRIGGER lis2dh_trigger.c)
|
||||
|
|
|
@ -34,56 +34,6 @@ static const u32_t lis2dh_reg_val_to_scale[] = {
|
|||
#endif
|
||||
};
|
||||
|
||||
#if defined(DT_ST_LIS2DH_BUS_SPI)
|
||||
int lis2dh_spi_access(struct lis2dh_data *ctx, u8_t cmd,
|
||||
void *data, size_t length)
|
||||
{
|
||||
const struct spi_buf buf[2] = {
|
||||
{
|
||||
.buf = &cmd,
|
||||
.len = 1
|
||||
},
|
||||
{
|
||||
.buf = data,
|
||||
.len = length
|
||||
}
|
||||
};
|
||||
const struct spi_buf_set tx = {
|
||||
.buffers = buf,
|
||||
.count = 2
|
||||
};
|
||||
|
||||
if (cmd & LIS2DH_SPI_READ_BIT) {
|
||||
const struct spi_buf_set rx = {
|
||||
.buffers = buf,
|
||||
.count = 2
|
||||
};
|
||||
|
||||
return spi_transceive(ctx->spi, &ctx->spi_cfg, &tx, &rx);
|
||||
}
|
||||
|
||||
return spi_write(ctx->spi, &ctx->spi_cfg, &tx);
|
||||
}
|
||||
#endif
|
||||
|
||||
int lis2dh_reg_field_update(struct device *dev, u8_t reg_addr,
|
||||
u8_t pos, u8_t mask, u8_t val)
|
||||
{
|
||||
int status;
|
||||
u8_t old_val;
|
||||
|
||||
/* just to remove gcc warning */
|
||||
old_val = 0U;
|
||||
|
||||
status = lis2dh_reg_read_byte(dev, reg_addr, &old_val);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return lis2dh_reg_write_byte(dev, reg_addr,
|
||||
(old_val & ~mask) | ((val << pos) & mask));
|
||||
}
|
||||
|
||||
static void lis2dh_convert(s16_t raw_val, u32_t scale,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
|
@ -146,9 +96,9 @@ static int lis2dh_sample_fetch(struct device *dev, enum sensor_channel chan)
|
|||
* since status and all accel data register addresses are consecutive,
|
||||
* a burst read can be used to read all the samples
|
||||
*/
|
||||
status = lis2dh_burst_read(dev, LIS2DH_REG_STATUS,
|
||||
lis2dh->sample.raw,
|
||||
sizeof(lis2dh->sample.raw));
|
||||
status = lis2dh->hw_tf->read_data(dev, LIS2DH_REG_STATUS,
|
||||
lis2dh->sample.raw,
|
||||
sizeof(lis2dh->sample.raw));
|
||||
if (status < 0) {
|
||||
LOG_WRN("Could not read accel axis data");
|
||||
return status;
|
||||
|
@ -161,13 +111,7 @@ static int lis2dh_sample_fetch(struct device *dev, enum sensor_channel chan)
|
|||
*sample = sys_le16_to_cpu(*sample);
|
||||
}
|
||||
|
||||
LOG_INF("status=0x%x x=%d y=%d z=%d", lis2dh->sample.status,
|
||||
lis2dh->sample.xyz[0], lis2dh->sample.xyz[1],
|
||||
lis2dh->sample.xyz[2]);
|
||||
|
||||
if (lis2dh->sample.status & LIS2DH_STATUS_OVR_MASK) {
|
||||
return -EBADMSG;
|
||||
} else if (lis2dh->sample.status & LIS2DH_STATUS_DRDY_MASK) {
|
||||
if (lis2dh->sample.status & LIS2DH_STATUS_DRDY_MASK) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -202,13 +146,14 @@ static int lis2dh_acc_odr_set(struct device *dev, u16_t freq)
|
|||
int odr;
|
||||
int status;
|
||||
u8_t value;
|
||||
struct lis2dh_data *data = dev->driver_data;
|
||||
|
||||
odr = lis2dh_freq_to_odr_val(freq);
|
||||
if (odr < 0) {
|
||||
return odr;
|
||||
}
|
||||
|
||||
status = lis2dh_reg_read_byte(dev, LIS2DH_REG_CTRL1, &value);
|
||||
status = data->hw_tf->read_reg(dev, LIS2DH_REG_CTRL1, &value);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
@ -224,9 +169,9 @@ static int lis2dh_acc_odr_set(struct device *dev, u16_t freq)
|
|||
odr--;
|
||||
}
|
||||
|
||||
return lis2dh_reg_write_byte(dev, LIS2DH_REG_CTRL1,
|
||||
(value & ~LIS2DH_ODR_MASK) |
|
||||
LIS2DH_ODR_RATE(odr));
|
||||
return data->hw_tf->write_reg(dev, LIS2DH_REG_CTRL1,
|
||||
(value & ~LIS2DH_ODR_MASK) |
|
||||
LIS2DH_ODR_RATE(odr));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -260,10 +205,9 @@ static int lis2dh_acc_range_set(struct device *dev, s32_t range)
|
|||
|
||||
lis2dh->scale = lis2dh_reg_val_to_scale[fs];
|
||||
|
||||
return lis2dh_reg_field_update(dev, LIS2DH_REG_CTRL4,
|
||||
LIS2DH_FS_SHIFT,
|
||||
LIS2DH_FS_MASK,
|
||||
fs);
|
||||
return lis2dh->hw_tf->update_reg(dev, LIS2DH_REG_CTRL4,
|
||||
LIS2DH_FS_MASK,
|
||||
(fs << LIS2DH_FS_SHIFT));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -323,18 +267,34 @@ static const struct sensor_driver_api lis2dh_driver_api = {
|
|||
int lis2dh_init(struct device *dev)
|
||||
{
|
||||
struct lis2dh_data *lis2dh = dev->driver_data;
|
||||
const struct lis2dh_config *cfg = dev->config->config_info;
|
||||
int status;
|
||||
u8_t id;
|
||||
u8_t raw[6];
|
||||
|
||||
status = lis2dh_bus_configure(dev);
|
||||
lis2dh->bus = device_get_binding(cfg->bus_name);
|
||||
if (!lis2dh->bus) {
|
||||
LOG_ERR("master not found: %s", cfg->bus_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cfg->bus_init(dev);
|
||||
|
||||
status = lis2dh->hw_tf->read_reg(dev, LIS2DH_REG_WAI, &id);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to read chip id.");
|
||||
return status;
|
||||
}
|
||||
|
||||
if (id != LIS2DH_CHIP_ID) {
|
||||
LOG_ERR("Invalid chip ID: %02x\n", id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (IS_ENABLED(DT_INST_0_ST_LIS2DH_DISCONNECT_SDO_SA0_PULL_UP)) {
|
||||
status = lis2dh_reg_field_update(dev, LIS2DH_REG_CTRL0,
|
||||
LIS2DH_SDO_PU_DISC_SHIFT,
|
||||
LIS2DH_SDO_PU_DISC_MASK, 1);
|
||||
status = lis2dh->hw_tf->update_reg(dev, LIS2DH_REG_CTRL0,
|
||||
LIS2DH_SDO_PU_DISC_MASK,
|
||||
LIS2DH_SDO_PU_DISC_MASK);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to disconnect SDO/SA0 pull-up.");
|
||||
return status;
|
||||
|
@ -349,8 +309,9 @@ int lis2dh_init(struct device *dev)
|
|||
(void)memset(raw, 0, sizeof(raw));
|
||||
raw[0] = LIS2DH_ACCEL_EN_BITS;
|
||||
|
||||
status = lis2dh_burst_write(dev, LIS2DH_REG_CTRL1, raw,
|
||||
sizeof(raw));
|
||||
status = lis2dh->hw_tf->write_data(dev, LIS2DH_REG_CTRL1, raw,
|
||||
sizeof(raw));
|
||||
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to reset ctrl registers.");
|
||||
return status;
|
||||
|
@ -358,8 +319,8 @@ int lis2dh_init(struct device *dev)
|
|||
|
||||
/* set full scale range and store it for later conversion */
|
||||
lis2dh->scale = lis2dh_reg_val_to_scale[LIS2DH_FS_IDX];
|
||||
status = lis2dh_reg_write_byte(dev, LIS2DH_REG_CTRL4,
|
||||
LIS2DH_FS_BITS | LIS2DH_HR_BIT);
|
||||
status = lis2dh->hw_tf->write_reg(dev, LIS2DH_REG_CTRL4,
|
||||
LIS2DH_FS_BITS | LIS2DH_HR_BIT);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Failed to set full scale ctrl register.");
|
||||
return status;
|
||||
|
@ -378,13 +339,37 @@ int lis2dh_init(struct device *dev)
|
|||
LIS2DH_ODR_IDX, (u8_t)LIS2DH_LP_EN_BIT, lis2dh->scale);
|
||||
|
||||
/* enable accel measurements and set power mode and data rate */
|
||||
return lis2dh_reg_write_byte(dev, LIS2DH_REG_CTRL1,
|
||||
LIS2DH_ACCEL_EN_BITS | LIS2DH_LP_EN_BIT |
|
||||
LIS2DH_ODR_BITS);
|
||||
return lis2dh->hw_tf->write_reg(dev, LIS2DH_REG_CTRL1,
|
||||
LIS2DH_ACCEL_EN_BITS | LIS2DH_LP_EN_BIT |
|
||||
LIS2DH_ODR_BITS);
|
||||
}
|
||||
|
||||
static struct lis2dh_data lis2dh_driver;
|
||||
static struct lis2dh_data lis2dh_data;
|
||||
|
||||
DEVICE_AND_API_INIT(lis2dh, DT_INST_0_ST_LIS2DH_LABEL, lis2dh_init, &lis2dh_driver,
|
||||
NULL, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,
|
||||
static const struct lis2dh_config lis2dh_config = {
|
||||
.bus_name = DT_INST_0_ST_LIS2DH_BUS_NAME,
|
||||
#if defined(DT_ST_LIS2DH_BUS_SPI)
|
||||
.bus_init = lis2dh_spi_init,
|
||||
.spi_conf.frequency = DT_INST_0_ST_LIS2DH_SPI_MAX_FREQUENCY,
|
||||
.spi_conf.operation = (SPI_OP_MODE_MASTER | SPI_MODE_CPOL |
|
||||
SPI_MODE_CPHA | SPI_WORD_SET(8) |
|
||||
SPI_LINES_SINGLE),
|
||||
.spi_conf.slave = DT_INST_0_ST_LIS2DH_BASE_ADDRESS,
|
||||
#if defined(DT_INST_0_ST_LIS2DH_CS_GPIOS_CONTROLLER)
|
||||
.gpio_cs_port = DT_INST_0_ST_LIS2DH_CS_GPIOS_CONTROLLER,
|
||||
.cs_gpio = DT_INST_0_ST_LIS2DH_CS_GPIOS_PIN,
|
||||
.spi_conf.cs = &lis2dh_data.cs_ctrl,
|
||||
#else
|
||||
.spi_conf.cs = NULL,
|
||||
#endif
|
||||
#elif defined(DT_ST_LIS2DH_BUS_I2C)
|
||||
.bus_init = lis2dh_i2c_init,
|
||||
.i2c_slv_addr = DT_INST_0_ST_LIS2DH_BASE_ADDRESS,
|
||||
#else
|
||||
#error "BUS MACRO NOT DEFINED IN DTS"
|
||||
#endif
|
||||
};
|
||||
|
||||
DEVICE_AND_API_INIT(lis2dh, DT_INST_0_ST_LIS2DH_LABEL, lis2dh_init, &lis2dh_data,
|
||||
&lis2dh_config, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,
|
||||
&lis2dh_driver_api);
|
||||
|
|
|
@ -18,11 +18,14 @@
|
|||
#define LIS2DH_BUS_ADDRESS DT_INST_0_ST_LIS2DH_BASE_ADDRESS
|
||||
#define LIS2DH_BUS_DEV_NAME DT_INST_0_ST_LIS2DH_BUS_NAME
|
||||
|
||||
#define LIS2DH_REG_WAI 0x0f
|
||||
#define LIS2DH_CHIP_ID 0x33
|
||||
|
||||
#if defined(DT_ST_LIS2DH_BUS_SPI)
|
||||
#include <drivers/spi.h>
|
||||
|
||||
#define LIS2DH_SPI_READ_BIT BIT(7)
|
||||
#define LIS2DH_SPI_AUTOINC_ADDR BIT(6)
|
||||
#define LIS2DH_SPI_AUTOINC BIT(6)
|
||||
#define LIS2DH_SPI_ADDR_MASK BIT_MASK(6)
|
||||
|
||||
/* LIS2DH supports only SPI mode 0, word size 8 bits, MSB first */
|
||||
|
@ -194,13 +197,38 @@ union lis2dh_sample {
|
|||
} __packed;
|
||||
};
|
||||
|
||||
struct lis2dh_config {
|
||||
char *bus_name;
|
||||
int (*bus_init)(struct device *dev);
|
||||
#ifdef DT_ST_LIS2DH_BUS_I2C
|
||||
u16_t i2c_slv_addr;
|
||||
#elif DT_ST_LIS2DH_BUS_SPI
|
||||
struct spi_config spi_conf;
|
||||
#if defined(DT_INST_0_ST_LIS2DH_CS_GPIOS_CONTROLLER)
|
||||
const char *gpio_cs_port;
|
||||
u8_t cs_gpio;
|
||||
#endif /* DT_INST_0_ST_LIS2DH_CS_GPIOS_CONTROLLER */
|
||||
#endif /* DT_ST_LIS2DH_BUS_SPI */
|
||||
|
||||
};
|
||||
|
||||
struct lis2dh_transfer_function {
|
||||
int (*read_data)(struct device *dev, u8_t reg_addr,
|
||||
u8_t *value, u8_t len);
|
||||
int (*write_data)(struct device *dev, u8_t reg_addr,
|
||||
u8_t *value, u8_t len);
|
||||
int (*read_reg)(struct device *dev, u8_t reg_addr,
|
||||
u8_t *value);
|
||||
int (*write_reg)(struct device *dev, u8_t reg_addr,
|
||||
u8_t value);
|
||||
int (*update_reg)(struct device *dev, u8_t reg_addr,
|
||||
u8_t mask, u8_t value);
|
||||
};
|
||||
|
||||
struct lis2dh_data {
|
||||
#if defined(DT_ST_LIS2DH_BUS_SPI)
|
||||
struct device *spi;
|
||||
struct spi_config spi_cfg;
|
||||
#else
|
||||
struct device *bus;
|
||||
#endif
|
||||
const struct lis2dh_transfer_function *hw_tf;
|
||||
|
||||
union lis2dh_sample sample;
|
||||
/* current scaling factor, in micro m/s^2 / lsb */
|
||||
u32_t scale;
|
||||
|
@ -226,6 +254,9 @@ struct lis2dh_data {
|
|||
#endif
|
||||
|
||||
#endif /* CONFIG_LIS2DH_TRIGGER */
|
||||
#if defined(DT_INST_0_ST_LIS2DH_CS_GPIOS_CONTROLLER)
|
||||
struct spi_cs_control cs_ctrl;
|
||||
#endif /* DT_INST_0_ST_LIS2MDL_CS_GPIOS_CONTROLLER */
|
||||
};
|
||||
|
||||
#if defined(DT_ST_LIS2DH_BUS_SPI)
|
||||
|
@ -233,131 +264,6 @@ int lis2dh_spi_access(struct lis2dh_data *ctx, u8_t cmd,
|
|||
void *data, size_t length);
|
||||
#endif
|
||||
|
||||
static inline int lis2dh_bus_configure(struct device *dev)
|
||||
{
|
||||
struct lis2dh_data *lis2dh = dev->driver_data;
|
||||
|
||||
#if defined(DT_ST_LIS2DH_BUS_SPI)
|
||||
lis2dh->spi = device_get_binding(LIS2DH_BUS_DEV_NAME);
|
||||
if (lis2dh->spi == NULL) {
|
||||
LOG_ERR("Could not get pointer to %s device",
|
||||
LIS2DH_BUS_DEV_NAME);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
lis2dh->spi_cfg.operation = LIS2DH_SPI_CFG;
|
||||
lis2dh->spi_cfg.frequency = DT_INST_0_ST_LIS2DH_SPI_MAX_FREQUENCY;
|
||||
lis2dh->spi_cfg.slave = LIS2DH_BUS_ADDRESS;
|
||||
|
||||
return 0;
|
||||
#elif defined(DT_ST_LIS2DH_BUS_I2C)
|
||||
lis2dh->bus = device_get_binding(LIS2DH_BUS_DEV_NAME);
|
||||
if (lis2dh->bus == NULL) {
|
||||
LOG_ERR("Could not get pointer to %s device",
|
||||
LIS2DH_BUS_DEV_NAME);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return -ENODEV;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int lis2dh_burst_read(struct device *dev, u8_t start_addr,
|
||||
u8_t *buf, u8_t num_bytes)
|
||||
{
|
||||
struct lis2dh_data *lis2dh = dev->driver_data;
|
||||
|
||||
#if defined(DT_ST_LIS2DH_BUS_SPI)
|
||||
start_addr |= LIS2DH_SPI_READ_BIT | LIS2DH_SPI_AUTOINC_ADDR;
|
||||
|
||||
return lis2dh_spi_access(lis2dh, start_addr, buf, num_bytes);
|
||||
#elif defined(DT_ST_LIS2DH_BUS_I2C)
|
||||
u8_t addr = start_addr | LIS2DH_AUTOINCREMENT_ADDR;
|
||||
|
||||
return i2c_write_read(lis2dh->bus, LIS2DH_BUS_ADDRESS,
|
||||
&addr, sizeof(addr),
|
||||
buf, num_bytes);
|
||||
#else
|
||||
return -ENODEV;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int lis2dh_reg_read_byte(struct device *dev, u8_t reg_addr,
|
||||
u8_t *value)
|
||||
{
|
||||
struct lis2dh_data *lis2dh = dev->driver_data;
|
||||
|
||||
#if defined(DT_ST_LIS2DH_BUS_SPI)
|
||||
reg_addr |= LIS2DH_SPI_READ_BIT;
|
||||
|
||||
return lis2dh_spi_access(lis2dh, reg_addr, value, 1);
|
||||
#elif defined(DT_ST_LIS2DH_BUS_I2C)
|
||||
return i2c_reg_read_byte(lis2dh->bus, LIS2DH_BUS_ADDRESS,
|
||||
reg_addr, value);
|
||||
#else
|
||||
return -ENODEV;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int lis2dh_burst_write(struct device *dev, u8_t start_addr,
|
||||
u8_t *buf, u8_t num_bytes)
|
||||
{
|
||||
struct lis2dh_data *lis2dh = dev->driver_data;
|
||||
|
||||
#if defined(DT_ST_LIS2DH_BUS_SPI)
|
||||
start_addr |= LIS2DH_SPI_AUTOINC_ADDR;
|
||||
|
||||
return lis2dh_spi_access(lis2dh, start_addr, buf, num_bytes);
|
||||
#elif defined(DT_ST_LIS2DH_BUS_I2C)
|
||||
/* NRF TWIM is default and does not support burst write. We
|
||||
* can't detect whether the I2C master uses TWI or TWIM, so
|
||||
* use a substitute implementation unconditionally on Nordic.
|
||||
*
|
||||
* See Zephyr issue #20154.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_I2C_NRFX)) {
|
||||
u8_t buffer[8];
|
||||
|
||||
/* Largest num_bytes used is 6 */
|
||||
__ASSERT((1U + num_bytes) <= sizeof(buffer),
|
||||
"burst buffer too small");
|
||||
buffer[0] = start_addr | LIS2DH_AUTOINCREMENT_ADDR;
|
||||
memmove(buffer + 1, buf, num_bytes);
|
||||
return i2c_write(lis2dh->bus, buffer, 1 + num_bytes,
|
||||
LIS2DH_BUS_ADDRESS);
|
||||
}
|
||||
return i2c_burst_write(lis2dh->bus, LIS2DH_BUS_ADDRESS,
|
||||
start_addr | LIS2DH_AUTOINCREMENT_ADDR,
|
||||
buf, num_bytes);
|
||||
#else
|
||||
return -ENODEV;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int lis2dh_reg_write_byte(struct device *dev, u8_t reg_addr,
|
||||
u8_t value)
|
||||
{
|
||||
struct lis2dh_data *lis2dh = dev->driver_data;
|
||||
|
||||
#if defined(DT_ST_LIS2DH_BUS_SPI)
|
||||
reg_addr &= LIS2DH_SPI_ADDR_MASK;
|
||||
|
||||
return lis2dh_spi_access(lis2dh, reg_addr, &value, 1);
|
||||
#elif defined(DT_ST_LIS2DH_BUS_I2C)
|
||||
u8_t tx_buf[2] = {reg_addr, value};
|
||||
|
||||
return i2c_write(lis2dh->bus, tx_buf, sizeof(tx_buf),
|
||||
LIS2DH_BUS_ADDRESS);
|
||||
#else
|
||||
return -ENODEV;
|
||||
#endif
|
||||
}
|
||||
|
||||
int lis2dh_reg_field_update(struct device *dev, u8_t reg_addr,
|
||||
u8_t pos, u8_t mask, u8_t val);
|
||||
|
||||
#ifdef CONFIG_LIS2DH_TRIGGER
|
||||
int lis2dh_trigger_set(struct device *dev,
|
||||
const struct sensor_trigger *trig,
|
||||
|
@ -369,4 +275,8 @@ int lis2dh_acc_slope_config(struct device *dev, enum sensor_attribute attr,
|
|||
const struct sensor_value *val);
|
||||
#endif
|
||||
|
||||
int lis2dh_spi_init(struct device *dev);
|
||||
int lis2dh_i2c_init(struct device *dev);
|
||||
|
||||
|
||||
#endif /* __SENSOR_LIS2DH__ */
|
||||
|
|
92
drivers/sensor/lis2dh/lis2dh_i2c.c
Normal file
92
drivers/sensor/lis2dh/lis2dh_i2c.c
Normal file
|
@ -0,0 +1,92 @@
|
|||
/* ST Microelectronics LIS2DH 3-axis accelerometer driver
|
||||
*
|
||||
* Copyright (c) 2020 STMicroelectronics
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Datasheet:
|
||||
* https://www.st.com/resource/en/datasheet/lis2dh.pdf
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <drivers/i2c.h>
|
||||
#include <logging/log.h>
|
||||
|
||||
#include "lis2dh.h"
|
||||
|
||||
#ifdef DT_ST_LIS2DH_BUS_I2C
|
||||
|
||||
LOG_MODULE_DECLARE(lis2dh, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
static int lis2dh_i2c_read_data(struct device *dev, u8_t reg_addr,
|
||||
u8_t *value, u8_t len)
|
||||
{
|
||||
struct lis2dh_data *data = dev->driver_data;
|
||||
const struct lis2dh_config *cfg = dev->config->config_info;
|
||||
|
||||
return i2c_burst_read(data->bus, cfg->i2c_slv_addr,
|
||||
reg_addr | LIS2DH_AUTOINCREMENT_ADDR,
|
||||
value, len);
|
||||
}
|
||||
|
||||
static int lis2dh_i2c_write_data(struct device *dev, u8_t reg_addr,
|
||||
u8_t *value, u8_t len)
|
||||
{
|
||||
struct lis2dh_data *data = dev->driver_data;
|
||||
const struct lis2dh_config *cfg = dev->config->config_info;
|
||||
|
||||
return i2c_burst_write(data->bus, cfg->i2c_slv_addr,
|
||||
reg_addr | LIS2DH_AUTOINCREMENT_ADDR,
|
||||
value, len);
|
||||
}
|
||||
|
||||
static int lis2dh_i2c_read_reg(struct device *dev, u8_t reg_addr,
|
||||
u8_t *value)
|
||||
{
|
||||
struct lis2dh_data *data = dev->driver_data;
|
||||
const struct lis2dh_config *cfg = dev->config->config_info;
|
||||
|
||||
return i2c_reg_read_byte(data->bus,
|
||||
cfg->i2c_slv_addr,
|
||||
reg_addr, value);
|
||||
}
|
||||
|
||||
static int lis2dh_i2c_write_reg(struct device *dev, u8_t reg_addr,
|
||||
u8_t value)
|
||||
{
|
||||
struct lis2dh_data *data = dev->driver_data;
|
||||
const struct lis2dh_config *cfg = dev->config->config_info;
|
||||
|
||||
return i2c_reg_write_byte(data->bus,
|
||||
cfg->i2c_slv_addr,
|
||||
reg_addr, value);
|
||||
}
|
||||
|
||||
static int lis2dh_i2c_update_reg(struct device *dev, u8_t reg_addr,
|
||||
u8_t mask, u8_t value)
|
||||
{
|
||||
struct lis2dh_data *data = dev->driver_data;
|
||||
const struct lis2dh_config *cfg = dev->config->config_info;
|
||||
|
||||
return i2c_reg_update_byte(data->bus,
|
||||
cfg->i2c_slv_addr,
|
||||
reg_addr, mask, value);
|
||||
}
|
||||
|
||||
static const struct lis2dh_transfer_function lis2dh_i2c_transfer_fn = {
|
||||
.read_data = lis2dh_i2c_read_data,
|
||||
.write_data = lis2dh_i2c_write_data,
|
||||
.read_reg = lis2dh_i2c_read_reg,
|
||||
.write_reg = lis2dh_i2c_write_reg,
|
||||
.update_reg = lis2dh_i2c_update_reg,
|
||||
};
|
||||
|
||||
int lis2dh_i2c_init(struct device *dev)
|
||||
{
|
||||
struct lis2dh_data *data = dev->driver_data;
|
||||
|
||||
data->hw_tf = &lis2dh_i2c_transfer_fn;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* DT_ST_LIS2DH_BUS_I2C */
|
173
drivers/sensor/lis2dh/lis2dh_spi.c
Normal file
173
drivers/sensor/lis2dh/lis2dh_spi.c
Normal file
|
@ -0,0 +1,173 @@
|
|||
/* ST Microelectronics LIS2DH 3-axis accelerometer driver
|
||||
*
|
||||
* Copyright (c) 2020 STMicroelectronics
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Datasheet:
|
||||
* https://www.st.com/resource/en/datasheet/lis2dh.pdf
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "lis2dh.h"
|
||||
#include <logging/log.h>
|
||||
|
||||
#ifdef DT_ST_LIS2DH_BUS_SPI
|
||||
|
||||
LOG_MODULE_DECLARE(lis2dh, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
static int lis2dh_raw_read(struct device *dev, u8_t reg_addr,
|
||||
u8_t *value, u8_t len)
|
||||
{
|
||||
struct lis2dh_data *data = dev->driver_data;
|
||||
const struct lis2dh_config *cfg = dev->config->config_info;
|
||||
const struct spi_config *spi_cfg = &cfg->spi_conf;
|
||||
u8_t buffer_tx[2] = { reg_addr | LIS2DH_SPI_READ_BIT, 0 };
|
||||
const struct spi_buf tx_buf = {
|
||||
.buf = buffer_tx,
|
||||
.len = 2,
|
||||
};
|
||||
const struct spi_buf_set tx = {
|
||||
.buffers = &tx_buf,
|
||||
.count = 1
|
||||
};
|
||||
const struct spi_buf rx_buf[2] = {
|
||||
{
|
||||
.buf = NULL,
|
||||
.len = 1,
|
||||
},
|
||||
{
|
||||
.buf = value,
|
||||
.len = len,
|
||||
}
|
||||
};
|
||||
const struct spi_buf_set rx = {
|
||||
.buffers = rx_buf,
|
||||
.count = 2
|
||||
};
|
||||
|
||||
|
||||
if (len > 64) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (len > 1) {
|
||||
buffer_tx[0] |= LIS2DH_SPI_AUTOINC;
|
||||
}
|
||||
|
||||
if (spi_transceive(data->bus, spi_cfg, &tx, &rx)) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lis2dh_raw_write(struct device *dev, u8_t reg_addr,
|
||||
u8_t *value, u8_t len)
|
||||
{
|
||||
struct lis2dh_data *data = dev->driver_data;
|
||||
const struct lis2dh_config *cfg = dev->config->config_info;
|
||||
const struct spi_config *spi_cfg = &cfg->spi_conf;
|
||||
u8_t buffer_tx[1] = { reg_addr & ~LIS2DH_SPI_READ_BIT };
|
||||
const struct spi_buf tx_buf[2] = {
|
||||
{
|
||||
.buf = buffer_tx,
|
||||
.len = 1,
|
||||
},
|
||||
{
|
||||
.buf = value,
|
||||
.len = len,
|
||||
}
|
||||
};
|
||||
const struct spi_buf_set tx = {
|
||||
.buffers = tx_buf,
|
||||
.count = 2
|
||||
};
|
||||
|
||||
|
||||
if (len > 64) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (len > 1) {
|
||||
buffer_tx[0] |= LIS2DH_SPI_AUTOINC;
|
||||
}
|
||||
|
||||
if (spi_write(data->bus, spi_cfg, &tx)) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lis2dh_spi_read_data(struct device *dev, u8_t reg_addr,
|
||||
u8_t *value, u8_t len)
|
||||
{
|
||||
return lis2dh_raw_read(dev, reg_addr, value, len);
|
||||
}
|
||||
|
||||
static int lis2dh_spi_write_data(struct device *dev, u8_t reg_addr,
|
||||
u8_t *value, u8_t len)
|
||||
{
|
||||
return lis2dh_raw_write(dev, reg_addr, value, len);
|
||||
}
|
||||
|
||||
static int lis2dh_spi_read_reg(struct device *dev, u8_t reg_addr,
|
||||
u8_t *value)
|
||||
{
|
||||
return lis2dh_raw_read(dev, reg_addr, value, 1);
|
||||
}
|
||||
|
||||
static int lis2dh_spi_write_reg(struct device *dev, u8_t reg_addr,
|
||||
u8_t value)
|
||||
{
|
||||
u8_t tmp_val = value;
|
||||
|
||||
return lis2dh_raw_write(dev, reg_addr, &tmp_val, 1);
|
||||
}
|
||||
|
||||
static int lis2dh_spi_update_reg(struct device *dev, u8_t reg_addr,
|
||||
u8_t mask, u8_t value)
|
||||
{
|
||||
u8_t tmp_val;
|
||||
|
||||
lis2dh_raw_read(dev, reg_addr, &tmp_val, 1);
|
||||
tmp_val = (tmp_val & ~mask) | (value & mask);
|
||||
|
||||
return lis2dh_raw_write(dev, reg_addr, &tmp_val, 1);
|
||||
}
|
||||
|
||||
static const struct lis2dh_transfer_function lis2dh_spi_transfer_fn = {
|
||||
.read_data = lis2dh_spi_read_data,
|
||||
.write_data = lis2dh_spi_write_data,
|
||||
.read_reg = lis2dh_spi_read_reg,
|
||||
.write_reg = lis2dh_spi_write_reg,
|
||||
.update_reg = lis2dh_spi_update_reg,
|
||||
};
|
||||
|
||||
int lis2dh_spi_init(struct device *dev)
|
||||
{
|
||||
struct lis2dh_data *data = dev->driver_data;
|
||||
|
||||
data->hw_tf = &lis2dh_spi_transfer_fn;
|
||||
|
||||
#if defined(DT_INST_0_ST_LIS2DH_CS_GPIOS_CONTROLLER)
|
||||
const struct lis2dh_config *cfg = dev->config->config_info;
|
||||
|
||||
/* handle SPI CS thru GPIO if it is the case */
|
||||
data->cs_ctrl.gpio_dev = device_get_binding(cfg->gpio_cs_port);
|
||||
if (!data->cs_ctrl.gpio_dev) {
|
||||
LOG_ERR("Unable to get GPIO SPI CS device");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
data->cs_ctrl.gpio_pin = cfg->cs_gpio;
|
||||
data->cs_ctrl.delay = 0;
|
||||
|
||||
LOG_DBG("SPI GPIO CS configured on %s:%u",
|
||||
cfg->gpio_cs_port, cfg->cs_gpio);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* DT_ST_LIS2DH_BUS_SPI */
|
|
@ -39,9 +39,8 @@ static int lis2dh_trigger_drdy_set(struct device *dev, enum sensor_channel chan,
|
|||
/* cancel potentially pending trigger */
|
||||
atomic_clear_bit(&lis2dh->trig_flags, TRIGGED_INT1);
|
||||
|
||||
status = lis2dh_reg_field_update(dev, LIS2DH_REG_CTRL3,
|
||||
LIS2DH_EN_DRDY1_INT1_SHIFT,
|
||||
LIS2DH_EN_DRDY1_INT1, 0);
|
||||
status = lis2dh->hw_tf->update_reg(dev, LIS2DH_REG_CTRL3,
|
||||
LIS2DH_EN_DRDY1_INT1, 0);
|
||||
|
||||
lis2dh->handler_drdy = handler;
|
||||
if ((handler == NULL) || (status < 0)) {
|
||||
|
@ -68,14 +67,16 @@ static int lis2dh_start_trigger_int1(struct device *dev)
|
|||
int status;
|
||||
u8_t raw[LIS2DH_BUF_SZ];
|
||||
u8_t ctrl1 = 0U;
|
||||
struct lis2dh_data *lis2dh = dev->driver_data;
|
||||
|
||||
/* power down temporarily to align interrupt & data output sampling */
|
||||
status = lis2dh_reg_read_byte(dev, LIS2DH_REG_CTRL1, &ctrl1);
|
||||
status = lis2dh->hw_tf->read_reg(dev, LIS2DH_REG_CTRL1, &ctrl1);
|
||||
if (unlikely(status < 0)) {
|
||||
return status;
|
||||
}
|
||||
status = lis2dh_reg_write_byte(dev, LIS2DH_REG_CTRL1,
|
||||
ctrl1 & ~LIS2DH_ODR_MASK);
|
||||
status = lis2dh->hw_tf->write_reg(dev, LIS2DH_REG_CTRL1,
|
||||
ctrl1 & ~LIS2DH_ODR_MASK);
|
||||
|
||||
if (unlikely(status < 0)) {
|
||||
return status;
|
||||
}
|
||||
|
@ -83,7 +84,8 @@ static int lis2dh_start_trigger_int1(struct device *dev)
|
|||
LOG_DBG("ctrl1=0x%x @tick=%u", ctrl1, k_cycle_get_32());
|
||||
|
||||
/* empty output data */
|
||||
status = lis2dh_burst_read(dev, LIS2DH_REG_STATUS, raw, sizeof(raw));
|
||||
status = lis2dh->hw_tf->read_data(dev, LIS2DH_REG_STATUS,
|
||||
raw, sizeof(raw));
|
||||
if (unlikely(status < 0)) {
|
||||
return status;
|
||||
}
|
||||
|
@ -91,14 +93,14 @@ static int lis2dh_start_trigger_int1(struct device *dev)
|
|||
setup_int1(dev, true);
|
||||
|
||||
/* re-enable output sampling */
|
||||
status = lis2dh_reg_write_byte(dev, LIS2DH_REG_CTRL1, ctrl1);
|
||||
status = lis2dh->hw_tf->write_reg(dev, LIS2DH_REG_CTRL1, ctrl1);
|
||||
if (unlikely(status < 0)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return lis2dh_reg_field_update(dev, LIS2DH_REG_CTRL3,
|
||||
LIS2DH_EN_DRDY1_INT1_SHIFT,
|
||||
LIS2DH_EN_DRDY1_INT1, 1);
|
||||
return lis2dh->hw_tf->update_reg(dev, LIS2DH_REG_CTRL3,
|
||||
LIS2DH_EN_DRDY1_INT1,
|
||||
LIS2DH_EN_DRDY1_INT1);
|
||||
}
|
||||
|
||||
#if defined(DT_INST_0_ST_LIS2DH_IRQ_GPIOS_CONTROLLER_1)
|
||||
|
@ -130,10 +132,10 @@ static int lis2dh_trigger_anym_set(struct device *dev,
|
|||
atomic_clear_bit(&lis2dh->trig_flags, TRIGGED_INT2);
|
||||
|
||||
/* disable all interrupt 2 events */
|
||||
status = lis2dh_reg_write_byte(dev, LIS2DH_REG_INT2_CFG, 0);
|
||||
status = lis2dh->hw_tf->write_reg(dev, LIS2DH_REG_INT2_CFG, 0);
|
||||
|
||||
/* make sure any pending interrupt is cleared */
|
||||
status = lis2dh_reg_read_byte(dev, LIS2DH_REG_INT2_SRC, ®_val);
|
||||
status = lis2dh->hw_tf->read_reg(dev, LIS2DH_REG_INT2_SRC, ®_val);
|
||||
|
||||
lis2dh->handler_anymotion = handler;
|
||||
if ((handler == NULL) || (status < 0)) {
|
||||
|
@ -158,8 +160,8 @@ static int lis2dh_start_trigger_int2(struct device *dev)
|
|||
|
||||
setup_int2(dev, true);
|
||||
|
||||
return lis2dh_reg_write_byte(dev, LIS2DH_REG_INT2_CFG,
|
||||
LIS2DH_ANYM_CFG);
|
||||
return lis2dh->hw_tf->write_reg(dev, LIS2DH_REG_INT2_CFG,
|
||||
LIS2DH_ANYM_CFG);
|
||||
}
|
||||
#endif /* DT_INST_0_ST_LIS2DH_IRQ_GPIOS_CONTROLLER_1 */
|
||||
|
||||
|
@ -182,13 +184,15 @@ int lis2dh_trigger_set(struct device *dev,
|
|||
int lis2dh_acc_slope_config(struct device *dev, enum sensor_attribute attr,
|
||||
const struct sensor_value *val)
|
||||
{
|
||||
struct lis2dh_data *lis2dh = dev->driver_data;
|
||||
int status;
|
||||
|
||||
if (attr == SENSOR_ATTR_SLOPE_TH) {
|
||||
u8_t range_g, reg_val;
|
||||
u32_t slope_th_ums2;
|
||||
|
||||
status = lis2dh_reg_read_byte(dev, LIS2DH_REG_CTRL4, ®_val);
|
||||
status = lis2dh->hw_tf->read_reg(dev, LIS2DH_REG_CTRL4,
|
||||
®_val);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
@ -210,8 +214,8 @@ int lis2dh_acc_slope_config(struct device *dev, enum sensor_attribute attr,
|
|||
LOG_INF("int2_ths=0x%x range_g=%d ums2=%u", reg_val,
|
||||
range_g, slope_th_ums2 - 1);
|
||||
|
||||
status = lis2dh_reg_write_byte(dev, LIS2DH_REG_INT2_THS,
|
||||
reg_val);
|
||||
status = lis2dh->hw_tf->write_reg(dev, LIS2DH_REG_INT2_THS,
|
||||
reg_val);
|
||||
} else { /* SENSOR_ATTR_SLOPE_DUR */
|
||||
/*
|
||||
* slope duration is measured in number of samples:
|
||||
|
@ -223,8 +227,8 @@ int lis2dh_acc_slope_config(struct device *dev, enum sensor_attribute attr,
|
|||
|
||||
LOG_INF("int2_dur=0x%x", val->val1);
|
||||
|
||||
status = lis2dh_reg_write_byte(dev, LIS2DH_REG_INT2_DUR,
|
||||
val->val1);
|
||||
status = lis2dh->hw_tf->write_reg(dev, LIS2DH_REG_INT2_DUR,
|
||||
val->val1);
|
||||
}
|
||||
|
||||
return status;
|
||||
|
@ -318,8 +322,8 @@ static void lis2dh_thread_cb(void *arg)
|
|||
u8_t reg_val;
|
||||
|
||||
/* clear interrupt 2 to de-assert int2 line */
|
||||
status = lis2dh_reg_read_byte(dev, LIS2DH_REG_INT2_SRC,
|
||||
®_val);
|
||||
status = lis2dh->hw_tf->read_reg(dev, LIS2DH_REG_INT2_SRC,
|
||||
®_val);
|
||||
if (status < 0) {
|
||||
LOG_ERR("clearing interrupt 2 failed: %d", status);
|
||||
return;
|
||||
|
@ -367,7 +371,9 @@ int lis2dh_init_interrupt(struct device *dev)
|
|||
{
|
||||
struct lis2dh_data *lis2dh = dev->driver_data;
|
||||
int status;
|
||||
#if defined(DT_INST_0_ST_LIS2DH_IRQ_GPIOS_CONTROLLER_1)
|
||||
u8_t raw[2];
|
||||
#endif
|
||||
|
||||
/* setup data ready gpio interrupt */
|
||||
lis2dh->gpio_int1 = device_get_binding(DT_LIS2DH_INT1_GPIO_DEV_NAME);
|
||||
|
@ -377,6 +383,19 @@ int lis2dh_init_interrupt(struct device *dev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_LIS2DH_TRIGGER_OWN_THREAD)
|
||||
k_sem_init(&lis2dh->gpio_sem, 0, UINT_MAX);
|
||||
|
||||
k_thread_create(&lis2dh->thread, lis2dh->thread_stack,
|
||||
CONFIG_LIS2DH_THREAD_STACK_SIZE,
|
||||
(k_thread_entry_t)lis2dh_thread, dev, NULL, NULL,
|
||||
K_PRIO_COOP(CONFIG_LIS2DH_THREAD_PRIORITY), 0,
|
||||
K_NO_WAIT);
|
||||
#elif defined(CONFIG_LIS2DH_TRIGGER_GLOBAL_THREAD)
|
||||
lis2dh->work.handler = lis2dh_work_cb;
|
||||
lis2dh->dev = dev;
|
||||
#endif
|
||||
|
||||
/* data ready int1 gpio configuration */
|
||||
status = gpio_pin_configure(lis2dh->gpio_int1, DT_LIS2DH_INT1_GPIOS_PIN,
|
||||
GPIO_INPUT | DT_LIS2DH_INT1_GPIOS_FLAGS);
|
||||
|
@ -428,21 +447,9 @@ int lis2dh_init_interrupt(struct device *dev)
|
|||
}
|
||||
|
||||
LOG_INF("int2 on pin=%d", DT_LIS2DH_INT2_GPIOS_PIN);
|
||||
#endif /* DT_INST_0_ST_LIS2DH_IRQ_GPIOS_CONTROLLER_1 */
|
||||
|
||||
#if defined(CONFIG_LIS2DH_TRIGGER_OWN_THREAD)
|
||||
k_sem_init(&lis2dh->gpio_sem, 0, UINT_MAX);
|
||||
|
||||
k_thread_create(&lis2dh->thread, lis2dh->thread_stack,
|
||||
CONFIG_LIS2DH_THREAD_STACK_SIZE,
|
||||
(k_thread_entry_t)lis2dh_thread, dev, NULL, NULL,
|
||||
K_PRIO_COOP(CONFIG_LIS2DH_THREAD_PRIORITY), 0, K_NO_WAIT);
|
||||
#elif defined(CONFIG_LIS2DH_TRIGGER_GLOBAL_THREAD)
|
||||
lis2dh->work.handler = lis2dh_work_cb;
|
||||
lis2dh->dev = dev;
|
||||
#endif
|
||||
/* disable interrupt 2 in case of warm (re)boot */
|
||||
status = lis2dh_reg_write_byte(dev, LIS2DH_REG_INT2_CFG, 0);
|
||||
status = lis2dh->hw_tf->write_reg(dev, LIS2DH_REG_INT2_CFG, 0);
|
||||
if (status < 0) {
|
||||
LOG_ERR("Interrupt 2 disable reg write failed (%d)",
|
||||
status);
|
||||
|
@ -450,21 +457,26 @@ int lis2dh_init_interrupt(struct device *dev)
|
|||
}
|
||||
|
||||
(void)memset(raw, 0, sizeof(raw));
|
||||
status = lis2dh_burst_write(dev, LIS2DH_REG_INT2_THS, raw, sizeof(raw));
|
||||
status = lis2dh->hw_tf->write_data(dev, LIS2DH_REG_INT2_THS,
|
||||
raw, sizeof(raw));
|
||||
if (status < 0) {
|
||||
LOG_ERR("Burst write to INT2 THS failed (%d)", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* enable interrupt 2 on int2 line */
|
||||
status = lis2dh->hw_tf->update_reg(dev, LIS2DH_REG_CTRL6,
|
||||
LIS2DH_EN_INT2_INT2,
|
||||
LIS2DH_EN_INT2_INT2);
|
||||
|
||||
/* latch int2 line interrupt */
|
||||
status = lis2dh_reg_write_byte(dev, LIS2DH_REG_CTRL5,
|
||||
LIS2DH_EN_LIR_INT2);
|
||||
status = lis2dh->hw_tf->write_reg(dev, LIS2DH_REG_CTRL5,
|
||||
LIS2DH_EN_LIR_INT2);
|
||||
if (status < 0) {
|
||||
LOG_ERR("INT2 latch enable reg write failed (%d)", status);
|
||||
return status;
|
||||
}
|
||||
#endif /* DT_INST_0_ST_LIS2DH_IRQ_GPIOS_CONTROLLER_1 */
|
||||
|
||||
/* enable interrupt 2 on int2 line */
|
||||
return lis2dh_reg_write_byte(dev, LIS2DH_REG_CTRL6,
|
||||
LIS2DH_EN_INT2_INT2);
|
||||
return status;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue