diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index 1d919a0f7b3..e946e17a4af 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory_ifdef(CONFIG_ADT7420 adt7420) add_subdirectory_ifdef(CONFIG_ADXL362 adxl362) +add_subdirectory_ifdef(CONFIG_ADXL372 adxl372) add_subdirectory_ifdef(CONFIG_AK8975 ak8975) add_subdirectory_ifdef(CONFIG_AMG88XX amg88xx) add_subdirectory_ifdef(CONFIG_APDS9960 apds9960) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index f84cb9fb5b2..283e4685ef4 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -39,6 +39,8 @@ source "drivers/sensor/adt7420/Kconfig" source "drivers/sensor/adxl362/Kconfig" +source "drivers/sensor/adxl372/Kconfig" + source "drivers/sensor/ak8975/Kconfig" source "drivers/sensor/amg88xx/Kconfig" diff --git a/drivers/sensor/adxl372/CMakeLists.txt b/drivers/sensor/adxl372/CMakeLists.txt new file mode 100644 index 00000000000..2bd454d23f8 --- /dev/null +++ b/drivers/sensor/adxl372/CMakeLists.txt @@ -0,0 +1,9 @@ +# +# Copyright (c) 2018 Analog Devices Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# +zephyr_library() + +zephyr_library_sources_ifdef(CONFIG_ADXL372 adxl372.c) +zephyr_library_sources_ifdef(CONFIG_ADXL372_TRIGGER adxl372_trigger.c) diff --git a/drivers/sensor/adxl372/Kconfig b/drivers/sensor/adxl372/Kconfig new file mode 100644 index 00000000000..fbd7949b71d --- /dev/null +++ b/drivers/sensor/adxl372/Kconfig @@ -0,0 +1,330 @@ +# Kconfig - Micropower, 3-Axis, +/-200g Digital Accelerometer +# +# Copyright (c) 2018 Analog Devices Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig ADXL372 + bool "ADXL372 Three Axis High-g I2C/SPI accelerometer" + depends on SENSOR && (I2C || SPI) + help + Enable driver for ADXL372 Three-Axis Digital Accelerometers. + +if ADXL372 + +choice ADXL372_BUS_TYPE + prompt "Interface type" + help + Select interface the digital interface type for the ADXL372 + +config ADXL372_I2C + depends on I2C + bool "I2C Interface" + +config ADXL372_SPI + depends on SPI + bool "SPI Interface" + +endchoice + +if !HAS_DTS_I2C_DEVICE + +config ADXL372_DEV_NAME + string "ADXL372 device name" + depends on ADXL372_I2C + default "ADXL372" + +config ADXL372_I2C_ADDR + hex "I2C address for ADXL372" + depends on ADXL372_I2C + default 0x1D + help + I2C address of the ADXL372 sensor. + + 0x1D: if MISO is pulled low + 0x53: if MISO is pulled high + +config ADXL372_I2C_MASTER_DEV_NAME + string "I2C master where ADXL372 is connected" + depends on ADXL372_I2C + default "I2C_0" + help + Specify the device name of the I2C master device to which the + ADXL372 chip is connected. + +endif # !HAS_DTS_I2C_DEVICE + +if !HAS_DTS_SPI_DEVICE + +config ADXL372_DEV_NAME + string "ADXL372 device name" + depends on ADXL372_SPI + default "ADXL372" + +config ADXL372_SPI_DEV_NAME + string "SPI device where ADXL372 is connected" + depends on ADXL372_SPI + default "SPI_0" + help + Specify the device name of the SPI device to which ADXL372 is + connected. + +config ADXL372_SPI_DEV_SLAVE + int "SPI Slave Select where ADXL372 is connected" + depends on ADXL372_SPI + default 2 + help + Specify the slave select pin of the SPI to which ADXL372 is + connected. + +config ADXL372_SPI_GPIO_CS + bool "ADXL372 SPI CS through a GPIO pin" + depends on ADXL372_SPI + help + This option is useful if one needs to manage SPI CS through a GPIO + pin to by-pass the SPI controller's CS logic. + +config ADXL372_SPI_BUS_FREQ + int "ADXL372 SPI bus speed in Hz" + range 10000 10000000 + default 8000000 + help + This is the maximum supported SPI bus frequency. The chip supports a + frequency up to 10MHz. + +endif # !HAS_DTS_SPI_DEVICE + +if !HAS_DTS_SPI_PINS + +config ADXL372_SPI_GPIO_CS_DRV_NAME + string "GPIO driver's name to use to drive SPI CS through" + default "GPIO_0" + depends on ADXL372_SPI_GPIO_CS + help + This option is mandatory to set which GPIO controller to use in order + to actually emulate the SPI CS. + +config ADXL372_SPI_GPIO_CS_PIN + int "GPIO PIN to use to drive SPI CS through" + default 0 + depends on ADXL372_SPI_GPIO_CS + help + This option is mandatory to set which GPIO pin to use in order + to actually emulate the SPI CS. + +endif # !HAS_DTS_SPI_PINS + +choice + prompt "Operating mode" + default ADXL372_PEAK_DETECT_MODE + +config ADXL372_PEAK_DETECT_MODE + bool "Max Peak detect mode" + help + In most high-g applications, a single (3-axis) acceleration sample at + the peak of an impact event contains sufficient information + about the event, and the full acceleration history is not required. + In this mode the device returns only the over threshold + Peak Acceleration between two consecutive sample fetches. + +config ADXL372_MEASUREMENT_MODE + bool "Measurement Mode" + help + In this mode, acceleration data is provided continuously at the + output data rate (ODR). + +endchoice + +choice + prompt "Accelerometer sampling frequency (ODR)" + default ADXL372_ODR_6400HZ if ADXL372_PEAK_DETECT_MODE + default ADXL372_ODR_400HZ if ADXL372_MEASUREMENT_MODE + +config ADXL372_ODR_400HZ + bool "400 Hz" + +config ADXL372_ODR_800HZ + bool "800 Hz" + +config ADXL372_ODR_1600HZ + bool "1600 Hz" + +config ADXL372_ODR_3200HZ + bool "3200 Hz" + +config ADXL372_ODR_6400HZ + bool "6400 Hz" + +endchoice + +choice + prompt "Low-Pass (Antialiasing) Filter corner frequency" + default ADXL372_BW_200HZ if ADXL372_ODR_400HZ + default ADXL372_BW_400HZ if ADXL372_ODR_800HZ + default ADXL372_BW_800HZ if ADXL372_ODR_1600HZ + default ADXL372_BW_1600HZ if ADXL372_ODR_3200HZ + default ADXL372_BW_3200HZ if ADXL372_ODR_6400HZ + help + High g events often include acceleration content over a wide range + of frequencies. The ADC of the ADXL372 samples the input acceleration + at the user selected ODR. + In the absence of antialiasing filters, input signals whose frequency + is more than half the ODR alias or fold into the measurement bandwidth + can lead to inaccurate measurements. + +config ADXL372_LPF_DISABLE + bool "Disabled" + +config ADXL372_BW_200HZ + bool "200 Hz" + +config ADXL372_BW_400HZ + bool "400 Hz" + +config ADXL372_BW_800HZ + bool "800 Hz" + +config ADXL372_BW_1600HZ + bool "1600 Hz" + +config ADXL372_BW_3200HZ + bool "3200 Hz" + +endchoice + +choice + prompt "High-Pass Filter corner frequency" + default ADXL372_HPF_CORNER0 + help + The ADXL372 offers a one-pole, high-pass filter with a user + selectable −3 dB frequency. Applications that do not require dc + acceleration measurements can use the high-pass filter to minimize + constant or slow varying offset errors including initial bias, + bias drift due to temperature, and bias drift due to supply voltage + +config ADXL372_HPF_DISABLE + bool "Disabled" + +config ADXL372_HPF_CORNER0 + bool "ODR/210" + +config ADXL372_HPF_CORNER1 + bool "ODR/411" + +config ADXL372_HPF_CORNER2 + bool "ODR/812" + +config ADXL372_HPF_CORNER3 + bool "ODR/1616" + +endchoice + + +config ADXL372_ACTIVITY_THRESHOLD + int "Activity threshold in mg" + range 0 200000 + default 500 + help + Threshold for activity detection. + +config ADXL372_INACTIVITY_THRESHOLD + int "In-activity threshold in mg" + range 0 200000 + default 400 + help + Threshold for in-activity detection. + +config ADXL372_ACTIVITY_TIME + int "Activity time" + range 0 255 + default 1 + help + The activity timer implements a robust activity detection that + minimizes false positive motion triggers. When the timer is used, + only sustained motion can trigger activity detection. + Number of multiples of 3.3 ms activity timer for which above threshold + acceleration is required to detect activity. It is 3.3 ms per code + for 6400 Hz ODR, and it is 6.6 ms per code for 3200 Hz ODR and below. + +config ADXL372_INACTIVITY_TIME + int "In-activity time" + range 0 255 + default 2 + help + The time that all enabled axes must be lower than the inactivity + threshold for an inactivity event to be detected. Number of multiples + of 26 ms inactivity timer for which below threshold acceleration is + required to detect inactivity. It is 26 ms per code for 3200 Hz ODR + and below, and it is 13 ms per code for 6400 Hz ODR. + +config ADXL372_REFERENCED_ACTIVITY_DETECTION_MODE + bool "Use referenced activity/in-activity detection" + default y + help + Activity detection can be configured as referenced or absolute. + When using absolute activity detection, acceleration samples are + compared directly to a user set threshold to determine whether + motion is present. In many applications, it is advantageous for + activity detection to be based not on an absolute threshold, + but on a deviation from a reference point or orientation. + +choice + prompt "Trigger mode" + default ADXL372_TRIGGER_NONE + help + Specify the type of triggering used by the driver. + +config ADXL372_TRIGGER_NONE + bool "No trigger" + +config ADXL372_TRIGGER_GLOBAL_THREAD + bool "Use global thread" + depends on GPIO + select ADXL372_TRIGGER + +config ADXL372_TRIGGER_OWN_THREAD + bool "Use own thread" + depends on GPIO + select ADXL372_TRIGGER + +endchoice + +config ADXL372_TRIGGER + bool + +if !HAS_DTS_GPIO_DEVICE + +config ADXL372_GPIO_DEV_NAME + string "GPIO device" + default "GPIO_0" + depends on ADXL372_TRIGGER + help + The GPIO device's name where the ADXL372 interrupt 1 or 2 pin is + connected. + +config ADXL372_GPIO_PIN_NUM + int "Interrupt GPIO pin number" + default 0 + depends on ADXL372_TRIGGER + help + The GPIO pin number receiving the interrupt signal from the + ADXL372 sensor. + +endif # !HAS_DTS_GPIO_DEVICE + +config ADXL372_THREAD_PRIORITY + int "Thread priority" + depends on ADXL372_TRIGGER_OWN_THREAD && ADXL372_TRIGGER + default 10 + help + Priority of thread used by the driver to handle interrupts. + +config ADXL372_THREAD_STACK_SIZE + int "Thread stack size" + depends on ADXL372_TRIGGER_OWN_THREAD && ADXL372_TRIGGER + default 1024 + help + Stack size of thread used by the driver to handle interrupts. + +endif #ADXL372 diff --git a/drivers/sensor/adxl372/adxl372.c b/drivers/sensor/adxl372/adxl372.c new file mode 100644 index 00000000000..be858429a4a --- /dev/null +++ b/drivers/sensor/adxl372/adxl372.c @@ -0,0 +1,1012 @@ +/* + * Copyright (c) 2018 Analog Devices Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "adxl372.h" + +static int adxl372_bus_access(struct device *dev, u8_t reg, + void *data, size_t length) +{ + struct adxl372_data *adxl372_data = dev->driver_data; + +#ifdef CONFIG_ADXL372_SPI + const struct spi_buf buf[2] = { + { + .buf = ®, + .len = 1 + }, { + .buf = data, + .len = length + } + }; + + struct spi_buf_set tx = { + .buffers = buf, + }; + + if (reg & ADXL372_READ) { + const struct spi_buf_set rx = { + .buffers = buf, + .count = 2 + }; + + tx.count = 1; + + return spi_transceive(adxl372_data->bus, + &adxl372_data->spi_cfg, &tx, &rx); + } + + tx.count = 2; + + return spi_write(adxl372_data->bus, &adxl372_data->spi_cfg, &tx); +#elif CONFIG_ADXL372_I2C + const struct adxl372_dev_config *cfg = dev->config->config_info; + + if (reg & ADXL372_READ) { + return i2c_burst_read(adxl372_data->bus, cfg->i2c_addr, + ADXL372_TO_I2C_REG(reg), + (u8_t *) data, length); + } else { + if (length != 1) { + return -EINVAL; + } + + return i2c_reg_write_byte(adxl372_data->bus, cfg->i2c_addr, + ADXL372_TO_I2C_REG(reg), + *(u8_t *)data); + } + +#endif +} + +/** + * Read from device. + * @param dev - The device structure. + * @param reg_addr - The register address. + * @param reg_data - The register data. + * @return 0 in case of success, negative error code otherwise. + */ +static int adxl372_reg_read(struct device *dev, + u8_t reg_addr, + u8_t *reg_data) +{ + return adxl372_bus_access(dev, ADXL372_REG_READ(reg_addr), reg_data, 1); +} + +/** + * Multibyte read from device. A register read begins with the address + * and autoincrements for each additional byte in the transfer. + * @param dev - The device structure. + * @param reg_addr - The register address. + * @param reg_data - The register data. + * @param count - Number of bytes to read. + * @return 0 in case of success, negative error code otherwise. + */ +static int adxl372_reg_read_multiple(struct device *dev, + u8_t reg_addr, + u8_t *reg_data, + u16_t count) +{ + return adxl372_bus_access(dev, ADXL372_REG_READ(reg_addr), + reg_data, count); +} + +/** + * Write to device. + * @param dev - The device structure. + * @param reg_addr - The register address. + * @param reg_data - The register data. + * @return 0 in case of success, negative error code otherwise. + */ +static int adxl372_reg_write(struct device *dev, + u8_t reg_addr, + u8_t reg_data) +{ + SYS_LOG_DBG("[0x%X] = 0x%X", reg_addr, reg_data); + + return adxl372_bus_access(dev, ADXL372_REG_WRITE(reg_addr), + ®_data, 1); +} + +/** + * SPI write to device using a mask. + * @param dev - The device structure. + * @param reg_addr - The register address. + * @param mask - The mask. + * @param data - The register data. + * @return 0 in case of success, negative error code otherwise. + */ +int adxl372_reg_write_mask(struct device *dev, + u8_t reg_addr, + u32_t mask, + u8_t data) +{ + int ret; + u8_t tmp; + + ret = adxl372_reg_read(dev, reg_addr, &tmp); + if (ret) { + return ret; + } + + tmp &= ~mask; + tmp |= data; + + return adxl372_reg_write(dev, reg_addr, tmp); +} + +/** + * Set the threshold for activity detection for a single axis + * @param dev - The device structure. + * @param axis_reg_h - The high part of the activity register. + * @param act - The activity config structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int adxl372_set_activity_threshold(struct device *dev, u8_t axis_reg_h, + const struct adxl372_activity_threshold *act) +{ + int ret; + u8_t val; + + ret = adxl372_reg_write(dev, axis_reg_h++, act->thresh >> 3); + if (ret) { + return ret; + } + + switch (axis_reg_h) { + case ADXL372_X_THRESH_ACT_L: + case ADXL372_X_THRESH_INACT_L: + case ADXL372_X_THRESH_ACT2_L: + val = (act->thresh << 5) | (act->referenced << 1) | act->enable; + break; + default: + val = (act->thresh << 5) | act->enable; + } + + return adxl372_reg_write(dev, axis_reg_h, val); +} + +/** + * Set the threshold for activity detection for all 3-axis + * @param dev - The device structure. + * @param axis_reg_h - The high part of the activity register. + * @param act - The activity config structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int adxl372_set_activity_threshold_xyz(struct device *dev, + u8_t axis_reg_h, + const struct adxl372_activity_threshold *act) +{ + int i, ret; + + for (i = 0; i < 3; i++) { + ret = adxl372_set_activity_threshold(dev, axis_reg_h, act); + if (ret) { + return ret; + } + axis_reg_h += 2; + } + + return 0; +} + +/** + * Set the mode of operation. + * @param dev - The device structure. + * @param op_mode - Mode of operation. + * Accepted values: ADXL372_STANDBY + * ADXL372_WAKE_UP + * ADXL372_INSTANT_ON + * ADXL372_FULL_BW_MEASUREMENT + * @return 0 in case of success, negative error code otherwise. + */ +static int adxl372_set_op_mode(struct device *dev, enum adxl372_op_mode op_mode) +{ + return adxl372_reg_write_mask(dev, ADXL372_POWER_CTL, + ADXL372_POWER_CTL_MODE_MSK, + ADXL372_POWER_CTL_MODE(op_mode)); +} + +/** + * Autosleep. When set to 1, autosleep is enabled, and the device enters + * wake-up mode automatically upon detection of inactivity. + * @param dev - The device structure. + * @param enable - Accepted values: true + * false + * @return 0 in case of success, negative error code otherwise. + */ +static int adxl372_set_autosleep(struct device *dev, bool enable) +{ + return adxl372_reg_write_mask(dev, ADXL372_MEASURE, + ADXL372_MEASURE_AUTOSLEEP_MSK, + ADXL372_MEASURE_AUTOSLEEP_MODE(enable)); +} + +/** + * Select the desired output signal bandwidth. + * @param dev - The device structure. + * @param bw - bandwidth. + * Accepted values: ADXL372_BW_200HZ + * ADXL372_BW_400HZ + * ADXL372_BW_800HZ + * ADXL372_BW_1600HZ + * ADXL372_BW_3200HZ + * ADXL372_BW_LPF_DISABLED + * @return 0 in case of success, negative error code otherwise. + */ +static int adxl372_set_bandwidth(struct device *dev, enum adxl372_bandwidth bw) +{ + int ret; + u8_t mask; + + if (bw == ADXL372_BW_LPF_DISABLED) { + mask = ADXL372_POWER_CTL_LPF_DIS_MSK; + } else { + mask = 0; + } + + ret = adxl372_reg_write_mask(dev, ADXL372_POWER_CTL, + ADXL372_POWER_CTL_LPF_DIS_MSK, mask); + if (ret) { + return ret; + } + + return adxl372_reg_write_mask(dev, ADXL372_MEASURE, + ADXL372_MEASURE_BANDWIDTH_MSK, + ADXL372_MEASURE_BANDWIDTH_MODE(bw)); +} + +/** + * Select the desired high-pass filter coner. + * @param dev - The device structure. + * @param bw - bandwidth. + * Accepted values: ADXL372_HPF_CORNER_0 + * ADXL372_HPF_CORNER_1 + * ADXL372_HPF_CORNER_2 + * ADXL372_HPF_CORNER_3 + * ADXL372_HPF_DISABLED + * @return 0 in case of success, negative error code otherwise. + */ +static int adxl372_set_hpf_corner(struct device *dev, enum adxl372_hpf_corner c) +{ + + int ret; + u8_t mask; + + if (c == ADXL372_HPF_DISABLED) { + mask = ADXL372_POWER_CTL_HPF_DIS_MSK; + } else { + mask = 0; + } + + ret = adxl372_reg_write_mask(dev, ADXL372_POWER_CTL, + ADXL372_POWER_CTL_HPF_DIS_MSK, mask); + if (ret) { + return ret; + } + + return adxl372_reg_write(dev, ADXL372_HPF, ADXL372_HPF_CORNER(c)); +} + + +/** + * Link/Loop Activity Processing. + * @param dev - The device structure. + * @param mode - Mode of operation. + * Accepted values: ADXL372_DEFAULT + * ADXL372_LINKED + * ADXL372_LOOPED + * @return 0 in case of success, negative error code otherwise. + */ +static int adxl372_set_act_proc_mode(struct device *dev, + enum adxl372_act_proc_mode mode) +{ + return adxl372_reg_write_mask(dev, ADXL372_MEASURE, + ADXL372_MEASURE_LINKLOOP_MSK, + ADXL372_MEASURE_LINKLOOP_MODE(mode)); +} + +/** + * Set Output data rate. + * @param dev - The device structure. + * @param odr - Output data rate. + * Accepted values: ADXL372_ODR_400HZ + * ADXL372_ODR_800HZ + * ADXL372_ODR_1600HZ + * ADXL372_ODR_3200HZ + * ADXL372_ODR_6400HZ + * @return 0 in case of success, negative error code otherwise. + */ +static int adxl372_set_odr(struct device *dev, enum adxl372_odr odr) +{ + return adxl372_reg_write_mask(dev, ADXL372_TIMING, + ADXL372_TIMING_ODR_MSK, + ADXL372_TIMING_ODR_MODE(odr)); +} + +/** + * Select instant on threshold + * @param dev - The device structure. + * @param mode - 0 = low threshold, 1 = high threshold. + * Accepted values: ADXL372_INSTANT_ON_LOW_TH + * ADXL372_INSTANT_ON_HIGH_TH + * @return 0 in case of success, negative error code otherwise. + */ +static int adxl372_set_instant_on_th(struct device *dev, + enum adxl372_instant_on_th_mode mode) +{ + return adxl372_reg_write_mask(dev, ADXL372_POWER_CTL, + ADXL372_POWER_CTL_INSTANT_ON_TH_MSK, + ADXL372_POWER_CTL_INSTANT_ON_TH_MODE(mode)); +} + +/** + * Set the Timer Rate for Wake-Up Mode. + * @param dev - The device structure. + * @param wur - wake up mode rate + * Accepted values: ADXL372_WUR_52ms + * ADXL372_WUR_104ms + * ADXL372_WUR_208ms + * ADXL372_WUR_512ms + * ADXL372_WUR_2048ms + * ADXL372_WUR_4096ms + * ADXL372_WUR_8192ms + * ADXL372_WUR_24576ms + * @return 0 in case of success, negative error code otherwise. + */ +static int adxl372_set_wakeup_rate(struct device *dev, + enum adxl372_wakeup_rate wur) +{ + return adxl372_reg_write_mask(dev, ADXL372_TIMING, + ADXL372_TIMING_WAKE_UP_RATE_MSK, + ADXL372_TIMING_WAKE_UP_RATE_MODE(wur)); +} + +/** + * Set the activity timer + * @param dev - The device structure. + * @param time - The value set in this register. + * @return 0 in case of success, negative error code otherwise. + */ +static int adxl372_set_activity_time(struct device *dev, u8_t time) +{ + return adxl372_reg_write(dev, ADXL372_TIME_ACT, time); +} + +/** + * Set the inactivity timer + * @param dev - The device structure. + * @param time - is the 16-bit value set by the TIME_INACT_L register + * (eight LSBs) and the TIME_INACT_H register (eight MSBs). + * @return 0 in case of success, negative error code otherwise. + */ +static int adxl372_set_inactivity_time(struct device *dev, u16_t time) +{ + int ret; + + ret = adxl372_reg_write(dev, ADXL372_TIME_INACT_H, time >> 8); + if (ret) { + return ret; + } + + return adxl372_reg_write(dev, ADXL372_TIME_INACT_L, time & 0xFF); +} + +/** + * Set the filter settling period. + * @param dev - The device structure. + * @param mode - settle period + * Accepted values: ADXL372_FILTER_SETTLE_370 + * ADXL372_FILTER_SETTLE_16 + * @return 0 in case of success, negative error code otherwise. + */ +static int adxl372_set_filter_settle(struct device *dev, + enum adxl372_filter_settle mode) +{ + return adxl372_reg_write_mask(dev, ADXL372_POWER_CTL, + ADXL372_POWER_CTL_FIL_SETTLE_MSK, + ADXL372_POWER_CTL_FIL_SETTLE_MODE(mode)); +} + +/** + * Configure the INT1 and INT2 interrupt pins. + * @param dev - The device structure. + * @param int1 - INT1 interrupt pins. + * @param int2 - INT2 interrupt pins. + * @return 0 in case of success, negative error code otherwise. + */ +static int adxl372_interrupt_config(struct device *dev, + u8_t int1, + u8_t int2) +{ + int ret; + + ret = adxl372_reg_write(dev, ADXL372_INT1_MAP, int1); + if (ret) { + return ret; + } + + return adxl372_reg_write(dev, ADXL372_INT2_MAP, int2); + +} + +/** + * Get the STATUS, STATUS2, FIFO_ENTRIES and FIFO_ENTRIES2 registers data + * @param dev - The device structure. + * @param status1 - Data stored in the STATUS1 register + * @param status2 - Data stored in the STATUS2 register + * @param fifo_entries - Number of valid data samples present in the + * FIFO buffer (0 to 512) + * @return 0 in case of success, negative error code otherwise. + */ +int adxl372_get_status(struct device *dev, + u8_t *status1, + u8_t *status2, + u16_t *fifo_entries) +{ + u8_t buf[4], length = 1; + int ret; + + if (status2) { + length++; + } + + if (fifo_entries) { + length += 2; + } + + ret = adxl372_reg_read_multiple(dev, ADXL372_STATUS_1, buf, length); + + *status1 = buf[0]; + + if (status2) { + *status2 = buf[1]; + } + + if (fifo_entries) { + *fifo_entries = ((buf[2] & 0x3) << 8) | buf[3]; + } + + return ret; +} + +/** + * Software reset. + * @param dev - The device structure. + * @return 0 in case of success, negative error code otherwise. + */ +static int adxl372_reset(struct device *dev) +{ + int ret; + + ret = adxl372_set_op_mode(dev, ADXL372_STANDBY); + if (ret) { + return ret; + } + /* Writing code 0x52 resets the device */ + ret = adxl372_reg_write(dev, ADXL372_RESET, ADXL372_RESET_CODE); + k_sleep(1000); + + return ret; +} + +/** + * Configure the operating parameters for the FIFO. + * @param dev - The device structure. + * @param mode - FIFO Mode. Specifies FIFO operating mode. + * Accepted values: ADXL372_FIFO_BYPASSED + * ADXL372_FIFO_STREAMED + * ADXL372_FIFO_TRIGGERED + * ADXL372_FIFO_OLD_SAVED + * @param format - FIFO Format. Specifies the data is stored in the FIFO buffer. + * Accepted values: ADXL372_XYZ_FIFO + * ADXL372_X_FIFO + * ADXL372_Y_FIFO + * ADXL372_XY_FIFO + * ADXL372_Z_FIFO + * ADXL372_XZ_FIFO + * ADXL372_YZ_FIFO + * ADXL372_XYZ_PEAK_FIFO + * @param fifo_samples - FIFO Samples. Watermark number of FIFO samples that + * triggers a FIFO_FULL condition when reached. + * Values range from 0 to 512. + + * @return 0 in case of success, negative error code otherwise. + */ +static int adxl372_configure_fifo(struct device *dev, + enum adxl372_fifo_mode mode, + enum adxl372_fifo_format format, + u16_t fifo_samples) +{ + struct adxl372_data *data = dev->driver_data; + u8_t fifo_config; + int ret; + + if (fifo_samples > 512) { + return -EINVAL; + } + + /* + * All FIFO modes must be configured while in standby mode. + */ + ret = adxl372_set_op_mode(dev, ADXL372_STANDBY); + if (ret) { + return ret; + } + + fifo_config = (ADXL372_FIFO_CTL_FORMAT_MODE(format) | + ADXL372_FIFO_CTL_MODE_MODE(mode) | + ADXL372_FIFO_CTL_SAMPLES_MODE(fifo_samples)); + + ret = adxl372_reg_write(dev, ADXL372_FIFO_CTL, fifo_config); + if (ret) { + return ret; + } + ret = adxl372_reg_write(dev, ADXL372_FIFO_SAMPLES, fifo_samples & 0xFF); + if (ret) { + return ret; + } + + data->fifo_config.fifo_format = format; + data->fifo_config.fifo_mode = mode; + data->fifo_config.fifo_samples = fifo_samples; + + return 0; +} + +/** + * Retrieve 3-axis acceleration data + * @param dev - The device structure. + * @param maxpeak - Retrieve the highest magnitude (x, y, z) sample recorded + * since the last read of the MAXPEAK registers + * @param accel_data - pointer to a variable of type adxl372_xyz_accel_data + * where (x, y, z) acceleration data will be stored. + * @return 0 in case of success, negative error code otherwise. + */ +static int adxl372_get_accel_data(struct device *dev, bool maxpeak, + struct adxl372_xyz_accel_data *accel_data) +{ + u8_t buf[6]; + u8_t status1; + int ret; + + if (!IS_ENABLED(CONFIG_ADXL372_TRIGGER)) { + do { + adxl372_get_status(dev, &status1, NULL, NULL); + } while (!(ADXL372_STATUS_1_DATA_RDY(status1))); + } + + ret = adxl372_reg_read_multiple(dev, maxpeak ? ADXL372_X_MAXPEAK_H : + ADXL372_X_DATA_H, buf, 6); + + accel_data->x = (buf[0] << 8) | (buf[1] & 0xF0); + accel_data->y = (buf[2] << 8) | (buf[3] & 0xF0); + accel_data->z = (buf[4] << 8) | (buf[5] & 0xF0); + + return ret; +} + +static int adxl372_attr_set_odr(struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + enum adxl372_odr odr; + + switch (val->val1) { + case 400: + odr = ADXL372_ODR_400HZ; + break; + case 800: + odr = ADXL372_ODR_800HZ; + break; + case 1600: + odr = ADXL372_ODR_1600HZ; + break; + case 3200: + odr = ADXL372_ODR_3200HZ; + break; + case 6400: + odr = ADXL372_ODR_6400HZ; + break; + default: + return -EINVAL; + } + + return adxl372_set_odr(dev, odr); +} + +static int adxl372_attr_set_thresh(struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + const struct adxl372_dev_config *cfg = dev->config->config_info; + struct adxl372_activity_threshold threshold; + s32_t value; + s64_t micro_ms2 = val->val1 * 1000000LL + val->val2; + u8_t reg; + + value = abs((micro_ms2 * 10) / SENSOR_G); + + if (value > 2047) { + return -EINVAL; + } + + threshold.thresh = value; + threshold.enable = cfg->activity_th.enable; + threshold.referenced = cfg->activity_th.referenced; + + if (attr == SENSOR_ATTR_UPPER_THRESH) { + reg = ADXL372_X_THRESH_ACT_H; + } else { + reg = ADXL372_X_THRESH_INACT_H; + } + + switch (chan) { + case SENSOR_CHAN_ACCEL_X: + return adxl372_set_activity_threshold(dev, reg, &threshold); + case SENSOR_CHAN_ACCEL_Y: + return adxl372_set_activity_threshold(dev, reg + 2, &threshold); + case SENSOR_CHAN_ACCEL_Z: + return adxl372_set_activity_threshold(dev, reg + 4, &threshold); + case SENSOR_CHAN_ACCEL_XYZ: + return adxl372_set_activity_threshold_xyz(dev, reg, &threshold); + default: + SYS_LOG_ERR("attr_set() not supported on this channel"); + return -ENOTSUP; + } +} + +static int adxl372_attr_set(struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + switch (attr) { + case SENSOR_ATTR_SAMPLING_FREQUENCY: + return adxl372_attr_set_odr(dev, chan, attr, val); + case SENSOR_ATTR_UPPER_THRESH: + case SENSOR_ATTR_LOWER_THRESH: + return adxl372_attr_set_thresh(dev, chan, attr, val); + default: + return -ENOTSUP; + } +} + +static int adxl372_sample_fetch(struct device *dev, enum sensor_channel chan) +{ + struct adxl372_data *data = dev->driver_data; + const struct adxl372_dev_config *cfg = dev->config->config_info; + + return adxl372_get_accel_data(dev, cfg->max_peak_detect_mode, + &data->sample); +} + +static void adxl372_accel_convert(struct sensor_value *val, s16_t value) +{ + /* + * Sensor resolution is 100mg/LSB, 12-bit value needs to be right + * shifted by 4 or divided by 16. Overall this results in a scale of 160 + */ + s32_t micro_ms2 = value * (SENSOR_G / (16 * 1000 / 100)); + + val->val1 = micro_ms2 / 1000000; + val->val2 = micro_ms2 % 1000000; +} + +static int adxl372_channel_get(struct device *dev, + enum sensor_channel chan, + struct sensor_value *val) +{ + struct adxl372_data *data = dev->driver_data; + + switch (chan) { + case SENSOR_CHAN_ACCEL_X: + adxl372_accel_convert(val, data->sample.x); + break; + case SENSOR_CHAN_ACCEL_Y: + adxl372_accel_convert(val, data->sample.y); + break; + case SENSOR_CHAN_ACCEL_Z: + adxl372_accel_convert(val, data->sample.z); + break; + case SENSOR_CHAN_ACCEL_XYZ: + adxl372_accel_convert(val++, data->sample.x); + adxl372_accel_convert(val++, data->sample.y); + adxl372_accel_convert(val, data->sample.z); + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static const struct sensor_driver_api adxl372_api_funcs = { + .attr_set = adxl372_attr_set, + .sample_fetch = adxl372_sample_fetch, + .channel_get = adxl372_channel_get, +#ifdef CONFIG_ADXL372_TRIGGER + .trigger_set = adxl372_trigger_set, +#endif + +}; + +static int adxl372_probe(struct device *dev) +{ + const struct adxl372_dev_config *cfg = dev->config->config_info; + u8_t dev_id, part_id; + int ret; + + ret = adxl372_reg_read(dev, ADXL372_DEVID, &dev_id); + if (ret) { + return ret; + } + ret = adxl372_reg_read(dev, ADXL372_PARTID, &part_id); + if (ret) { + return ret; + } + + if (dev_id != ADXL372_DEVID_VAL || part_id != ADXL372_PARTID_VAL) { + SYS_LOG_ERR("failed to read id (0x%X:0x%X)", dev_id, part_id); + return -ENODEV; + } + +#ifdef CONFIG_ADXL372_I2C + /* + * When sharing an SDA bus, the ADXL372 Silcon REV < 3 may prevent + * communication with other devices on that bus. + */ + adxl372_reg_read(dev, ADXL372_REVID, &dev_id); + if (dev_id < 3) { + SYS_LOG_WRN("The ADXL372 Rev %u only supports point to point I2C communication!", + dev_id); + } +#endif + + /* Device settings */ + ret = adxl372_set_op_mode(dev, ADXL372_STANDBY); + if (ret) { + return ret; + } + + ret = adxl372_reset(dev); + if (ret) { + return ret; + } + + ret = adxl372_set_hpf_corner(dev, cfg->hpf); + if (ret) { + return ret; + } + + ret = adxl372_set_bandwidth(dev, cfg->bw); + if (ret) { + return ret; + } + + ret = adxl372_set_odr(dev, cfg->odr); + if (ret) { + return ret; + } + + ret = adxl372_set_wakeup_rate(dev, cfg->wur); + if (ret) { + return ret; + } + + ret = adxl372_set_autosleep(dev, cfg->autosleep); + if (ret) { + return ret; + } + + ret = adxl372_set_instant_on_th(dev, cfg->th_mode); + if (ret) { + return ret; + } + + ret = adxl372_set_activity_threshold_xyz(dev, ADXL372_X_THRESH_ACT_H, + &cfg->activity_th); + if (ret) { + return ret; + } + + ret = adxl372_set_activity_threshold_xyz(dev, ADXL372_X_THRESH_INACT_H, + &cfg->inactivity_th); + if (ret) { + return ret; + } + + ret = adxl372_set_activity_time(dev, cfg->activity_time); + if (ret) { + return ret; + } + + ret = adxl372_set_inactivity_time(dev, cfg->inactivity_time); + if (ret) { + return ret; + } + + ret = adxl372_set_filter_settle(dev, cfg->filter_settle); + if (ret) { + return ret; + } + + ret = adxl372_configure_fifo(dev, cfg->fifo_config.fifo_mode, + cfg->fifo_config.fifo_format, + cfg->fifo_config.fifo_samples); + if (ret) { + return ret; + } + +#ifdef CONFIG_ADXL372_TRIGGER + if (adxl372_init_interrupt(dev) < 0) { + SYS_LOG_ERR("Failed to initialize interrupt!"); + return -EIO; + } +#endif + + ret = adxl372_interrupt_config(dev, cfg->int1_config, cfg->int2_config); + if (ret) { + return ret; + } + + ret = adxl372_set_op_mode(dev, cfg->op_mode); + if (ret) { + return ret; + } + + return adxl372_set_act_proc_mode(dev, cfg->act_proc_mode); +} + +static int adxl372_init(struct device *dev) +{ + struct adxl372_data *data = dev->driver_data; + const struct adxl372_dev_config *cfg = dev->config->config_info; + +#ifdef CONFIG_ADXL372_I2C + data->bus = device_get_binding(cfg->i2c_port); + if (data->bus == NULL) { + SYS_LOG_ERR("Failed to get pointer to %s device!", + cfg->i2c_port); + return -EINVAL; + } +#endif +#ifdef CONFIG_ADXL372_SPI + data->bus = device_get_binding(cfg->spi_port); + if (!data->bus) { + SYS_LOG_ERR("spi device not found: %s", cfg->spi_port); + return -EINVAL; + } + /* CPOL=0, CPHA=0, max 10MHz */ + data->spi_cfg.operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB; + data->spi_cfg.frequency = cfg->spi_max_frequency; + data->spi_cfg.slave = cfg->spi_slave; + +#if defined(CONFIG_ADXL372_SPI_GPIO_CS) + /* handle SPI CS thru GPIO if it is the case */ + + data->adxl372_cs_ctrl.gpio_dev = device_get_binding(cfg->gpio_cs_port); + if (!data->adxl372_cs_ctrl.gpio_dev) { + SYS_LOG_ERR("Unable to get GPIO SPI CS device"); + return -ENODEV; + } + + data->adxl372_cs_ctrl.gpio_pin = cfg->cs_gpio; + data->adxl372_cs_ctrl.delay = 0; + + data->spi_cfg.cs = &data->adxl372_cs_ctrl; +#endif +#endif /* CONFIG_ADXL372_SPI */ + + if (adxl372_probe(dev) < 0) { + return -ENODEV; + } + + return 0; +} + +static struct adxl372_data adxl372_data; + +static const struct adxl372_dev_config adxl372_config = { +#ifdef CONFIG_ADXL372_I2C + .i2c_port = CONFIG_ADXL372_I2C_MASTER_DEV_NAME, + .i2c_addr = CONFIG_ADXL372_I2C_ADDR, +#endif +#ifdef CONFIG_ADXL372_SPI + .spi_port = CONFIG_ADXL372_SPI_DEV_NAME, + .spi_slave = CONFIG_ADXL372_SPI_DEV_SLAVE, + .spi_max_frequency = CONFIG_ADXL372_SPI_BUS_FREQ, +#ifdef CONFIG_ADXL372_SPI_GPIO_CS + .gpio_cs_port = CONFIG_ADXL372_SPI_GPIO_CS_DRV_NAME, + .cs_gpio = CONFIG_ADXL372_SPI_GPIO_CS_PIN, +#endif +#endif +#ifdef CONFIG_ADXL372_TRIGGER + .gpio_port = CONFIG_ADXL372_GPIO_DEV_NAME, + .int_gpio = CONFIG_ADXL372_GPIO_PIN_NUM, +#endif + + .max_peak_detect_mode = IS_ENABLED(CONFIG_ADXL372_PEAK_DETECT_MODE), + +#ifdef CONFIG_ADXL372_ODR_400HZ + .odr = ADXL372_ODR_400HZ, +#elif CONFIG_ADXL372_ODR_800HZ + .odr = ADXL372_ODR_800HZ, +#elif CONFIG_ADXL372_ODR_1600HZ + .odr = ADXL372_ODR_1600HZ, +#elif CONFIG_ADXL372_ODR_3200HZ + .odr = ADXL372_ODR_3200HZ, +#elif CONFIG_ADXL372_ODR_6400HZ + .odr = ADXL372_ODR_6400HZ, +#endif + +#ifdef CONFIG_ADXL372_BW_200HZ + .bw = ADXL372_BW_200HZ, +#elif CONFIG_ADXL372_BW_400HZ + .bw = ADXL372_BW_400HZ, +#elif CONFIG_ADXL372_BW_800HZ + .bw = ADXL372_BW_800HZ, +#elif CONFIG_ADXL372_BW_1600HZ + .bw = ADXL372_BW_1600HZ, +#elif CONFIG_ADXL372_BW_3200HZ + .bw = ADXL372_BW_3200HZ, +#elif CONFIG_ADXL372_LPF_DISABLE + .bw = ADXL372_BW_LPF_DISABLED, +#endif + +#ifdef CONFIG_ADXL372_HPF_CORNER0 + .hpf = ADXL372_HPF_CORNER_0, +#elif CONFIG_ADXL372_HPF_CORNER1 + .hpf = ADXL372_HPF_CORNER_1, +#elif CONFIG_ADXL372_HPF_CORNER2 + .hpf = ADXL372_HPF_CORNER_2, +#elif CONFIG_ADXL372_HPF_CORNER3 + .hpf = ADXL372_HPF_CORNER_3, +#elif CONFIG_ADXL372_HPF_DISABLE + .hpf = ADXL372_HPF_DISABLED, +#endif + +#ifdef CONFIG_ADXL372_TRIGGER + .act_proc_mode = ADXL372_LINKED, +#else + .act_proc_mode = ADXL372_LOOPED, +#endif + .th_mode = ADXL372_INSTANT_ON_LOW_TH, + .autosleep = false, + .wur = ADXL372_WUR_52ms, + + .activity_th.thresh = CONFIG_ADXL372_ACTIVITY_THRESHOLD / 100, + .activity_th.referenced = + IS_ENABLED(CONFIG_ADXL372_REFERENCED_ACTIVITY_DETECTION_MODE), + .activity_th.enable = 1, + .activity_time = CONFIG_ADXL372_ACTIVITY_TIME, + + .inactivity_th.thresh = CONFIG_ADXL372_INACTIVITY_THRESHOLD / 100, + .inactivity_th.referenced = + IS_ENABLED(CONFIG_ADXL372_REFERENCED_ACTIVITY_DETECTION_MODE), + .inactivity_th.enable = 1, + .inactivity_time = CONFIG_ADXL372_INACTIVITY_TIME, + + .filter_settle = ADXL372_FILTER_SETTLE_370, + .fifo_config.fifo_mode = ADXL372_FIFO_STREAMED, + .fifo_config.fifo_format = ADXL372_XYZ_PEAK_FIFO, + .fifo_config.fifo_samples = 128, + + .op_mode = ADXL372_FULL_BW_MEASUREMENT, +}; + +DEVICE_AND_API_INIT(adxl372, CONFIG_ADXL372_DEV_NAME, adxl372_init, + &adxl372_data, &adxl372_config, POST_KERNEL, + CONFIG_SENSOR_INIT_PRIORITY, &adxl372_api_funcs); diff --git a/drivers/sensor/adxl372/adxl372.h b/drivers/sensor/adxl372/adxl372.h new file mode 100644 index 00000000000..09cde71c040 --- /dev/null +++ b/drivers/sensor/adxl372/adxl372.h @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2018 Analog Devices Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __SENSOR_ADXL372_H__ +#define __SENSOR_ADXL372_H__ + +#include +#include +#include +#include +#include + +#define GENMASK(h, l) (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (31 - (h)))) + +/* + * ADXL372 registers definition + */ +#define ADXL372_DEVID 0x00u /* Analog Devices accelerometer ID */ +#define ADXL372_DEVID_MST 0x01u /* Analog Devices MEMS device ID */ +#define ADXL372_PARTID 0x02u /* Device ID */ +#define ADXL372_REVID 0x03u /* product revision ID*/ +#define ADXL372_STATUS_1 0x04u /* Status register 1 */ +#define ADXL372_STATUS_2 0x05u /* Status register 2 */ +#define ADXL372_FIFO_ENTRIES_2 0x06u /* Valid data samples in the FIFO */ +#define ADXL372_FIFO_ENTRIES_1 0x07u /* Valid data samples in the FIFO */ +#define ADXL372_X_DATA_H 0x08u /* X-axis acceleration data [11:4] */ +#define ADXL372_X_DATA_L 0x09u /* X-axis acceleration data [3:0] */ +#define ADXL372_Y_DATA_H 0x0Au /* Y-axis acceleration data [11:4] */ +#define ADXL372_Y_DATA_L 0x0Bu /* Y-axis acceleration data [3:0] */ +#define ADXL372_Z_DATA_H 0x0Cu /* Z-axis acceleration data [11:4] */ +#define ADXL372_Z_DATA_L 0x0Du /* Z-axis acceleration data [3:0] */ +#define ADXL372_X_MAXPEAK_H 0x15u /* X-axis MaxPeak acceleration data */ +#define ADXL372_X_MAXPEAK_L 0x16u /* X-axis MaxPeak acceleration data */ +#define ADXL372_Y_MAXPEAK_H 0x17u /* Y-axis MaxPeak acceleration data */ +#define ADXL372_Y_MAXPEAK_L 0x18u /* Y-axis MaxPeak acceleration data */ +#define ADXL372_Z_MAXPEAK_H 0x19u /* Z-axis MaxPeak acceleration data */ +#define ADXL372_Z_MAXPEAK_L 0x1Au /* Z-axis MaxPeak acceleration data */ +#define ADXL372_OFFSET_X 0x20u /* X axis offset */ +#define ADXL372_OFFSET_Y 0x21u /* Y axis offset */ +#define ADXL372_OFFSET_Z 0x22u /* Z axis offset */ +#define ADXL372_X_THRESH_ACT_H 0x23u /* X axis Activity Threshold [15:8] */ +#define ADXL372_X_THRESH_ACT_L 0x24u /* X axis Activity Threshold [7:0] */ +#define ADXL372_Y_THRESH_ACT_H 0x25u /* Y axis Activity Threshold [15:8] */ +#define ADXL372_Y_THRESH_ACT_L 0x26u /* Y axis Activity Threshold [7:0] */ +#define ADXL372_Z_THRESH_ACT_H 0x27u /* Z axis Activity Threshold [15:8] */ +#define ADXL372_Z_THRESH_ACT_L 0x28u /* Z axis Activity Threshold [7:0] */ +#define ADXL372_TIME_ACT 0x29u /* Activity Time */ +#define ADXL372_X_THRESH_INACT_H 0x2Au /* X axis Inactivity Threshold */ +#define ADXL372_X_THRESH_INACT_L 0x2Bu /* X axis Inactivity Threshold */ +#define ADXL372_Y_THRESH_INACT_H 0x2Cu /* Y axis Inactivity Threshold */ +#define ADXL372_Y_THRESH_INACT_L 0x2Du /* Y axis Inactivity Threshold */ +#define ADXL372_Z_THRESH_INACT_H 0x2Eu /* Z axis Inactivity Threshold */ +#define ADXL372_Z_THRESH_INACT_L 0x2Fu /* Z axis Inactivity Threshold */ +#define ADXL372_TIME_INACT_H 0x30u /* Inactivity Time [15:8] */ +#define ADXL372_TIME_INACT_L 0x31u /* Inactivity Time [7:0] */ +#define ADXL372_X_THRESH_ACT2_H 0x32u /* X axis Activity2 Threshold [15:8] */ +#define ADXL372_X_THRESH_ACT2_L 0x33u /* X axis Activity2 Threshold [7:0] */ +#define ADXL372_Y_THRESH_ACT2_H 0x34u /* Y axis Activity2 Threshold [15:8] */ +#define ADXL372_Y_THRESH_ACT2_L 0x35u /* Y axis Activity2 Threshold [7:0] */ +#define ADXL372_Z_THRESH_ACT2_H 0x36u /* Z axis Activity2 Threshold [15:8] */ +#define ADXL372_Z_THRESH_ACT2_L 0x37u /* Z axis Activity2 Threshold [7:0] */ +#define ADXL372_HPF 0x38u /* High Pass Filter */ +#define ADXL372_FIFO_SAMPLES 0x39u /* FIFO Samples */ +#define ADXL372_FIFO_CTL 0x3Au /* FIFO Control */ +#define ADXL372_INT1_MAP 0x3Bu /* Interrupt 1 mapping control */ +#define ADXL372_INT2_MAP 0x3Cu /* Interrupt 2 mapping control */ +#define ADXL372_TIMING 0x3Du /* Timing */ +#define ADXL372_MEASURE 0x3Eu /* Measure */ +#define ADXL372_POWER_CTL 0x3Fu /* Power control */ +#define ADXL372_SELF_TEST 0x40u /* Self Test */ +#define ADXL372_RESET 0x41u /* Reset */ +#define ADXL372_FIFO_DATA 0x42u /* FIFO Data */ + +#define ADXL372_DEVID_VAL 0xADu /* Analog Devices accelerometer ID */ +#define ADXL372_MST_DEVID_VAL 0x1Du /* Analog Devices MEMS device ID */ +#define ADXL372_PARTID_VAL 0xFAu /* Device ID */ +#define ADXL372_REVID_VAL 0x02u /* product revision ID*/ +#define ADXL372_RESET_CODE 0x52u /* Writing code 0x52 resets the device */ + +#define ADXL372_READ 0x01u +#define ADXL372_REG_READ(x) (((x & 0xFF) << 1) | ADXL372_READ) +#define ADXL372_REG_WRITE(x) ((x & 0xFF) << 1) +#define ADXL372_TO_I2C_REG(x) ((x) >> 1) + +/* ADXL372_POWER_CTL */ +#define ADXL372_POWER_CTL_INSTANT_ON_TH_MSK BIT(5) +#define ADXL372_POWER_CTL_INSTANT_ON_TH_MODE(x) (((x) & 0x1) << 5) +#define ADXL372_POWER_CTL_FIL_SETTLE_MSK BIT(4) +#define ADXL372_POWER_CTL_FIL_SETTLE_MODE(x) (((x) & 0x1) << 4) +#define ADXL372_POWER_CTL_LPF_DIS_MSK BIT(3) +#define ADXL372_POWER_CTL_LPF_DIS_MODE(x) (((x) & 0x1) << 3) +#define ADXL372_POWER_CTL_HPF_DIS_MSK BIT(2) +#define ADXL372_POWER_CTL_HPF_DIS_MODE(x) (((x) & 0x1) << 2) +#define ADXL372_POWER_CTL_MODE_MSK GENMASK(1, 0) +#define ADXL372_POWER_CTL_MODE(x) (((x) & 0x3) << 0) + +/* ADXL372_MEASURE */ +#define ADXL372_MEASURE_AUTOSLEEP_MSK BIT(6) +#define ADXL372_MEASURE_AUTOSLEEP_MODE(x) (((x) & 0x1) << 6) +#define ADXL372_MEASURE_LINKLOOP_MSK GENMASK(5, 4) +#define ADXL372_MEASURE_LINKLOOP_MODE(x) (((x) & 0x3) << 4) +#define ADXL372_MEASURE_LOW_NOISE_MSK BIT(3) +#define ADXL372_MEASURE_LOW_NOISE_MODE(x) (((x) & 0x1) << 3) +#define ADXL372_MEASURE_BANDWIDTH_MSK GENMASK(2, 0) +#define ADXL372_MEASURE_BANDWIDTH_MODE(x) (((x) & 0x7) << 0) + +/* ADXL372_TIMING */ +#define ADXL372_TIMING_ODR_MSK GENMASK(7, 5) +#define ADXL372_TIMING_ODR_MODE(x) (((x) & 0x7) << 5) +#define ADXL372_TIMING_WAKE_UP_RATE_MSK GENMASK(4, 2) +#define ADXL372_TIMING_WAKE_UP_RATE_MODE(x) (((x) & 0x7) << 2) +#define ADXL372_TIMING_EXT_CLK_MSK BIT(1) +#define ADXL372_TIMING_EXT_CLK_MODE(x) (((x) & 0x1) << 1) +#define ADXL372_TIMING_EXT_SYNC_MSK BIT(0) +#define ADXL372_TIMING_EXT_SYNC_MODE(x) (((x) & 0x1) << 0) + +/* ADXL372_FIFO_CTL */ +#define ADXL372_FIFO_CTL_FORMAT_MSK GENMASK(5, 3) +#define ADXL372_FIFO_CTL_FORMAT_MODE(x) (((x) & 0x7) << 3) +#define ADXL372_FIFO_CTL_MODE_MSK GENMASK(2, 1) +#define ADXL372_FIFO_CTL_MODE_MODE(x) (((x) & 0x3) << 1) +#define ADXL372_FIFO_CTL_SAMPLES_MSK BIT(0) +#define ADXL372_FIFO_CTL_SAMPLES_MODE(x) (((x) > 0xFF) ? 1 : 0) + +/* ADXL372_STATUS_1 */ +#define ADXL372_STATUS_1_DATA_RDY(x) (((x) >> 0) & 0x1) +#define ADXL372_STATUS_1_FIFO_RDY(x) (((x) >> 1) & 0x1) +#define ADXL372_STATUS_1_FIFO_FULL(x) (((x) >> 2) & 0x1) +#define ADXL372_STATUS_1_FIFO_OVR(x) (((x) >> 3) & 0x1) +#define ADXL372_STATUS_1_USR_NVM_BUSY(x) (((x) >> 5) & 0x1) +#define ADXL372_STATUS_1_AWAKE(x) (((x) >> 6) & 0x1) +#define ADXL372_STATUS_1_ERR_USR_REGS(x) (((x) >> 7) & 0x1) + +/* ADXL372_STATUS_2 */ +#define ADXL372_STATUS_2_INACT(x) (((x) >> 4) & 0x1) +#define ADXL372_STATUS_2_ACTIVITY(x) (((x) >> 5) & 0x1) +#define ADXL372_STATUS_2_ACTIVITY2(x) (((x) >> 6) & 0x1) + +/* ADXL372_INT1_MAP */ +#define ADXL372_INT1_MAP_DATA_RDY_MSK BIT(0) +#define ADXL372_INT1_MAP_DATA_RDY_MODE(x) (((x) & 0x1) << 0) +#define ADXL372_INT1_MAP_FIFO_RDY_MSK BIT(1) +#define ADXL372_INT1_MAP_FIFO_RDY_MODE(x) (((x) & 0x1) << 1) +#define ADXL372_INT1_MAP_FIFO_FULL_MSK BIT(2) +#define ADXL372_INT1_MAP_FIFO_FULL_MODE(x) (((x) & 0x1) << 2) +#define ADXL372_INT1_MAP_FIFO_OVR_MSK BIT(3) +#define ADXL372_INT1_MAP_FIFO_OVR_MODE(x) (((x) & 0x1) << 3) +#define ADXL372_INT1_MAP_INACT_MSK BIT(4) +#define ADXL372_INT1_MAP_INACT_MODE(x) (((x) & 0x1) << 4) +#define ADXL372_INT1_MAP_ACT_MSK BIT(5) +#define ADXL372_INT1_MAP_ACT_MODE(x) (((x) & 0x1) << 5) +#define ADXL372_INT1_MAP_AWAKE_MSK BIT(6) +#define ADXL372_INT1_MAP_AWAKE_MODE(x) (((x) & 0x1) << 6) +#define ADXL372_INT1_MAP_LOW_MSK BIT(7) +#define ADXL372_INT1_MAP_LOW_MODE(x) (((x) & 0x1) << 7) + +/* ADXL372_INT2_MAP */ +#define ADXL372_INT2_MAP_DATA_RDY_MSK BIT(0) +#define ADXL372_INT2_MAP_DATA_RDY_MODE(x) (((x) & 0x1) << 0) +#define ADXL372_INT2_MAP_FIFO_RDY_MSK BIT(1) +#define ADXL372_INT2_MAP_FIFO_RDY_MODE(x) (((x) & 0x1) << 1) +#define ADXL372_INT2_MAP_FIFO_FULL_MSK BIT(2) +#define ADXL372_INT2_MAP_FIFO_FULL_MODE(x) (((x) & 0x1) << 2) +#define ADXL372_INT2_MAP_FIFO_OVR_MSK BIT(3) +#define ADXL372_INT2_MAP_FIFO_OVR_MODE(x) (((x) & 0x1) << 3) +#define ADXL372_INT2_MAP_INACT_MSK BIT(4) +#define ADXL372_INT2_MAP_INACT_MODE(x) (((x) & 0x1) << 4) +#define ADXL372_INT2_MAP_ACT_MSK BIT(5) +#define ADXL372_INT2_MAP_ACT_MODE(x) (((x) & 0x1) << 5) +#define ADXL372_INT2_MAP_AWAKE_MSK BIT(6) +#define ADXL372_INT2_MAP_AWAKE_MODE(x) (((x) & 0x1) << 6) +#define ADXL372_INT2_MAP_LOW_MSK BIT(7) +#define ADXL372_INT2_MAP_LOW_MODE(x) (((x) & 0x1) << 7) + +/* ADXL372_HPF */ +#define ADXL372_HPF_CORNER(x) (((x) & 0x3) << 0) + +enum adxl372_axis { + ADXL372_X_AXIS, + ADXL372_Y_AXIS, + ADXL372_Z_AXIS +}; + +enum adxl372_op_mode { + ADXL372_STANDBY, + ADXL372_WAKE_UP, + ADXL372_INSTANT_ON, + ADXL372_FULL_BW_MEASUREMENT +}; + +enum adxl372_bandwidth { + ADXL372_BW_200HZ, + ADXL372_BW_400HZ, + ADXL372_BW_800HZ, + ADXL372_BW_1600HZ, + ADXL372_BW_3200HZ, + ADXL372_BW_LPF_DISABLED = 0xC, +}; + +enum adxl372_hpf_corner { + ADXL372_HPF_CORNER_0, + ADXL372_HPF_CORNER_1, + ADXL372_HPF_CORNER_2, + ADXL372_HPF_CORNER_3, + ADXL372_HPF_DISABLED, +}; + +enum adxl372_act_proc_mode { + ADXL372_DEFAULT, + ADXL372_LINKED, + ADXL372_LOOPED +}; + +enum adxl372_odr { + ADXL372_ODR_400HZ, + ADXL372_ODR_800HZ, + ADXL372_ODR_1600HZ, + ADXL372_ODR_3200HZ, + ADXL372_ODR_6400HZ +}; + +enum adxl372_instant_on_th_mode { + ADXL372_INSTANT_ON_LOW_TH, + ADXL372_INSTANT_ON_HIGH_TH +}; + +enum adxl372_wakeup_rate { + ADXL372_WUR_52ms, + ADXL372_WUR_104ms, + ADXL372_WUR_208ms, + ADXL372_WUR_512ms, + ADXL372_WUR_2048ms, + ADXL372_WUR_4096ms, + ADXL372_WUR_8192ms, + ADXL372_WUR_24576ms +}; + +enum adxl372_filter_settle { + ADXL372_FILTER_SETTLE_370, + ADXL372_FILTER_SETTLE_16 +}; + +enum adxl372_fifo_format { + ADXL372_XYZ_FIFO, + ADXL372_X_FIFO, + ADXL372_Y_FIFO, + ADXL372_XY_FIFO, + ADXL372_Z_FIFO, + ADXL372_XZ_FIFO, + ADXL372_YZ_FIFO, + ADXL372_XYZ_PEAK_FIFO, +}; + +enum adxl372_fifo_mode { + ADXL372_FIFO_BYPASSED, + ADXL372_FIFO_STREAMED, + ADXL372_FIFO_TRIGGERED, + ADXL372_FIFO_OLD_SAVED +}; + +struct adxl372_fifo_config { + enum adxl372_fifo_mode fifo_mode; + enum adxl372_fifo_format fifo_format; + u16_t fifo_samples; +}; + +struct adxl372_activity_threshold { + u16_t thresh; + bool referenced; + bool enable; +}; + +struct adxl372_xyz_accel_data { + s16_t x; + s16_t y; + s16_t z; +}; + +struct adxl372_data { + struct device *bus; +#ifdef CONFIG_ADXL372_SPI + struct spi_config spi_cfg; +#endif +#if defined(CONFIG_ADXL372_SPI_GPIO_CS) + struct spi_cs_control adxl372_cs_ctrl; +#endif + + struct adxl372_xyz_accel_data sample; + struct adxl372_fifo_config fifo_config; + +#ifdef CONFIG_ADXL372_TRIGGER + struct device *gpio; + struct gpio_callback gpio_cb; + + sensor_trigger_handler_t th_handler; + struct sensor_trigger th_trigger; + sensor_trigger_handler_t drdy_handler; + struct sensor_trigger drdy_trigger; + +#if defined(CONFIG_ADXL372_TRIGGER_OWN_THREAD) + K_THREAD_STACK_MEMBER(thread_stack, CONFIG_ADXL372_THREAD_STACK_SIZE); + struct k_sem gpio_sem; + struct k_thread thread; +#elif defined(CONFIG_ADXL372_TRIGGER_GLOBAL_THREAD) + struct k_work work; + struct device *dev; +#endif +#endif /* CONFIG_ADXL372_TRIGGER */ +}; + +struct adxl372_dev_config { +#ifdef CONFIG_ADXL372_I2C + const char *i2c_port; + u16_t i2c_addr; +#endif +#ifdef CONFIG_ADXL372_SPI + const char *spi_port; + u16_t spi_slave; + u32_t spi_max_frequency; +#ifdef CONFIG_ADXL372_SPI_GPIO_CS + const char *gpio_cs_port; + u8_t cs_gpio; +#endif +#endif /* CONFIG_ADXL372_SPI */ +#ifdef CONFIG_ADXL372_TRIGGER + const char *gpio_port; + u8_t int_gpio; +#endif + bool max_peak_detect_mode; + + /* Device Settings */ + bool autosleep; + + struct adxl372_activity_threshold activity_th; + struct adxl372_activity_threshold activity2_th; + struct adxl372_activity_threshold inactivity_th; + struct adxl372_fifo_config fifo_config; + + enum adxl372_bandwidth bw; + enum adxl372_hpf_corner hpf; + enum adxl372_odr odr; + enum adxl372_wakeup_rate wur; + enum adxl372_act_proc_mode act_proc_mode; + enum adxl372_instant_on_th_mode th_mode; + enum adxl372_filter_settle filter_settle; + enum adxl372_op_mode op_mode; + + u16_t inactivity_time; + u8_t activity_time; + u8_t int1_config; + u8_t int2_config; +}; + +#ifdef CONFIG_ADXL372_TRIGGER +int adxl372_get_status(struct device *dev, + u8_t *status1, u8_t *status2, u16_t *fifo_entries); + +int adxl372_reg_write_mask(struct device *dev, + u8_t reg_addr, u32_t mask, u8_t data); + +int adxl372_trigger_set(struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler); + +int adxl372_init_interrupt(struct device *dev); +#endif /* CONFIG_ADT7420_TRIGGER */ + +#define SYS_LOG_DOMAIN "ADXL372" +#define SYS_LOG_LEVEL CONFIG_SYS_LOG_SENSOR_LEVEL +#include +#endif /* __SENSOR_ADXL372_H__ */ diff --git a/drivers/sensor/adxl372/adxl372_trigger.c b/drivers/sensor/adxl372/adxl372_trigger.c new file mode 100644 index 00000000000..e084e5d6a24 --- /dev/null +++ b/drivers/sensor/adxl372/adxl372_trigger.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2018 Analog Devices Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include "adxl372.h" + +static void adxl372_thread_cb(void *arg) +{ + struct device *dev = arg; + struct adxl372_data *drv_data = dev->driver_data; + const struct adxl372_dev_config *cfg = dev->config->config_info; + u8_t status1, status2; + + /* Clear the status */ + if (adxl372_get_status(dev, &status1, &status2, NULL) < 0) { + return; + } + + if (drv_data->th_handler != NULL) { + /* In max peak mode we wait until we settle below the inactivity + * threshold and then call the trigger handler. + */ + if (cfg->max_peak_detect_mode && + ADXL372_STATUS_2_INACT(status2)) { + drv_data->th_handler(dev, &drv_data->th_trigger); + } else if (!cfg->max_peak_detect_mode && + (ADXL372_STATUS_2_INACT(status2) || + ADXL372_STATUS_2_ACTIVITY(status2))) { + drv_data->th_handler(dev, &drv_data->th_trigger); + } + } + + if ((drv_data->drdy_handler != NULL) && + ADXL372_STATUS_1_DATA_RDY(status1)) { + drv_data->drdy_handler(dev, &drv_data->drdy_trigger); + } + + gpio_pin_enable_callback(drv_data->gpio, cfg->int_gpio); +} + +static void adxl372_gpio_callback(struct device *dev, + struct gpio_callback *cb, u32_t pins) +{ + struct adxl372_data *drv_data = + CONTAINER_OF(cb, struct adxl372_data, gpio_cb); + const struct adxl372_dev_config *cfg = dev->config->config_info; + + gpio_pin_disable_callback(dev, cfg->int_gpio); + +#if defined(CONFIG_ADXL372_TRIGGER_OWN_THREAD) + k_sem_give(&drv_data->gpio_sem); +#elif defined(CONFIG_ADXL372_TRIGGER_GLOBAL_THREAD) + k_work_submit(&drv_data->work); +#endif +} + +#if defined(CONFIG_ADXL372_TRIGGER_OWN_THREAD) +static void adxl372_thread(int dev_ptr, int unused) +{ + struct device *dev = INT_TO_POINTER(dev_ptr); + struct adxl372_data *drv_data = dev->driver_data; + + ARG_UNUSED(unused); + + while (true) { + k_sem_take(&drv_data->gpio_sem, K_FOREVER); + adxl372_thread_cb(dev); + } +} + +#elif defined(CONFIG_ADXL372_TRIGGER_GLOBAL_THREAD) +static void adxl372_work_cb(struct k_work *work) +{ + struct adxl372_data *drv_data = + CONTAINER_OF(work, struct adxl372_data, work); + + adxl372_thread_cb(drv_data->dev); +} +#endif + +int adxl372_trigger_set(struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + struct adxl372_data *drv_data = dev->driver_data; + const struct adxl372_dev_config *cfg = dev->config->config_info; + u8_t int_mask, int_en, status1, status2; + int ret; + + gpio_pin_disable_callback(drv_data->gpio, cfg->int_gpio); + + switch (trig->type) { + case SENSOR_TRIG_THRESHOLD: + drv_data->th_handler = handler; + drv_data->th_trigger = *trig; + int_mask = ADXL372_INT1_MAP_ACT_MSK | + ADXL372_INT1_MAP_INACT_MSK; + break; + case SENSOR_TRIG_DATA_READY: + drv_data->drdy_handler = handler; + drv_data->drdy_trigger = *trig; + int_mask = ADXL372_INT1_MAP_DATA_RDY_MSK; + break; + default: + SYS_LOG_ERR("Unsupported sensor trigger"); + ret = -ENOTSUP; + goto out; + } + + if (handler) { + int_en = int_mask; + } else { + int_en = 0; + }; + + ret = adxl372_reg_write_mask(dev, ADXL372_INT1_MAP, int_mask, int_en); + + adxl372_get_status(dev, &status1, &status2, NULL); /* Clear status */ +out: + gpio_pin_enable_callback(drv_data->gpio, cfg->int_gpio); + + return ret; +} + +int adxl372_init_interrupt(struct device *dev) +{ + struct adxl372_data *drv_data = dev->driver_data; + const struct adxl372_dev_config *cfg = dev->config->config_info; + + drv_data->gpio = device_get_binding(cfg->gpio_port); + if (drv_data->gpio == NULL) { + SYS_LOG_ERR("Failed to get pointer to %s device!", + cfg->gpio_port); + return -EINVAL; + } + + gpio_pin_configure(drv_data->gpio, cfg->int_gpio, + GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE | + GPIO_INT_ACTIVE_HIGH | GPIO_INT_DEBOUNCE); + + gpio_init_callback(&drv_data->gpio_cb, + adxl372_gpio_callback, + BIT(cfg->int_gpio)); + + if (gpio_add_callback(drv_data->gpio, &drv_data->gpio_cb) < 0) { + SYS_LOG_ERR("Failed to set gpio callback!"); + return -EIO; + } + +#if defined(CONFIG_ADXL372_TRIGGER_OWN_THREAD) + k_sem_init(&drv_data->gpio_sem, 0, UINT_MAX); + + k_thread_create(&drv_data->thread, drv_data->thread_stack, + CONFIG_ADXL372_THREAD_STACK_SIZE, + (k_thread_entry_t)adxl372_thread, dev, + 0, NULL, K_PRIO_COOP(CONFIG_ADXL372_THREAD_PRIORITY), + 0, 0); +#elif defined(CONFIG_ADXL372_TRIGGER_GLOBAL_THREAD) + drv_data->work.handler = adxl372_work_cb; + drv_data->dev = dev; +#endif + + return 0; +}