From d5de0788ad30973dabd03dc3b59cbb79f6501446 Mon Sep 17 00:00:00 2001 From: Tomasz Bursztyka Date: Wed, 8 Sep 2021 13:05:32 +0200 Subject: [PATCH] drivers/sensors: Enable 3-wire SPI access to HTS221 sensor driver HTS221 is a humidity and temperature sensor (thus HTS) that can be wired on i2c or SPI bus. On SPI bus however, it uses the 3-wire mode, aka: half-duplex. Now that SPI API exposes half duplex operation, let's enable the SPI bus on that sensor. Let's move to a better DTS integrated driver as well, and also use stmemsc interface. Signed-off-by: Tomasz Bursztyka --- drivers/sensor/hts221/CMakeLists.txt | 2 + drivers/sensor/hts221/Kconfig | 6 +- drivers/sensor/hts221/hts221.c | 191 ++++++++++++++++++------- drivers/sensor/hts221/hts221.h | 62 ++++---- drivers/sensor/hts221/hts221_trigger.c | 53 ++++--- 5 files changed, 210 insertions(+), 104 deletions(-) diff --git a/drivers/sensor/hts221/CMakeLists.txt b/drivers/sensor/hts221/CMakeLists.txt index 1e650006ce9..e6d4b36bcbc 100644 --- a/drivers/sensor/hts221/CMakeLists.txt +++ b/drivers/sensor/hts221/CMakeLists.txt @@ -4,3 +4,5 @@ zephyr_library() zephyr_library_sources(hts221.c) zephyr_library_sources_ifdef(CONFIG_HTS221_TRIGGER hts221_trigger.c) + +zephyr_library_include_directories(../stmemsc) diff --git a/drivers/sensor/hts221/Kconfig b/drivers/sensor/hts221/Kconfig index be1987a525e..e14e3a23c5b 100644 --- a/drivers/sensor/hts221/Kconfig +++ b/drivers/sensor/hts221/Kconfig @@ -3,9 +3,11 @@ menuconfig HTS221 bool "HTS221 temperature and humidity sensor" - depends on I2C + depends on I2C || SPI + select HAS_STMEMSC + select USE_STDC_HTS221 help - Enable driver for HTS221 I2C-based temperature and humidity sensor. + Enable driver for HTS221 I2C/SPI-based temperature and humidity sensor. if HTS221 diff --git a/drivers/sensor/hts221/hts221.c b/drivers/sensor/hts221/hts221.c index d448562833a..f62b10c629f 100644 --- a/drivers/sensor/hts221/hts221.c +++ b/drivers/sensor/hts221/hts221.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include @@ -18,8 +17,15 @@ LOG_MODULE_REGISTER(HTS221, CONFIG_SENSOR_LOG_LEVEL); -static const char * const hts221_odr_strings[] = { - "1", "7", "12.5" +struct str2odr { + const char *str; + hts221_odr_t odr; +}; + +static const struct str2odr hts221_odrs[] = { + { "1", HTS221_ODR_1Hz }, + { "7", HTS221_ODR_7Hz }, + { "12.5", HTS221_ODR_12Hz5 }, }; static int hts221_channel_get(const struct device *dev, @@ -63,15 +69,17 @@ static int hts221_sample_fetch(const struct device *dev, { struct hts221_data *data = dev->data; const struct hts221_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; uint8_t buf[4]; + int status; __ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL); - if (i2c_burst_read(data->i2c, cfg->i2c_addr, - HTS221_REG_DATA_START | HTS221_AUTOINCREMENT_ADDR, - buf, 4) < 0) { + status = hts221_read_reg(ctx, HTS221_HUMIDITY_OUT_L | + HTS221_AUTOINCREMENT_ADDR, buf, 4); + if (status < 0) { LOG_ERR("Failed to fetch data sample."); - return -EIO; + return status; } data->rh_sample = sys_le16_to_cpu(buf[0] | (buf[1] << 8)); @@ -84,13 +92,15 @@ static int hts221_read_conversion_data(const struct device *dev) { struct hts221_data *data = dev->data; const struct hts221_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; uint8_t buf[16]; + int status; - if (i2c_burst_read(data->i2c, cfg->i2c_addr, - HTS221_REG_CONVERSION_START | - HTS221_AUTOINCREMENT_ADDR, buf, 16) < 0) { + status = hts221_read_reg(ctx, HTS221_H0_RH_X2 | + HTS221_AUTOINCREMENT_ADDR, buf, 16); + if (status < 0) { LOG_ERR("Failed to read conversion data."); - return -EIO; + return status; } data->h0_rh_x2 = buf[0]; @@ -116,45 +126,51 @@ static const struct sensor_driver_api hts221_driver_api = { int hts221_init(const struct device *dev) { const struct hts221_config *cfg = dev->config; - struct hts221_data *data = dev->data; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; uint8_t id, idx; - - data->i2c = device_get_binding(cfg->i2c_bus); - if (data->i2c == NULL) { - LOG_ERR("Could not get pointer to %s device.", cfg->i2c_bus); - return -EINVAL; - } + int status; /* check chip ID */ - if (i2c_reg_read_byte(data->i2c, cfg->i2c_addr, - HTS221_REG_WHO_AM_I, &id) < 0) { + + status = hts221_device_id_get(ctx, &id); + if (status < 0) { LOG_ERR("Failed to read chip ID."); - return -EIO; + return status; } - if (id != HTS221_CHIP_ID) { + if (id != HTS221_ID) { LOG_ERR("Invalid chip ID."); return -EINVAL; } /* check if CONFIG_HTS221_ODR is valid */ - for (idx = 0U; idx < ARRAY_SIZE(hts221_odr_strings); idx++) { - if (!strcmp(hts221_odr_strings[idx], CONFIG_HTS221_ODR)) { + for (idx = 0U; idx < ARRAY_SIZE(hts221_odrs); idx++) { + if (!strcmp(hts221_odrs[idx].str, CONFIG_HTS221_ODR)) { break; } } - if (idx == ARRAY_SIZE(hts221_odr_strings)) { - LOG_ERR("Invalid ODR value."); + if (idx == ARRAY_SIZE(hts221_odrs)) { + LOG_ERR("Invalid ODR value %s.", CONFIG_HTS221_ODR); return -EINVAL; } - if (i2c_reg_write_byte(data->i2c, cfg->i2c_addr, - HTS221_REG_CTRL1, - (idx + 1) << HTS221_ODR_SHIFT | HTS221_BDU_BIT | - HTS221_PD_BIT) < 0) { - LOG_ERR("Failed to configure chip."); - return -EIO; + status = hts221_data_rate_set(ctx, hts221_odrs[idx].odr); + if (status < 0) { + LOG_ERR("Could not set output data rate"); + return status; + } + + status = hts221_block_data_update_set(ctx, 1); + if (status < 0) { + LOG_ERR("Could not set BDU bit"); + return status; + } + + status = hts221_power_on_set(ctx, 1); + if (status < 0) { + LOG_ERR("Could not set PD bit"); + return status; } /* @@ -163,15 +179,17 @@ int hts221_init(const struct device *dev) */ k_sleep(K_MSEC(3)); - if (hts221_read_conversion_data(dev) < 0) { + status = hts221_read_conversion_data(dev); + if (status < 0) { LOG_ERR("Failed to read conversion data."); - return -EINVAL; + return status; } #if HTS221_TRIGGER_ENABLED - if (hts221_init_interrupt(dev) < 0) { + status = hts221_init_interrupt(dev); + if (status < 0) { LOG_ERR("Failed to initialize interrupt."); - return -EIO; + return status; } #else LOG_INF("Cannot enable trigger without drdy-gpios"); @@ -180,17 +198,92 @@ int hts221_init(const struct device *dev) return 0; } -static struct hts221_data hts221_driver; -static const struct hts221_config hts221_cfg = { - .i2c_bus = DT_INST_BUS_LABEL(0), - .i2c_addr = DT_INST_REG_ADDR(0), -#if HTS221_TRIGGER_ENABLED - .drdy_pin = DT_INST_GPIO_PIN(0, drdy_gpios), - .drdy_flags = DT_INST_GPIO_FLAGS(0, drdy_gpios), - .drdy_controller = DT_INST_GPIO_LABEL(0, drdy_gpios), -#endif /* HTS221_TRIGGER_ENABLED */ -}; +#if DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 0 +#warning "HTS221 driver enabled without any devices" +#endif -DEVICE_DT_INST_DEFINE(0, hts221_init, NULL, - &hts221_driver, &hts221_cfg, POST_KERNEL, - CONFIG_SENSOR_INIT_PRIORITY, &hts221_driver_api); +/* + * Device creation macros + */ + +#define HTS221_DEVICE_INIT(inst) \ + DEVICE_DT_INST_DEFINE(inst, \ + hts221_init, \ + NULL, \ + &hts221_data_##inst, \ + &hts221_config_##inst, \ + POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, \ + &hts221_driver_api); + +/* + * Instantiation macros used when a device is on a SPI bus. + */ + +#ifdef CONFIG_HTS221_TRIGGER +#define HTS221_CFG_IRQ(inst) \ + .gpio_drdy = GPIO_DT_SPEC_INST_GET(inst, irq_gpios) +#else +#define HTS221_CFG_IRQ(inst) +#endif /* CONFIG_HTS221_TRIGGER */ + +#define HTS221_SPI_OPERATION (SPI_WORD_SET(8) | \ + SPI_OP_MODE_MASTER | \ + SPI_MODE_CPOL | \ + SPI_MODE_CPHA | \ + SPI_HALF_DUPLEX) \ + +#define HTS221_CONFIG_SPI(inst) \ + { \ + .ctx = { \ + .read_reg = \ + (stmdev_read_ptr) stmemsc_spi_read, \ + .write_reg = \ + (stmdev_write_ptr) stmemsc_spi_write, \ + .handle = \ + (void *)&hts221_config_##inst.stmemsc_cfg, \ + }, \ + .stmemsc_cfg = { \ + .spi = SPI_DT_SPEC_INST_GET(inst, \ + HTS221_SPI_OPERATION, \ + 0), \ + }, \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, irq_gpios), \ + (HTS221_CFG_IRQ(inst)), ()) \ + } + +/* + * Instantiation macros used when a device is on an I2C bus. + */ + +#define HTS221_CONFIG_I2C(inst) \ + { \ + .ctx = { \ + .read_reg = \ + (stmdev_read_ptr) stmemsc_i2c_read, \ + .write_reg = \ + (stmdev_write_ptr) stmemsc_i2c_write, \ + .handle = \ + (void *)&hts221_config_##inst.stmemsc_cfg, \ + }, \ + .stmemsc_cfg = { \ + .i2c = I2C_DT_SPEC_INST_GET(inst), \ + }, \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, irq_gpios), \ + (HTS221_CFG_IRQ(inst)), ()) \ + } + +/* + * Main instantiation macro. Use of COND_CODE_1() selects the right + * bus-specific macro at preprocessor time. + */ + +#define HTS221_DEFINE(inst) \ + static struct hts221_data hts221_data_##inst; \ + static const struct hts221_config hts221_config_##inst = \ + COND_CODE_1(DT_INST_ON_BUS(inst, spi), \ + (HTS221_CONFIG_SPI(inst)), \ + (HTS221_CONFIG_I2C(inst))); \ + HTS221_DEVICE_INIT(inst) + +DT_INST_FOREACH_STATUS_OKAY(HTS221_DEFINE) diff --git a/drivers/sensor/hts221/hts221.h b/drivers/sensor/hts221/hts221.h index 2df0d6d8fd7..f315b82c88e 100644 --- a/drivers/sensor/hts221/hts221.h +++ b/drivers/sensor/hts221/hts221.h @@ -10,29 +10,23 @@ #include #include #include +#include #include +#include -#define HTS221_TRIGGER_ENABLED (DT_INST_NODE_HAS_PROP(0, drdy_gpios) && \ - IS_ENABLED(CONFIG_HTS221_TRIGGER)) +#include "hts221_reg.h" -#define HTS221_AUTOINCREMENT_ADDR BIT(7) +#define HTS221_AUTOINCREMENT_ADDR BIT(7) -#define HTS221_REG_WHO_AM_I 0x0F -#define HTS221_CHIP_ID 0xBC +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) +#include +#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) */ -#define HTS221_REG_CTRL1 0x20 -#define HTS221_PD_BIT BIT(7) -#define HTS221_BDU_BIT BIT(2) -#define HTS221_ODR_SHIFT 0 - -#define HTS221_REG_CTRL3 0x22 -#define HTS221_DRDY_EN BIT(2) - -#define HTS221_REG_DATA_START 0x28 -#define HTS221_REG_CONVERSION_START 0x30 +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) +#include +#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */ struct hts221_data { - const struct device *i2c; int16_t rh_sample; int16_t t_sample; @@ -45,9 +39,8 @@ struct hts221_data { int16_t t0_out; int16_t t1_out; -#if HTS221_TRIGGER_ENABLED +#ifdef CONFIG_HTS221_TRIGGER const struct device *dev; - const struct device *drdy_dev; struct gpio_callback drdy_cb; struct sensor_trigger data_ready_trigger; @@ -60,26 +53,35 @@ struct hts221_data { #elif defined(CONFIG_HTS221_TRIGGER_GLOBAL_THREAD) struct k_work work; #endif - -#endif /* HTS221_TRIGGER_ENABLED */ +#endif /* CONFIG_HTS221_TRIGGER */ }; struct hts221_config { - const char *i2c_bus; - uint16_t i2c_addr; -#if HTS221_TRIGGER_ENABLED - gpio_pin_t drdy_pin; - gpio_flags_t drdy_flags; - const char *drdy_controller; -#endif /* HTS221_TRIGGER_ENABLED */ + stmdev_ctx_t ctx; + union { +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) + const struct i2c_dt_spec i2c; +#endif +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) + const struct spi_dt_spec spi; +#endif + } stmemsc_cfg; + +#ifdef CONFIG_HTS221_TRIGGER + const struct gpio_dt_spec gpio_drdy; + const struct gpio_dt_spec gpio_int; +#endif /* CONFIG_HTS221_TRIGGER */ }; -#if HTS221_TRIGGER_ENABLED +#ifdef CONFIG_HTS221_TRIGGER int hts221_trigger_set(const struct device *dev, const struct sensor_trigger *trig, sensor_trigger_handler_t handler); int hts221_init_interrupt(const struct device *dev); -#endif +#endif /* CONFIG_HTS221_TRIGGER */ -#endif /* __SENSOR_HTS221__ */ +int hts221_spi_init(const struct device *dev); +int hts221_i2c_init(const struct device *dev); + +#endif /* ZEPHYR_DRIVERS_SENSOR_HTS221_HTS221_H_ */ diff --git a/drivers/sensor/hts221/hts221_trigger.c b/drivers/sensor/hts221/hts221_trigger.c index 59c61f3b12a..45665385630 100644 --- a/drivers/sensor/hts221/hts221_trigger.c +++ b/drivers/sensor/hts221/hts221_trigger.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016 Intel Corporation + * Copyright (c) 2016-2021 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ @@ -7,27 +7,24 @@ #define DT_DRV_COMPAT st_hts221 #include -#include #include #include #include -#include #include + #include "hts221.h" -#if HTS221_TRIGGER_ENABLED LOG_MODULE_DECLARE(HTS221, CONFIG_SENSOR_LOG_LEVEL); static inline void setup_drdy(const struct device *dev, bool enable) { - struct hts221_data *data = dev->data; const struct hts221_config *cfg = dev->config; unsigned int flags = enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE; - gpio_pin_interrupt_configure(data->drdy_dev, cfg->drdy_pin, flags); + gpio_pin_interrupt_configure_dt(&cfg->gpio_drdy, flags); } static inline void handle_drdy(const struct device *dev) @@ -79,7 +76,7 @@ int hts221_trigger_set(const struct device *dev, /* If DRDY is active we probably won't get the rising edge, so * invoke the callback manually. */ - if (gpio_pin_get(data->drdy_dev, cfg->drdy_pin) > 0) { + if (gpio_pin_get_dt(&cfg->gpio_drdy) > 0) { handle_drdy(dev); } @@ -121,33 +118,44 @@ int hts221_init_interrupt(const struct device *dev) { struct hts221_data *data = dev->data; const struct hts221_config *cfg = dev->config; + stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx; + int status; + + if (cfg->gpio_drdy.port == NULL) { + LOG_DBG("gpio_drdy not defined in DT"); + return 0; + } + + if (!device_is_ready(cfg->gpio_drdy.port)) { + LOG_ERR("device %s is not ready", cfg->gpio_drdy.port->name); + return -ENODEV; + } data->dev = dev; /* setup data ready gpio interrupt */ - data->drdy_dev = device_get_binding(cfg->drdy_controller); - if (data->drdy_dev == NULL) { - LOG_ERR("Cannot get pointer to %s device.", - cfg->drdy_controller); - return -EINVAL; + status = gpio_pin_configure_dt(&cfg->gpio_drdy, GPIO_INPUT); + if (status < 0) { + LOG_ERR("Could not configure %s.%02u", + cfg->gpio_drdy.port->name, cfg->gpio_drdy.pin); + return status; } - gpio_pin_configure(data->drdy_dev, cfg->drdy_pin, - GPIO_INPUT | cfg->drdy_flags); + gpio_init_callback(&data->drdy_cb, + hts221_drdy_callback, + BIT(cfg->gpio_drdy.pin)); - gpio_init_callback(&data->drdy_cb, hts221_drdy_callback, - BIT(cfg->drdy_pin)); - - if (gpio_add_callback(data->drdy_dev, &data->drdy_cb) < 0) { + status = gpio_add_callback(cfg->gpio_drdy.port, &data->drdy_cb); + if (status < 0) { LOG_ERR("Could not set gpio callback."); - return -EIO; + return status; } /* enable data-ready interrupt */ - if (i2c_reg_write_byte(data->i2c, cfg->i2c_addr, - HTS221_REG_CTRL3, HTS221_DRDY_EN) < 0) { + status = hts221_drdy_on_int_set(ctx, 1); + if (status < 0) { LOG_ERR("Could not enable data-ready interrupt."); - return -EIO; + return status; } #if defined(CONFIG_HTS221_TRIGGER_OWN_THREAD) @@ -166,4 +174,3 @@ int hts221_init_interrupt(const struct device *dev) return 0; } -#endif /* HTS221_TRIGGER_ENABLED */