From a302786e9bee7990f00586f31b7fe0625a3049f0 Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Fri, 31 Jan 2020 10:39:41 +0100 Subject: [PATCH] drivers/sensor: add support to IIS2MDC magnetometer sensor The IIS2MDC is a 3D digital magnetometer ultra-low power sensor for industrial applications, which can be interfaced through either I2C or SPI bus. https://www.st.com/resource/en/datasheet/iis2mdc.pdf This driver is based on stmemsc i/f v1.02. Signed-off-by: Armando Visconti --- drivers/sensor/CMakeLists.txt | 1 + drivers/sensor/Kconfig | 2 + drivers/sensor/iis2mdc/CMakeLists.txt | 11 + drivers/sensor/iis2mdc/Kconfig | 60 +++ drivers/sensor/iis2mdc/iis2mdc.c | 364 ++++++++++++++++++ drivers/sensor/iis2mdc/iis2mdc.h | 94 +++++ drivers/sensor/iis2mdc/iis2mdc_i2c.c | 54 +++ drivers/sensor/iis2mdc/iis2mdc_spi.c | 134 +++++++ drivers/sensor/iis2mdc/iis2mdc_trigger.c | 151 ++++++++ dts/bindings/sensor/st,iis2mdc-i2c.yaml | 19 + dts/bindings/sensor/st,iis2mdc-spi.yaml | 19 + tests/drivers/build_all/dts_fixup.h | 21 + tests/drivers/build_all/sensors_stmemsc.conf | 1 + .../build_all/sensors_stmemsc_trigger.conf | 2 + 14 files changed, 933 insertions(+) create mode 100644 drivers/sensor/iis2mdc/CMakeLists.txt create mode 100644 drivers/sensor/iis2mdc/Kconfig create mode 100644 drivers/sensor/iis2mdc/iis2mdc.c create mode 100644 drivers/sensor/iis2mdc/iis2mdc.h create mode 100644 drivers/sensor/iis2mdc/iis2mdc_i2c.c create mode 100644 drivers/sensor/iis2mdc/iis2mdc_spi.c create mode 100644 drivers/sensor/iis2mdc/iis2mdc_trigger.c create mode 100644 dts/bindings/sensor/st,iis2mdc-i2c.yaml create mode 100644 dts/bindings/sensor/st,iis2mdc-spi.yaml diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index 4e9909ef97c..4d519ca32c1 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -26,6 +26,7 @@ add_subdirectory_ifdef(CONFIG_HMC5883L hmc5883l) add_subdirectory_ifdef(CONFIG_HP206C hp206c) add_subdirectory_ifdef(CONFIG_HTS221 hts221) add_subdirectory_ifdef(CONFIG_IIS2DLPC iis2dlpc) +add_subdirectory_ifdef(CONFIG_IIS2MDC iis2mdc) add_subdirectory_ifdef(CONFIG_IIS3DHHC iis3dhhc) add_subdirectory_ifdef(CONFIG_ISL29035 isl29035) add_subdirectory_ifdef(CONFIG_ISM330DHCX ism330dhcx) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index 8a98d3aad06..1dc1300638d 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -81,6 +81,8 @@ source "drivers/sensor/hts221/Kconfig" source "drivers/sensor/iis2dlpc/Kconfig" +source "drivers/sensor/iis2mdc/Kconfig" + source "drivers/sensor/iis3dhhc/Kconfig" source "drivers/sensor/isl29035/Kconfig" diff --git a/drivers/sensor/iis2mdc/CMakeLists.txt b/drivers/sensor/iis2mdc/CMakeLists.txt new file mode 100644 index 00000000000..0aa64728a7c --- /dev/null +++ b/drivers/sensor/iis2mdc/CMakeLists.txt @@ -0,0 +1,11 @@ +# +# Copyright (c) 2020 STMicroelectronics +# +# SPDX-License-Identifier: Apache-2.0 +# + +zephyr_library() +zephyr_library_sources_ifdef(CONFIG_IIS2MDC iis2mdc.c) +zephyr_library_sources_ifdef(CONFIG_IIS2MDC iis2mdc_i2c.c) +zephyr_library_sources_ifdef(CONFIG_IIS2MDC iis2mdc_spi.c) +zephyr_library_sources_ifdef(CONFIG_IIS2MDC_TRIGGER iis2mdc_trigger.c) diff --git a/drivers/sensor/iis2mdc/Kconfig b/drivers/sensor/iis2mdc/Kconfig new file mode 100644 index 00000000000..2aba947aa80 --- /dev/null +++ b/drivers/sensor/iis2mdc/Kconfig @@ -0,0 +1,60 @@ +# Copyright (c) 2020 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +menuconfig IIS2MDC + bool "IIS2MDC Magnetometer" + depends on (I2C && HAS_DTS_I2C) || SPI + select HAS_STMEMSC + select USE_STDC_IIS2MDC + help + Enable driver for IIS2MDC I2C-based magnetometer sensor. + +if IIS2MDC + +choice IIS2MDC_TRIGGER_MODE + prompt "Trigger mode" + default IIS2MDC_TRIGGER_GLOBAL_THREAD + help + Specify the type of triggering to be used by the driver. + +config IIS2MDC_TRIGGER_NONE + bool "No trigger" + +config IIS2MDC_TRIGGER_GLOBAL_THREAD + bool "Use global thread" + depends on GPIO + select IIS2MDC_TRIGGER + +config IIS2MDC_TRIGGER_OWN_THREAD + bool "Use own thread" + depends on GPIO + select IIS2MDC_TRIGGER + +endchoice # IIS2MDC_TRIGGER_MODE + +config IIS2MDC_TRIGGER + bool + +config IIS2MDC_THREAD_PRIORITY + int "Thread priority" + depends on IIS2MDC_TRIGGER_OWN_THREAD + default 10 + help + Priority of thread used by the driver to handle interrupts. + +config IIS2MDC_THREAD_STACK_SIZE + int "Thread stack size" + depends on IIS2MDC_TRIGGER_OWN_THREAD + default 1024 + help + Stack size of thread used by the driver to handle interrupts. + +config IIS2MDC_MAG_ODR_RUNTIME + bool "Set magnetometer sampling frequency (ODR) at runtime (default: 10 Hz)" + default y + +config IIS2MDC_SPI_FULL_DUPLEX + bool "Enable SPI 4wire mode (separated MISO and MOSI lines)" + depends on SPI + +endif # IIS2MDC diff --git a/drivers/sensor/iis2mdc/iis2mdc.c b/drivers/sensor/iis2mdc/iis2mdc.c new file mode 100644 index 00000000000..d133d20ee63 --- /dev/null +++ b/drivers/sensor/iis2mdc/iis2mdc.c @@ -0,0 +1,364 @@ +/* ST Microelectronics IIS2MDC 3-axis magnetometer sensor + * + * Copyright (c) 2020 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/iis2mdc.pdf + */ + +#include +#include +#include +#include +#include +#include +#include "iis2mdc.h" + +struct iis2mdc_data iis2mdc_data; + +LOG_MODULE_REGISTER(IIS2MDC, CONFIG_SENSOR_LOG_LEVEL); + +#ifdef CONFIG_IIS2MDC_MAG_ODR_RUNTIME +static int iis2mdc_set_odr(struct device *dev, const struct sensor_value *val) +{ + struct iis2mdc_data *iis2mdc = dev->driver_data; + iis2mdc_odr_t odr; + + switch (val->val1) { + case 10: + odr = IIS2MDC_ODR_10Hz; + break; + case 20: + odr = IIS2MDC_ODR_20Hz; + break; + case 50: + odr = IIS2MDC_ODR_50Hz; + break; + case 100: + odr = IIS2MDC_ODR_100Hz; + break; + default: + return -EINVAL; + } + + if (iis2mdc_data_rate_set(iis2mdc->ctx, odr)) { + return -EIO; + } + + return 0; +} +#endif /* CONFIG_IIS2MDC_MAG_ODR_RUNTIME */ + +static int iis2mdc_set_hard_iron(struct device *dev, enum sensor_channel chan, + const struct sensor_value *val) +{ + struct iis2mdc_data *iis2mdc = dev->driver_data; + u8_t i; + union axis3bit16_t offset; + + for (i = 0U; i < 3; i++) { + offset.i16bit[i] = sys_cpu_to_le16(val->val1); + val++; + } + + return iis2mdc_mag_user_offset_set(iis2mdc->ctx, offset.u8bit); +} + +static void iis2mdc_channel_get_mag(struct device *dev, + enum sensor_channel chan, + struct sensor_value *val) +{ + s32_t cval; + int i; + u8_t ofs_start, ofs_stop; + struct iis2mdc_data *iis2mdc = dev->driver_data; + struct sensor_value *pval = val; + + switch (chan) { + case SENSOR_CHAN_MAGN_X: + ofs_start = ofs_stop = 0U; + break; + case SENSOR_CHAN_MAGN_Y: + ofs_start = ofs_stop = 1U; + break; + case SENSOR_CHAN_MAGN_Z: + ofs_start = ofs_stop = 2U; + break; + default: + ofs_start = 0U; ofs_stop = 2U; + break; + } + + for (i = ofs_start; i <= ofs_stop; i++) { + cval = iis2mdc->mag[i] * 1500; + pval->val1 = cval / 1000000; + pval->val2 = cval % 1000000; + pval++; + } +} + +/* read internal temperature */ +static void iis2mdc_channel_get_temp(struct device *dev, + struct sensor_value *val) +{ + struct iis2mdc_data *drv_data = dev->driver_data; + + val->val1 = drv_data->temp_sample / 100; + val->val2 = (drv_data->temp_sample % 100) * 10000; +} + +static int iis2mdc_channel_get(struct device *dev, enum sensor_channel chan, + struct sensor_value *val) +{ + switch (chan) { + case SENSOR_CHAN_MAGN_X: + case SENSOR_CHAN_MAGN_Y: + case SENSOR_CHAN_MAGN_Z: + case SENSOR_CHAN_MAGN_XYZ: + iis2mdc_channel_get_mag(dev, chan, val); + break; + case SENSOR_CHAN_DIE_TEMP: + iis2mdc_channel_get_temp(dev, val); + break; + default: + LOG_DBG("Channel not supported"); + return -ENOTSUP; + } + + return 0; +} + +static int iis2mdc_config(struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + switch (attr) { +#ifdef CONFIG_IIS2MDC_MAG_ODR_RUNTIME + case SENSOR_ATTR_SAMPLING_FREQUENCY: + return iis2mdc_set_odr(dev, val); +#endif /* CONFIG_IIS2MDC_MAG_ODR_RUNTIME */ + case SENSOR_ATTR_OFFSET: + return iis2mdc_set_hard_iron(dev, chan, val); + default: + LOG_DBG("Mag attribute not supported"); + return -ENOTSUP; + } + + return 0; +} + +static int iis2mdc_attr_set(struct device *dev, + enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + switch (chan) { + case SENSOR_CHAN_ALL: + case SENSOR_CHAN_MAGN_X: + case SENSOR_CHAN_MAGN_Y: + case SENSOR_CHAN_MAGN_Z: + case SENSOR_CHAN_MAGN_XYZ: + return iis2mdc_config(dev, chan, attr, val); + default: + LOG_DBG("attr_set() not supported on %d channel", chan); + return -ENOTSUP; + } + + return 0; +} + +static int iis2mdc_sample_fetch_mag(struct device *dev) +{ + struct iis2mdc_data *iis2mdc = dev->driver_data; + union axis3bit16_t raw_mag; + + /* fetch raw data sample */ + if (iis2mdc_magnetic_raw_get(iis2mdc->ctx, raw_mag.u8bit) < 0) { + LOG_DBG("Failed to read sample"); + return -EIO; + } + + iis2mdc->mag[0] = sys_le16_to_cpu(raw_mag.i16bit[0]); + iis2mdc->mag[1] = sys_le16_to_cpu(raw_mag.i16bit[1]); + iis2mdc->mag[2] = sys_le16_to_cpu(raw_mag.i16bit[2]); + + return 0; +} + +static int iis2mdc_sample_fetch_temp(struct device *dev) +{ + struct iis2mdc_data *iis2mdc = dev->driver_data; + union axis1bit16_t raw_temp; + s32_t temp; + + /* fetch raw temperature sample */ + if (iis2mdc_temperature_raw_get(iis2mdc->ctx, raw_temp.u8bit) < 0) { + LOG_DBG("Failed to read sample"); + return -EIO; + } + + /* formula is temp = 25 + (temp / 8) C */ + temp = (sys_le16_to_cpu(raw_temp.i16bit) & 0x8FFF); + iis2mdc->temp_sample = 2500 + (temp * 100) / 8; + + return 0; +} + +static int iis2mdc_sample_fetch(struct device *dev, enum sensor_channel chan) +{ + switch (chan) { + case SENSOR_CHAN_MAGN_X: + case SENSOR_CHAN_MAGN_Y: + case SENSOR_CHAN_MAGN_Z: + case SENSOR_CHAN_MAGN_XYZ: + iis2mdc_sample_fetch_mag(dev); + break; + case SENSOR_CHAN_DIE_TEMP: + iis2mdc_sample_fetch_temp(dev); + break; + case SENSOR_CHAN_ALL: + iis2mdc_sample_fetch_mag(dev); + iis2mdc_sample_fetch_temp(dev); + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static const struct sensor_driver_api iis2mdc_driver_api = { + .attr_set = iis2mdc_attr_set, +#if CONFIG_IIS2MDC_TRIGGER + .trigger_set = iis2mdc_trigger_set, +#endif + .sample_fetch = iis2mdc_sample_fetch, + .channel_get = iis2mdc_channel_get, +}; + +static int iis2mdc_init_interface(struct device *dev) +{ + const struct iis2mdc_config *const config = + dev->config->config_info; + struct iis2mdc_data *iis2mdc = dev->driver_data; + + iis2mdc->bus = device_get_binding(config->master_dev_name); + if (!iis2mdc->bus) { + LOG_DBG("Could not get pointer to %s device", + config->master_dev_name); + return -EINVAL; + } + + return config->bus_init(dev); +} + +static const struct iis2mdc_config iis2mdc_dev_config = { + .master_dev_name = DT_INST_0_ST_IIS2MDC_BUS_NAME, +#ifdef CONFIG_IIS2MDC_TRIGGER + .drdy_port = DT_INST_0_ST_IIS2MDC_DRDY_GPIOS_CONTROLLER, + .drdy_pin = DT_INST_0_ST_IIS2MDC_DRDY_GPIOS_PIN, + .drdy_flags = DT_INST_0_ST_IIS2MDC_DRDY_GPIOS_FLAGS, +#endif /* CONFIG_IIS2MDC_TRIGGER */ +#if defined(DT_ST_IIS2MDC_BUS_SPI) + .bus_init = iis2mdc_spi_init, + .spi_conf.frequency = DT_INST_0_ST_IIS2MDC_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_IIS2MDC_BASE_ADDRESS, +#if defined(DT_INST_0_ST_IIS2MDC_CS_GPIOS_CONTROLLER) + .gpio_cs_port = DT_INST_0_ST_IIS2MDC_CS_GPIOS_CONTROLLER, + .cs_gpio = DT_INST_0_ST_IIS2MDC_CS_GPIOS_PIN, + + .spi_conf.cs = &iis2mdc_data.cs_ctrl, +#else + .spi_conf.cs = NULL, +#endif +#elif defined(DT_ST_IIS2MDC_BUS_I2C) + .bus_init = iis2mdc_i2c_init, + .i2c_slv_addr = DT_INST_0_ST_IIS2MDC_BASE_ADDRESS, +#else +#error "BUS MACRO NOT DEFINED IN DTS" +#endif +}; + +static int iis2mdc_init(struct device *dev) +{ + struct iis2mdc_data *iis2mdc = dev->driver_data; + u8_t wai; + + if (iis2mdc_init_interface(dev)) { + return -EINVAL; + } + + /* check chip ID */ + if (iis2mdc_device_id_get(iis2mdc->ctx, &wai) < 0) { + return -EIO; + } + + if (wai != IIS2MDC_ID) { + LOG_DBG("Invalid chip ID: %02x\n", wai); + return -EINVAL; + } + + /* reset sensor configuration */ + if (iis2mdc_reset_set(iis2mdc->ctx, PROPERTY_ENABLE) < 0) { + LOG_DBG("s/w reset failed\n"); + return -EIO; + } + + k_busy_wait(100); + +#if CONFIG_IIS2MDC_SPI_FULL_DUPLEX + /* After s/w reset set SPI 4wires again if the case */ + if (iis2mdc_spi_mode_set(iis2mdc->ctx, IIS2MDC_SPI_4_WIRE) < 0) { + return -EIO; + } +#endif + + /* enable BDU */ + if (iis2mdc_block_data_update_set(iis2mdc->ctx, PROPERTY_ENABLE) < 0) { + LOG_DBG("setting bdu failed\n"); + return -EIO; + } + + /* Set Output Data Rate */ + if (iis2mdc_data_rate_set(iis2mdc->ctx, IIS2MDC_ODR_10Hz)) { + LOG_DBG("set odr failed\n"); + return -EIO; + } + + /* Set / Reset sensor mode */ + if (iis2mdc_set_rst_mode_set(iis2mdc->ctx, + IIS2MDC_SENS_OFF_CANC_EVERY_ODR)) { + LOG_DBG("reset sensor mode failed\n"); + return -EIO; + } + + /* Enable temperature compensation */ + if (iis2mdc_offset_temp_comp_set(iis2mdc->ctx, PROPERTY_ENABLE)) { + LOG_DBG("enable temp compensation failed\n"); + return -EIO; + } + + /* Set device in continuous mode */ + if (iis2mdc_operating_mode_set(iis2mdc->ctx, IIS2MDC_CONTINUOUS_MODE)) { + LOG_DBG("set continuos mode failed\n"); + return -EIO; + } + +#ifdef CONFIG_IIS2MDC_TRIGGER + if (iis2mdc_init_interrupt(dev) < 0) { + LOG_DBG("Failed to initialize interrupts"); + return -EIO; + } +#endif + + return 0; +} + +DEVICE_AND_API_INIT(iis2mdc, DT_INST_0_ST_IIS2MDC_LABEL, iis2mdc_init, + &iis2mdc_data, &iis2mdc_dev_config, POST_KERNEL, + CONFIG_SENSOR_INIT_PRIORITY, &iis2mdc_driver_api); diff --git a/drivers/sensor/iis2mdc/iis2mdc.h b/drivers/sensor/iis2mdc/iis2mdc.h new file mode 100644 index 00000000000..b9e5cfe71b5 --- /dev/null +++ b/drivers/sensor/iis2mdc/iis2mdc.h @@ -0,0 +1,94 @@ +/* ST Microelectronics IIS2MDC 3-axis magnetometer sensor + * + * Copyright (c) 2020 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/iis2mdc.pdf + */ + +#ifndef __MAG_IIS2MDC_H +#define __MAG_IIS2MDC_H + +#include +#include +#include +#include +#include "iis2mdc_reg.h" + +union axis3bit16_t { + s16_t i16bit[3]; + u8_t u8bit[6]; +}; + +union axis1bit16_t { + s16_t i16bit; + u8_t u8bit[2]; +}; + +struct iis2mdc_config { + char *master_dev_name; + int (*bus_init)(struct device *dev); +#ifdef CONFIG_IIS2MDC_TRIGGER + const char *drdy_port; + gpio_pin_t drdy_pin; + gpio_dt_flags_t drdy_flags; +#endif /* CONFIG_IIS2MDC_TRIGGER */ +#ifdef DT_ST_IIS2MDC_BUS_I2C + u16_t i2c_slv_addr; +#elif DT_ST_IIS2MDC_BUS_SPI + struct spi_config spi_conf; +#if defined(DT_INST_0_ST_IIS2MDC_CS_GPIOS_CONTROLLER) + const char *gpio_cs_port; + u8_t cs_gpio; +#endif /* DT_INST_0_ST_IIS2MDC_CS_GPIOS_CONTROLLER */ +#endif /* DT_ST_IIS2MDC_BUS_SPI */ +}; + +/* Sensor data */ +struct iis2mdc_data { + struct device *bus; + u16_t i2c_addr; + s16_t mag[3]; + s32_t temp_sample; + + stmdev_ctx_t *ctx; + +#ifdef DT_ST_IIS2MDC_BUS_I2C + stmdev_ctx_t ctx_i2c; +#elif DT_ST_IIS2MDC_BUS_SPI + stmdev_ctx_t ctx_spi; +#endif + +#ifdef CONFIG_IIS2MDC_TRIGGER + struct device *gpio; + struct gpio_callback gpio_cb; + + sensor_trigger_handler_t handler_drdy; + +#if defined(CONFIG_IIS2MDC_TRIGGER_OWN_THREAD) + K_THREAD_STACK_MEMBER(thread_stack, CONFIG_IIS2MDC_THREAD_STACK_SIZE); + struct k_thread thread; + struct k_sem gpio_sem; +#elif defined(CONFIG_IIS2MDC_TRIGGER_GLOBAL_THREAD) + struct k_work work; + struct device *dev; +#endif /* CONFIG_IIS2MDC_TRIGGER_GLOBAL_THREAD */ +#endif /* CONFIG_IIS2MDC_TRIGGER */ +#if defined(DT_INST_0_ST_IIS2MDC_CS_GPIOS_CONTROLLER) + struct spi_cs_control cs_ctrl; +#endif /* DT_INST_0_ST_IIS2MDC_CS_GPIOS_CONTROLLER */ +}; + +int iis2mdc_spi_init(struct device *dev); +int iis2mdc_i2c_init(struct device *dev); + +#ifdef CONFIG_IIS2MDC_TRIGGER +int iis2mdc_init_interrupt(struct device *dev); +int iis2mdc_trigger_set(struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler); +#endif /* CONFIG_IIS2MDC_TRIGGER */ + +#endif /* __MAG_IIS2MDC_H */ diff --git a/drivers/sensor/iis2mdc/iis2mdc_i2c.c b/drivers/sensor/iis2mdc/iis2mdc_i2c.c new file mode 100644 index 00000000000..843671c3da8 --- /dev/null +++ b/drivers/sensor/iis2mdc/iis2mdc_i2c.c @@ -0,0 +1,54 @@ +/* ST Microelectronics IIS2MDC 3-axis magnetometer sensor + * + * Copyright (c) 2020 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/iis2mdc.pdf + */ + +#include +#include +#include + +#include "iis2mdc.h" + +#ifdef DT_ST_IIS2MDC_BUS_I2C + +#define LOG_LEVEL CONFIG_SENSOR_LOG_LEVEL +LOG_MODULE_DECLARE(IIS2MDC); + +static int iis2mdc_i2c_read(struct device *dev, u8_t reg_addr, + u8_t *value, u16_t len) +{ + struct iis2mdc_data *data = dev->driver_data; + const struct iis2mdc_config *cfg = dev->config->config_info; + + return i2c_burst_read(data->bus, cfg->i2c_slv_addr, + reg_addr, value, len); +} + +static int iis2mdc_i2c_write(struct device *dev, u8_t reg_addr, + u8_t *value, u16_t len) +{ + struct iis2mdc_data *data = dev->driver_data; + const struct iis2mdc_config *cfg = dev->config->config_info; + + return i2c_burst_write(data->bus, cfg->i2c_slv_addr, + reg_addr, value, len); +} + +int iis2mdc_i2c_init(struct device *dev) +{ + struct iis2mdc_data *data = dev->driver_data; + + data->ctx_i2c.read_reg = (stmdev_read_ptr) iis2mdc_i2c_read; + data->ctx_i2c.write_reg = (stmdev_write_ptr) iis2mdc_i2c_write; + + data->ctx = &data->ctx_i2c; + data->ctx->handle = dev; + + return 0; +} +#endif /* DT_ST_IIS2MDC_BUS_I2C */ diff --git a/drivers/sensor/iis2mdc/iis2mdc_spi.c b/drivers/sensor/iis2mdc/iis2mdc_spi.c new file mode 100644 index 00000000000..d04f7427649 --- /dev/null +++ b/drivers/sensor/iis2mdc/iis2mdc_spi.c @@ -0,0 +1,134 @@ +/* ST Microelectronics IIS2MDC 3-axis magnetometer sensor + * + * Copyright (c) 2020 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/iis2mdc.pdf + */ + +#include +#include "iis2mdc.h" +#include + +#ifdef DT_ST_IIS2MDC_BUS_SPI + +#define IIS2MDC_SPI_READ (1 << 7) + +#define LOG_LEVEL CONFIG_SENSOR_LOG_LEVEL +LOG_MODULE_DECLARE(IIS2MDC); + +static int iis2mdc_spi_read(struct device *dev, u8_t reg_addr, + u8_t *value, u8_t len) +{ + struct iis2mdc_data *data = dev->driver_data; + const struct iis2mdc_config *cfg = dev->config->config_info; + const struct spi_config *spi_cfg = &cfg->spi_conf; + u8_t buffer_tx[2] = { reg_addr | IIS2MDC_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 iis2mdc_spi_write(struct device *dev, u8_t reg_addr, + u8_t *value, u8_t len) +{ + struct iis2mdc_data *data = dev->driver_data; + const struct iis2mdc_config *cfg = dev->config->config_info; + const struct spi_config *spi_cfg = &cfg->spi_conf; + u8_t buffer_tx[1] = { reg_addr & ~IIS2MDC_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 iis2mdc_spi_init(struct device *dev) +{ + struct iis2mdc_data *data = dev->driver_data; + + data->ctx_spi.read_reg = (stmdev_read_ptr) iis2mdc_spi_read; + data->ctx_spi.write_reg = (stmdev_write_ptr) iis2mdc_spi_write; + + data->ctx = &data->ctx_spi; + data->ctx->handle = dev; + +#if defined(DT_INST_0_ST_IIS2MDC_CS_GPIOS_CONTROLLER) + const struct iis2mdc_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 + +#if CONFIG_IIS2MDC_SPI_FULL_DUPLEX + /* Set SPI 4wires */ + if (iis2mdc_spi_mode_set(data->ctx, IIS2MDC_SPI_4_WIRE) < 0) { + return -EIO; + } +#endif + + return 0; +} +#endif /* DT_ST_IIS2MDC_BUS_SPI */ diff --git a/drivers/sensor/iis2mdc/iis2mdc_trigger.c b/drivers/sensor/iis2mdc/iis2mdc_trigger.c new file mode 100644 index 00000000000..458f5c2a93f --- /dev/null +++ b/drivers/sensor/iis2mdc/iis2mdc_trigger.c @@ -0,0 +1,151 @@ +/* ST Microelectronics IIS2MDC 3-axis magnetometer sensor + * + * Copyright (c) 2020 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + * + * Datasheet: + * https://www.st.com/resource/en/datasheet/iis2mdc.pdf + */ + +#include +#include +#include +#include +#include "iis2mdc.h" + +LOG_MODULE_DECLARE(IIS2MDC, CONFIG_SENSOR_LOG_LEVEL); + +static int iis2mdc_enable_int(struct device *dev, int enable) +{ + struct iis2mdc_data *iis2mdc = dev->driver_data; + + /* set interrupt on mag */ + return iis2mdc_drdy_on_pin_set(iis2mdc->ctx, enable); +} + +/* link external trigger to event data ready */ +int iis2mdc_trigger_set(struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + struct iis2mdc_data *iis2mdc = dev->driver_data; + union axis3bit16_t raw; + + if (trig->chan == SENSOR_CHAN_MAGN_XYZ) { + iis2mdc->handler_drdy = handler; + if (handler) { + /* fetch raw data sample: re-trigger lost interrupt */ + iis2mdc_magnetic_raw_get(iis2mdc->ctx, raw.u8bit); + + return iis2mdc_enable_int(dev, 1); + } else { + return iis2mdc_enable_int(dev, 0); + } + } + + return -ENOTSUP; +} + +/* handle the drdy event: read data and call handler if registered any */ +static void iis2mdc_handle_interrupt(void *arg) +{ + struct device *dev = arg; + struct iis2mdc_data *iis2mdc = dev->driver_data; + const struct iis2mdc_config *const config = + dev->config->config_info; + struct sensor_trigger drdy_trigger = { + .type = SENSOR_TRIG_DATA_READY, + }; + + if (iis2mdc->handler_drdy != NULL) { + iis2mdc->handler_drdy(dev, &drdy_trigger); + } + + gpio_pin_interrupt_configure(iis2mdc->gpio, config->drdy_pin, + GPIO_INT_EDGE_TO_ACTIVE); +} + +static void iis2mdc_gpio_callback(struct device *dev, + struct gpio_callback *cb, u32_t pins) +{ + struct iis2mdc_data *iis2mdc = + CONTAINER_OF(cb, struct iis2mdc_data, gpio_cb); + const struct iis2mdc_config *const config = dev->config->config_info; + + ARG_UNUSED(pins); + + gpio_pin_interrupt_configure(dev, config->drdy_pin, GPIO_INT_DISABLE); + +#if defined(CONFIG_IIS2MDC_TRIGGER_OWN_THREAD) + k_sem_give(&iis2mdc->gpio_sem); +#elif defined(CONFIG_IIS2MDC_TRIGGER_GLOBAL_THREAD) + k_work_submit(&iis2mdc->work); +#endif +} + +#ifdef CONFIG_IIS2MDC_TRIGGER_OWN_THREAD +static void iis2mdc_thread(int dev_ptr, int unused) +{ + struct device *dev = INT_TO_POINTER(dev_ptr); + struct iis2mdc_data *iis2mdc = dev->driver_data; + + ARG_UNUSED(unused); + + while (1) { + k_sem_take(&iis2mdc->gpio_sem, K_FOREVER); + iis2mdc_handle_interrupt(dev); + } +} +#endif + +#ifdef CONFIG_IIS2MDC_TRIGGER_GLOBAL_THREAD +static void iis2mdc_work_cb(struct k_work *work) +{ + struct iis2mdc_data *iis2mdc = + CONTAINER_OF(work, struct iis2mdc_data, work); + + iis2mdc_handle_interrupt(iis2mdc->dev); +} +#endif + +int iis2mdc_init_interrupt(struct device *dev) +{ + struct iis2mdc_data *iis2mdc = dev->driver_data; + const struct iis2mdc_config *const config = dev->config->config_info; + + /* setup data ready gpio interrupt */ + iis2mdc->gpio = device_get_binding(config->drdy_port); + if (iis2mdc->gpio == NULL) { + LOG_DBG("Cannot get pointer to %s device", + config->drdy_port); + return -EINVAL; + } + +#if defined(CONFIG_IIS2MDC_TRIGGER_OWN_THREAD) + k_sem_init(&iis2mdc->gpio_sem, 0, UINT_MAX); + k_thread_create(&iis2mdc->thread, iis2mdc->thread_stack, + CONFIG_IIS2MDC_THREAD_STACK_SIZE, + (k_thread_entry_t)iis2mdc_thread, dev, + 0, NULL, K_PRIO_COOP(CONFIG_IIS2MDC_THREAD_PRIORITY), + 0, K_NO_WAIT); +#elif defined(CONFIG_IIS2MDC_TRIGGER_GLOBAL_THREAD) + iis2mdc->work.handler = iis2mdc_work_cb; + iis2mdc->dev = dev; +#endif + + gpio_pin_configure(iis2mdc->gpio, config->drdy_pin, + GPIO_INPUT | config->drdy_flags); + + gpio_init_callback(&iis2mdc->gpio_cb, + iis2mdc_gpio_callback, + BIT(config->drdy_pin)); + + if (gpio_add_callback(iis2mdc->gpio, &iis2mdc->gpio_cb) < 0) { + LOG_DBG("Could not set gpio callback"); + return -EIO; + } + + return gpio_pin_interrupt_configure(iis2mdc->gpio, config->drdy_pin, + GPIO_INT_EDGE_TO_ACTIVE); +} diff --git a/dts/bindings/sensor/st,iis2mdc-i2c.yaml b/dts/bindings/sensor/st,iis2mdc-i2c.yaml new file mode 100644 index 00000000000..d51b481d90f --- /dev/null +++ b/dts/bindings/sensor/st,iis2mdc-i2c.yaml @@ -0,0 +1,19 @@ +# Copyright (c) 2018 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +description: | + STMicroelectronics IIS2MDC magnetometer accessed through I2C bus + +compatible: "st,iis2mdc" + +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,iis2mdc-spi.yaml b/dts/bindings/sensor/st,iis2mdc-spi.yaml new file mode 100644 index 00000000000..72583a61d39 --- /dev/null +++ b/dts/bindings/sensor/st,iis2mdc-spi.yaml @@ -0,0 +1,19 @@ +# Copyright (c) 2019 STMicroelectronics +# SPDX-License-Identifier: Apache-2.0 + +description: | + STMicroelectronics IIS2MDC magnetometer accessed through SPI bus + +compatible: "st,iis2mdc" + +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/dts_fixup.h b/tests/drivers/build_all/dts_fixup.h index 13197700bb9..d509450c6f2 100644 --- a/tests/drivers/build_all/dts_fixup.h +++ b/tests/drivers/build_all/dts_fixup.h @@ -139,6 +139,16 @@ #define DT_ST_IIS2DLPC_BUS_I2C 1 #endif +#ifndef DT_INST_0_ST_IIS2MDC_LABEL +#define DT_INST_0_ST_IIS2MDC_LABEL "" +#define DT_INST_0_ST_IIS2MDC_BUS_NAME "" +#define DT_INST_0_ST_IIS2MDC_BASE_ADDRESS 0 +#define DT_INST_0_ST_IIS2MDC_DRDY_GPIOS_FLAGS 0 +#define DT_INST_0_ST_IIS2MDC_DRDY_GPIOS_PIN 0 +#define DT_INST_0_ST_IIS2MDC_DRDY_GPIOS_CONTROLLER "" +#define DT_ST_IIS2MDC_BUS_I2C 1 +#endif + #ifndef DT_INST_0_ST_LIS3MDL_MAGN_LABEL #define DT_INST_0_ST_LIS3MDL_MAGN_LABEL "" #define DT_INST_0_ST_LIS3MDL_MAGN_BUS_NAME "" @@ -527,6 +537,17 @@ #define DT_ST_ISM330DHCX_BUS_SPI 1 #endif +#ifndef DT_INST_0_ST_IIS2MDC_LABEL +#define DT_INST_0_ST_IIS2MDC_LABEL "" +#define DT_INST_0_ST_IIS2MDC_BUS_NAME "" +#define DT_INST_0_ST_IIS2MDC_SPI_MAX_FREQUENCY 100000 +#define DT_INST_0_ST_IIS2MDC_BASE_ADDRESS 1 +#define DT_INST_0_ST_IIS2MDC_DRDY_GPIOS_CONTROLLER "" +#define DT_INST_0_ST_IIS2MDC_DRDY_GPIOS_FLAGS 0 +#define DT_INST_0_ST_IIS2MDC_DRDY_GPIOS_PIN 0 +#define DT_ST_IIS2MDC_BUS_SPI 1 +#endif + #ifndef DT_INST_0_ST_IIS3DHHC_LABEL #define DT_INST_0_ST_IIS3DHHC_LABEL "" #define DT_INST_0_ST_IIS3DHHC_BASE_ADDRESS 0 diff --git a/tests/drivers/build_all/sensors_stmemsc.conf b/tests/drivers/build_all/sensors_stmemsc.conf index e901801b118..75e2259915d 100644 --- a/tests/drivers/build_all/sensors_stmemsc.conf +++ b/tests/drivers/build_all/sensors_stmemsc.conf @@ -9,3 +9,4 @@ CONFIG_IIS2DLPC=y CONFIG_LIS2DW12=y CONFIG_STTS751=y CONFIG_ISM330DHCX=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 ccbb8f0f943..3c077dd36ac 100644 --- a/tests/drivers/build_all/sensors_stmemsc_trigger.conf +++ b/tests/drivers/build_all/sensors_stmemsc_trigger.conf @@ -13,3 +13,5 @@ CONFIG_STTS751=y CONFIG_STTS751_TRIGGER_OWN_THREAD=y CONFIG_ISM330DHCX=y CONFIG_ISM330DHCX_TRIGGER_OWN_THREAD=y +CONFIG_IIS2MDC=y +CONFIG_IIS2MDC_TRIGGER_OWN_THREAD=y