diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index 3fbaaa98c60..96405b8464b 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -29,6 +29,7 @@ add_subdirectory_ifdef(CONFIG_HP206C hp206c) add_subdirectory_ifdef(CONFIG_HTS221 hts221) add_subdirectory_ifdef(CONFIG_IIS2DH iis2dh) add_subdirectory_ifdef(CONFIG_IIS2DLPC iis2dlpc) +add_subdirectory_ifdef(CONFIG_IIS2ICLX iis2iclx) add_subdirectory_ifdef(CONFIG_IIS2MDC iis2mdc) add_subdirectory_ifdef(CONFIG_IIS3DHHC iis3dhhc) add_subdirectory_ifdef(CONFIG_ISL29035 isl29035) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index 16d69bc8d08..047843dc94a 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -95,6 +95,8 @@ source "drivers/sensor/iis2dh/Kconfig" source "drivers/sensor/iis2dlpc/Kconfig" +source "drivers/sensor/iis2iclx/Kconfig" + source "drivers/sensor/iis2mdc/Kconfig" source "drivers/sensor/iis3dhhc/Kconfig" diff --git a/drivers/sensor/iis2iclx/CMakeLists.txt b/drivers/sensor/iis2iclx/CMakeLists.txt new file mode 100644 index 00000000000..481798b1fea --- /dev/null +++ b/drivers/sensor/iis2iclx/CMakeLists.txt @@ -0,0 +1,13 @@ +# ST Microelectronics IIS2ICLX 2-axis accelerometer sensor driver +# +# Copyright (c) 2020 STMicroelectronics +# +# SPDX-License-Identifier: Apache-2.0 +# +zephyr_library() + +zephyr_library_sources_ifdef(CONFIG_IIS2ICLX iis2iclx.c) +zephyr_library_sources_ifdef(CONFIG_IIS2ICLX iis2iclx_i2c.c) +zephyr_library_sources_ifdef(CONFIG_IIS2ICLX iis2iclx_spi.c) +zephyr_library_sources_ifdef(CONFIG_IIS2ICLX_SENSORHUB iis2iclx_shub.c) +zephyr_library_sources_ifdef(CONFIG_IIS2ICLX_TRIGGER iis2iclx_trigger.c) diff --git a/drivers/sensor/iis2iclx/Kconfig b/drivers/sensor/iis2iclx/Kconfig new file mode 100644 index 00000000000..cb287a65de1 --- /dev/null +++ b/drivers/sensor/iis2iclx/Kconfig @@ -0,0 +1,138 @@ +# ST Microelectronics IIS2ICLX 2-axis accelerometer sensor driver + +# Copyright (c) 2020 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +menuconfig IIS2ICLX + bool "IIS2ICLX I2C/SPI accelerometer Chip" + depends on I2C || SPI + select HAS_STMEMSC + select USE_STDC_IIS2ICLX + help + Enable driver for IIS2ICLX accelerometer + sensor. + +if IIS2ICLX + +choice IIS2ICLX_TRIGGER_MODE + prompt "Trigger mode" + help + Specify the type of triggering to be used by the driver. + +config IIS2ICLX_TRIGGER_NONE + bool "No trigger" + +config IIS2ICLX_TRIGGER_GLOBAL_THREAD + bool "Use global thread" + depends on GPIO + select IIS2ICLX_TRIGGER + +config IIS2ICLX_TRIGGER_OWN_THREAD + bool "Use own thread" + depends on GPIO + select IIS2ICLX_TRIGGER + +endchoice + +config IIS2ICLX_TRIGGER + bool + +if IIS2ICLX_TRIGGER + +config IIS2ICLX_THREAD_PRIORITY + int "Thread priority" + depends on IIS2ICLX_TRIGGER_OWN_THREAD + default 10 + help + Priority of thread used by the driver to handle interrupts. + +config IIS2ICLX_THREAD_STACK_SIZE + int "Thread stack size" + depends on IIS2ICLX_TRIGGER_OWN_THREAD + default 1024 + help + Stack size of thread used by the driver to handle interrupts. + +choice IIS2ICLX_INT_PIN + prompt "Sensor INT pin number" + default IIS2ICLX_INT_PIN_1 + help + The number of IIS2ICLX int pin used to generate interrupt to cpu. + Supported values are int1 or int2 + +config IIS2ICLX_INT_PIN_1 + bool "int1" + +config IIS2ICLX_INT_PIN_2 + bool "int2" +endchoice + +endif # IIS2ICLX_TRIGGER + +config IIS2ICLX_ENABLE_TEMP + bool "Enable temperature" + help + Enable/disable temperature + +config IIS2ICLX_SENSORHUB + bool "Enable I2C sensorhub feature" + help + Enable/disable internal sensorhub. You can enable + a maximum of two external sensors (if more than two are enabled + the system would enumerate only the first two found) + +if IIS2ICLX_SENSORHUB + +config IIS2ICLX_EXT_LIS2MDL + bool "Enable LIS2MDL as external sensor" + +config IIS2ICLX_EXT_IIS2MDC + bool "Enable IIS2MDC as external sensor" + +config IIS2ICLX_EXT_LPS22HH + bool "Enable LPS22HH as external sensor" + +config IIS2ICLX_EXT_HTS221 + bool "Enable HTS221 as external sensor" + +config IIS2ICLX_EXT_LPS22HB + bool "Enable LPS22HB as external sensor" + +endif # IIS2ICLX_SENSORHUB + +menu "Attributes" + +config IIS2ICLX_ACCEL_FS + int "Accelerometer full-scale range" + default 0 + help + Specify the default accelerometer full-scale range. + An X value for the config represents a range of +/- X G. Valid values + are: + 0: Full Scale selected at runtime + 500: +/- 500mg + 1000: +/- 1g + 2000: +/- 2g + 3000: +/- 3g + +config IIS2ICLX_ACCEL_ODR + int "Accelerometer Output data rate frequency" + range 0 10 + default 0 + help + Specify the default accelerometer output data rate expressed in + samples per second (Hz). + 0: ODR selected at runtime + 1: 12.5Hz + 2: 26Hz + 3: 52Hz + 4: 104Hz + 5: 208Hz + 6: 416Hz + 7: 833Hz + 8: 1660Hz + 9: 3330Hz + 10: 6660Hz +endmenu + +endif # IIS2ICLX diff --git a/drivers/sensor/iis2iclx/iis2iclx.c b/drivers/sensor/iis2iclx/iis2iclx.c new file mode 100644 index 00000000000..5b2aa2ba2cf --- /dev/null +++ b/drivers/sensor/iis2iclx/iis2iclx.c @@ -0,0 +1,657 @@ +/* ST Microelectronics IIS2ICLX 2-axis accelerometer sensor driver + * + * Copyright (c) 2020 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/iis2iclx.pdf + */ + +#define DT_DRV_COMPAT st_iis2iclx + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iis2iclx.h" + +LOG_MODULE_REGISTER(IIS2ICLX, CONFIG_SENSOR_LOG_LEVEL); + +static const uint16_t iis2iclx_odr_map[] = {0, 12, 26, 52, 104, 208, 416, 833, + 1660, 3330, 6660}; + +#if defined(IIS2ICLX_ACCEL_ODR_RUNTIME) +static int iis2iclx_freq_to_odr_val(uint16_t freq) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(iis2iclx_odr_map); i++) { + if (freq == iis2iclx_odr_map[i]) { + return i; + } + } + + return -EINVAL; +} +#endif + +static int iis2iclx_odr_to_freq_val(uint16_t odr) +{ + /* for valid index, return value from map */ + if (odr < ARRAY_SIZE(iis2iclx_odr_map)) { + return iis2iclx_odr_map[odr]; + } + + /* invalid index, return last entry */ + return iis2iclx_odr_map[ARRAY_SIZE(iis2iclx_odr_map) - 1]; +} + +#ifdef IIS2ICLX_ACCEL_FS_RUNTIME +static const uint16_t iis2iclx_accel_fs_map[] = {500, 3000, 1000, 2000}; +static const uint16_t iis2iclx_accel_fs_sens[] = {1, 8, 2, 4}; + +static int iis2iclx_accel_range_to_fs_val(int32_t range) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(iis2iclx_accel_fs_map); i++) { + if (range == iis2iclx_accel_fs_map[i]) { + return i; + } + } + + return -EINVAL; +} +#endif + +static inline int iis2iclx_reboot(const struct device *dev) +{ + struct iis2iclx_data *data = dev->data; + + if (iis2iclx_boot_set(data->ctx, 1) < 0) { + return -EIO; + } + + /* Wait sensor turn-on time as per datasheet */ + k_msleep(35); + + return 0; +} + +static int iis2iclx_accel_set_fs_raw(const struct device *dev, uint8_t fs) +{ + struct iis2iclx_data *data = dev->data; + + if (iis2iclx_xl_full_scale_set(data->ctx, fs) < 0) { + return -EIO; + } + + data->accel_fs = fs; + + return 0; +} + +static int iis2iclx_accel_set_odr_raw(const struct device *dev, uint8_t odr) +{ + struct iis2iclx_data *data = dev->data; + + if (iis2iclx_xl_data_rate_set(data->ctx, odr) < 0) { + return -EIO; + } + + data->accel_freq = iis2iclx_odr_to_freq_val(odr); + + return 0; +} + +#ifdef IIS2ICLX_ACCEL_ODR_RUNTIME +static int iis2iclx_accel_odr_set(const struct device *dev, uint16_t freq) +{ + int odr; + + odr = iis2iclx_freq_to_odr_val(freq); + if (odr < 0) { + return odr; + } + + if (iis2iclx_accel_set_odr_raw(dev, odr) < 0) { + LOG_ERR("failed to set accelerometer sampling rate"); + return -EIO; + } + + return 0; +} +#endif + +#ifdef IIS2ICLX_ACCEL_FS_RUNTIME +static int iis2iclx_accel_range_set(const struct device *dev, int32_t range) +{ + int fs; + struct iis2iclx_data *data = dev->data; + + fs = iis2iclx_accel_range_to_fs_val(range); + if (fs < 0) { + return fs; + } + + if (iis2iclx_accel_set_fs_raw(dev, fs) < 0) { + LOG_ERR("failed to set accelerometer full-scale"); + return -EIO; + } + + data->acc_gain = (iis2iclx_accel_fs_sens[fs] * GAIN_UNIT_XL); + return 0; +} +#endif + +static int iis2iclx_accel_config(const struct device *dev, + enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + switch (attr) { +#ifdef IIS2ICLX_ACCEL_FS_RUNTIME + case SENSOR_ATTR_FULL_SCALE: + return iis2iclx_accel_range_set(dev, sensor_ms2_to_g(val)); +#endif +#ifdef IIS2ICLX_ACCEL_ODR_RUNTIME + case SENSOR_ATTR_SAMPLING_FREQUENCY: + return iis2iclx_accel_odr_set(dev, val->val1); +#endif + default: + LOG_ERR("Accel attribute not supported."); + return -ENOTSUP; + } + + return 0; +} + +static int iis2iclx_attr_set(const struct device *dev, + enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + switch (chan) { + case SENSOR_CHAN_ACCEL_XYZ: + return iis2iclx_accel_config(dev, chan, attr, val); +#if defined(CONFIG_IIS2ICLX_SENSORHUB) + case SENSOR_CHAN_MAGN_XYZ: + case SENSOR_CHAN_PRESS: + case SENSOR_CHAN_HUMIDITY: + return iis2iclx_shub_config(dev, chan, attr, val); +#endif /* CONFIG_IIS2ICLX_SENSORHUB */ + default: + LOG_ERR("attr_set() not supported on this channel."); + return -ENOTSUP; + } + + return 0; +} + +static int iis2iclx_sample_fetch_accel(const struct device *dev) +{ + struct iis2iclx_data *data = dev->data; + int16_t buf[2]; + + if (iis2iclx_acceleration_raw_get(data->ctx, buf) < 0) { + LOG_ERR("Failed to read sample"); + return -EIO; + } + + data->acc[0] = sys_le16_to_cpu(buf[0]); + data->acc[1] = sys_le16_to_cpu(buf[1]); + + return 0; +} + +#if defined(CONFIG_IIS2ICLX_ENABLE_TEMP) +static int iis2iclx_sample_fetch_temp(const struct device *dev) +{ + struct iis2iclx_data *data = dev->data; + int16_t buf; + + if (iis2iclx_temperature_raw_get(data->ctx, &buf) < 0) { + LOG_ERR("Failed to read sample"); + return -EIO; + } + + data->temp_sample = sys_le16_to_cpu(buf); + + return 0; +} +#endif + +#if defined(CONFIG_IIS2ICLX_SENSORHUB) +static int iis2iclx_sample_fetch_shub(const struct device *dev) +{ + if (iis2iclx_shub_fetch_external_devs(dev) < 0) { + LOG_ERR("failed to read ext shub devices"); + return -EIO; + } + + return 0; +} +#endif /* CONFIG_IIS2ICLX_SENSORHUB */ + +static int iis2iclx_sample_fetch(const struct device *dev, + enum sensor_channel chan) +{ + switch (chan) { + case SENSOR_CHAN_ACCEL_XYZ: + iis2iclx_sample_fetch_accel(dev); +#if defined(CONFIG_IIS2ICLX_SENSORHUB) + iis2iclx_sample_fetch_shub(dev); +#endif + break; +#if defined(CONFIG_IIS2ICLX_ENABLE_TEMP) + case SENSOR_CHAN_DIE_TEMP: + iis2iclx_sample_fetch_temp(dev); + break; +#endif + case SENSOR_CHAN_ALL: + iis2iclx_sample_fetch_accel(dev); +#if defined(CONFIG_IIS2ICLX_ENABLE_TEMP) + iis2iclx_sample_fetch_temp(dev); +#endif +#if defined(CONFIG_IIS2ICLX_SENSORHUB) + iis2iclx_sample_fetch_shub(dev); +#endif + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static inline void iis2iclx_accel_convert(struct sensor_value *val, int raw_val, + uint32_t sensitivity) +{ + int64_t dval; + + /* Sensitivity is exposed in ug/LSB */ + /* Convert to m/s^2 */ + dval = (int64_t)(raw_val) * sensitivity * SENSOR_G_DOUBLE; + val->val1 = (int32_t)(dval / 1000000); + val->val2 = (int32_t)(dval % 1000000); + +} + +static inline int iis2iclx_accel_get_channel(enum sensor_channel chan, + struct sensor_value *val, + struct iis2iclx_data *data, + uint32_t sensitivity) +{ + uint8_t i; + + switch (chan) { + case SENSOR_CHAN_ACCEL_X: + iis2iclx_accel_convert(val, data->acc[0], sensitivity); + break; + case SENSOR_CHAN_ACCEL_Y: + iis2iclx_accel_convert(val, data->acc[1], sensitivity); + break; + case SENSOR_CHAN_ACCEL_XYZ: + for (i = 0; i < 2; i++) { + iis2iclx_accel_convert(val++, data->acc[i], sensitivity); + } + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static int iis2iclx_accel_channel_get(enum sensor_channel chan, + struct sensor_value *val, + struct iis2iclx_data *data) +{ + return iis2iclx_accel_get_channel(chan, val, data, data->acc_gain); +} + +#if defined(CONFIG_IIS2ICLX_ENABLE_TEMP) +static void iis2iclx_temp_channel_get(struct sensor_value *val, + struct iis2iclx_data *data) +{ + /* val = temp_sample / 256 + 25 */ + val->val1 = data->temp_sample / 256 + 25; + val->val2 = (data->temp_sample % 256) * (1000000 / 256); +} +#endif + +#if defined(CONFIG_IIS2ICLX_SENSORHUB) +static inline void iis2iclx_magn_convert(struct sensor_value *val, int raw_val, + uint16_t sensitivity) +{ + double dval; + + /* Sensitivity is exposed in mgauss/LSB */ + dval = (double)(raw_val * sensitivity); + val->val1 = (int32_t)dval / 1000000; + val->val2 = (int32_t)dval % 1000000; +} + +static inline int iis2iclx_magn_get_channel(enum sensor_channel chan, + struct sensor_value *val, + struct iis2iclx_data *data) +{ + int16_t sample[3]; + int idx; + + idx = iis2iclx_shub_get_idx(SENSOR_CHAN_MAGN_XYZ); + if (idx < 0) { + LOG_ERR("external magn not supported"); + return -ENOTSUP; + } + + + sample[0] = (int16_t)(data->ext_data[idx][0] | + (data->ext_data[idx][1] << 8)); + sample[1] = (int16_t)(data->ext_data[idx][2] | + (data->ext_data[idx][3] << 8)); + sample[2] = (int16_t)(data->ext_data[idx][4] | + (data->ext_data[idx][5] << 8)); + + switch (chan) { + case SENSOR_CHAN_MAGN_X: + iis2iclx_magn_convert(val, sample[0], data->magn_gain); + break; + case SENSOR_CHAN_MAGN_Y: + iis2iclx_magn_convert(val, sample[1], data->magn_gain); + break; + case SENSOR_CHAN_MAGN_Z: + iis2iclx_magn_convert(val, sample[2], data->magn_gain); + break; + case SENSOR_CHAN_MAGN_XYZ: + iis2iclx_magn_convert(val, sample[0], data->magn_gain); + iis2iclx_magn_convert(val + 1, sample[1], data->magn_gain); + iis2iclx_magn_convert(val + 2, sample[2], data->magn_gain); + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static inline void iis2iclx_hum_convert(struct sensor_value *val, + struct iis2iclx_data *data) +{ + float rh; + int16_t raw_val; + struct hts221_data *ht = &data->hts221; + int idx; + + idx = iis2iclx_shub_get_idx(SENSOR_CHAN_HUMIDITY); + if (idx < 0) { + LOG_DBG("external press/temp not supported"); + return; + } + + raw_val = ((int16_t)(data->ext_data[idx][0] | + (data->ext_data[idx][1] << 8))); + + /* find relative humidty by linear interpolation */ + rh = (ht->y1 - ht->y0) * raw_val + ht->x1 * ht->y0 - ht->x0 * ht->y1; + rh /= (ht->x1 - ht->x0); + + /* convert humidity to integer and fractional part */ + val->val1 = rh; + val->val2 = rh * 1000000; +} + +static inline void iis2iclx_press_convert(struct sensor_value *val, + struct iis2iclx_data *data) +{ + int32_t raw_val; + int idx; + + idx = iis2iclx_shub_get_idx(SENSOR_CHAN_PRESS); + if (idx < 0) { + LOG_DBG("external press/temp not supported"); + return; + } + + raw_val = (int32_t)(data->ext_data[idx][0] | + (data->ext_data[idx][1] << 8) | + (data->ext_data[idx][2] << 16)); + + /* Pressure sensitivity is 4096 LSB/hPa */ + /* Convert raw_val to val in kPa */ + val->val1 = (raw_val >> 12) / 10; + val->val2 = (raw_val >> 12) % 10 * 100000 + + (((int32_t)((raw_val) & 0x0FFF) * 100000L) >> 12); +} + +static inline void iis2iclx_temp_convert(struct sensor_value *val, + struct iis2iclx_data *data) +{ + int16_t raw_val; + int idx; + + idx = iis2iclx_shub_get_idx(SENSOR_CHAN_PRESS); + if (idx < 0) { + LOG_DBG("external press/temp not supported"); + return; + } + + raw_val = (int16_t)(data->ext_data[idx][3] | + (data->ext_data[idx][4] << 8)); + + /* Temperature sensitivity is 100 LSB/deg C */ + val->val1 = raw_val / 100; + val->val2 = (int32_t)raw_val % 100 * (10000); +} +#endif + +static int iis2iclx_channel_get(const struct device *dev, + enum sensor_channel chan, + struct sensor_value *val) +{ + struct iis2iclx_data *data = dev->data; + + switch (chan) { + case SENSOR_CHAN_ACCEL_X: + case SENSOR_CHAN_ACCEL_Y: + case SENSOR_CHAN_ACCEL_Z: + case SENSOR_CHAN_ACCEL_XYZ: + iis2iclx_accel_channel_get(chan, val, data); + break; +#if defined(CONFIG_IIS2ICLX_ENABLE_TEMP) + case SENSOR_CHAN_DIE_TEMP: + iis2iclx_temp_channel_get(val, data); + break; +#endif +#if defined(CONFIG_IIS2ICLX_SENSORHUB) + case SENSOR_CHAN_MAGN_X: + case SENSOR_CHAN_MAGN_Y: + case SENSOR_CHAN_MAGN_Z: + case SENSOR_CHAN_MAGN_XYZ: + iis2iclx_magn_get_channel(chan, val, data); + break; + + case SENSOR_CHAN_HUMIDITY: + iis2iclx_hum_convert(val, data); + break; + + case SENSOR_CHAN_PRESS: + iis2iclx_press_convert(val, data); + break; + + case SENSOR_CHAN_AMBIENT_TEMP: + iis2iclx_temp_convert(val, data); + break; +#endif + default: + return -ENOTSUP; + } + + return 0; +} + +static const struct sensor_driver_api iis2iclx_api_funcs = { + .attr_set = iis2iclx_attr_set, +#if CONFIG_IIS2ICLX_TRIGGER + .trigger_set = iis2iclx_trigger_set, +#endif + .sample_fetch = iis2iclx_sample_fetch, + .channel_get = iis2iclx_channel_get, +}; + +static int iis2iclx_init_chip(const struct device *dev) +{ + struct iis2iclx_data *iis2iclx = dev->data; + uint8_t chip_id; + + iis2iclx->dev = dev; + + if (iis2iclx_device_id_get(iis2iclx->ctx, &chip_id) < 0) { + LOG_ERR("Failed reading chip id"); + return -EIO; + } + + LOG_INF("chip id 0x%x", chip_id); + + if (chip_id != IIS2ICLX_ID) { + LOG_ERR("Invalid chip id 0x%x", chip_id); + return -EIO; + } + + /* reset device */ + if (iis2iclx_reset_set(iis2iclx->ctx, 1) < 0) { + return -EIO; + } + + k_usleep(100); + + if (iis2iclx_accel_set_fs_raw(dev, + IIS2ICLX_DEFAULT_ACCEL_FULLSCALE) < 0) { + LOG_ERR("failed to set accelerometer full-scale"); + return -EIO; + } + iis2iclx->acc_gain = IIS2ICLX_DEFAULT_ACCEL_SENSITIVITY; + + iis2iclx->accel_freq = iis2iclx_odr_to_freq_val(CONFIG_IIS2ICLX_ACCEL_ODR); + if (iis2iclx_accel_set_odr_raw(dev, CONFIG_IIS2ICLX_ACCEL_ODR) < 0) { + LOG_ERR("failed to set accelerometer sampling rate"); + return -EIO; + } + + /* Set FIFO bypass mode */ + if (iis2iclx_fifo_mode_set(iis2iclx->ctx, IIS2ICLX_BYPASS_MODE) < 0) { + LOG_ERR("failed to set FIFO mode"); + return -EIO; + } + + if (iis2iclx_block_data_update_set(iis2iclx->ctx, 1) < 0) { + LOG_ERR("failed to set BDU mode"); + return -EIO; + } + + return 0; +} + +static struct iis2iclx_data iis2iclx_data; + +static const struct iis2iclx_config iis2iclx_config = { + .bus_name = DT_INST_BUS_LABEL(0), +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) + .bus_init = iis2iclx_spi_init, + .spi_conf.frequency = DT_INST_PROP(0, 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_REG_ADDR(0), +#if DT_INST_SPI_DEV_HAS_CS_GPIOS(0) + .gpio_cs_port = DT_INST_SPI_DEV_CS_GPIOS_LABEL(0), + .cs_gpio = DT_INST_SPI_DEV_CS_GPIOS_PIN(0), + .cs_gpio_flags = DT_INST_SPI_DEV_CS_GPIOS_FLAGS(0), + + .spi_conf.cs = &iis2iclx_data.cs_ctrl, +#else + .spi_conf.cs = NULL, +#endif +#elif DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) + .bus_init = iis2iclx_i2c_init, + .i2c_slv_addr = DT_INST_REG_ADDR(0), +#else +#error "BUS MACRO NOT DEFINED IN DTS" +#endif +#ifdef CONFIG_IIS2ICLX_TRIGGER +#if DT_INST_PROP_HAS_IDX(0, drdy_gpios, 1) + /* Two gpio pins declared in DTS */ +#if defined(CONFIG_IIS2ICLX_INT_PIN_1) + .int_gpio_port = DT_INST_GPIO_LABEL_BY_IDX(0, drdy_gpios, 0), + .int_gpio_pin = DT_INST_GPIO_PIN_BY_IDX(0, drdy_gpios, 0), + .int_gpio_flags = DT_INST_GPIO_FLAGS_BY_IDX(0, drdy_gpios, 0), + .int_pin = 1, +#elif defined(CONFIG_IIS2ICLX_INT_PIN_2) + .int_gpio_port = DT_INST_GPIO_LABEL_BY_IDX(0, drdy_gpios, 1), + .int_gpio_pin = DT_INST_GPIO_PIN_BY_IDX(0, drdy_gpios, 1), + .int_gpio_flags = DT_INST_GPIO_FLAGS_BY_IDX(0, drdy_gpios, 1), + .int_pin = 2, +#endif /* CONFIG_IIS2ICLX_INT_PIN_* */ +#else + /* One gpio pin declared in DTS */ + .int_gpio_port = DT_INST_GPIO_LABEL(0, drdy_gpios), + .int_gpio_pin = DT_INST_GPIO_PIN(0, drdy_gpios), + .int_gpio_flags = DT_INST_GPIO_FLAGS(0, drdy_gpios), +#if defined(CONFIG_IIS2ICLX_INT_PIN_1) + .int_pin = 1, +#elif defined(CONFIG_IIS2ICLX_INT_PIN_2) + .int_pin = 2, +#endif /* CONFIG_IIS2ICLX_INT_PIN_* */ +#endif /* DT_INST_PROP_HAS_IDX(0, drdy_gpios, 1) */ + +#endif /* CONFIG_IIS2ICLX_TRIGGER */ +}; + +static int iis2iclx_init(const struct device *dev) +{ + const struct iis2iclx_config * const config = dev->config; + struct iis2iclx_data *data = dev->data; + + data->bus = device_get_binding(config->bus_name); + if (!data->bus) { + LOG_DBG("master not found: %s", + config->bus_name); + return -EINVAL; + } + + config->bus_init(dev); + + if (iis2iclx_init_chip(dev) < 0) { + LOG_ERR("failed to initialize chip"); + return -EIO; + } + +#ifdef CONFIG_IIS2ICLX_TRIGGER + if (iis2iclx_init_interrupt(dev) < 0) { + LOG_ERR("Failed to initialize interrupt."); + return -EIO; + } +#endif + +#ifdef CONFIG_IIS2ICLX_SENSORHUB + if (iis2iclx_shub_init(dev) < 0) { + LOG_ERR("failed to initialize external chip"); + return -EIO; + } +#endif + + return 0; +} + + +static struct iis2iclx_data iis2iclx_data; + +DEVICE_AND_API_INIT(iis2iclx, DT_INST_LABEL(0), iis2iclx_init, + &iis2iclx_data, &iis2iclx_config, POST_KERNEL, + CONFIG_SENSOR_INIT_PRIORITY, &iis2iclx_api_funcs); diff --git a/drivers/sensor/iis2iclx/iis2iclx.h b/drivers/sensor/iis2iclx/iis2iclx.h new file mode 100644 index 00000000000..d4cbd047171 --- /dev/null +++ b/drivers/sensor/iis2iclx/iis2iclx.h @@ -0,0 +1,162 @@ +/* ST Microelectronics IIS2ICLX 2-axis accelerometer sensor driver + * + * Copyright (c) 2020 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/iis2iclx.pdf + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_IIS2ICLX_IIS2ICLX_H_ +#define ZEPHYR_DRIVERS_SENSOR_IIS2ICLX_IIS2ICLX_H_ + +#include +#include +#include +#include +#include +#include "iis2iclx_reg.h" + +#define IIS2ICLX_EN_BIT 0x01 +#define IIS2ICLX_DIS_BIT 0x00 + +/* Accel sensor sensitivity grain is 15 ug/LSB */ +#define GAIN_UNIT_XL (15LL) + +#define SENSOR_PI_DOUBLE (SENSOR_PI / 1000000.0) +#define SENSOR_DEG2RAD_DOUBLE (SENSOR_PI_DOUBLE / 180) +#define SENSOR_G_DOUBLE (SENSOR_G / 1000000.0) + +#if CONFIG_IIS2ICLX_ACCEL_FS == 0 + #define IIS2ICLX_ACCEL_FS_RUNTIME 1 + #define IIS2ICLX_DEFAULT_ACCEL_FULLSCALE 0 + #define IIS2ICLX_DEFAULT_ACCEL_SENSITIVITY GAIN_UNIT_XL +#elif CONFIG_IIS2ICLX_ACCEL_FS == 500 + #define IIS2ICLX_DEFAULT_ACCEL_FULLSCALE 0 + #define IIS2ICLX_DEFAULT_ACCEL_SENSITIVITY GAIN_UNIT_XL +#elif CONFIG_IIS2ICLX_ACCEL_FS == 1000 + #define IIS2ICLX_DEFAULT_ACCEL_FULLSCALE 2 + #define IIS2ICLX_DEFAULT_ACCEL_SENSITIVITY (2.0 * GAIN_UNIT_XL) +#elif CONFIG_IIS2ICLX_ACCEL_FS == 2000 + #define IIS2ICLX_DEFAULT_ACCEL_FULLSCALE 3 + #define IIS2ICLX_DEFAULT_ACCEL_SENSITIVITY (4.0 * GAIN_UNIT_XL) +#elif CONFIG_IIS2ICLX_ACCEL_FS == 3000 + #define IIS2ICLX_DEFAULT_ACCEL_FULLSCALE 1 + #define IIS2ICLX_DEFAULT_ACCEL_SENSITIVITY (8.0 * GAIN_UNIT_XL) +#endif + +#if (CONFIG_IIS2ICLX_ACCEL_ODR == 0) +#define IIS2ICLX_ACCEL_ODR_RUNTIME 1 +#endif + +struct iis2iclx_config { + char *bus_name; + int (*bus_init)(const struct device *dev); +#ifdef CONFIG_IIS2ICLX_TRIGGER + const char *int_gpio_port; + uint8_t int_gpio_pin; + uint8_t int_gpio_flags; + uint8_t int_pin; +#endif /* CONFIG_IIS2ICLX_TRIGGER */ +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) + uint16_t i2c_slv_addr; +#elif DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) + struct spi_config spi_conf; +#if DT_INST_SPI_DEV_HAS_CS_GPIOS(0) + const char *gpio_cs_port; + uint8_t cs_gpio; + uint8_t cs_gpio_flags; +#endif /* DT_INST_SPI_DEV_HAS_CS_GPIOS(0) */ +#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */ +}; + +/* sensor data forward declaration (member definition is below) */ +struct iis2iclx_data; + +struct iis2iclx_tf { + int (*read_data)(struct iis2iclx_data *data, uint8_t reg_addr, + uint8_t *value, uint8_t len); + int (*write_data)(struct iis2iclx_data *data, uint8_t reg_addr, + uint8_t *value, uint8_t len); + int (*read_reg)(struct iis2iclx_data *data, uint8_t reg_addr, + uint8_t *value); + int (*write_reg)(struct iis2iclx_data *data, uint8_t reg_addr, + uint8_t value); + int (*update_reg)(struct iis2iclx_data *data, uint8_t reg_addr, + uint8_t mask, uint8_t value); +}; + +#define IIS2ICLX_SHUB_MAX_NUM_SLVS 2 + +struct iis2iclx_data { + const struct device *dev; + const struct device *bus; + int16_t acc[2]; + uint32_t acc_gain; +#if defined(CONFIG_IIS2ICLX_ENABLE_TEMP) + int temp_sample; +#endif +#if defined(CONFIG_IIS2ICLX_SENSORHUB) + uint8_t ext_data[2][6]; + uint16_t magn_gain; + + struct hts221_data { + int16_t x0; + int16_t x1; + int16_t y0; + int16_t y1; + } hts221; +#endif /* CONFIG_IIS2ICLX_SENSORHUB */ + + stmdev_ctx_t *ctx; + + #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) + stmdev_ctx_t ctx_i2c; + #elif DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) + stmdev_ctx_t ctx_spi; + #endif + + uint16_t accel_freq; + uint8_t accel_fs; + +#ifdef CONFIG_IIS2ICLX_TRIGGER + const struct device *gpio; + struct gpio_callback gpio_cb; + sensor_trigger_handler_t handler_drdy_acc; + sensor_trigger_handler_t handler_drdy_temp; + +#if defined(CONFIG_IIS2ICLX_TRIGGER_OWN_THREAD) + K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_IIS2ICLX_THREAD_STACK_SIZE); + struct k_thread thread; + struct k_sem gpio_sem; +#elif defined(CONFIG_IIS2ICLX_TRIGGER_GLOBAL_THREAD) + struct k_work work; +#endif +#endif /* CONFIG_IIS2ICLX_TRIGGER */ + +#if DT_INST_SPI_DEV_HAS_CS_GPIOS(0) + struct spi_cs_control cs_ctrl; +#endif +}; + +int iis2iclx_spi_init(const struct device *dev); +int iis2iclx_i2c_init(const struct device *dev); +#if defined(CONFIG_IIS2ICLX_SENSORHUB) +int iis2iclx_shub_init(const struct device *dev); +int iis2iclx_shub_fetch_external_devs(const struct device *dev); +int iis2iclx_shub_get_idx(enum sensor_channel type); +int iis2iclx_shub_config(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val); +#endif /* CONFIG_IIS2ICLX_SENSORHUB */ + +#ifdef CONFIG_IIS2ICLX_TRIGGER +int iis2iclx_trigger_set(const struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler); + +int iis2iclx_init_interrupt(const struct device *dev); +#endif + +#endif /* ZEPHYR_DRIVERS_SENSOR_IIS2ICLX_IIS2ICLX_H_ */ diff --git a/drivers/sensor/iis2iclx/iis2iclx_i2c.c b/drivers/sensor/iis2iclx/iis2iclx_i2c.c new file mode 100644 index 00000000000..19afe2add5f --- /dev/null +++ b/drivers/sensor/iis2iclx/iis2iclx_i2c.c @@ -0,0 +1,53 @@ +/* ST Microelectronics IIS2ICLX 2-axis accelerometer sensor driver + * + * Copyright (c) 2020 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/iis2iclx.pdf + */ + +#define DT_DRV_COMPAT st_iis2iclx + +#include +#include +#include + +#include "iis2iclx.h" + +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) + +LOG_MODULE_DECLARE(IIS2ICLX, CONFIG_SENSOR_LOG_LEVEL); + +static int iis2iclx_i2c_read(struct iis2iclx_data *data, uint8_t reg_addr, + uint8_t *value, uint8_t len) +{ + const struct iis2iclx_config *cfg = data->dev->config; + + return i2c_burst_read(data->bus, cfg->i2c_slv_addr, + reg_addr, value, len); +} + +static int iis2iclx_i2c_write(struct iis2iclx_data *data, uint8_t reg_addr, + uint8_t *value, uint8_t len) +{ + const struct iis2iclx_config *cfg = data->dev->config; + + return i2c_burst_write(data->bus, cfg->i2c_slv_addr, + reg_addr, value, len); +} + +int iis2iclx_i2c_init(const struct device *dev) +{ + struct iis2iclx_data *data = dev->data; + + data->ctx_i2c.read_reg = (stmdev_read_ptr) iis2iclx_i2c_read, + data->ctx_i2c.write_reg = (stmdev_write_ptr) iis2iclx_i2c_write, + + data->ctx = &data->ctx_i2c; + data->ctx->handle = data; + + return 0; +} +#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */ diff --git a/drivers/sensor/iis2iclx/iis2iclx_shub.c b/drivers/sensor/iis2iclx/iis2iclx_shub.c new file mode 100644 index 00000000000..c7e31834dba --- /dev/null +++ b/drivers/sensor/iis2iclx/iis2iclx_shub.c @@ -0,0 +1,793 @@ +/* ST Microelectronics IIS2ICLX 2-axis accelerometer sensor driver + * + * Copyright (c) 2020 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/iis2iclx.pdf + */ + +#define DT_DRV_COMPAT st_iis2iclx + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iis2iclx.h" + +LOG_MODULE_DECLARE(IIS2ICLX, CONFIG_SENSOR_LOG_LEVEL); + +#define IIS2ICLX_SHUB_DATA_OUT 0x02 + +#define IIS2ICLX_SHUB_SLV0_ADDR 0x15 +#define IIS2ICLX_SHUB_SLV0_SUBADDR 0x16 +#define IIS2ICLX_SHUB_SLV0_CONFIG 0x17 +#define IIS2ICLX_SHUB_SLV1_ADDR 0x18 +#define IIS2ICLX_SHUB_SLV1_SUBADDR 0x19 +#define IIS2ICLX_SHUB_SLV1_CONFIG 0x1A +#define IIS2ICLX_SHUB_SLV2_ADDR 0x1B +#define IIS2ICLX_SHUB_SLV2_SUBADDR 0x1C +#define IIS2ICLX_SHUB_SLV2_CONFIG 0x1D +#define IIS2ICLX_SHUB_SLV3_ADDR 0x1E +#define IIS2ICLX_SHUB_SLV3_SUBADDR 0x1F +#define IIS2ICLX_SHUB_SLV3_CONFIG 0x20 +#define IIS2ICLX_SHUB_SLV0_DATAWRITE 0x21 + +#define IIS2ICLX_SHUB_STATUS_MASTER 0x22 +#define IIS2ICLX_SHUB_STATUS_SLV0_NACK BIT(3) +#define IIS2ICLX_SHUB_STATUS_ENDOP BIT(0) + +#define IIS2ICLX_SHUB_SLVX_WRITE 0x0 +#define IIS2ICLX_SHUB_SLVX_READ 0x1 + +static uint8_t num_ext_dev; +static uint8_t shub_ext[IIS2ICLX_SHUB_MAX_NUM_SLVS]; + +static int iis2iclx_shub_write_slave_reg(struct iis2iclx_data *data, + uint8_t slv_addr, uint8_t slv_reg, + uint8_t *value, uint16_t len); +static int iis2iclx_shub_read_slave_reg(struct iis2iclx_data *data, + uint8_t slv_addr, uint8_t slv_reg, + uint8_t *value, uint16_t len); +static void iis2iclx_shub_enable(struct iis2iclx_data *data, uint8_t enable); + +/* + * LIS2MDL magn device specific part + */ +#if defined(CONFIG_IIS2ICLX_EXT_LIS2MDL) || defined(CONFIG_IIS2ICLX_EXT_IIS2MDC) + +#define LIS2MDL_CFG_REG_A 0x60 +#define LIS2MDL_CFG_REG_B 0x61 +#define LIS2MDL_CFG_REG_C 0x62 +#define LIS2MDL_STATUS_REG 0x67 + +#define LIS2MDL_SW_RESET 0x20 +#define LIS2MDL_ODR_10HZ 0x00 +#define LIS2MDL_ODR_100HZ 0x0C +#define LIS2MDL_OFF_CANC 0x02 +#define LIS2MDL_SENSITIVITY 1500 + +static int iis2iclx_lis2mdl_init(struct iis2iclx_data *data, uint8_t i2c_addr) +{ + uint8_t mag_cfg[2]; + + data->magn_gain = LIS2MDL_SENSITIVITY; + + /* sw reset device */ + mag_cfg[0] = LIS2MDL_SW_RESET; + iis2iclx_shub_write_slave_reg(data, i2c_addr, + LIS2MDL_CFG_REG_A, mag_cfg, 1); + + k_sleep(K_MSEC(10)); /* turn-on time in ms */ + + /* configure mag */ + mag_cfg[0] = LIS2MDL_ODR_10HZ; + mag_cfg[1] = LIS2MDL_OFF_CANC; + iis2iclx_shub_write_slave_reg(data, i2c_addr, + LIS2MDL_CFG_REG_A, mag_cfg, 2); + + return 0; +} + +static const uint16_t lis2mdl_map[] = {10, 20, 50, 100}; + +static int iis2iclx_lis2mdl_odr_set(struct iis2iclx_data *data, + uint8_t i2c_addr, uint16_t freq) +{ + uint8_t odr, cfg; + + for (odr = 0; odr < ARRAY_SIZE(lis2mdl_map); odr++) { + if (freq == lis2mdl_map[odr]) { + break; + } + } + + if (odr == ARRAY_SIZE(lis2mdl_map)) { + LOG_ERR("shub: LIS2MDL freq val %d not supported.", freq); + return -ENOTSUP; + } + + cfg = (odr << 2); + iis2iclx_shub_write_slave_reg(data, i2c_addr, + LIS2MDL_CFG_REG_A, &cfg, 1); + + iis2iclx_shub_enable(data, 1); + return 0; +} + +static int iis2iclx_lis2mdl_conf(struct iis2iclx_data *data, uint8_t i2c_addr, + enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + switch (attr) { + case SENSOR_ATTR_SAMPLING_FREQUENCY: + return iis2iclx_lis2mdl_odr_set(data, i2c_addr, val->val1); + default: + LOG_ERR("shub: LIS2MDL attribute not supported."); + return -ENOTSUP; + } + + return 0; +} +#endif /* CONFIG_IIS2ICLX_EXT_LIS2MDL || CONFIG_IIS2ICLX_EXT_IIS2MDC */ + +/* + * HTS221 humidity device specific part + */ +#ifdef CONFIG_IIS2ICLX_EXT_HTS221 + +#define HTS221_AUTOINCREMENT BIT(7) + +#define HTS221_REG_CTRL1 0x20 +#define HTS221_ODR_1HZ 0x01 +#define HTS221_BDU 0x04 +#define HTS221_PD 0x80 + +#define HTS221_REG_CONV_START 0x30 + +static int lsmdso_hts221_read_conv_data(struct iis2iclx_data *data, + uint8_t i2c_addr) +{ + uint8_t buf[16], i; + struct hts221_data *ht = &data->hts221; + + for (i = 0; i < sizeof(buf); i += 7) { + unsigned char len = MIN(7, sizeof(buf) - i); + + if (iis2iclx_shub_read_slave_reg(data, i2c_addr, + (HTS221_REG_CONV_START + i) | + HTS221_AUTOINCREMENT, + &buf[i], len) < 0) { + LOG_ERR("shub: failed to read hts221 conv data"); + return -EIO; + } + } + + ht->y0 = buf[0] / 2; + ht->y1 = buf[1] / 2; + ht->x0 = sys_le16_to_cpu(buf[6] | (buf[7] << 8)); + ht->x1 = sys_le16_to_cpu(buf[10] | (buf[11] << 8)); + + return 0; +} + +static int iis2iclx_hts221_init(struct iis2iclx_data *data, uint8_t i2c_addr) +{ + uint8_t hum_cfg; + + /* configure ODR and BDU */ + hum_cfg = HTS221_ODR_1HZ | HTS221_BDU | HTS221_PD; + iis2iclx_shub_write_slave_reg(data, i2c_addr, + HTS221_REG_CTRL1, &hum_cfg, 1); + + return lsmdso_hts221_read_conv_data(data, i2c_addr); +} + +static const uint16_t hts221_map[] = {0, 1, 7, 12}; + +static int iis2iclx_hts221_odr_set(struct iis2iclx_data *data, + uint8_t i2c_addr, uint16_t freq) +{ + uint8_t odr, cfg; + + for (odr = 0; odr < ARRAY_SIZE(hts221_map); odr++) { + if (freq == hts221_map[odr]) { + break; + } + } + + if (odr == ARRAY_SIZE(hts221_map)) { + LOG_ERR("shub: HTS221 freq val %d not supported.", freq); + return -ENOTSUP; + } + + cfg = odr | HTS221_BDU | HTS221_PD; + iis2iclx_shub_write_slave_reg(data, i2c_addr, + HTS221_REG_CTRL1, &cfg, 1); + + iis2iclx_shub_enable(data, 1); + return 0; +} + +static int iis2iclx_hts221_conf(struct iis2iclx_data *data, uint8_t i2c_addr, + enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + switch (attr) { + case SENSOR_ATTR_SAMPLING_FREQUENCY: + return iis2iclx_hts221_odr_set(data, i2c_addr, val->val1); + default: + LOG_ERR("shub: HTS221 attribute not supported."); + return -ENOTSUP; + } + + return 0; +} +#endif /* CONFIG_IIS2ICLX_EXT_HTS221 */ + +/* + * LPS22HB baro/temp device specific part + */ +#ifdef CONFIG_IIS2ICLX_EXT_LPS22HB + +#define LPS22HB_CTRL_REG1 0x10 +#define LPS22HB_CTRL_REG2 0x11 + +#define LPS22HB_SW_RESET 0x04 +#define LPS22HB_ODR_10HZ 0x20 +#define LPS22HB_LPF_EN 0x08 +#define LPS22HB_BDU_EN 0x02 + +static int iis2iclx_lps22hb_init(struct iis2iclx_data *data, uint8_t i2c_addr) +{ + uint8_t baro_cfg[2]; + + /* sw reset device */ + baro_cfg[0] = LPS22HB_SW_RESET; + iis2iclx_shub_write_slave_reg(data, i2c_addr, + LPS22HB_CTRL_REG2, baro_cfg, 1); + + k_sleep(K_MSEC(1)); /* turn-on time in ms */ + + /* configure device */ + baro_cfg[0] = LPS22HB_ODR_10HZ | LPS22HB_LPF_EN | LPS22HB_BDU_EN; + iis2iclx_shub_write_slave_reg(data, i2c_addr, + LPS22HB_CTRL_REG1, baro_cfg, 1); + + return 0; +} +#endif /* CONFIG_IIS2ICLX_EXT_LPS22HB */ + +/* + * LPS22HH baro/temp device specific part + */ +#ifdef CONFIG_IIS2ICLX_EXT_LPS22HH + +#define LPS22HH_CTRL_REG1 0x10 +#define LPS22HH_CTRL_REG2 0x11 + +#define LPS22HH_SW_RESET 0x04 +#define LPS22HH_IF_ADD_INC 0x10 +#define LPS22HH_ODR_10HZ 0x20 +#define LPS22HH_LPF_EN 0x08 +#define LPS22HH_BDU_EN 0x02 + +static int iis2iclx_lps22hh_init(struct iis2iclx_data *data, uint8_t i2c_addr) +{ + uint8_t baro_cfg[2]; + + /* sw reset device */ + baro_cfg[0] = LPS22HH_SW_RESET; + iis2iclx_shub_write_slave_reg(data, i2c_addr, + LPS22HH_CTRL_REG2, baro_cfg, 1); + + k_sleep(K_MSEC(100)); /* turn-on time in ms */ + + /* configure device */ + baro_cfg[0] = LPS22HH_IF_ADD_INC; + iis2iclx_shub_write_slave_reg(data, i2c_addr, + LPS22HH_CTRL_REG2, baro_cfg, 1); + + baro_cfg[0] = LPS22HH_ODR_10HZ | LPS22HH_LPF_EN | LPS22HH_BDU_EN; + iis2iclx_shub_write_slave_reg(data, i2c_addr, + LPS22HH_CTRL_REG1, baro_cfg, 1); + + return 0; +} + +static const uint16_t lps22hh_map[] = {0, 1, 10, 25, 50, 75, 100, 200}; + +static int iis2iclx_lps22hh_odr_set(struct iis2iclx_data *data, + uint8_t i2c_addr, uint16_t freq) +{ + uint8_t odr, cfg; + + for (odr = 0; odr < ARRAY_SIZE(lps22hh_map); odr++) { + if (freq == lps22hh_map[odr]) { + break; + } + } + + if (odr == ARRAY_SIZE(lps22hh_map)) { + LOG_ERR("shub: LPS22HH freq val %d not supported.", freq); + return -ENOTSUP; + } + + cfg = (odr << 4) | LPS22HH_LPF_EN | LPS22HH_BDU_EN; + iis2iclx_shub_write_slave_reg(data, i2c_addr, + LPS22HH_CTRL_REG1, &cfg, 1); + + iis2iclx_shub_enable(data, 1); + return 0; +} + +static int iis2iclx_lps22hh_conf(struct iis2iclx_data *data, uint8_t i2c_addr, + enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + switch (attr) { + case SENSOR_ATTR_SAMPLING_FREQUENCY: + return iis2iclx_lps22hh_odr_set(data, i2c_addr, val->val1); + default: + LOG_ERR("shub: LPS22HH attribute not supported."); + return -ENOTSUP; + } + + return 0; +} +#endif /* CONFIG_IIS2ICLX_EXT_LPS22HH */ + +/* List of supported external sensors */ +static struct iis2iclx_shub_slist { + enum sensor_channel type; + uint8_t i2c_addr[2]; + uint8_t ext_i2c_addr; + uint8_t wai_addr; + uint8_t wai_val; + uint8_t out_data_addr; + uint8_t out_data_len; + uint8_t sh_out_reg; + int (*dev_init)(struct iis2iclx_data *data, uint8_t i2c_addr); + int (*dev_conf)(struct iis2iclx_data *data, uint8_t i2c_addr, + enum sensor_channel chan, enum sensor_attribute attr, + const struct sensor_value *val); +} iis2iclx_shub_slist[] = { +#if defined(CONFIG_IIS2ICLX_EXT_LIS2MDL) || defined(CONFIG_IIS2ICLX_EXT_IIS2MDC) + { + /* LIS2MDL */ + .type = SENSOR_CHAN_MAGN_XYZ, + .i2c_addr = { 0x1E }, + .wai_addr = 0x4F, + .wai_val = 0x40, + .out_data_addr = 0x68, + .out_data_len = 0x06, + .dev_init = (iis2iclx_lis2mdl_init), + .dev_conf = (iis2iclx_lis2mdl_conf), + }, +#endif /* CONFIG_IIS2ICLX_EXT_LIS2MDL || CONFIG_IIS2ICLX_EXT_IIS2MDC */ + +#ifdef CONFIG_IIS2ICLX_EXT_HTS221 + { + /* HTS221 */ + .type = SENSOR_CHAN_HUMIDITY, + .i2c_addr = { 0x5F }, + .wai_addr = 0x0F, + .wai_val = 0xBC, + .out_data_addr = 0x28 | HTS221_AUTOINCREMENT, + .out_data_len = 0x02, + .dev_init = (iis2iclx_hts221_init), + .dev_conf = (iis2iclx_hts221_conf), + }, +#endif /* CONFIG_IIS2ICLX_EXT_HTS221 */ + +#ifdef CONFIG_IIS2ICLX_EXT_LPS22HB + { + /* LPS22HB */ + .type = SENSOR_CHAN_PRESS, + .i2c_addr = { 0x5C, 0x5D }, + .wai_addr = 0x0F, + .wai_val = 0xB1, + .out_data_addr = 0x28, + .out_data_len = 0x05, + .dev_init = (iis2iclx_lps22hb_init), + }, +#endif /* CONFIG_IIS2ICLX_EXT_LPS22HB */ + +#ifdef CONFIG_IIS2ICLX_EXT_LPS22HH + { + /* LPS22HH */ + .type = SENSOR_CHAN_PRESS, + .i2c_addr = { 0x5C, 0x5D }, + .wai_addr = 0x0F, + .wai_val = 0xB3, + .out_data_addr = 0x28, + .out_data_len = 0x05, + .dev_init = (iis2iclx_lps22hh_init), + .dev_conf = (iis2iclx_lps22hh_conf), + }, +#endif /* CONFIG_IIS2ICLX_EXT_LPS22HH */ +}; + +static inline void iis2iclx_shub_wait_completed(struct iis2iclx_data *data) +{ + iis2iclx_status_master_t status; + + do { + k_msleep(1); + iis2iclx_sh_status_get(data->ctx, &status); + } while (status.sens_hub_endop == 0); +} + +static inline void iis2iclx_shub_embedded_en(struct iis2iclx_data *data, bool on) +{ + if (on) { + (void) iis2iclx_mem_bank_set(data->ctx, IIS2ICLX_SENSOR_HUB_BANK); + } else { + (void) iis2iclx_mem_bank_set(data->ctx, IIS2ICLX_USER_BANK); + } + + k_busy_wait(150); +} + +static int iis2iclx_shub_read_embedded_regs(struct iis2iclx_data *data, + uint8_t reg_addr, + uint8_t *value, int len) +{ + iis2iclx_shub_embedded_en(data, true); + + if (iis2iclx_read_reg(data->ctx, reg_addr, value, len) < 0) { + LOG_ERR("shub: failed to read external reg: %02x", reg_addr); + iis2iclx_shub_embedded_en(data, false); + return -EIO; + } + + iis2iclx_shub_embedded_en(data, false); + + return 0; +} + +static int iis2iclx_shub_write_embedded_regs(struct iis2iclx_data *data, + uint8_t reg_addr, + uint8_t *value, uint8_t len) +{ + iis2iclx_shub_embedded_en(data, true); + + if (iis2iclx_write_reg(data->ctx, reg_addr, value, len) < 0) { + LOG_ERR("shub: failed to write external reg: %02x", reg_addr); + iis2iclx_shub_embedded_en(data, false); + return -EIO; + } + + iis2iclx_shub_embedded_en(data, false); + + return 0; +} + +static void iis2iclx_shub_enable(struct iis2iclx_data *data, uint8_t enable) +{ + /* Enable Accel @26hz */ + if (!data->accel_freq) { + uint8_t odr = (enable) ? 2 : 0; + + if (iis2iclx_xl_data_rate_set(data->ctx, odr) < 0) { + LOG_DBG("shub: failed to set XL sampling rate"); + return; + } + } + + iis2iclx_shub_embedded_en(data, true); + + if (iis2iclx_sh_master_set(data->ctx, enable) < 0) { + LOG_DBG("shub: failed to set master on"); + iis2iclx_shub_embedded_en(data, false); + return; + } + + iis2iclx_shub_embedded_en(data, false); +} + +/* must be called with master on */ +static int iis2iclx_shub_check_slv0_nack(struct iis2iclx_data *data) +{ + uint8_t status; + + if (iis2iclx_shub_read_embedded_regs(data, IIS2ICLX_SHUB_STATUS_MASTER, + &status, 1) < 0) { + LOG_ERR("shub: error reading embedded reg"); + return -EIO; + } + + if (status & (IIS2ICLX_SHUB_STATUS_SLV0_NACK)) { + LOG_ERR("shub: SLV0 nacked"); + return -EIO; + } + + return 0; +} + +/* + * use SLV0 for generic read to slave device + */ +static int iis2iclx_shub_read_slave_reg(struct iis2iclx_data *data, + uint8_t slv_addr, uint8_t slv_reg, + uint8_t *value, uint16_t len) +{ + uint8_t slave[3]; + + slave[0] = (slv_addr << 1) | IIS2ICLX_SHUB_SLVX_READ; + slave[1] = slv_reg; + slave[2] = (len & 0x7); + + if (iis2iclx_shub_write_embedded_regs(data, IIS2ICLX_SHUB_SLV0_ADDR, + slave, 3) < 0) { + LOG_ERR("shub: error writing embedded reg"); + return -EIO; + } + + /* turn SH on */ + iis2iclx_shub_enable(data, 1); + iis2iclx_shub_wait_completed(data); + + if (iis2iclx_shub_check_slv0_nack(data) < 0) { + iis2iclx_shub_enable(data, 0); + return -EIO; + } + + /* read data from external slave */ + iis2iclx_shub_embedded_en(data, true); + if (iis2iclx_read_reg(data->ctx, IIS2ICLX_SHUB_DATA_OUT, + value, len) < 0) { + LOG_ERR("shub: error reading sensor data"); + iis2iclx_shub_embedded_en(data, false); + return -EIO; + } + iis2iclx_shub_embedded_en(data, false); + + iis2iclx_shub_enable(data, 0); + return 0; +} + +/* + * use SLV0 to configure slave device + */ +static int iis2iclx_shub_write_slave_reg(struct iis2iclx_data *data, + uint8_t slv_addr, uint8_t slv_reg, + uint8_t *value, uint16_t len) +{ + uint8_t slv_cfg[3]; + uint8_t cnt = 0U; + + while (cnt < len) { + slv_cfg[0] = (slv_addr << 1) & ~IIS2ICLX_SHUB_SLVX_READ; + slv_cfg[1] = slv_reg + cnt; + + if (iis2iclx_shub_write_embedded_regs(data, + IIS2ICLX_SHUB_SLV0_ADDR, + slv_cfg, 2) < 0) { + LOG_ERR("shub: error writing embedded reg"); + return -EIO; + } + + slv_cfg[0] = value[cnt]; + if (iis2iclx_shub_write_embedded_regs(data, + IIS2ICLX_SHUB_SLV0_DATAWRITE, + slv_cfg, 1) < 0) { + LOG_ERR("shub: error writing embedded reg"); + return -EIO; + } + + /* turn SH on */ + iis2iclx_shub_enable(data, 1); + iis2iclx_shub_wait_completed(data); + + if (iis2iclx_shub_check_slv0_nack(data) < 0) { + iis2iclx_shub_enable(data, 0); + return -EIO; + } + + iis2iclx_shub_enable(data, 0); + + cnt++; + } + + /* Put SLV0 in IDLE mode */ + slv_cfg[0] = 0x7; + slv_cfg[1] = 0x0; + slv_cfg[2] = 0x0; + if (iis2iclx_shub_write_embedded_regs(data, IIS2ICLX_SHUB_SLV0_ADDR, + slv_cfg, 3) < 0) { + LOG_ERR("shub: error writing embedded reg"); + return -EIO; + } + + return 0; +} + +/* + * SLAVEs configurations: + * + * - SLAVE 0: used for configuring all slave devices + * - SLAVE 1: used as data read channel for external slave device #1 + * - SLAVE 2: used as data read channel for external slave device #2 + * - SLAVE 3: used for generic reads while data channel is enabled + */ +static int iis2iclx_shub_set_data_channel(struct iis2iclx_data *data) +{ + uint8_t n, i, slv_cfg[6]; + struct iis2iclx_shub_slist *sp; + + /* Set data channel for slave devices */ + for (n = 0; n < num_ext_dev; n++) { + sp = &iis2iclx_shub_slist[shub_ext[n]]; + + i = n * 3; + slv_cfg[i] = (sp->ext_i2c_addr << 1) | IIS2ICLX_SHUB_SLVX_READ; + slv_cfg[i + 1] = sp->out_data_addr; + slv_cfg[i + 2] = sp->out_data_len; + } + + if (iis2iclx_shub_write_embedded_regs(data, + IIS2ICLX_SHUB_SLV1_ADDR, + slv_cfg, n*3) < 0) { + LOG_ERR("shub: error writing embedded reg"); + return -EIO; + } + + /* Configure the master */ + iis2iclx_aux_sens_on_t aux = IIS2ICLX_SLV_0_1_2; + + if (iis2iclx_sh_slave_connected_set(data->ctx, aux) < 0) { + LOG_ERR("shub: error setting aux sensors"); + return -EIO; + } + + iis2iclx_write_once_t wo = IIS2ICLX_ONLY_FIRST_CYCLE; + + if (iis2iclx_sh_write_mode_set(data->ctx, wo) < 0) { + LOG_ERR("shub: error setting write once"); + return -EIO; + } + + + /* turn SH on */ + iis2iclx_shub_enable(data, 1); + iis2iclx_shub_wait_completed(data); + + return 0; +} + +int iis2iclx_shub_get_idx(enum sensor_channel type) +{ + uint8_t n; + struct iis2iclx_shub_slist *sp; + + for (n = 0; n < num_ext_dev; n++) { + sp = &iis2iclx_shub_slist[shub_ext[n]]; + + if (sp->type == type) + return n; + } + + return -ENOTSUP; +} + +int iis2iclx_shub_fetch_external_devs(const struct device *dev) +{ + uint8_t n; + struct iis2iclx_data *data = dev->data; + struct iis2iclx_shub_slist *sp; + + /* read data from external slave */ + iis2iclx_shub_embedded_en(data, true); + + for (n = 0; n < num_ext_dev; n++) { + sp = &iis2iclx_shub_slist[shub_ext[n]]; + + if (iis2iclx_read_reg(data->ctx, sp->sh_out_reg, + data->ext_data[n], sp->out_data_len) < 0) { + LOG_ERR("shub: failed to read sample"); + iis2iclx_shub_embedded_en(data, false); + return -EIO; + } + } + + iis2iclx_shub_embedded_en(data, false); + + return 0; +} + +int iis2iclx_shub_config(const struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + struct iis2iclx_data *data = dev->data; + struct iis2iclx_shub_slist *sp = NULL; + uint8_t n; + + for (n = 0; n < num_ext_dev; n++) { + sp = &iis2iclx_shub_slist[shub_ext[n]]; + + if (sp->type == chan) + break; + } + + if (n == num_ext_dev) { + LOG_ERR("shub: chan not supported"); + return -ENOTSUP; + } + + if (sp == NULL || sp->dev_conf == NULL) { + LOG_ERR("shub: chan not configurable"); + return -ENOTSUP; + } + + return sp->dev_conf(data, sp->ext_i2c_addr, chan, attr, val); +} + +int iis2iclx_shub_init(const struct device *dev) +{ + struct iis2iclx_data *data = dev->data; + uint8_t i, n = 0, regn; + uint8_t chip_id; + struct iis2iclx_shub_slist *sp; + + for (n = 0; n < ARRAY_SIZE(iis2iclx_shub_slist); n++) { + if (num_ext_dev >= IIS2ICLX_SHUB_MAX_NUM_SLVS) + break; + + chip_id = 0; + sp = &iis2iclx_shub_slist[n]; + + /* + * The external sensor may have different I2C address. + * So, try them one by one until we read the correct + * chip ID. + */ + for (i = 0U; i < ARRAY_SIZE(sp->i2c_addr); i++) { + if (iis2iclx_shub_read_slave_reg(data, + sp->i2c_addr[i], + sp->wai_addr, + &chip_id, 1) < 0) { + continue; + } + if (chip_id == sp->wai_val) { + break; + } + } + + if (i >= ARRAY_SIZE(sp->i2c_addr)) { + LOG_DBG("shub: invalid chip id 0x%x", chip_id); + continue; + } + LOG_INF("shub: Ext Device Chip Id: 0x%02x", chip_id); + sp->ext_i2c_addr = sp->i2c_addr[i]; + + shub_ext[num_ext_dev++] = n; + } + + if (num_ext_dev == 0) { + LOG_ERR("shub: no slave devices found"); + return -EINVAL; + } + + /* init external devices */ + for (n = 0, regn = 0; n < num_ext_dev; n++) { + sp = &iis2iclx_shub_slist[shub_ext[n]]; + sp->sh_out_reg = IIS2ICLX_SHUB_DATA_OUT + regn; + regn += sp->out_data_len; + sp->dev_init(data, sp->ext_i2c_addr); + } + + iis2iclx_shub_set_data_channel(data); + + return 0; +} diff --git a/drivers/sensor/iis2iclx/iis2iclx_spi.c b/drivers/sensor/iis2iclx/iis2iclx_spi.c new file mode 100644 index 00000000000..0c8d89547eb --- /dev/null +++ b/drivers/sensor/iis2iclx/iis2iclx_spi.c @@ -0,0 +1,127 @@ +/* ST Microelectronics IIS2ICLX 2-axis accelerometer sensor driver + * + * Copyright (c) 2020 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/iis2iclx.pdf + */ + +#define DT_DRV_COMPAT st_iis2iclx + +#include +#include "iis2iclx.h" +#include + +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) + +#define IIS2ICLX_SPI_READ (1 << 7) + +LOG_MODULE_DECLARE(IIS2ICLX, CONFIG_SENSOR_LOG_LEVEL); + +static int iis2iclx_spi_read(struct iis2iclx_data *data, uint8_t reg_addr, + uint8_t *value, uint8_t len) +{ + const struct iis2iclx_config *cfg = data->dev->config; + const struct spi_config *spi_cfg = &cfg->spi_conf; + uint8_t buffer_tx[2] = { reg_addr | IIS2ICLX_SPI_READ, 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 (spi_transceive(data->bus, spi_cfg, &tx, &rx)) { + return -EIO; + } + + return 0; +} + +static int iis2iclx_spi_write(struct iis2iclx_data *data, uint8_t reg_addr, + uint8_t *value, uint8_t len) +{ + const struct iis2iclx_config *cfg = data->dev->config; + const struct spi_config *spi_cfg = &cfg->spi_conf; + uint8_t buffer_tx[1] = { reg_addr & ~IIS2ICLX_SPI_READ }; + 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 (spi_write(data->bus, spi_cfg, &tx)) { + return -EIO; + } + + return 0; +} + +int iis2iclx_spi_init(const struct device *dev) +{ + struct iis2iclx_data *data = dev->data; + + data->ctx_spi.read_reg = (stmdev_read_ptr) iis2iclx_spi_read, + data->ctx_spi.write_reg = (stmdev_write_ptr) iis2iclx_spi_write, + + data->ctx = &data->ctx_spi; + data->ctx->handle = data; + +#if DT_INST_SPI_DEV_HAS_CS_GPIOS(0) + const struct iis2iclx_config *cfg = dev->config; + + /* 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.gpio_dt_flags = cfg->cs_gpio_flags; + 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_ANY_INST_ON_BUS_STATUS_OKAY(spi) */ diff --git a/drivers/sensor/iis2iclx/iis2iclx_trigger.c b/drivers/sensor/iis2iclx/iis2iclx_trigger.c new file mode 100644 index 00000000000..3e1f23a7891 --- /dev/null +++ b/drivers/sensor/iis2iclx/iis2iclx_trigger.c @@ -0,0 +1,250 @@ +/* ST Microelectronics IIS2ICLX 2-axis accelerometer sensor driver + * + * Copyright (c) 2020 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/iis2iclx.pdf + */ + +#define DT_DRV_COMPAT st_iis2iclx + +#include +#include +#include +#include + +#include "iis2iclx.h" + +LOG_MODULE_DECLARE(IIS2ICLX, CONFIG_SENSOR_LOG_LEVEL); + +#if defined(CONFIG_IIS2ICLX_ENABLE_TEMP) +/** + * iis2iclx_enable_t_int - TEMP enable selected int pin to generate interrupt + */ +static int iis2iclx_enable_t_int(const struct device *dev, int enable) +{ + const struct iis2iclx_config *cfg = dev->config; + struct iis2iclx_data *iis2iclx = dev->data; + iis2iclx_pin_int2_route_t int2_route; + + if (enable) { + int16_t buf; + + /* dummy read: re-trigger interrupt */ + iis2iclx_temperature_raw_get(iis2iclx->ctx, &buf); + } + + /* set interrupt (TEMP DRDY interrupt is only on INT2) */ + if (cfg->int_pin == 1) + return -EIO; + + iis2iclx_read_reg(iis2iclx->ctx, IIS2ICLX_INT2_CTRL, + (uint8_t *)&int2_route.int2_ctrl, 1); + int2_route.int2_ctrl.int2_drdy_temp = enable; + return iis2iclx_write_reg(iis2iclx->ctx, IIS2ICLX_INT2_CTRL, + (uint8_t *)&int2_route.int2_ctrl, 1); +} +#endif + +/** + * iis2iclx_enable_xl_int - XL enable selected int pin to generate interrupt + */ +static int iis2iclx_enable_xl_int(const struct device *dev, int enable) +{ + const struct iis2iclx_config *cfg = dev->config; + struct iis2iclx_data *iis2iclx = dev->data; + + if (enable) { + int16_t buf[3]; + + /* dummy read: re-trigger interrupt */ + iis2iclx_acceleration_raw_get(iis2iclx->ctx, buf); + } + + /* set interrupt */ + if (cfg->int_pin == 1) { + iis2iclx_pin_int1_route_t int1_route; + + iis2iclx_read_reg(iis2iclx->ctx, IIS2ICLX_INT1_CTRL, + (uint8_t *)&int1_route.int1_ctrl, 1); + + int1_route.int1_ctrl.int1_drdy_xl = enable; + return iis2iclx_write_reg(iis2iclx->ctx, IIS2ICLX_INT1_CTRL, + (uint8_t *)&int1_route.int1_ctrl, 1); + } else { + iis2iclx_pin_int2_route_t int2_route; + + iis2iclx_read_reg(iis2iclx->ctx, IIS2ICLX_INT2_CTRL, + (uint8_t *)&int2_route.int2_ctrl, 1); + int2_route.int2_ctrl.int2_drdy_xl = enable; + return iis2iclx_write_reg(iis2iclx->ctx, IIS2ICLX_INT2_CTRL, + (uint8_t *)&int2_route.int2_ctrl, 1); + } +} + +/** + * iis2iclx_trigger_set - link external trigger to event data ready + */ +int iis2iclx_trigger_set(const struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + struct iis2iclx_data *iis2iclx = dev->data; + + if (trig->chan == SENSOR_CHAN_ACCEL_XYZ) { + iis2iclx->handler_drdy_acc = handler; + if (handler) { + return iis2iclx_enable_xl_int(dev, IIS2ICLX_EN_BIT); + } else { + return iis2iclx_enable_xl_int(dev, IIS2ICLX_DIS_BIT); + } + } +#if defined(CONFIG_IIS2ICLX_ENABLE_TEMP) + else if (trig->chan == SENSOR_CHAN_DIE_TEMP) { + iis2iclx->handler_drdy_temp = handler; + if (handler) { + return iis2iclx_enable_t_int(dev, IIS2ICLX_EN_BIT); + } else { + return iis2iclx_enable_t_int(dev, IIS2ICLX_DIS_BIT); + } + } +#endif + + return -ENOTSUP; +} + +/** + * iis2iclx_handle_interrupt - handle the drdy event + * read data and call handler if registered any + */ +static void iis2iclx_handle_interrupt(const struct device *dev) +{ + struct iis2iclx_data *iis2iclx = dev->data; + struct sensor_trigger drdy_trigger = { + .type = SENSOR_TRIG_DATA_READY, + }; + const struct iis2iclx_config *cfg = dev->config; + iis2iclx_status_reg_t status; + + while (1) { + if (iis2iclx_status_reg_get(iis2iclx->ctx, &status) < 0) { + LOG_DBG("failed reading status reg"); + return; + } + + if ((status.xlda == 0) +#if defined(CONFIG_IIS2ICLX_ENABLE_TEMP) + && (status.tda == 0) +#endif + ) { + break; + } + + if ((status.xlda) && (iis2iclx->handler_drdy_acc != NULL)) { + iis2iclx->handler_drdy_acc(dev, &drdy_trigger); + } + +#if defined(CONFIG_IIS2ICLX_ENABLE_TEMP) + if ((status.tda) && (iis2iclx->handler_drdy_temp != NULL)) { + iis2iclx->handler_drdy_temp(dev, &drdy_trigger); + } +#endif + } + + gpio_pin_interrupt_configure(iis2iclx->gpio, cfg->int_gpio_pin, + GPIO_INT_EDGE_TO_ACTIVE); +} + +static void iis2iclx_gpio_callback(const struct device *dev, + struct gpio_callback *cb, uint32_t pins) +{ + struct iis2iclx_data *iis2iclx = + CONTAINER_OF(cb, struct iis2iclx_data, gpio_cb); + const struct iis2iclx_config *cfg = iis2iclx->dev->config; + + ARG_UNUSED(pins); + + gpio_pin_interrupt_configure(iis2iclx->gpio, cfg->int_gpio_pin, + GPIO_INT_DISABLE); + +#if defined(CONFIG_IIS2ICLX_TRIGGER_OWN_THREAD) + k_sem_give(&iis2iclx->gpio_sem); +#elif defined(CONFIG_IIS2ICLX_TRIGGER_GLOBAL_THREAD) + k_work_submit(&iis2iclx->work); +#endif /* CONFIG_IIS2ICLX_TRIGGER_OWN_THREAD */ +} + +#ifdef CONFIG_IIS2ICLX_TRIGGER_OWN_THREAD +static void iis2iclx_thread(struct iis2iclx_data *iis2iclx) +{ + while (1) { + k_sem_take(&iis2iclx->gpio_sem, K_FOREVER); + iis2iclx_handle_interrupt(iis2iclx->dev); + } +} +#endif /* CONFIG_IIS2ICLX_TRIGGER_OWN_THREAD */ + +#ifdef CONFIG_IIS2ICLX_TRIGGER_GLOBAL_THREAD +static void iis2iclx_work_cb(struct k_work *work) +{ + struct iis2iclx_data *iis2iclx = + CONTAINER_OF(work, struct iis2iclx_data, work); + + iis2iclx_handle_interrupt(iis2iclx->dev); +} +#endif /* CONFIG_IIS2ICLX_TRIGGER_GLOBAL_THREAD */ + +int iis2iclx_init_interrupt(const struct device *dev) +{ + struct iis2iclx_data *iis2iclx = dev->data; + const struct iis2iclx_config *cfg = dev->config; + int ret; + + /* setup data ready gpio interrupt (INT1 or INT2) */ + iis2iclx->gpio = device_get_binding(cfg->int_gpio_port); + if (iis2iclx->gpio == NULL) { + LOG_ERR("Cannot get pointer to %s device", cfg->int_gpio_port); + return -EINVAL; + } + +#if defined(CONFIG_IIS2ICLX_TRIGGER_OWN_THREAD) + k_sem_init(&iis2iclx->gpio_sem, 0, UINT_MAX); + + k_thread_create(&iis2iclx->thread, iis2iclx->thread_stack, + CONFIG_IIS2ICLX_THREAD_STACK_SIZE, + (k_thread_entry_t)iis2iclx_thread, + iis2iclx, NULL, NULL, + K_PRIO_COOP(CONFIG_IIS2ICLX_THREAD_PRIORITY), + 0, K_NO_WAIT); +#elif defined(CONFIG_IIS2ICLX_TRIGGER_GLOBAL_THREAD) + iis2iclx->work.handler = iis2iclx_work_cb; +#endif /* CONFIG_IIS2ICLX_TRIGGER_OWN_THREAD */ + + ret = gpio_pin_configure(iis2iclx->gpio, cfg->int_gpio_pin, + GPIO_INPUT | cfg->int_gpio_flags); + if (ret < 0) { + LOG_ERR("Could not configure gpio"); + return ret; + } + + gpio_init_callback(&iis2iclx->gpio_cb, + iis2iclx_gpio_callback, + BIT(cfg->int_gpio_pin)); + + if (gpio_add_callback(iis2iclx->gpio, &iis2iclx->gpio_cb) < 0) { + LOG_ERR("Could not set gpio callback"); + return -EIO; + } + + /* enable interrupt on int1/int2 in pulse mode */ + if (iis2iclx_int_notification_set(iis2iclx->ctx, + IIS2ICLX_ALL_INT_PULSED) < 0) { + LOG_ERR("Could not set pulse mode"); + return -EIO; + } + + return gpio_pin_interrupt_configure(iis2iclx->gpio, cfg->int_gpio_pin, + GPIO_INT_EDGE_TO_ACTIVE); +} diff --git a/dts/bindings/sensor/st,iis2iclx-i2c.yaml b/dts/bindings/sensor/st,iis2iclx-i2c.yaml new file mode 100644 index 00000000000..e1b04a781e0 --- /dev/null +++ b/dts/bindings/sensor/st,iis2iclx-i2c.yaml @@ -0,0 +1,20 @@ +# Copyright (c) 2020 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +description: | + STMicroelectronics IIS2ICLX 2-axis accelerometer sensor + accessed through I2C bus + +compatible: "st,iis2iclx" + +include: i2c-device.yaml + +properties: + drdy-gpios: + type: phandle-array + required: false + description: DRDY pin + + This pin defaults to active high when produced by the sensor. + The property value should ensure the flags properly describe + the signal that is presented to the driver. diff --git a/dts/bindings/sensor/st,iis2iclx-spi.yaml b/dts/bindings/sensor/st,iis2iclx-spi.yaml new file mode 100644 index 00000000000..09651426950 --- /dev/null +++ b/dts/bindings/sensor/st,iis2iclx-spi.yaml @@ -0,0 +1,20 @@ +# Copyright (c) 2020 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +description: | + STMicroelectronics IIS2ICLX 2-axis accelerometer sensor + accessed through SPI bus + +compatible: "st,iis2iclx" + +include: spi-device.yaml + +properties: + drdy-gpios: + type: phandle-array + required: false + description: DRDY pin + + This pin defaults to active high when produced by the sensor. + The property value should ensure the flags properly describe + the signal that is presented to the driver. diff --git a/tests/drivers/build_all/app.overlay b/tests/drivers/build_all/app.overlay index e7893cd061d..be5274903f2 100644 --- a/tests/drivers/build_all/app.overlay +++ b/tests/drivers/build_all/app.overlay @@ -194,6 +194,11 @@ local-mac-address = [00 00 00 00 00 00]; }; +/* disable device to conflict with i2c version */ +&test_spi_iis2iclx { + status = "disabled"; +}; + /* disable device to conflict with i2c version */ &test_spi_iis2mdc { status = "disabled"; diff --git a/tests/drivers/build_all/i2c.dtsi b/tests/drivers/build_all/i2c.dtsi index 06fe2d6d7de..2e1023f9d37 100644 --- a/tests/drivers/build_all/i2c.dtsi +++ b/tests/drivers/build_all/i2c.dtsi @@ -543,6 +543,13 @@ test_i2c_iis2dh: iis2dh@18 { drdy-gpios = <&test_gpio 0 0>; }; +test_i2c_iis2iclx: iis2iclx@6a { + compatible = "st,iis2iclx"; + label = "IIS2ICLX"; + reg = <0x6a>; + drdy-gpios = <&test_gpio 0 0>; +}; + test_i2c_itds: itds@18 { compatible = "we,wsen-itds"; label = "WSEN-ITDS"; diff --git a/tests/drivers/build_all/sensors_stmemsc.conf b/tests/drivers/build_all/sensors_stmemsc.conf index 0e34e3007fb..977ed07d8ed 100644 --- a/tests/drivers/build_all/sensors_stmemsc.conf +++ b/tests/drivers/build_all/sensors_stmemsc.conf @@ -10,4 +10,5 @@ CONFIG_LIS2DW12=y CONFIG_STTS751=y CONFIG_ISM330DHCX=y CONFIG_IIS2DH=y +CONFIG_IIS2ICLX=y CONFIG_IIS2MDC=y diff --git a/tests/drivers/build_all/sensors_stmemsc_trigger.conf b/tests/drivers/build_all/sensors_stmemsc_trigger.conf index 75299d6f2cc..5712c08a50f 100644 --- a/tests/drivers/build_all/sensors_stmemsc_trigger.conf +++ b/tests/drivers/build_all/sensors_stmemsc_trigger.conf @@ -15,5 +15,7 @@ CONFIG_ISM330DHCX=y CONFIG_ISM330DHCX_TRIGGER_OWN_THREAD=y CONFIG_IIS2DH=y CONFIG_IIS2DH_TRIGGER_OWN_THREAD=y +CONFIG_IIS2ICLX=y +CONFIG_IIS2ICLX_TRIGGER_OWN_THREAD=y CONFIG_IIS2MDC=y CONFIG_IIS2MDC_TRIGGER_OWN_THREAD=y diff --git a/tests/drivers/build_all/spi.dtsi b/tests/drivers/build_all/spi.dtsi index 3175f5e355b..e69ea106192 100644 --- a/tests/drivers/build_all/spi.dtsi +++ b/tests/drivers/build_all/spi.dtsi @@ -551,3 +551,11 @@ test_spi_w5500: w5500@36 { int-gpios = <&test_gpio 0 0>; reset-gpios = <&test_gpio 0 0>; }; + +test_spi_iis2iclx: iis2iclx@37 { + compatible = "st,iis2iclx"; + label = "IIS2ICLX"; + reg = <0x37>; + spi-max-frequency = <0>; + drdy-gpios = <&test_gpio 0 0>; +};