diff --git a/drivers/sensor/st/lis3mdl/CMakeLists.txt b/drivers/sensor/st/lis3mdl/CMakeLists.txt index 75579f8e2222a8..3f651134d8343c 100644 --- a/drivers/sensor/st/lis3mdl/CMakeLists.txt +++ b/drivers/sensor/st/lis3mdl/CMakeLists.txt @@ -3,4 +3,6 @@ zephyr_library() zephyr_library_sources(lis3mdl.c) +zephyr_library_sources(lis3mdl_i2c.c) +zephyr_library_sources(lis3mdl_spi.c) zephyr_library_sources_ifdef(CONFIG_LIS3MDL_TRIGGER lis3mdl_trigger.c) diff --git a/drivers/sensor/st/lis3mdl/Kconfig b/drivers/sensor/st/lis3mdl/Kconfig index 55e80400822ce6..df7ab5606ab459 100644 --- a/drivers/sensor/st/lis3mdl/Kconfig +++ b/drivers/sensor/st/lis3mdl/Kconfig @@ -5,9 +5,10 @@ menuconfig LIS3MDL bool "LIS3MDL magnetometer" default y depends on DT_HAS_ST_LIS3MDL_MAGN_ENABLED - select I2C + select I2C if $(dt_compat_on_bus,$(DT_COMPAT_ST_LIS3MDL_MAGN),i2c) + select SPI if $(dt_compat_on_bus,$(DT_COMPAT_ST_LIS3MDL_MAGN),spi) help - Enable driver for LIS3MDL I2C-based magnetometer. + Enable driver for LIS3MDL magnetometer. if LIS3MDL diff --git a/drivers/sensor/st/lis3mdl/lis3mdl.c b/drivers/sensor/st/lis3mdl/lis3mdl.c index 801cb238363b5e..b00151e4b4787b 100644 --- a/drivers/sensor/st/lis3mdl/lis3mdl.c +++ b/drivers/sensor/st/lis3mdl/lis3mdl.c @@ -6,7 +6,6 @@ #define DT_DRV_COMPAT st_lis3mdl_magn -#include #include #include #include @@ -74,7 +73,7 @@ int lis3mdl_sample_fetch(const struct device *dev, enum sensor_channel chan) __ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL); /* fetch magnetometer sample */ - if (i2c_burst_read_dt(&config->i2c, LIS3MDL_REG_SAMPLE_START, + if (drv_data->hw_tf->read_data(dev, LIS3MDL_REG_SAMPLE_START, (uint8_t *)buf, 6) < 0) { LOG_DBG("Failed to fetch magnetometer sample."); return -EIO; @@ -86,7 +85,7 @@ int lis3mdl_sample_fetch(const struct device *dev, enum sensor_channel chan) * the same read as magnetometer data, so do another * burst read to fetch the temperature sample */ - if (i2c_burst_read_dt(&config->i2c, LIS3MDL_REG_SAMPLE_START + 6, + if (drv_data->hw_tf->read_data(dev, LIS3MDL_REG_SAMPLE_START + 6, (uint8_t *)(buf + 3), 2) < 0) { LOG_DBG("Failed to fetch temperature sample."); return -EIO; @@ -112,16 +111,20 @@ static const struct sensor_driver_api lis3mdl_driver_api = { int lis3mdl_init(const struct device *dev) { const struct lis3mdl_config *config = dev->config; - uint8_t chip_cfg[6]; + const struct lis3mdl_data *data = dev->data; + uint8_t chip_cfg[5]; uint8_t id, idx; - if (!device_is_ready(config->i2c.bus)) { - LOG_ERR("I2C bus device not ready"); + int ret; + + ret = config->bus_init(dev); + if (ret < 0) { + LOG_ERR("bus device not ready"); return -ENODEV; } /* check chip ID */ - if (i2c_reg_read_byte_dt(&config->i2c, LIS3MDL_REG_WHO_AM_I, &id) < 0) { + if (data->hw_tf->read_reg(dev, LIS3MDL_REG_WHO_AM_I, &id) < 0) { LOG_ERR("Failed to read chip ID."); return -EIO; } @@ -144,18 +147,17 @@ int lis3mdl_init(const struct device *dev) } /* Configure sensor */ - chip_cfg[0] = LIS3MDL_REG_CTRL1; - chip_cfg[1] = lis3mdl_odr_bits[idx]; + chip_cfg[0] = lis3mdl_odr_bits[idx]; #ifdef CONFIG_LIS3MDL_DIE_TEMP_EN - chip_cfg[1] |= LIS3MDL_TEMP_EN_MASK; + chip_cfg[0] |= LIS3MDL_TEMP_EN_MASK; #endif /*LIS3MDL_DIE_TEMP_EN*/ - chip_cfg[2] = LIS3MDL_FS_IDX << LIS3MDL_FS_SHIFT; - chip_cfg[3] = LIS3MDL_MD_CONTINUOUS; - chip_cfg[4] = ((lis3mdl_odr_bits[idx] & LIS3MDL_OM_MASK) >> + chip_cfg[1] = LIS3MDL_FS_IDX << LIS3MDL_FS_SHIFT; + chip_cfg[2] = LIS3MDL_MD_CONTINUOUS; + chip_cfg[3] = ((lis3mdl_odr_bits[idx] & LIS3MDL_OM_MASK) >> LIS3MDL_OM_SHIFT) << LIS3MDL_OMZ_SHIFT; - chip_cfg[5] = LIS3MDL_BDU_EN; + chip_cfg[4] = LIS3MDL_BDU_EN; - if (i2c_write_dt(&config->i2c, chip_cfg, 6) < 0) { + if (data->hw_tf->write_data(dev, LIS3MDL_REG_CTRL1, chip_cfg, 5) < 0) { LOG_DBG("Failed to configure chip."); return -EIO; } @@ -172,17 +174,64 @@ int lis3mdl_init(const struct device *dev) return 0; } -#define LIS3MDL_DEFINE(inst) \ - static struct lis3mdl_data lis3mdl_data_##inst; \ - \ - static struct lis3mdl_config lis3mdl_config_##inst = { \ - .i2c = I2C_DT_SPEC_INST_GET(inst), \ - IF_ENABLED(CONFIG_LIS3MDL_TRIGGER, \ - (.irq_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, irq_gpios, { 0 }),)) \ - }; \ - \ - SENSOR_DEVICE_DT_INST_DEFINE(inst, lis3mdl_init, NULL, \ - &lis3mdl_data_##inst, &lis3mdl_config_##inst, POST_KERNEL, \ - CONFIG_SENSOR_INIT_PRIORITY, &lis3mdl_driver_api); \ + + +/* + * Device creation macro, shared by LIS3MDL_DEFINE_SPI() and + * LIS3MDL_DEFINE_I2C(). + */ + +#define LIS3MDL_DEVICE_INIT(inst) \ + \ + SENSOR_DEVICE_DT_INST_DEFINE(inst, \ + lis3mdl_init, \ + NULL, \ + &lis3mdl_data_##inst, \ + &lis3mdl_config_##inst, \ + POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, \ + &lis3mdl_driver_api); + +#ifdef CONFIG_LIS3MDL_TRIGGER +#define LIS3MDL_CFG_IRQ(inst) \ + .irq_gpio = GPIO_DT_SPEC_INST_GET(inst, irq_gpios, { 0 }), +#else +#define LIS3MDL_CFG_IRQ(inst) +#endif /* CONFIG_LIS3MDL_TRIGGER */ + +/* + * config macro used when a device is on a SPI bus. + */ + +#define LIS3MDL_CONFIG_SPI(inst) \ + { \ + .bus_init = lis3mdl_spi_init, \ + .bus_cfg.spi = SPI_DT_SPEC_INST_GET(inst, SPI_WORD_SET(8) | \ + SPI_OP_MODE_MASTER | \ + SPI_MODE_CPOL | \ + SPI_MODE_CPHA, 0), \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, irq_gpios), \ + (LIS3MDL_CFG_IRQ(inst)), ()) \ + } + +/* + * config macro used when a device is on an I2C bus. + */ + +#define LIS3MDL_CONFIG_I2C(inst) \ + { \ + .bus_init = lis3mdl_i2c_init, \ + .bus_cfg.i2c = I2C_DT_SPEC_INST_GET(inst), \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, irq_gpios), \ + (LIS3MDL_CFG_IRQ(inst)), ()) \ + } + +#define LIS3MDL_DEFINE(inst) \ + static struct lis3mdl_data lis3mdl_data_##inst; \ + static struct lis3mdl_config lis3mdl_config_##inst = \ + COND_CODE_1(DT_INST_ON_BUS(inst, spi), \ + (LIS3MDL_CONFIG_SPI(inst)), \ + (LIS3MDL_CONFIG_I2C(inst))) \ + LIS3MDL_DEVICE_INIT(inst) DT_INST_FOREACH_STATUS_OKAY(LIS3MDL_DEFINE) diff --git a/drivers/sensor/st/lis3mdl/lis3mdl.h b/drivers/sensor/st/lis3mdl/lis3mdl.h index ffda6558f1b128..aa615c844a9038 100644 --- a/drivers/sensor/st/lis3mdl/lis3mdl.h +++ b/drivers/sensor/st/lis3mdl/lis3mdl.h @@ -10,10 +10,17 @@ #include #include #include -#include #include #include +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) +#include +#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) */ + +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) +#include +#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */ + #define LIS3MDL_REG_WHO_AM_I 0x0F #define LIS3MDL_CHIP_ID 0x3D @@ -109,6 +116,35 @@ static const uint16_t lis3mdl_magn_gain[] = { 6842, 3421, 2281, 1711 }; +union lis3mdl_bus_cfg { +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) + struct i2c_dt_spec i2c; +#endif + +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) + struct spi_dt_spec spi; +#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) */ +}; + +struct lis3mdl_transfer_function { + int (*read_data)(const struct device *dev, uint8_t reg_addr, + uint8_t *value, uint8_t len); + int (*write_data)(const struct device *dev, uint8_t reg_addr, + uint8_t *value, uint8_t len); + int (*read_reg)(const struct device *dev, uint8_t reg_addr, + uint8_t *value); + int (*update_reg)(const struct device *dev, uint8_t reg_addr, + uint8_t mask, uint8_t value); +}; + +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) +int lis3mdl_i2c_init(const struct device *dev); +#endif /*DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)*/ + +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) +int lis3mdl_spi_init(const struct device *dev); +#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) */ + struct lis3mdl_data { int16_t x_sample; int16_t y_sample; @@ -117,6 +153,7 @@ struct lis3mdl_data { int16_t temp_sample; #endif /*CONFIG_LIS3MDL_DIE_TEMP_EN*/ + const struct lis3mdl_transfer_function *hw_tf; #ifdef CONFIG_LIS3MDL_TRIGGER const struct device *dev; struct gpio_callback gpio_cb; @@ -136,7 +173,8 @@ struct lis3mdl_data { }; struct lis3mdl_config { - struct i2c_dt_spec i2c; + int (*bus_init)(const struct device *dev); + const union lis3mdl_bus_cfg bus_cfg; #ifdef CONFIG_LIS3MDL_TRIGGER struct gpio_dt_spec irq_gpio; #endif /*CONFIG_LIS3MDL_TRIGGER*/ diff --git a/drivers/sensor/st/lis3mdl/lis3mdl_i2c.c b/drivers/sensor/st/lis3mdl/lis3mdl_i2c.c new file mode 100644 index 00000000000000..d87f26c688db98 --- /dev/null +++ b/drivers/sensor/st/lis3mdl/lis3mdl_i2c.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* adapted from lsm6dsl_i2c.c - I2C routines for LSM6DSL driver + */ +#define DT_DRV_COMPAT st_lis3mdl_magn + +#include +#include + +#include "lis3mdl.h" + +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) + +LOG_MODULE_DECLARE(LIS3MDL, CONFIG_SENSOR_LOG_LEVEL); + +static int lis3mdl_i2c_read_data(const struct device *dev, uint8_t reg_addr, + uint8_t *value, uint8_t len) +{ + const struct lis3mld_config *cfg = dev->config; + + return i2c_burst_read_dt(&cfg->bus_cfg.i2c, reg_addr, value, len); +} + +static int lis3mdl_i2c_write_data(const struct device *dev, uint8_t reg_addr, + uint8_t *value, uint8_t len) +{ + const struct lis3mdl_config *cfg = dev->config; + + return i2c_burst_write_dt(&cfg->bus_cfg.i2c, reg_addr, value, len); +} + +static int lis3mdl_i2c_read_reg(const struct device *dev, uint8_t reg_addr, + uint8_t *value) +{ + const struct lis3mdl_config *cfg = dev->config; + + return i2c_reg_read_byte_dt(&cfg->bus_cfg.i2c, reg_addr, value); +} + +static int lis3mdl_i2c_update_reg(const struct device *dev, uint8_t reg_addr, + uint8_t mask, uint8_t value) +{ + const struct lis3mdl_config *cfg = dev->config; + + return i2c_reg_update_byte_dt(&cfg->bus_cfg.i2c, reg_addr, mask, value); +} + + +static const struct lis3mdl_transfer_function lis3mdl_i2c_transfer_fn = { + .read_data = lis3mdl_i2c_read_data, + .write_data = lis3mdl_i2c_write_data, + .read_reg = lis3mdl_i2c_read_reg, + .update_reg = lis3mdl_i2c_update_reg, +}; + +int lis3mdl_i2c_init(const struct device *dev) +{ + struct lis3mdl_data *data = dev->data; + const struct lis3mdl_config *cfg = dev->config; + + data->hw_tf = &lis3mdl_i2c_transfer_fn; + + if (!device_is_ready(cfg->bus_cfg.i2c.bus)) { + return -ENODEV; + } + + return 0; +} +#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */ \ No newline at end of file diff --git a/drivers/sensor/st/lis3mdl/lis3mdl_spi.c b/drivers/sensor/st/lis3mdl/lis3mdl_spi.c new file mode 100644 index 00000000000000..4e8f6c6f83f147 --- /dev/null +++ b/drivers/sensor/st/lis3mdl/lis3mdl_spi.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2018 STMicroelectronics + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* adapted from lsm6dsl_spi.c - SPI routines for LSM6DSL driver + */ +#define DT_DRV_COMPAT st_lis3mdl_magn + +#include +#include + +#include "lis3mdl.h" + +#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) + +#define LIS3MDL_SPI_READ (1 << 7) + +LOG_MODULE_DECLARE(LIS3MDL, CONFIG_SENSOR_LOG_LEVEL); + +static int lis3mdl_raw_read(const struct device *dev, uint8_t reg_addr, + uint8_t *value, uint8_t len) +{ + const struct lis3mdl_config *cfg = dev->config; + uint8_t buffer_tx[2] = { reg_addr | LIS3MDL_SPI_READ, 0 }; + const struct spi_buf tx_buf = { + .buf = buffer_tx, + .len = 2, + }; + if (len > 1){ + // Set ms bit to auto increment address to be read + buffer_tx[0] |= BIT(6); + } + 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_dt(&cfg->bus_cfg.spi, &tx, &rx)) { + return -EIO; + } + + return 0; +} + +static int lis3mdl_raw_write(const struct device *dev, uint8_t reg_addr, + uint8_t *value, uint8_t len) +{ + const struct lis3mdl_config *cfg = dev->config; + uint8_t buffer_tx[1] = { reg_addr & ~LIS3MDL_SPI_READ }; + if (len > 1){ + // Set ms bit to auto increment address to be read + buffer_tx[0] |= BIT(6); + } + 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_dt(&cfg->bus_cfg.spi, &tx)) { + return -EIO; + } + + return 0; +} + +static int lis3mdl_spi_read_data(const struct device *dev, uint8_t reg_addr, + uint8_t *value, uint8_t len) +{ + return lis3mdl_raw_read(dev, reg_addr, value, len); +} + +static int lis3mdl_spi_write_data(const struct device *dev, uint8_t reg_addr, + uint8_t *value, uint8_t len) +{ + return lis3mdl_raw_write(dev, reg_addr, value, len); +} + +static int lis3mdl_spi_read_reg(const struct device *dev, uint8_t reg_addr, + uint8_t *value) +{ + return lis3mdl_raw_read(dev, reg_addr, value, 1); +} + +static int lis3mdl_spi_update_reg(const struct device *dev, uint8_t reg_addr, + uint8_t mask, uint8_t value) +{ + uint8_t tmp_val; + + lis3mdl_raw_read(dev, reg_addr, &tmp_val, 1); + tmp_val = (tmp_val & ~mask) | (value & mask); + + return lis3mdl_raw_write(dev, reg_addr, &tmp_val, 1); +} + +static const struct lis3mdl_transfer_function lis3mdl_spi_transfer_fn = { + .read_data = lis3mdl_spi_read_data, + .write_data = lis3mdl_spi_write_data, + .read_reg = lis3mdl_spi_read_reg, + .update_reg = lis3mdl_spi_update_reg, +}; + +int lis3mdl_spi_init(const struct device *dev) +{ + struct lis3mdl_data *data = dev->data; + const struct lis3mdl_config *cfg = dev->config; + + data->hw_tf = &lis3mdl_spi_transfer_fn; + + if (!spi_is_ready_dt(&cfg->bus_cfg.spi)) { + return -ENODEV; + } + + return 0; +} +#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) */ \ No newline at end of file diff --git a/drivers/sensor/st/lis3mdl/lis3mdl_trigger.c b/drivers/sensor/st/lis3mdl/lis3mdl_trigger.c index ba94076bd14f43..3c17271cc4c8d1 100644 --- a/drivers/sensor/st/lis3mdl/lis3mdl_trigger.c +++ b/drivers/sensor/st/lis3mdl/lis3mdl_trigger.c @@ -7,7 +7,6 @@ #define DT_DRV_COMPAT st_lis3mdl_magn #include -#include #include #include #include @@ -33,7 +32,7 @@ int lis3mdl_trigger_set(const struct device *dev, __ASSERT_NO_MSG(trig->type == SENSOR_TRIG_DATA_READY); /* dummy read: re-trigger interrupt */ - ret = i2c_burst_read_dt(&config->i2c, LIS3MDL_REG_SAMPLE_START, + ret = drv_data->hw_tf->read_data(dev, LIS3MDL_REG_SAMPLE_START, (uint8_t *)buf, 6); if (ret != 0) { return ret; diff --git a/dts/bindings/sensor/st,lis3mdl-i2c.yaml b/dts/bindings/sensor/st,lis3mdl-i2c.yaml new file mode 100644 index 00000000000000..9ffde44c66a715 --- /dev/null +++ b/dts/bindings/sensor/st,lis3mdl-i2c.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024, DeMurlot +# SPDX-License-Identifier: Apache-2.0 + +description: STMicroelectronics LIS3MDL magnetometer accessed through I2C bus + +compatible: "st,lis3mdl-magn" + +include: ["i2c-device.yaml", "st,lis3mdl-magn.yaml"] diff --git a/dts/bindings/sensor/st,lis3mdl-magn.yaml b/dts/bindings/sensor/st,lis3mdl-magn.yaml index 4e9b79387dc95c..699f3fff2de3c2 100644 --- a/dts/bindings/sensor/st,lis3mdl-magn.yaml +++ b/dts/bindings/sensor/st,lis3mdl-magn.yaml @@ -1,11 +1,7 @@ -# Copyright (c) 2017, Linaro Limited +# Copyright (c) 2024, DeMurlot # SPDX-License-Identifier: Apache-2.0 -description: STMicroelectronics LIS3MDL magnetometer - -compatible: "st,lis3mdl-magn" - -include: [sensor-device.yaml, i2c-device.yaml] +include: [sensor-device.yaml] properties: irq-gpios: diff --git a/dts/bindings/sensor/st,lis3mdl-spi.yaml b/dts/bindings/sensor/st,lis3mdl-spi.yaml new file mode 100644 index 00000000000000..6fa852883fb519 --- /dev/null +++ b/dts/bindings/sensor/st,lis3mdl-spi.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2024, DeMurlot +# SPDX-License-Identifier: Apache-2.0 + +description: STMicroelectronics LIS3MDL magnetometer accessed through SPI bus + +compatible: "st,lis3mdl-magn" + +include: ["spi-device.yaml", "st,lis3mdl-magn.yaml"] diff --git a/tests/drivers/build_all/sensor/spi.dtsi b/tests/drivers/build_all/sensor/spi.dtsi index c173c5ccecb61f..e32bf2dfe074c5 100644 --- a/tests/drivers/build_all/sensor/spi.dtsi +++ b/tests/drivers/build_all/sensor/spi.dtsi @@ -327,6 +327,14 @@ test_spi_lis2de12: lis2de12@29 { status = "okay"; }; +test_spi_lis3mdl_magn: lis3mdl-magn@30 { + compatible = "st,lis3mdl-magn"; + reg = <0x30>; + spi-max-frequency = <0>; + irq-gpios = <&test_gpio 0 0>; + status = "okay"; +}; + test_spi_ens160: ens160@2a { compatible = "sciosense,ens160"; reg = <0x2a>;