From f40e8005305418522eeff217cb72e806eeaa3598 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Thu, 21 Jan 2021 16:39:44 +0100 Subject: [PATCH 001/119] iio: st_sensors: fix typo in comment s/timetamping/timestamping/ Cc: trivial@kernel.org Signed-off-by: Ahmad Fatoum Link: https://lore.kernel.org/r/20210121153945.5499-1-a.fatoum@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/common/st_sensors/st_sensors_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c index eee30130ae23..802f9ae04cf4 100644 --- a/drivers/iio/common/st_sensors/st_sensors_buffer.c +++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c @@ -57,7 +57,7 @@ irqreturn_t st_sensors_trigger_handler(int irq, void *p) s64 timestamp; /* - * If we do timetamping here, do it before reading the values, because + * If we do timestamping here, do it before reading the values, because * once we've read the values, new interrupts can occur (when using * the hardware trigger) and the hw_timestamp may get updated. * By storing it in a local variable first, we are safe. From c03e2df6e1d51f4e1252318275007214a3bd8e85 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 24 Jan 2021 19:10:22 +0000 Subject: [PATCH 002/119] iio:adc:stm32-adc: Add HAS_IOMEM dependency Seems that there are config combinations in which this driver gets enabled and hence selects the MFD, but with out HAS_IOMEM getting pulled in via some other route. MFD is entirely contained in an if HAS_IOMEM block, leading to the build issue in this bugzilla. https://bugzilla.kernel.org/show_bug.cgi?id=209889 Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index bf7d22fa4be2..6605c263949c 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -923,6 +923,7 @@ config STM32_ADC_CORE depends on ARCH_STM32 || COMPILE_TEST depends on OF depends on REGULATOR + depends on HAS_IOMEM select IIO_BUFFER select MFD_STM32_TIMERS select IIO_STM32_TIMER_TRIGGER From 01f68f067dc39df9c9d95d759ee61517eb4b0fcf Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Fri, 29 Jan 2021 14:22:22 +0100 Subject: [PATCH 003/119] counter: stm32-lptimer-cnt: remove iio counter abi Currently, the STM32 LP Timer counter driver registers into both IIO and counter subsystems, which is redundant. Remove the IIO counter ABI and IIO registration from the STM32 LP Timer counter driver since it's been superseded by the Counter subsystem as discussed in [1]. Keep only the counter subsystem related part. Move a part of the ABI documentation into a driver comment. This also removes a duplicate ABI warning $ scripts/get_abi.pl validate ... /sys/bus/iio/devices/iio:deviceX/in_count0_preset is defined 2 times: ./Documentation/ABI/testing/sysfs-bus-iio-timer-stm32:100 ./Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32:0 [1] https://lkml.org/lkml/2021/1/19/347 Acked-by: William Breathitt Gray Signed-off-by: Fabrice Gasnier Link: https://lore.kernel.org/r/1611926542-2490-1-git-send-email-fabrice.gasnier@foss.st.com Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-iio-lptimer-stm32 | 62 ---- drivers/counter/Kconfig | 2 +- drivers/counter/stm32-lptimer-cnt.c | 297 +++--------------- 3 files changed, 37 insertions(+), 324 deletions(-) delete mode 100644 Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32 diff --git a/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32 deleted file mode 100644 index 73498ff666bd..000000000000 --- a/Documentation/ABI/testing/sysfs-bus-iio-lptimer-stm32 +++ /dev/null @@ -1,62 +0,0 @@ -What: /sys/bus/iio/devices/iio:deviceX/in_count0_preset -KernelVersion: 4.13 -Contact: fabrice.gasnier@st.com -Description: - Reading returns the current preset value. Writing sets the - preset value. Encoder counts continuously from 0 to preset - value, depending on direction (up/down). - -What: /sys/bus/iio/devices/iio:deviceX/in_count_quadrature_mode_available -KernelVersion: 4.13 -Contact: fabrice.gasnier@st.com -Description: - Reading returns the list possible quadrature modes. - -What: /sys/bus/iio/devices/iio:deviceX/in_count0_quadrature_mode -KernelVersion: 4.13 -Contact: fabrice.gasnier@st.com -Description: - Configure the device counter quadrature modes: - - - non-quadrature: - Encoder IN1 input servers as the count input (up - direction). - - - quadrature: - Encoder IN1 and IN2 inputs are mixed to get direction - and count. - -What: /sys/bus/iio/devices/iio:deviceX/in_count_polarity_available -KernelVersion: 4.13 -Contact: fabrice.gasnier@st.com -Description: - Reading returns the list possible active edges. - -What: /sys/bus/iio/devices/iio:deviceX/in_count0_polarity -KernelVersion: 4.13 -Contact: fabrice.gasnier@st.com -Description: - Configure the device encoder/counter active edge: - - - rising-edge - - falling-edge - - both-edges - - In non-quadrature mode, device counts up on active edge. - - In quadrature mode, encoder counting scenarios are as follows: - - +---------+----------+--------------------+--------------------+ - | Active | Level on | IN1 signal | IN2 signal | - | edge | opposite +----------+---------+----------+---------+ - | | signal | Rising | Falling | Rising | Falling | - +---------+----------+----------+---------+----------+---------+ - | Rising | High -> | Down | - | Up | - | - | edge | Low -> | Up | - | Down | - | - +---------+----------+----------+---------+----------+---------+ - | Falling | High -> | - | Up | - | Down | - | edge | Low -> | - | Down | - | Up | - +---------+----------+----------+---------+----------+---------+ - | Both | High -> | Down | Up | Up | Down | - | edges | Low -> | Up | Down | Down | Up | - +---------+----------+----------+---------+----------+---------+ diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index 2de53ab0dd25..cbdf84200e27 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -41,7 +41,7 @@ config STM32_TIMER_CNT config STM32_LPTIMER_CNT tristate "STM32 LP Timer encoder counter driver" - depends on (MFD_STM32_LPTIMER || COMPILE_TEST) && IIO + depends on MFD_STM32_LPTIMER || COMPILE_TEST help Select this option to enable STM32 Low-Power Timer quadrature encoder and counter driver. diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c index fd6828e2d34f..937439635d53 100644 --- a/drivers/counter/stm32-lptimer-cnt.c +++ b/drivers/counter/stm32-lptimer-cnt.c @@ -12,8 +12,8 @@ #include #include -#include #include +#include #include #include #include @@ -107,249 +107,27 @@ static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable) return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val); } -static int stm32_lptim_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, int val2, long mask) -{ - struct stm32_lptim_cnt *priv = iio_priv(indio_dev); - int ret; - - switch (mask) { - case IIO_CHAN_INFO_ENABLE: - if (val < 0 || val > 1) - return -EINVAL; - - /* Check nobody uses the timer, or already disabled/enabled */ - ret = stm32_lptim_is_enabled(priv); - if ((ret < 0) || (!ret && !val)) - return ret; - if (val && ret) - return -EBUSY; - - ret = stm32_lptim_setup(priv, val); - if (ret) - return ret; - return stm32_lptim_set_enable_state(priv, val); - - default: - return -EINVAL; - } -} - -static int stm32_lptim_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, long mask) -{ - struct stm32_lptim_cnt *priv = iio_priv(indio_dev); - u32 dat; - int ret; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - ret = regmap_read(priv->regmap, STM32_LPTIM_CNT, &dat); - if (ret) - return ret; - *val = dat; - return IIO_VAL_INT; - - case IIO_CHAN_INFO_ENABLE: - ret = stm32_lptim_is_enabled(priv); - if (ret < 0) - return ret; - *val = ret; - return IIO_VAL_INT; - - case IIO_CHAN_INFO_SCALE: - /* Non-quadrature mode: scale = 1 */ - *val = 1; - *val2 = 0; - if (priv->quadrature_mode) { - /* - * Quadrature encoder mode: - * - both edges, quarter cycle, scale is 0.25 - * - either rising/falling edge scale is 0.5 - */ - if (priv->polarity > 1) - *val2 = 2; - else - *val2 = 1; - } - return IIO_VAL_FRACTIONAL_LOG2; - - default: - return -EINVAL; - } -} - -static const struct iio_info stm32_lptim_cnt_iio_info = { - .read_raw = stm32_lptim_read_raw, - .write_raw = stm32_lptim_write_raw, -}; - -static const char *const stm32_lptim_quadrature_modes[] = { - "non-quadrature", - "quadrature", -}; - -static int stm32_lptim_get_quadrature_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct stm32_lptim_cnt *priv = iio_priv(indio_dev); - - return priv->quadrature_mode; -} - -static int stm32_lptim_set_quadrature_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - unsigned int type) -{ - struct stm32_lptim_cnt *priv = iio_priv(indio_dev); - - if (stm32_lptim_is_enabled(priv)) - return -EBUSY; - - priv->quadrature_mode = type; - - return 0; -} - -static const struct iio_enum stm32_lptim_quadrature_mode_en = { - .items = stm32_lptim_quadrature_modes, - .num_items = ARRAY_SIZE(stm32_lptim_quadrature_modes), - .get = stm32_lptim_get_quadrature_mode, - .set = stm32_lptim_set_quadrature_mode, -}; - -static const char * const stm32_lptim_cnt_polarity[] = { - "rising-edge", "falling-edge", "both-edges", -}; - -static int stm32_lptim_cnt_get_polarity(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct stm32_lptim_cnt *priv = iio_priv(indio_dev); - - return priv->polarity; -} - -static int stm32_lptim_cnt_set_polarity(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - unsigned int type) -{ - struct stm32_lptim_cnt *priv = iio_priv(indio_dev); - - if (stm32_lptim_is_enabled(priv)) - return -EBUSY; - - priv->polarity = type; - - return 0; -} - -static const struct iio_enum stm32_lptim_cnt_polarity_en = { - .items = stm32_lptim_cnt_polarity, - .num_items = ARRAY_SIZE(stm32_lptim_cnt_polarity), - .get = stm32_lptim_cnt_get_polarity, - .set = stm32_lptim_cnt_set_polarity, -}; - -static ssize_t stm32_lptim_cnt_get_ceiling(struct stm32_lptim_cnt *priv, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%u\n", priv->ceiling); -} - -static ssize_t stm32_lptim_cnt_set_ceiling(struct stm32_lptim_cnt *priv, - const char *buf, size_t len) -{ - int ret; - - if (stm32_lptim_is_enabled(priv)) - return -EBUSY; - - ret = kstrtouint(buf, 0, &priv->ceiling); - if (ret) - return ret; - - if (priv->ceiling > STM32_LPTIM_MAX_ARR) - return -EINVAL; - - return len; -} - -static ssize_t stm32_lptim_cnt_get_preset_iio(struct iio_dev *indio_dev, - uintptr_t private, - const struct iio_chan_spec *chan, - char *buf) -{ - struct stm32_lptim_cnt *priv = iio_priv(indio_dev); - - return stm32_lptim_cnt_get_ceiling(priv, buf); -} - -static ssize_t stm32_lptim_cnt_set_preset_iio(struct iio_dev *indio_dev, - uintptr_t private, - const struct iio_chan_spec *chan, - const char *buf, size_t len) -{ - struct stm32_lptim_cnt *priv = iio_priv(indio_dev); - - return stm32_lptim_cnt_set_ceiling(priv, buf, len); -} - -/* LP timer with encoder */ -static const struct iio_chan_spec_ext_info stm32_lptim_enc_ext_info[] = { - { - .name = "preset", - .shared = IIO_SEPARATE, - .read = stm32_lptim_cnt_get_preset_iio, - .write = stm32_lptim_cnt_set_preset_iio, - }, - IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en), - IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en), - IIO_ENUM("quadrature_mode", IIO_SEPARATE, - &stm32_lptim_quadrature_mode_en), - IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_lptim_quadrature_mode_en), - {} -}; - -static const struct iio_chan_spec stm32_lptim_enc_channels = { - .type = IIO_COUNT, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_ENABLE) | - BIT(IIO_CHAN_INFO_SCALE), - .ext_info = stm32_lptim_enc_ext_info, - .indexed = 1, -}; - -/* LP timer without encoder (counter only) */ -static const struct iio_chan_spec_ext_info stm32_lptim_cnt_ext_info[] = { - { - .name = "preset", - .shared = IIO_SEPARATE, - .read = stm32_lptim_cnt_get_preset_iio, - .write = stm32_lptim_cnt_set_preset_iio, - }, - IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en), - IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en), - {} -}; - -static const struct iio_chan_spec stm32_lptim_cnt_channels = { - .type = IIO_COUNT, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_ENABLE) | - BIT(IIO_CHAN_INFO_SCALE), - .ext_info = stm32_lptim_cnt_ext_info, - .indexed = 1, -}; - /** * enum stm32_lptim_cnt_function - enumerates LPTimer counter & encoder modes * @STM32_LPTIM_COUNTER_INCREASE: up count on IN1 rising, falling or both edges * @STM32_LPTIM_ENCODER_BOTH_EDGE: count on both edges (IN1 & IN2 quadrature) + * + * In non-quadrature mode, device counts up on active edge. + * In quadrature mode, encoder counting scenarios are as follows: + * +---------+----------+--------------------+--------------------+ + * | Active | Level on | IN1 signal | IN2 signal | + * | edge | opposite +----------+---------+----------+---------+ + * | | signal | Rising | Falling | Rising | Falling | + * +---------+----------+----------+---------+----------+---------+ + * | Rising | High -> | Down | - | Up | - | + * | edge | Low -> | Up | - | Down | - | + * +---------+----------+----------+---------+----------+---------+ + * | Falling | High -> | - | Up | - | Down | + * | edge | Low -> | - | Down | - | Up | + * +---------+----------+----------+---------+----------+---------+ + * | Both | High -> | Down | Up | Up | Down | + * | edges | Low -> | Up | Down | Down | Up | + * +---------+----------+----------+---------+----------+---------+ */ enum stm32_lptim_cnt_function { STM32_LPTIM_COUNTER_INCREASE, @@ -484,7 +262,7 @@ static ssize_t stm32_lptim_cnt_ceiling_read(struct counter_device *counter, { struct stm32_lptim_cnt *const priv = counter->priv; - return stm32_lptim_cnt_get_ceiling(priv, buf); + return snprintf(buf, PAGE_SIZE, "%u\n", priv->ceiling); } static ssize_t stm32_lptim_cnt_ceiling_write(struct counter_device *counter, @@ -493,8 +271,22 @@ static ssize_t stm32_lptim_cnt_ceiling_write(struct counter_device *counter, const char *buf, size_t len) { struct stm32_lptim_cnt *const priv = counter->priv; + unsigned int ceiling; + int ret; - return stm32_lptim_cnt_set_ceiling(priv, buf, len); + if (stm32_lptim_is_enabled(priv)) + return -EBUSY; + + ret = kstrtouint(buf, 0, &ceiling); + if (ret) + return ret; + + if (ceiling > STM32_LPTIM_MAX_ARR) + return -EINVAL; + + priv->ceiling = ceiling; + + return len; } static const struct counter_count_ext stm32_lptim_cnt_ext[] = { @@ -630,32 +422,19 @@ static int stm32_lptim_cnt_probe(struct platform_device *pdev) { struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent); struct stm32_lptim_cnt *priv; - struct iio_dev *indio_dev; - int ret; if (IS_ERR_OR_NULL(ddata)) return -EINVAL; - indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); - if (!indio_dev) + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) return -ENOMEM; - priv = iio_priv(indio_dev); priv->dev = &pdev->dev; priv->regmap = ddata->regmap; priv->clk = ddata->clk; priv->ceiling = STM32_LPTIM_MAX_ARR; - /* Initialize IIO device */ - indio_dev->name = dev_name(&pdev->dev); - indio_dev->dev.of_node = pdev->dev.of_node; - indio_dev->info = &stm32_lptim_cnt_iio_info; - if (ddata->has_encoder) - indio_dev->channels = &stm32_lptim_enc_channels; - else - indio_dev->channels = &stm32_lptim_cnt_channels; - indio_dev->num_channels = 1; - /* Initialize Counter device */ priv->counter.name = dev_name(&pdev->dev); priv->counter.parent = &pdev->dev; @@ -673,10 +452,6 @@ static int stm32_lptim_cnt_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); - ret = devm_iio_device_register(&pdev->dev, indio_dev); - if (ret) - return ret; - return devm_counter_register(&pdev->dev, &priv->counter); } From e357e81fcf115bce68f3c8ca76cfc89caf24ec5a Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Sat, 30 Jan 2021 11:37:03 +0900 Subject: [PATCH 004/119] counter: 104-quad-8: Remove IIO counter ABI The IIO counter driver has been superseded by the Counter subsystem as discussed in [1]. This patch removes the IIO counter ABI from the 104-QUAD-8 driver. [1] https://lore.kernel.org/lkml/20210119104105.000010df@Huawei.com/ Cc: Syed Nayyar Waris Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/98a39983d5df761c058a469d1346fd8ffdef8516.1611973018.git.vilhelm.gray@gmail.com Signed-off-by: Jonathan Cameron --- .../testing/sysfs-bus-iio-counter-104-quad-8 | 133 ---- MAINTAINERS | 1 - drivers/counter/104-quad-8.c | 653 ++---------------- drivers/counter/Kconfig | 2 +- 4 files changed, 65 insertions(+), 724 deletions(-) delete mode 100644 Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8 diff --git a/Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8 b/Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8 deleted file mode 100644 index bac3d0d48b7b..000000000000 --- a/Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8 +++ /dev/null @@ -1,133 +0,0 @@ -What: /sys/bus/iio/devices/iio:deviceX/in_count_count_mode_available -What: /sys/bus/iio/devices/iio:deviceX/in_count_noise_error_available -What: /sys/bus/iio/devices/iio:deviceX/in_count_quadrature_mode_available -What: /sys/bus/iio/devices/iio:deviceX/in_index_index_polarity_available -What: /sys/bus/iio/devices/iio:deviceX/in_index_synchronous_mode_available -KernelVersion: 4.10 -Contact: linux-iio@vger.kernel.org -Description: - This interface is deprecated; please use the Counter subsystem. - - Discrete set of available values for the respective counter - configuration are listed in this file. - -What: /sys/bus/iio/devices/iio:deviceX/in_countY_count_mode -KernelVersion: 4.10 -Contact: linux-iio@vger.kernel.org -Description: - This interface is deprecated; please use the Counter subsystem. - - Count mode for channel Y. Four count modes are available: - normal, range limit, non-recycle, and modulo-n. The preset value - for channel Y is used by the count mode where required. - - Normal: - Counting is continuous in either direction. - - Range Limit: - An upper or lower limit is set, mimicking limit switches - in the mechanical counterpart. The upper limit is set to - the preset value, while the lower limit is set to 0. The - counter freezes at count = preset when counting up, and - at count = 0 when counting down. At either of these - limits, the counting is resumed only when the count - direction is reversed. - - Non-recycle: - Counter is disabled whenever a 24-bit count overflow or - underflow takes place. The counter is re-enabled when a - new count value is loaded to the counter via a preset - operation or write to raw. - - Modulo-N: - A count boundary is set between 0 and the preset value. - The counter is reset to 0 at count = preset when - counting up, while the counter is set to the preset - value at count = 0 when counting down; the counter does - not freeze at the bundary points, but counts - continuously throughout. - -What: /sys/bus/iio/devices/iio:deviceX/in_countY_noise_error -KernelVersion: 4.10 -Contact: linux-iio@vger.kernel.org -Description: - This interface is deprecated; please use the Counter subsystem. - - Read-only attribute that indicates whether excessive noise is - present at the channel Y count inputs in quadrature clock mode; - irrelevant in non-quadrature clock mode. - -What: /sys/bus/iio/devices/iio:deviceX/in_countY_preset -KernelVersion: 4.10 -Contact: linux-iio@vger.kernel.org -Description: - This interface is deprecated; please use the Counter subsystem. - - If the counter device supports preset registers, the preset - count for channel Y is provided by this attribute. - -What: /sys/bus/iio/devices/iio:deviceX/in_countY_quadrature_mode -KernelVersion: 4.10 -Contact: linux-iio@vger.kernel.org -Description: - This interface is deprecated; please use the Counter subsystem. - - Configure channel Y counter for non-quadrature or quadrature - clock mode. Selecting non-quadrature clock mode will disable - synchronous load mode. In quadrature clock mode, the channel Y - scale attribute selects the encoder phase division (scale of 1 - selects full-cycle, scale of 0.5 selects half-cycle, scale of - 0.25 selects quarter-cycle) processed by the channel Y counter. - - Non-quadrature: - The filter and decoder circuit are bypassed. Encoder A - input serves as the count input and B as the UP/DOWN - direction control input, with B = 1 selecting UP Count - mode and B = 0 selecting Down Count mode. - - Quadrature: - Encoder A and B inputs are digitally filtered and - decoded for UP/DN clock. - -What: /sys/bus/iio/devices/iio:deviceX/in_countY_set_to_preset_on_index -KernelVersion: 4.10 -Contact: linux-iio@vger.kernel.org -Description: - This interface is deprecated; please use the Counter subsystem. - - Whether to set channel Y counter with channel Y preset value - when channel Y index input is active, or continuously count. - Valid attribute values are boolean. - -What: /sys/bus/iio/devices/iio:deviceX/in_indexY_index_polarity -KernelVersion: 4.10 -Contact: linux-iio@vger.kernel.org -Description: - This interface is deprecated; please use the Counter subsystem. - - Active level of channel Y index input; irrelevant in - non-synchronous load mode. - -What: /sys/bus/iio/devices/iio:deviceX/in_indexY_synchronous_mode -KernelVersion: 4.10 -Contact: linux-iio@vger.kernel.org -Description: - This interface is deprecated; please use the Counter subsystem. - - Configure channel Y counter for non-synchronous or synchronous - load mode. Synchronous load mode cannot be selected in - non-quadrature clock mode. - - Non-synchronous: - A logic low level is the active level at this index - input. The index function (as enabled via - set_to_preset_on_index) is performed directly on the - active level of the index input. - - Synchronous: - Intended for interfacing with encoder Index output in - quadrature clock mode. The active level is configured - via index_polarity. The index function (as enabled via - set_to_preset_on_index) is performed synchronously with - the quadrature clock on the active level of the index - input. diff --git a/MAINTAINERS b/MAINTAINERS index d92f85ca831d..dda153b7c102 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -300,7 +300,6 @@ M: Syed Nayyar Waris L: linux-iio@vger.kernel.org S: Maintained F: Documentation/ABI/testing/sysfs-bus-counter-104-quad-8 -F: Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8 F: drivers/counter/104-quad-8.c ACCES PCI-IDIO-16 GPIO DRIVER diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c index 78766b6ec271..9691f8612be8 100644 --- a/drivers/counter/104-quad-8.c +++ b/drivers/counter/104-quad-8.c @@ -9,8 +9,6 @@ #include #include #include -#include -#include #include #include #include @@ -29,7 +27,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses"); #define QUAD8_NUM_COUNTERS 8 /** - * struct quad8_iio - IIO device private data structure + * struct quad8 - device private data structure * @counter: instance of the counter_device * @fck_prescaler: array of filter clock prescaler configurations * @preset: array of preset values @@ -41,9 +39,9 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses"); * @synchronous_mode: array of index function synchronous mode configurations * @index_polarity: array of index function polarity configurations * @cable_fault_enable: differential encoder cable status enable configurations - * @base: base port address of the IIO device + * @base: base port address of the device */ -struct quad8_iio { +struct quad8 { struct mutex lock; struct counter_device counter; unsigned int fck_prescaler[QUAD8_NUM_COUNTERS]; @@ -98,532 +96,10 @@ struct quad8_iio { #define QUAD8_CMR_QUADRATURE_X2 0x10 #define QUAD8_CMR_QUADRATURE_X4 0x18 - -static int quad8_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int *val, int *val2, long mask) -{ - struct quad8_iio *const priv = iio_priv(indio_dev); - const int base_offset = priv->base + 2 * chan->channel; - unsigned int flags; - unsigned int borrow; - unsigned int carry; - int i; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - if (chan->type == IIO_INDEX) { - *val = !!(inb(priv->base + QUAD8_REG_INDEX_INPUT_LEVELS) - & BIT(chan->channel)); - return IIO_VAL_INT; - } - - flags = inb(base_offset + 1); - borrow = flags & QUAD8_FLAG_BT; - carry = !!(flags & QUAD8_FLAG_CT); - - /* Borrow XOR Carry effectively doubles count range */ - *val = (borrow ^ carry) << 24; - - mutex_lock(&priv->lock); - - /* Reset Byte Pointer; transfer Counter to Output Latch */ - outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT, - base_offset + 1); - - for (i = 0; i < 3; i++) - *val |= (unsigned int)inb(base_offset) << (8 * i); - - mutex_unlock(&priv->lock); - - return IIO_VAL_INT; - case IIO_CHAN_INFO_ENABLE: - *val = priv->ab_enable[chan->channel]; - return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - *val = 1; - *val2 = priv->quadrature_scale[chan->channel]; - return IIO_VAL_FRACTIONAL_LOG2; - } - - return -EINVAL; -} - -static int quad8_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, int val, int val2, long mask) -{ - struct quad8_iio *const priv = iio_priv(indio_dev); - const int base_offset = priv->base + 2 * chan->channel; - int i; - unsigned int ior_cfg; - - switch (mask) { - case IIO_CHAN_INFO_RAW: - if (chan->type == IIO_INDEX) - return -EINVAL; - - /* Only 24-bit values are supported */ - if ((unsigned int)val > 0xFFFFFF) - return -EINVAL; - - mutex_lock(&priv->lock); - - /* Reset Byte Pointer */ - outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); - - /* Counter can only be set via Preset Register */ - for (i = 0; i < 3; i++) - outb(val >> (8 * i), base_offset); - - /* Transfer Preset Register to Counter */ - outb(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, base_offset + 1); - - /* Reset Byte Pointer */ - outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); - - /* Set Preset Register back to original value */ - val = priv->preset[chan->channel]; - for (i = 0; i < 3; i++) - outb(val >> (8 * i), base_offset); - - /* Reset Borrow, Carry, Compare, and Sign flags */ - outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1); - /* Reset Error flag */ - outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1); - - mutex_unlock(&priv->lock); - - return 0; - case IIO_CHAN_INFO_ENABLE: - /* only boolean values accepted */ - if (val < 0 || val > 1) - return -EINVAL; - - mutex_lock(&priv->lock); - - priv->ab_enable[chan->channel] = val; - - ior_cfg = val | priv->preset_enable[chan->channel] << 1; - - /* Load I/O control configuration */ - outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1); - - mutex_unlock(&priv->lock); - - return 0; - case IIO_CHAN_INFO_SCALE: - mutex_lock(&priv->lock); - - /* Quadrature scaling only available in quadrature mode */ - if (!priv->quadrature_mode[chan->channel] && - (val2 || val != 1)) { - mutex_unlock(&priv->lock); - return -EINVAL; - } - - /* Only three gain states (1, 0.5, 0.25) */ - if (val == 1 && !val2) - priv->quadrature_scale[chan->channel] = 0; - else if (!val) - switch (val2) { - case 500000: - priv->quadrature_scale[chan->channel] = 1; - break; - case 250000: - priv->quadrature_scale[chan->channel] = 2; - break; - default: - mutex_unlock(&priv->lock); - return -EINVAL; - } - else { - mutex_unlock(&priv->lock); - return -EINVAL; - } - - mutex_unlock(&priv->lock); - return 0; - } - - return -EINVAL; -} - -static const struct iio_info quad8_info = { - .read_raw = quad8_read_raw, - .write_raw = quad8_write_raw -}; - -static ssize_t quad8_read_preset(struct iio_dev *indio_dev, uintptr_t private, - const struct iio_chan_spec *chan, char *buf) -{ - const struct quad8_iio *const priv = iio_priv(indio_dev); - - return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset[chan->channel]); -} - -static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private, - const struct iio_chan_spec *chan, const char *buf, size_t len) -{ - struct quad8_iio *const priv = iio_priv(indio_dev); - const int base_offset = priv->base + 2 * chan->channel; - unsigned int preset; - int ret; - int i; - - ret = kstrtouint(buf, 0, &preset); - if (ret) - return ret; - - /* Only 24-bit values are supported */ - if (preset > 0xFFFFFF) - return -EINVAL; - - mutex_lock(&priv->lock); - - priv->preset[chan->channel] = preset; - - /* Reset Byte Pointer */ - outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); - - /* Set Preset Register */ - for (i = 0; i < 3; i++) - outb(preset >> (8 * i), base_offset); - - mutex_unlock(&priv->lock); - - return len; -} - -static ssize_t quad8_read_set_to_preset_on_index(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, char *buf) -{ - const struct quad8_iio *const priv = iio_priv(indio_dev); - - return snprintf(buf, PAGE_SIZE, "%u\n", - !priv->preset_enable[chan->channel]); -} - -static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, const char *buf, - size_t len) -{ - struct quad8_iio *const priv = iio_priv(indio_dev); - const int base_offset = priv->base + 2 * chan->channel + 1; - bool preset_enable; - int ret; - unsigned int ior_cfg; - - ret = kstrtobool(buf, &preset_enable); - if (ret) - return ret; - - /* Preset enable is active low in Input/Output Control register */ - preset_enable = !preset_enable; - - mutex_lock(&priv->lock); - - priv->preset_enable[chan->channel] = preset_enable; - - ior_cfg = priv->ab_enable[chan->channel] | - (unsigned int)preset_enable << 1; - - /* Load I/O control configuration to Input / Output Control Register */ - outb(QUAD8_CTR_IOR | ior_cfg, base_offset); - - mutex_unlock(&priv->lock); - - return len; -} - -static const char *const quad8_noise_error_states[] = { - "No excessive noise is present at the count inputs", - "Excessive noise is present at the count inputs" -}; - -static int quad8_get_noise_error(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct quad8_iio *const priv = iio_priv(indio_dev); - const int base_offset = priv->base + 2 * chan->channel + 1; - - return !!(inb(base_offset) & QUAD8_FLAG_E); -} - -static const struct iio_enum quad8_noise_error_enum = { - .items = quad8_noise_error_states, - .num_items = ARRAY_SIZE(quad8_noise_error_states), - .get = quad8_get_noise_error -}; - -static const char *const quad8_count_direction_states[] = { - "down", - "up" -}; - -static int quad8_get_count_direction(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct quad8_iio *const priv = iio_priv(indio_dev); - const int base_offset = priv->base + 2 * chan->channel + 1; - - return !!(inb(base_offset) & QUAD8_FLAG_UD); -} - -static const struct iio_enum quad8_count_direction_enum = { - .items = quad8_count_direction_states, - .num_items = ARRAY_SIZE(quad8_count_direction_states), - .get = quad8_get_count_direction -}; - -static const char *const quad8_count_modes[] = { - "normal", - "range limit", - "non-recycle", - "modulo-n" -}; - -static int quad8_set_count_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int cnt_mode) -{ - struct quad8_iio *const priv = iio_priv(indio_dev); - unsigned int mode_cfg = cnt_mode << 1; - const int base_offset = priv->base + 2 * chan->channel + 1; - - mutex_lock(&priv->lock); - - priv->count_mode[chan->channel] = cnt_mode; - - /* Add quadrature mode configuration */ - if (priv->quadrature_mode[chan->channel]) - mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3; - - /* Load mode configuration to Counter Mode Register */ - outb(QUAD8_CTR_CMR | mode_cfg, base_offset); - - mutex_unlock(&priv->lock); - - return 0; -} - -static int quad8_get_count_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - const struct quad8_iio *const priv = iio_priv(indio_dev); - - return priv->count_mode[chan->channel]; -} - -static const struct iio_enum quad8_count_mode_enum = { - .items = quad8_count_modes, - .num_items = ARRAY_SIZE(quad8_count_modes), - .set = quad8_set_count_mode, - .get = quad8_get_count_mode -}; - -static const char *const quad8_synchronous_modes[] = { - "non-synchronous", - "synchronous" -}; - -static int quad8_set_synchronous_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int synchronous_mode) -{ - struct quad8_iio *const priv = iio_priv(indio_dev); - const int base_offset = priv->base + 2 * chan->channel + 1; - unsigned int idr_cfg = synchronous_mode; - - mutex_lock(&priv->lock); - - idr_cfg |= priv->index_polarity[chan->channel] << 1; - - /* Index function must be non-synchronous in non-quadrature mode */ - if (synchronous_mode && !priv->quadrature_mode[chan->channel]) { - mutex_unlock(&priv->lock); - return -EINVAL; - } - - priv->synchronous_mode[chan->channel] = synchronous_mode; - - /* Load Index Control configuration to Index Control Register */ - outb(QUAD8_CTR_IDR | idr_cfg, base_offset); - - mutex_unlock(&priv->lock); - - return 0; -} - -static int quad8_get_synchronous_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - const struct quad8_iio *const priv = iio_priv(indio_dev); - - return priv->synchronous_mode[chan->channel]; -} - -static const struct iio_enum quad8_synchronous_mode_enum = { - .items = quad8_synchronous_modes, - .num_items = ARRAY_SIZE(quad8_synchronous_modes), - .set = quad8_set_synchronous_mode, - .get = quad8_get_synchronous_mode -}; - -static const char *const quad8_quadrature_modes[] = { - "non-quadrature", - "quadrature" -}; - -static int quad8_set_quadrature_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int quadrature_mode) -{ - struct quad8_iio *const priv = iio_priv(indio_dev); - const int base_offset = priv->base + 2 * chan->channel + 1; - unsigned int mode_cfg; - - mutex_lock(&priv->lock); - - mode_cfg = priv->count_mode[chan->channel] << 1; - - if (quadrature_mode) - mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3; - else { - /* Quadrature scaling only available in quadrature mode */ - priv->quadrature_scale[chan->channel] = 0; - - /* Synchronous function not supported in non-quadrature mode */ - if (priv->synchronous_mode[chan->channel]) - quad8_set_synchronous_mode(indio_dev, chan, 0); - } - - priv->quadrature_mode[chan->channel] = quadrature_mode; - - /* Load mode configuration to Counter Mode Register */ - outb(QUAD8_CTR_CMR | mode_cfg, base_offset); - - mutex_unlock(&priv->lock); - - return 0; -} - -static int quad8_get_quadrature_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - const struct quad8_iio *const priv = iio_priv(indio_dev); - - return priv->quadrature_mode[chan->channel]; -} - -static const struct iio_enum quad8_quadrature_mode_enum = { - .items = quad8_quadrature_modes, - .num_items = ARRAY_SIZE(quad8_quadrature_modes), - .set = quad8_set_quadrature_mode, - .get = quad8_get_quadrature_mode -}; - -static const char *const quad8_index_polarity_modes[] = { - "negative", - "positive" -}; - -static int quad8_set_index_polarity(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int index_polarity) -{ - struct quad8_iio *const priv = iio_priv(indio_dev); - const int base_offset = priv->base + 2 * chan->channel + 1; - unsigned int idr_cfg = index_polarity << 1; - - mutex_lock(&priv->lock); - - idr_cfg |= priv->synchronous_mode[chan->channel]; - - priv->index_polarity[chan->channel] = index_polarity; - - /* Load Index Control configuration to Index Control Register */ - outb(QUAD8_CTR_IDR | idr_cfg, base_offset); - - mutex_unlock(&priv->lock); - - return 0; -} - -static int quad8_get_index_polarity(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - const struct quad8_iio *const priv = iio_priv(indio_dev); - - return priv->index_polarity[chan->channel]; -} - -static const struct iio_enum quad8_index_polarity_enum = { - .items = quad8_index_polarity_modes, - .num_items = ARRAY_SIZE(quad8_index_polarity_modes), - .set = quad8_set_index_polarity, - .get = quad8_get_index_polarity -}; - -static const struct iio_chan_spec_ext_info quad8_count_ext_info[] = { - { - .name = "preset", - .shared = IIO_SEPARATE, - .read = quad8_read_preset, - .write = quad8_write_preset - }, - { - .name = "set_to_preset_on_index", - .shared = IIO_SEPARATE, - .read = quad8_read_set_to_preset_on_index, - .write = quad8_write_set_to_preset_on_index - }, - IIO_ENUM("noise_error", IIO_SEPARATE, &quad8_noise_error_enum), - IIO_ENUM_AVAILABLE("noise_error", &quad8_noise_error_enum), - IIO_ENUM("count_direction", IIO_SEPARATE, &quad8_count_direction_enum), - IIO_ENUM_AVAILABLE("count_direction", &quad8_count_direction_enum), - IIO_ENUM("count_mode", IIO_SEPARATE, &quad8_count_mode_enum), - IIO_ENUM_AVAILABLE("count_mode", &quad8_count_mode_enum), - IIO_ENUM("quadrature_mode", IIO_SEPARATE, &quad8_quadrature_mode_enum), - IIO_ENUM_AVAILABLE("quadrature_mode", &quad8_quadrature_mode_enum), - {} -}; - -static const struct iio_chan_spec_ext_info quad8_index_ext_info[] = { - IIO_ENUM("synchronous_mode", IIO_SEPARATE, - &quad8_synchronous_mode_enum), - IIO_ENUM_AVAILABLE("synchronous_mode", &quad8_synchronous_mode_enum), - IIO_ENUM("index_polarity", IIO_SEPARATE, &quad8_index_polarity_enum), - IIO_ENUM_AVAILABLE("index_polarity", &quad8_index_polarity_enum), - {} -}; - -#define QUAD8_COUNT_CHAN(_chan) { \ - .type = IIO_COUNT, \ - .channel = (_chan), \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ - BIT(IIO_CHAN_INFO_ENABLE) | BIT(IIO_CHAN_INFO_SCALE), \ - .ext_info = quad8_count_ext_info, \ - .indexed = 1 \ -} - -#define QUAD8_INDEX_CHAN(_chan) { \ - .type = IIO_INDEX, \ - .channel = (_chan), \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .ext_info = quad8_index_ext_info, \ - .indexed = 1 \ -} - -static const struct iio_chan_spec quad8_channels[] = { - QUAD8_COUNT_CHAN(0), QUAD8_INDEX_CHAN(0), - QUAD8_COUNT_CHAN(1), QUAD8_INDEX_CHAN(1), - QUAD8_COUNT_CHAN(2), QUAD8_INDEX_CHAN(2), - QUAD8_COUNT_CHAN(3), QUAD8_INDEX_CHAN(3), - QUAD8_COUNT_CHAN(4), QUAD8_INDEX_CHAN(4), - QUAD8_COUNT_CHAN(5), QUAD8_INDEX_CHAN(5), - QUAD8_COUNT_CHAN(6), QUAD8_INDEX_CHAN(6), - QUAD8_COUNT_CHAN(7), QUAD8_INDEX_CHAN(7) -}; - static int quad8_signal_read(struct counter_device *counter, struct counter_signal *signal, enum counter_signal_value *val) { - const struct quad8_iio *const priv = counter->priv; + const struct quad8 *const priv = counter->priv; unsigned int state; /* Only Index signal levels can be read */ @@ -641,7 +117,7 @@ static int quad8_signal_read(struct counter_device *counter, static int quad8_count_read(struct counter_device *counter, struct counter_count *count, unsigned long *val) { - struct quad8_iio *const priv = counter->priv; + struct quad8 *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id; unsigned int flags; unsigned int borrow; @@ -672,7 +148,7 @@ static int quad8_count_read(struct counter_device *counter, static int quad8_count_write(struct counter_device *counter, struct counter_count *count, unsigned long val) { - struct quad8_iio *const priv = counter->priv; + struct quad8 *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id; int i; @@ -727,7 +203,7 @@ static enum counter_count_function quad8_count_functions_list[] = { static int quad8_function_get(struct counter_device *counter, struct counter_count *count, size_t *function) { - struct quad8_iio *const priv = counter->priv; + struct quad8 *const priv = counter->priv; const int id = count->id; mutex_lock(&priv->lock); @@ -755,7 +231,7 @@ static int quad8_function_get(struct counter_device *counter, static int quad8_function_set(struct counter_device *counter, struct counter_count *count, size_t function) { - struct quad8_iio *const priv = counter->priv; + struct quad8 *const priv = counter->priv; const int id = count->id; unsigned int *const quadrature_mode = priv->quadrature_mode + id; unsigned int *const scale = priv->quadrature_scale + id; @@ -811,7 +287,7 @@ static int quad8_function_set(struct counter_device *counter, static void quad8_direction_get(struct counter_device *counter, struct counter_count *count, enum counter_count_direction *direction) { - const struct quad8_iio *const priv = counter->priv; + const struct quad8 *const priv = counter->priv; unsigned int ud_flag; const unsigned int flag_addr = priv->base + 2 * count->id + 1; @@ -845,7 +321,7 @@ static int quad8_action_get(struct counter_device *counter, struct counter_count *count, struct counter_synapse *synapse, size_t *action) { - struct quad8_iio *const priv = counter->priv; + struct quad8 *const priv = counter->priv; int err; size_t function = 0; const size_t signal_a_id = count->synapses[0].signal->id; @@ -905,10 +381,15 @@ static const struct counter_ops quad8_ops = { .action_get = quad8_action_get }; +static const char *const quad8_index_polarity_modes[] = { + "negative", + "positive" +}; + static int quad8_index_polarity_get(struct counter_device *counter, struct counter_signal *signal, size_t *index_polarity) { - const struct quad8_iio *const priv = counter->priv; + const struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id - 16; *index_polarity = priv->index_polarity[channel_id]; @@ -919,7 +400,7 @@ static int quad8_index_polarity_get(struct counter_device *counter, static int quad8_index_polarity_set(struct counter_device *counter, struct counter_signal *signal, size_t index_polarity) { - struct quad8_iio *const priv = counter->priv; + struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id - 16; const int base_offset = priv->base + 2 * channel_id + 1; unsigned int idr_cfg = index_polarity << 1; @@ -945,10 +426,15 @@ static struct counter_signal_enum_ext quad8_index_pol_enum = { .set = quad8_index_polarity_set }; +static const char *const quad8_synchronous_modes[] = { + "non-synchronous", + "synchronous" +}; + static int quad8_synchronous_mode_get(struct counter_device *counter, struct counter_signal *signal, size_t *synchronous_mode) { - const struct quad8_iio *const priv = counter->priv; + const struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id - 16; *synchronous_mode = priv->synchronous_mode[channel_id]; @@ -959,7 +445,7 @@ static int quad8_synchronous_mode_get(struct counter_device *counter, static int quad8_synchronous_mode_set(struct counter_device *counter, struct counter_signal *signal, size_t synchronous_mode) { - struct quad8_iio *const priv = counter->priv; + struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id - 16; const int base_offset = priv->base + 2 * channel_id + 1; unsigned int idr_cfg = synchronous_mode; @@ -1001,7 +487,7 @@ static ssize_t quad8_count_floor_read(struct counter_device *counter, static int quad8_count_mode_get(struct counter_device *counter, struct counter_count *count, size_t *cnt_mode) { - const struct quad8_iio *const priv = counter->priv; + const struct quad8 *const priv = counter->priv; /* Map 104-QUAD-8 count mode to Generic Counter count mode */ switch (priv->count_mode[count->id]) { @@ -1025,7 +511,7 @@ static int quad8_count_mode_get(struct counter_device *counter, static int quad8_count_mode_set(struct counter_device *counter, struct counter_count *count, size_t cnt_mode) { - struct quad8_iio *const priv = counter->priv; + struct quad8 *const priv = counter->priv; unsigned int mode_cfg; const int base_offset = priv->base + 2 * count->id + 1; @@ -1084,7 +570,7 @@ static ssize_t quad8_count_direction_read(struct counter_device *counter, static ssize_t quad8_count_enable_read(struct counter_device *counter, struct counter_count *count, void *private, char *buf) { - const struct quad8_iio *const priv = counter->priv; + const struct quad8 *const priv = counter->priv; return sprintf(buf, "%u\n", priv->ab_enable[count->id]); } @@ -1092,7 +578,7 @@ static ssize_t quad8_count_enable_read(struct counter_device *counter, static ssize_t quad8_count_enable_write(struct counter_device *counter, struct counter_count *count, void *private, const char *buf, size_t len) { - struct quad8_iio *const priv = counter->priv; + struct quad8 *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id; int err; bool ab_enable; @@ -1116,10 +602,15 @@ static ssize_t quad8_count_enable_write(struct counter_device *counter, return len; } +static const char *const quad8_noise_error_states[] = { + "No excessive noise is present at the count inputs", + "Excessive noise is present at the count inputs" +}; + static int quad8_error_noise_get(struct counter_device *counter, struct counter_count *count, size_t *noise_error) { - const struct quad8_iio *const priv = counter->priv; + const struct quad8 *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id + 1; *noise_error = !!(inb(base_offset) & QUAD8_FLAG_E); @@ -1136,18 +627,18 @@ static struct counter_count_enum_ext quad8_error_noise_enum = { static ssize_t quad8_count_preset_read(struct counter_device *counter, struct counter_count *count, void *private, char *buf) { - const struct quad8_iio *const priv = counter->priv; + const struct quad8 *const priv = counter->priv; return sprintf(buf, "%u\n", priv->preset[count->id]); } -static void quad8_preset_register_set(struct quad8_iio *quad8iio, int id, - unsigned int preset) +static void quad8_preset_register_set(struct quad8 *priv, int id, + unsigned int preset) { - const unsigned int base_offset = quad8iio->base + 2 * id; + const unsigned int base_offset = priv->base + 2 * id; int i; - quad8iio->preset[id] = preset; + priv->preset[id] = preset; /* Reset Byte Pointer */ outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1); @@ -1160,7 +651,7 @@ static void quad8_preset_register_set(struct quad8_iio *quad8iio, int id, static ssize_t quad8_count_preset_write(struct counter_device *counter, struct counter_count *count, void *private, const char *buf, size_t len) { - struct quad8_iio *const priv = counter->priv; + struct quad8 *const priv = counter->priv; unsigned int preset; int ret; @@ -1184,7 +675,7 @@ static ssize_t quad8_count_preset_write(struct counter_device *counter, static ssize_t quad8_count_ceiling_read(struct counter_device *counter, struct counter_count *count, void *private, char *buf) { - struct quad8_iio *const priv = counter->priv; + struct quad8 *const priv = counter->priv; mutex_lock(&priv->lock); @@ -1205,7 +696,7 @@ static ssize_t quad8_count_ceiling_read(struct counter_device *counter, static ssize_t quad8_count_ceiling_write(struct counter_device *counter, struct counter_count *count, void *private, const char *buf, size_t len) { - struct quad8_iio *const priv = counter->priv; + struct quad8 *const priv = counter->priv; unsigned int ceiling; int ret; @@ -1235,7 +726,7 @@ static ssize_t quad8_count_ceiling_write(struct counter_device *counter, static ssize_t quad8_count_preset_enable_read(struct counter_device *counter, struct counter_count *count, void *private, char *buf) { - const struct quad8_iio *const priv = counter->priv; + const struct quad8 *const priv = counter->priv; return sprintf(buf, "%u\n", !priv->preset_enable[count->id]); } @@ -1243,7 +734,7 @@ static ssize_t quad8_count_preset_enable_read(struct counter_device *counter, static ssize_t quad8_count_preset_enable_write(struct counter_device *counter, struct counter_count *count, void *private, const char *buf, size_t len) { - struct quad8_iio *const priv = counter->priv; + struct quad8 *const priv = counter->priv; const int base_offset = priv->base + 2 * count->id + 1; bool preset_enable; int ret; @@ -1274,7 +765,7 @@ static ssize_t quad8_signal_cable_fault_read(struct counter_device *counter, struct counter_signal *signal, void *private, char *buf) { - struct quad8_iio *const priv = counter->priv; + struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id / 2; bool disabled; unsigned int status; @@ -1304,7 +795,7 @@ static ssize_t quad8_signal_cable_fault_enable_read( struct counter_device *counter, struct counter_signal *signal, void *private, char *buf) { - const struct quad8_iio *const priv = counter->priv; + const struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id / 2; const unsigned int enb = !!(priv->cable_fault_enable & BIT(channel_id)); @@ -1315,7 +806,7 @@ static ssize_t quad8_signal_cable_fault_enable_write( struct counter_device *counter, struct counter_signal *signal, void *private, const char *buf, size_t len) { - struct quad8_iio *const priv = counter->priv; + struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id / 2; bool enable; int ret; @@ -1345,7 +836,7 @@ static ssize_t quad8_signal_cable_fault_enable_write( static ssize_t quad8_signal_fck_prescaler_read(struct counter_device *counter, struct counter_signal *signal, void *private, char *buf) { - const struct quad8_iio *const priv = counter->priv; + const struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id / 2; return sprintf(buf, "%u\n", priv->fck_prescaler[channel_id]); @@ -1355,7 +846,7 @@ static ssize_t quad8_signal_fck_prescaler_write(struct counter_device *counter, struct counter_signal *signal, void *private, const char *buf, size_t len) { - struct quad8_iio *const priv = counter->priv; + struct quad8 *const priv = counter->priv; const size_t channel_id = signal->id / 2; const int base_offset = priv->base + 2 * channel_id; u8 prescaler; @@ -1531,11 +1022,9 @@ static struct counter_count quad8_counts[] = { static int quad8_probe(struct device *dev, unsigned int id) { - struct iio_dev *indio_dev; - struct quad8_iio *quad8iio; + struct quad8 *priv; int i, j; unsigned int base_offset; - int err; if (!devm_request_region(dev, base[id], QUAD8_EXTENT, dev_name(dev))) { dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n", @@ -1543,32 +1032,23 @@ static int quad8_probe(struct device *dev, unsigned int id) return -EBUSY; } - /* Allocate IIO device; this also allocates driver data structure */ - indio_dev = devm_iio_device_alloc(dev, sizeof(*quad8iio)); - if (!indio_dev) + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) return -ENOMEM; - /* Initialize IIO device */ - indio_dev->info = &quad8_info; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->num_channels = ARRAY_SIZE(quad8_channels); - indio_dev->channels = quad8_channels; - indio_dev->name = dev_name(dev); - /* Initialize Counter device and driver data */ - quad8iio = iio_priv(indio_dev); - quad8iio->counter.name = dev_name(dev); - quad8iio->counter.parent = dev; - quad8iio->counter.ops = &quad8_ops; - quad8iio->counter.counts = quad8_counts; - quad8iio->counter.num_counts = ARRAY_SIZE(quad8_counts); - quad8iio->counter.signals = quad8_signals; - quad8iio->counter.num_signals = ARRAY_SIZE(quad8_signals); - quad8iio->counter.priv = quad8iio; - quad8iio->base = base[id]; + priv->counter.name = dev_name(dev); + priv->counter.parent = dev; + priv->counter.ops = &quad8_ops; + priv->counter.counts = quad8_counts; + priv->counter.num_counts = ARRAY_SIZE(quad8_counts); + priv->counter.signals = quad8_signals; + priv->counter.num_signals = ARRAY_SIZE(quad8_signals); + priv->counter.priv = priv; + priv->base = base[id]; /* Initialize mutex */ - mutex_init(&quad8iio->lock); + mutex_init(&priv->lock); /* Reset all counters and disable interrupt function */ outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP); @@ -1602,13 +1082,8 @@ static int quad8_probe(struct device *dev, unsigned int id) /* Enable all counters */ outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP); - /* Register IIO device */ - err = devm_iio_device_register(dev, indio_dev); - if (err) - return err; - /* Register Counter device */ - return devm_counter_register(dev, &quad8iio->counter); + return devm_counter_register(dev, &priv->counter); } static struct isa_driver quad8_driver = { @@ -1621,5 +1096,5 @@ static struct isa_driver quad8_driver = { module_isa_driver(quad8_driver, num_quad8); MODULE_AUTHOR("William Breathitt Gray "); -MODULE_DESCRIPTION("ACCES 104-QUAD-8 IIO driver"); +MODULE_DESCRIPTION("ACCES 104-QUAD-8 driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index cbdf84200e27..1391e8ea64fe 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -14,7 +14,7 @@ if COUNTER config 104_QUAD_8 tristate "ACCES 104-QUAD-8 driver" - depends on PC104 && X86 && IIO + depends on PC104 && X86 select ISA_BUS_API help Say yes here to build support for the ACCES 104-QUAD-8 quadrature From 6c3b615379d7cd90d2f70b3cf9860c5a4910546a Mon Sep 17 00:00:00 2001 From: Ye Xiang Date: Sat, 30 Jan 2021 18:25:46 +0800 Subject: [PATCH 005/119] iio: hid-sensor-rotation: Fix quaternion data not correct Because the data of HID_USAGE_SENSOR_ORIENT_QUATERNION defined by ISH FW is s16, but quaternion data type is in_rot_quaternion_type(le:s16/32X4>>0), need to transform data type from s16 to s32 May require manual backporting. Fixes: fc18dddc0625 ("iio: hid-sensors: Added device rotation support") Signed-off-by: Ye Xiang Link: https://lore.kernel.org/r/20210130102546.31397-1-xiang.ye@intel.com Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/orientation/hid-sensor-rotation.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index 18e4ef060096..c087d8f72a54 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -21,7 +21,7 @@ struct dev_rot_state { struct hid_sensor_common common_attributes; struct hid_sensor_hub_attribute_info quaternion; struct { - u32 sampled_vals[4] __aligned(16); + s32 sampled_vals[4] __aligned(16); u64 timestamp __aligned(8); } scan; int scale_pre_decml; @@ -170,8 +170,15 @@ static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev, struct dev_rot_state *rot_state = iio_priv(indio_dev); if (usage_id == HID_USAGE_SENSOR_ORIENT_QUATERNION) { - memcpy(&rot_state->scan.sampled_vals, raw_data, - sizeof(rot_state->scan.sampled_vals)); + if (raw_len / 4 == sizeof(s16)) { + rot_state->scan.sampled_vals[0] = ((s16 *)raw_data)[0]; + rot_state->scan.sampled_vals[1] = ((s16 *)raw_data)[1]; + rot_state->scan.sampled_vals[2] = ((s16 *)raw_data)[2]; + rot_state->scan.sampled_vals[3] = ((s16 *)raw_data)[3]; + } else { + memcpy(&rot_state->scan.sampled_vals, raw_data, + sizeof(rot_state->scan.sampled_vals)); + } dev_dbg(&indio_dev->dev, "Recd Quat len:%zu::%zu\n", raw_len, sizeof(rot_state->scan.sampled_vals)); From 8a09054f3e8afd0521309b15baab716fa7454785 Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Mon, 25 Jan 2021 20:48:23 +0100 Subject: [PATCH 006/119] iio: adc: stm32-adc: enable timestamping for non-DMA usage For non-DMA usage, we have an easy way to associate a timestamp with a sample: iio_pollfunc_store_time stores a timestamp in the primary trigger IRQ handler and stm32_adc_trigger_handler runs in the IRQ thread to push out the buffer along with the timestamp. For this to work, the driver needs to register an IIO_TIMESTAMP channel. Do this. For DMA, it's not as easy, because we don't push the buffers out of stm32_adc_trigger, but out of stm32_adc_dma_buffer_done, which runs in a tasklet scheduled after a DMA completion. Preferably, the DMA controller would copy us the timestamp into that buffer as well. Until this is implemented, restrict timestamping support to only PIO. For low-frequency sampling, PIO is probably good enough. Cc: Holger Assmann Acked-by: Fabrice Gasnier Signed-off-by: Ahmad Fatoum Link: https://lore.kernel.org/r/20210125194824.30549-1-a.fatoum@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc.c | 39 +++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index f7c53cea509a..b25386b19373 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -177,7 +177,7 @@ struct stm32_adc_cfg { * @offset: ADC instance register offset in ADC block * @cfg: compatible configuration data * @completion: end of single conversion completion - * @buffer: data buffer + * @buffer: data buffer + 8 bytes for timestamp if enabled * @clk: clock for this adc instance * @irq: interrupt for this adc instance * @lock: spinlock @@ -200,7 +200,7 @@ struct stm32_adc { u32 offset; const struct stm32_adc_cfg *cfg; struct completion completion; - u16 buffer[STM32_ADC_MAX_SQ]; + u16 buffer[STM32_ADC_MAX_SQ + 4] __aligned(8); struct clk *clk; int irq; spinlock_t lock; /* interrupt lock */ @@ -1714,7 +1714,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev, } } -static int stm32_adc_chan_of_init(struct iio_dev *indio_dev) +static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) { struct device_node *node = indio_dev->dev.of_node; struct stm32_adc *adc = iio_priv(indio_dev); @@ -1762,6 +1762,9 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev) return -EINVAL; } + if (timestamping) + num_channels++; + channels = devm_kcalloc(&indio_dev->dev, num_channels, sizeof(struct iio_chan_spec), GFP_KERNEL); if (!channels) @@ -1812,6 +1815,19 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev) stm32_adc_smpr_init(adc, channels[i].channel, smp); } + if (timestamping) { + struct iio_chan_spec *timestamp = &channels[scan_index]; + + timestamp->type = IIO_TIMESTAMP; + timestamp->channel = -1; + timestamp->scan_index = scan_index; + timestamp->scan_type.sign = 's'; + timestamp->scan_type.realbits = 64; + timestamp->scan_type.storagebits = 64; + + scan_index++; + } + indio_dev->num_channels = scan_index; indio_dev->channels = channels; @@ -1871,6 +1887,7 @@ static int stm32_adc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; irqreturn_t (*handler)(int irq, void *p) = NULL; struct stm32_adc *adc; + bool timestamping = false; int ret; if (!pdev->dev.of_node) @@ -1927,16 +1944,22 @@ static int stm32_adc_probe(struct platform_device *pdev) if (ret < 0) return ret; - ret = stm32_adc_chan_of_init(indio_dev); - if (ret < 0) - return ret; - ret = stm32_adc_dma_request(dev, indio_dev); if (ret < 0) return ret; - if (!adc->dma_chan) + if (!adc->dma_chan) { + /* For PIO mode only, iio_pollfunc_store_time stores a timestamp + * in the primary trigger IRQ handler and stm32_adc_trigger_handler + * runs in the IRQ thread to push out buffer along with timestamp. + */ handler = &stm32_adc_trigger_handler; + timestamping = true; + } + + ret = stm32_adc_chan_of_init(indio_dev, timestamping); + if (ret < 0) + goto err_dma_disable; ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, handler, From 53fa791ada020da84dcd1c1c595510a4ca588693 Mon Sep 17 00:00:00 2001 From: Peter Meerwald-Stadler Date: Mon, 25 Jan 2021 20:56:54 +0100 Subject: [PATCH 007/119] MAINTAINERS: iio: move Peter Meerwald-Stadler to CREDITS Haven't had much time lately and moved on to different things. Thanks Jonathan for the gentle introduction to Linux land. Signed-off-by: Peter Meerwald-Stadler Reviewed-by: Matt Ranostay Link: https://lore.kernel.org/r/20210125195654.580465-1-pmeerw@pmeerw.net Signed-off-by: Jonathan Cameron --- CREDITS | 8 ++++++++ MAINTAINERS | 1 - 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CREDITS b/CREDITS index cef83b958cbe..91ea9b780bd9 100644 --- a/CREDITS +++ b/CREDITS @@ -2536,6 +2536,14 @@ D: Linux/PARISC hacker D: AD1889 sound driver S: Ottawa, Canada +N: Peter Meerwald-Stadler +E: pmeerw@pmeerw.net +W: https://pmeerw.net +D: IIO reviewing, drivers +S: Schießstandstr. 3a +S: A-5061 Elsbethen +S: Austria + N: Dirk Melchers E: dirk@merlin.nbg.sub.org D: 8 bit XT hard disk driver for OMTI5520 diff --git a/MAINTAINERS b/MAINTAINERS index dda153b7c102..380dec802d93 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8694,7 +8694,6 @@ F: drivers/iio/multiplexer/iio-mux.c IIO SUBSYSTEM AND DRIVERS M: Jonathan Cameron R: Lars-Peter Clausen -R: Peter Meerwald-Stadler L: linux-iio@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git From 0071aa300271a4c6328e4d2c43f53c5a91ddd2ba Mon Sep 17 00:00:00 2001 From: zuoqilin Date: Thu, 28 Jan 2021 10:19:05 +0800 Subject: [PATCH 008/119] iio:adc:dac:ad5791 typo fix of regster change 'regster' to 'register' Signed-off-by: zuoqilin Link: https://lore.kernel.org/r/20210128021905.963-1-zuoqilin1@163.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ad5791.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c index e3ffa4b9f84c..615d72cd59bc 100644 --- a/drivers/iio/dac/ad5791.c +++ b/drivers/iio/dac/ad5791.c @@ -76,7 +76,7 @@ struct ad5791_chip_info { * @chip_info: chip model specific constants * @vref_mv: actual reference voltage used * @vref_neg_mv: voltage of the negative supply - * @ctrl: control regster cache + * @ctrl: control register cache * @pwr_down_mode: current power down mode * @pwr_down: true if device is powered down * @data: spi transfer buffers From 14aae60174db9415b2db8a9d57586a11d8280961 Mon Sep 17 00:00:00 2001 From: Mike Looijmans Date: Mon, 25 Jan 2021 16:07:31 +0100 Subject: [PATCH 009/119] dt-bindings: iio: accel: Add bmi088 accelerometer bindings This adds the device-tree bindings for the Bosch Sensortec BMI088 IMU, the accelerometer part. Signed-off-by: Mike Looijmans Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20210125150732.23873-1-mike.looijmans@topic.nl Signed-off-by: Jonathan Cameron --- .../bindings/iio/accel/bosch,bmi088.yaml | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/accel/bosch,bmi088.yaml diff --git a/Documentation/devicetree/bindings/iio/accel/bosch,bmi088.yaml b/Documentation/devicetree/bindings/iio/accel/bosch,bmi088.yaml new file mode 100644 index 000000000000..911a1ae9c83f --- /dev/null +++ b/Documentation/devicetree/bindings/iio/accel/bosch,bmi088.yaml @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/accel/bosch,bmi088.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Bosch BMI088 IMU accelerometer part + +maintainers: + - Mike Looijmans + +description: | + Acceleration part of the IMU sensor with an SPI interface + Specifications about the sensor can be found at: + https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmi088-ds001.pdf + +properties: + compatible: + enum: + - bosch,bmi088-accel + + reg: + maxItems: 1 + + spi-max-frequency: true + + vdd-supply: true + + vddio-supply: true + + interrupts: + minItems: 1 + maxItems: 2 + description: | + Type should be either IRQ_TYPE_LEVEL_HIGH or IRQ_TYPE_LEVEL_LOW. + Two configurable interrupt lines exist. + + interrupt-names: + description: Specify which interrupt line is in use. + items: + enum: + - INT1 + - INT2 + minItems: 1 + maxItems: 2 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + bmi088-accel@1 { + compatible = "bosch,bmi088-accel"; + reg = <1>; + spi-max-frequency = <10000000>; + interrupt-parent = <&gpio6>; + interrupts = <19 IRQ_TYPE_LEVEL_LOW>; + interrupt-names = "INT2"; + }; + }; +... From c19ae6be7555abbee985d73372d0e78878a337f8 Mon Sep 17 00:00:00 2001 From: Mike Looijmans Date: Mon, 25 Jan 2021 16:07:32 +0100 Subject: [PATCH 010/119] iio: accel: Add support for the Bosch-Sensortec BMI088 The BMI088 is a combined module with both accelerometer and gyroscope. This adds the accelerometer driver support for the SPI interface. The gyroscope part is already supported by the BMG160 driver. Signed-off-by: Mike Looijmans Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20210125150732.23873-2-mike.looijmans@topic.nl Signed-off-by: Jonathan Cameron --- drivers/iio/accel/Kconfig | 18 + drivers/iio/accel/Makefile | 2 + drivers/iio/accel/bmi088-accel-core.c | 567 ++++++++++++++++++++++++++ drivers/iio/accel/bmi088-accel-spi.c | 83 ++++ drivers/iio/accel/bmi088-accel.h | 18 + 5 files changed, 688 insertions(+) create mode 100644 drivers/iio/accel/bmi088-accel-core.c create mode 100644 drivers/iio/accel/bmi088-accel-spi.c create mode 100644 drivers/iio/accel/bmi088-accel.h diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 2e0c62c39155..cceda3cecbcf 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -157,6 +157,24 @@ config BMC150_ACCEL_SPI tristate select REGMAP_SPI +config BMI088_ACCEL + tristate "Bosch BMI088 Accelerometer Driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select REGMAP + select BMI088_ACCEL_SPI + help + Say yes here to build support for the Bosch BMI088 accelerometer. + + This is a combo module with both accelerometer and gyroscope. This + driver only implements the accelerometer part, which has its own + address and register map. BMG160 provides the gyroscope driver. + +config BMI088_ACCEL_SPI + tristate + select REGMAP_SPI + config DA280 tristate "MiraMEMS DA280 3-axis 14-bit digital accelerometer driver" depends on I2C diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 4f6c1ebe13b0..32cd1342a31a 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -20,6 +20,8 @@ obj-$(CONFIG_BMA400_SPI) += bma400_spi.o obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o +obj-$(CONFIG_BMI088_ACCEL) += bmi088-accel-core.o +obj-$(CONFIG_BMI088_ACCEL_SPI) += bmi088-accel-spi.o obj-$(CONFIG_DA280) += da280.o obj-$(CONFIG_DA311) += da311.o obj-$(CONFIG_DMARD06) += dmard06.o diff --git a/drivers/iio/accel/bmi088-accel-core.c b/drivers/iio/accel/bmi088-accel-core.c new file mode 100644 index 000000000000..12d00658e46f --- /dev/null +++ b/drivers/iio/accel/bmi088-accel-core.c @@ -0,0 +1,567 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 3-axis accelerometer driver supporting following Bosch-Sensortec chips: + * - BMI088 + * + * Copyright (c) 2018-2021, Topic Embedded Products + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bmi088-accel.h" + +#define BMI088_ACCEL_REG_CHIP_ID 0x00 +#define BMI088_ACCEL_REG_ERROR 0x02 + +#define BMI088_ACCEL_REG_INT_STATUS 0x1D +#define BMI088_ACCEL_INT_STATUS_BIT_DRDY BIT(7) + +#define BMI088_ACCEL_REG_RESET 0x7E +#define BMI088_ACCEL_RESET_VAL 0xB6 + +#define BMI088_ACCEL_REG_PWR_CTRL 0x7D +#define BMI088_ACCEL_REG_PWR_CONF 0x7C + +#define BMI088_ACCEL_REG_INT_MAP_DATA 0x58 +#define BMI088_ACCEL_INT_MAP_DATA_BIT_INT1_DRDY BIT(2) +#define BMI088_ACCEL_INT_MAP_DATA_BIT_INT2_FWM BIT(5) + +#define BMI088_ACCEL_REG_INT1_IO_CONF 0x53 +#define BMI088_ACCEL_INT1_IO_CONF_BIT_ENABLE_OUT BIT(3) +#define BMI088_ACCEL_INT1_IO_CONF_BIT_LVL BIT(1) + +#define BMI088_ACCEL_REG_INT2_IO_CONF 0x54 +#define BMI088_ACCEL_INT2_IO_CONF_BIT_ENABLE_OUT BIT(3) +#define BMI088_ACCEL_INT2_IO_CONF_BIT_LVL BIT(1) + +#define BMI088_ACCEL_REG_ACC_CONF 0x40 +#define BMI088_ACCEL_MODE_ODR_MASK 0x0f + +#define BMI088_ACCEL_REG_ACC_RANGE 0x41 +#define BMI088_ACCEL_RANGE_3G 0x00 +#define BMI088_ACCEL_RANGE_6G 0x01 +#define BMI088_ACCEL_RANGE_12G 0x02 +#define BMI088_ACCEL_RANGE_24G 0x03 + +#define BMI088_ACCEL_REG_TEMP 0x22 +#define BMI088_ACCEL_REG_TEMP_SHIFT 5 +#define BMI088_ACCEL_TEMP_UNIT 125 +#define BMI088_ACCEL_TEMP_OFFSET 23000 + +#define BMI088_ACCEL_REG_XOUT_L 0x12 +#define BMI088_ACCEL_AXIS_TO_REG(axis) \ + (BMI088_ACCEL_REG_XOUT_L + (axis * 2)) + +#define BMI088_ACCEL_MAX_STARTUP_TIME_US 1000 +#define BMI088_AUTO_SUSPEND_DELAY_MS 2000 + +#define BMI088_ACCEL_REG_FIFO_STATUS 0x0E +#define BMI088_ACCEL_REG_FIFO_CONFIG0 0x48 +#define BMI088_ACCEL_REG_FIFO_CONFIG1 0x49 +#define BMI088_ACCEL_REG_FIFO_DATA 0x3F +#define BMI088_ACCEL_FIFO_LENGTH 100 + +#define BMI088_ACCEL_FIFO_MODE_FIFO 0x40 +#define BMI088_ACCEL_FIFO_MODE_STREAM 0x80 + +enum bmi088_accel_axis { + AXIS_X, + AXIS_Y, + AXIS_Z, +}; + +static const int bmi088_sample_freqs[] = { + 12, 500000, + 25, 0, + 50, 0, + 100, 0, + 200, 0, + 400, 0, + 800, 0, + 1600, 0, +}; + +/* Available OSR (over sampling rate) sets the 3dB cut-off frequency */ +enum bmi088_osr_modes { + BMI088_ACCEL_MODE_OSR_NORMAL = 0xA, + BMI088_ACCEL_MODE_OSR_2 = 0x9, + BMI088_ACCEL_MODE_OSR_4 = 0x8, +}; + +/* Available ODR (output data rates) in Hz */ +enum bmi088_odr_modes { + BMI088_ACCEL_MODE_ODR_12_5 = 0x5, + BMI088_ACCEL_MODE_ODR_25 = 0x6, + BMI088_ACCEL_MODE_ODR_50 = 0x7, + BMI088_ACCEL_MODE_ODR_100 = 0x8, + BMI088_ACCEL_MODE_ODR_200 = 0x9, + BMI088_ACCEL_MODE_ODR_400 = 0xa, + BMI088_ACCEL_MODE_ODR_800 = 0xb, + BMI088_ACCEL_MODE_ODR_1600 = 0xc, +}; + +struct bmi088_scale_info { + int scale; + u8 reg_range; +}; + +struct bmi088_accel_chip_info { + const char *name; + u8 chip_id; + const struct iio_chan_spec *channels; + int num_channels; +}; + +struct bmi088_accel_data { + struct regmap *regmap; + const struct bmi088_accel_chip_info *chip_info; + u8 buffer[2] ____cacheline_aligned; /* shared DMA safe buffer */ +}; + +static const struct regmap_range bmi088_volatile_ranges[] = { + /* All registers below 0x40 are volatile, except the CHIP ID. */ + regmap_reg_range(BMI088_ACCEL_REG_ERROR, 0x3f), + /* Mark the RESET as volatile too, it is self-clearing */ + regmap_reg_range(BMI088_ACCEL_REG_RESET, BMI088_ACCEL_REG_RESET), +}; + +static const struct regmap_access_table bmi088_volatile_table = { + .yes_ranges = bmi088_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(bmi088_volatile_ranges), +}; + +const struct regmap_config bmi088_regmap_conf = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x7E, + .volatile_table = &bmi088_volatile_table, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_GPL(bmi088_regmap_conf); + +static int bmi088_accel_power_up(struct bmi088_accel_data *data) +{ + int ret; + + /* Enable accelerometer and temperature sensor */ + ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CTRL, 0x4); + if (ret) + return ret; + + /* Datasheet recommends to wait at least 5ms before communication */ + usleep_range(5000, 6000); + + /* Disable suspend mode */ + ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CONF, 0x0); + if (ret) + return ret; + + /* Recommended at least 1ms before further communication */ + usleep_range(1000, 1200); + + return 0; +} + +static int bmi088_accel_power_down(struct bmi088_accel_data *data) +{ + int ret; + + /* Enable suspend mode */ + ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CONF, 0x3); + if (ret) + return ret; + + /* Recommended at least 1ms before further communication */ + usleep_range(1000, 1200); + + /* Disable accelerometer and temperature sensor */ + ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CTRL, 0x0); + if (ret) + return ret; + + /* Datasheet recommends to wait at least 5ms before communication */ + usleep_range(5000, 6000); + + return 0; +} + +static int bmi088_accel_get_sample_freq(struct bmi088_accel_data *data, + int *val, int *val2) +{ + unsigned int value; + int ret; + + ret = regmap_read(data->regmap, BMI088_ACCEL_REG_ACC_CONF, + &value); + if (ret) + return ret; + + value &= BMI088_ACCEL_MODE_ODR_MASK; + value -= BMI088_ACCEL_MODE_ODR_12_5; + value <<= 1; + + if (value >= ARRAY_SIZE(bmi088_sample_freqs) - 1) + return -EINVAL; + + *val = bmi088_sample_freqs[value]; + *val2 = bmi088_sample_freqs[value + 1]; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int bmi088_accel_set_sample_freq(struct bmi088_accel_data *data, int val) +{ + unsigned int regval; + int index = 0; + + while (index < ARRAY_SIZE(bmi088_sample_freqs) && + bmi088_sample_freqs[index] != val) + index += 2; + + if (index >= ARRAY_SIZE(bmi088_sample_freqs)) + return -EINVAL; + + regval = (index >> 1) + BMI088_ACCEL_MODE_ODR_12_5; + + return regmap_update_bits(data->regmap, BMI088_ACCEL_REG_ACC_CONF, + BMI088_ACCEL_MODE_ODR_MASK, regval); +} + +static int bmi088_accel_get_temp(struct bmi088_accel_data *data, int *val) +{ + int ret; + s16 temp; + + ret = regmap_bulk_read(data->regmap, BMI088_ACCEL_REG_TEMP, + &data->buffer, sizeof(__be16)); + if (ret) + return ret; + + /* data->buffer is cacheline aligned */ + temp = be16_to_cpu(*(__be16 *)data->buffer); + + *val = temp >> BMI088_ACCEL_REG_TEMP_SHIFT; + + return IIO_VAL_INT; +} + +static int bmi088_accel_get_axis(struct bmi088_accel_data *data, + struct iio_chan_spec const *chan, + int *val) +{ + int ret; + s16 raw_val; + + ret = regmap_bulk_read(data->regmap, + BMI088_ACCEL_AXIS_TO_REG(chan->scan_index), + data->buffer, sizeof(__le16)); + if (ret) + return ret; + + raw_val = le16_to_cpu(*(__le16 *)data->buffer); + *val = raw_val; + + return IIO_VAL_INT; +} + +static int bmi088_accel_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct bmi088_accel_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_TEMP: + pm_runtime_get_sync(dev); + ret = bmi088_accel_get_temp(data, val); + goto out_read_raw_pm_put; + case IIO_ACCEL: + pm_runtime_get_sync(dev); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + goto out_read_raw_pm_put; + + ret = bmi088_accel_get_axis(data, chan, val); + iio_device_release_direct_mode(indio_dev); + if (!ret) + ret = IIO_VAL_INT; + + goto out_read_raw_pm_put; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_TEMP: + /* Offset applies before scale */ + *val = BMI088_ACCEL_TEMP_OFFSET/BMI088_ACCEL_TEMP_UNIT; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_TEMP: + /* 0.125 degrees per LSB */ + *val = BMI088_ACCEL_TEMP_UNIT; + return IIO_VAL_INT; + case IIO_ACCEL: + pm_runtime_get_sync(dev); + ret = regmap_read(data->regmap, + BMI088_ACCEL_REG_ACC_RANGE, val); + if (ret) + goto out_read_raw_pm_put; + + *val2 = 15 - (*val & 0x3); + *val = 3 * 980; + ret = IIO_VAL_FRACTIONAL_LOG2; + + goto out_read_raw_pm_put; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + pm_runtime_get_sync(dev); + ret = bmi088_accel_get_sample_freq(data, val, val2); + goto out_read_raw_pm_put; + default: + break; + } + + return -EINVAL; + +out_read_raw_pm_put: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static int bmi088_accel_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT_PLUS_MICRO; + *vals = bmi088_sample_freqs; + *length = ARRAY_SIZE(bmi088_sample_freqs); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int bmi088_accel_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct bmi088_accel_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + pm_runtime_get_sync(dev); + ret = bmi088_accel_set_sample_freq(data, val); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return ret; + default: + return -EINVAL; + } +} + +#define BMI088_ACCEL_CHANNEL(_axis) { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = AXIS_##_axis, \ +} + +static const struct iio_chan_spec bmi088_accel_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .scan_index = -1, + }, + BMI088_ACCEL_CHANNEL(X), + BMI088_ACCEL_CHANNEL(Y), + BMI088_ACCEL_CHANNEL(Z), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static const struct bmi088_accel_chip_info bmi088_accel_chip_info_tbl[] = { + [0] = { + .name = "bmi088a", + .chip_id = 0x1E, + .channels = bmi088_accel_channels, + .num_channels = ARRAY_SIZE(bmi088_accel_channels), + }, +}; + +static const struct iio_info bmi088_accel_info = { + .read_raw = bmi088_accel_read_raw, + .write_raw = bmi088_accel_write_raw, + .read_avail = bmi088_accel_read_avail, +}; + +static const unsigned long bmi088_accel_scan_masks[] = { + BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z), + 0 +}; + +static int bmi088_accel_chip_init(struct bmi088_accel_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + int ret, i; + unsigned int val; + + /* Do a dummy read to enable SPI interface, won't harm I2C */ + regmap_read(data->regmap, BMI088_ACCEL_REG_INT_STATUS, &val); + + /* + * Reset chip to get it in a known good state. A delay of 1ms after + * reset is required according to the data sheet + */ + ret = regmap_write(data->regmap, BMI088_ACCEL_REG_RESET, + BMI088_ACCEL_RESET_VAL); + if (ret) + return ret; + + usleep_range(1000, 2000); + + /* Do a dummy read again after a reset to enable the SPI interface */ + regmap_read(data->regmap, BMI088_ACCEL_REG_INT_STATUS, &val); + + /* Read chip ID */ + ret = regmap_read(data->regmap, BMI088_ACCEL_REG_CHIP_ID, &val); + if (ret) { + dev_err(dev, "Error: Reading chip id\n"); + return ret; + } + + /* Validate chip ID */ + for (i = 0; i < ARRAY_SIZE(bmi088_accel_chip_info_tbl); i++) { + if (bmi088_accel_chip_info_tbl[i].chip_id == val) { + data->chip_info = &bmi088_accel_chip_info_tbl[i]; + break; + } + } + if (i == ARRAY_SIZE(bmi088_accel_chip_info_tbl)) { + dev_err(dev, "Invalid chip %x\n", val); + return -ENODEV; + } + + return 0; +} + +int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap, + int irq, const char *name, bool block_supported) +{ + struct bmi088_accel_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + dev_set_drvdata(dev, indio_dev); + + data->regmap = regmap; + + ret = bmi088_accel_chip_init(data); + if (ret) + return ret; + + indio_dev->dev.parent = dev; + indio_dev->channels = data->chip_info->channels; + indio_dev->num_channels = data->chip_info->num_channels; + indio_dev->name = name ? name : data->chip_info->name; + indio_dev->available_scan_masks = bmi088_accel_scan_masks; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &bmi088_accel_info; + + /* Enable runtime PM */ + pm_runtime_get_noresume(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + /* We need ~6ms to startup, so set the delay to 6 seconds */ + pm_runtime_set_autosuspend_delay(dev, 6000); + pm_runtime_use_autosuspend(dev); + pm_runtime_put(dev); + + ret = iio_device_register(indio_dev); + if (ret) + dev_err(dev, "Unable to register iio device\n"); + + return ret; +} +EXPORT_SYMBOL_GPL(bmi088_accel_core_probe); + + +int bmi088_accel_core_remove(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmi088_accel_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + bmi088_accel_power_down(data); + + return 0; +} +EXPORT_SYMBOL_GPL(bmi088_accel_core_remove); + +static int __maybe_unused bmi088_accel_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmi088_accel_data *data = iio_priv(indio_dev); + + return bmi088_accel_power_down(data); +} + +static int __maybe_unused bmi088_accel_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmi088_accel_data *data = iio_priv(indio_dev); + + return bmi088_accel_power_up(data); +} + +const struct dev_pm_ops bmi088_accel_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(bmi088_accel_runtime_suspend, + bmi088_accel_runtime_resume, NULL) +}; +EXPORT_SYMBOL_GPL(bmi088_accel_pm_ops); + +MODULE_AUTHOR("Niek van Agt "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("BMI088 accelerometer driver (core)"); diff --git a/drivers/iio/accel/bmi088-accel-spi.c b/drivers/iio/accel/bmi088-accel-spi.c new file mode 100644 index 000000000000..dd1e3f6cf211 --- /dev/null +++ b/drivers/iio/accel/bmi088-accel-spi.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 3-axis accelerometer driver supporting following Bosch-Sensortec chips: + * - BMI088 + * + * Copyright (c) 2018-2020, Topic Embedded Products + */ + +#include +#include +#include +#include + +#include "bmi088-accel.h" + +static int bmi088_regmap_spi_write(void *context, const void *data, size_t count) +{ + struct spi_device *spi = context; + + /* Write register is same as generic SPI */ + return spi_write(spi, data, count); +} + +static int bmi088_regmap_spi_read(void *context, const void *reg, + size_t reg_size, void *val, size_t val_size) +{ + struct spi_device *spi = context; + u8 addr[2]; + + addr[0] = *(u8 *)reg; + addr[0] |= BIT(7); /* Set RW = '1' */ + addr[1] = 0; /* Read requires a dummy byte transfer */ + + return spi_write_then_read(spi, addr, sizeof(addr), val, val_size); +} + +static struct regmap_bus bmi088_regmap_bus = { + .write = bmi088_regmap_spi_write, + .read = bmi088_regmap_spi_read, +}; + +static int bmi088_accel_probe(struct spi_device *spi) +{ + struct regmap *regmap; + const struct spi_device_id *id = spi_get_device_id(spi); + + regmap = devm_regmap_init(&spi->dev, &bmi088_regmap_bus, + spi, &bmi088_regmap_conf); + + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "Failed to initialize spi regmap\n"); + return PTR_ERR(regmap); + } + + return bmi088_accel_core_probe(&spi->dev, regmap, spi->irq, id->name, + true); +} + +static int bmi088_accel_remove(struct spi_device *spi) +{ + return bmi088_accel_core_remove(&spi->dev); +} + +static const struct spi_device_id bmi088_accel_id[] = { + {"bmi088-accel", }, + {} +}; +MODULE_DEVICE_TABLE(spi, bmi088_accel_id); + +static struct spi_driver bmi088_accel_driver = { + .driver = { + .name = "bmi088_accel_spi", + .pm = &bmi088_accel_pm_ops, + }, + .probe = bmi088_accel_probe, + .remove = bmi088_accel_remove, + .id_table = bmi088_accel_id, +}; +module_spi_driver(bmi088_accel_driver); + +MODULE_AUTHOR("Niek van Agt "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("BMI088 accelerometer driver (SPI)"); diff --git a/drivers/iio/accel/bmi088-accel.h b/drivers/iio/accel/bmi088-accel.h new file mode 100644 index 000000000000..5c25f16b672c --- /dev/null +++ b/drivers/iio/accel/bmi088-accel.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef BMI088_ACCEL_H +#define BMI088_ACCEL_H + +#include +#include +#include + +struct device; + +extern const struct regmap_config bmi088_regmap_conf; +extern const struct dev_pm_ops bmi088_accel_pm_ops; + +int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap, int irq, + const char *name, bool block_supported); +int bmi088_accel_core_remove(struct device *dev); + +#endif /* BMI088_ACCEL_H */ From d935eddd2799f2559d3c909e5977c0a85a5af1b7 Mon Sep 17 00:00:00 2001 From: Tomislav Denis Date: Tue, 2 Feb 2021 09:41:06 +0100 Subject: [PATCH 011/119] iio: adc: Add driver for Texas Instruments ADS131E0x ADC family The ADS131E0x are a family of multichannel, simultaneous sampling, 24-bit, delta-sigma, analog-to-digital converters (ADCs) with a built-in programmable gain amplifier (PGA), internal reference and an onboard oscillator. Datasheet: https://www.ti.com/lit/ds/symlink/ads131e08.pdf Signed-off-by: Tomislav Denis Link: https://lore.kernel.org/r/20210202084107.3260-2-tomislav.denis@avl.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 6 + drivers/iio/adc/Kconfig | 12 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ti-ads131e08.c | 948 +++++++++++++++++++++++++++++++++ 4 files changed, 967 insertions(+) create mode 100644 drivers/iio/adc/ti-ads131e08.c diff --git a/MAINTAINERS b/MAINTAINERS index 380dec802d93..180d13dab902 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17826,6 +17826,12 @@ M: Robert Richter S: Odd Fixes F: drivers/gpio/gpio-thunderx.c +TI ADS131E0X ADC SERIES DRIVER +M: Tomislav Denis +L: linux-iio@vger.kernel.org +S: Maintained +F: drivers/iio/adc/ti-ads131e08.c + TI AM437X VPFE DRIVER M: "Lad, Prabhakar" L: linux-media@vger.kernel.org diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 6605c263949c..769381b05b9a 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1152,6 +1152,18 @@ config TI_ADS124S08 This driver can also be built as a module. If so, the module will be called ti-ads124s08. +config TI_ADS131E08 + tristate "Texas Instruments ADS131E08" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to get support for Texas Instruments ADS131E04, ADS131E06 + and ADS131E08 chips. + + This driver can also be built as a module. If so, the module will be + called ti-ads131e08. + config TI_AM335X_ADC tristate "TI's AM335X ADC driver" depends on MFD_TI_AM335X_TSCADC && HAS_DMA diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 5fca90ada0ec..a226657d19c0 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -103,6 +103,7 @@ obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o +obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c new file mode 100644 index 000000000000..0060d5f0abb0 --- /dev/null +++ b/drivers/iio/adc/ti-ads131e08.c @@ -0,0 +1,948 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments ADS131E0x 4-, 6- and 8-Channel ADCs + * + * Copyright (c) 2020 AVL DiTEST GmbH + * Tomislav Denis + * + * Datasheet: https://www.ti.com/lit/ds/symlink/ads131e08.pdf + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* Commands */ +#define ADS131E08_CMD_RESET 0x06 +#define ADS131E08_CMD_START 0x08 +#define ADS131E08_CMD_STOP 0x0A +#define ADS131E08_CMD_OFFSETCAL 0x1A +#define ADS131E08_CMD_SDATAC 0x11 +#define ADS131E08_CMD_RDATA 0x12 +#define ADS131E08_CMD_RREG(r) (BIT(5) | (r & GENMASK(4, 0))) +#define ADS131E08_CMD_WREG(r) (BIT(6) | (r & GENMASK(4, 0))) + +/* Registers */ +#define ADS131E08_ADR_CFG1R 0x01 +#define ADS131E08_ADR_CFG3R 0x03 +#define ADS131E08_ADR_CH0R 0x05 + +/* Configuration register 1 */ +#define ADS131E08_CFG1R_DR_MASK GENMASK(2, 0) + +/* Configuration register 3 */ +#define ADS131E08_CFG3R_PDB_REFBUF_MASK BIT(7) +#define ADS131E08_CFG3R_VREF_4V_MASK BIT(5) + +/* Channel settings register */ +#define ADS131E08_CHR_GAIN_MASK GENMASK(6, 4) +#define ADS131E08_CHR_MUX_MASK GENMASK(2, 0) +#define ADS131E08_CHR_PWD_MASK BIT(7) + +/* ADC misc */ +#define ADS131E08_DEFAULT_DATA_RATE 1 +#define ADS131E08_DEFAULT_PGA_GAIN 1 +#define ADS131E08_DEFAULT_MUX 0 + +#define ADS131E08_VREF_2V4_mV 2400 +#define ADS131E08_VREF_4V_mV 4000 + +#define ADS131E08_WAIT_RESET_CYCLES 18 +#define ADS131E08_WAIT_SDECODE_CYCLES 4 +#define ADS131E08_WAIT_OFFSETCAL_MS 153 +#define ADS131E08_MAX_SETTLING_TIME_MS 6 + +#define ADS131E08_NUM_STATUS_BYTES 3 +#define ADS131E08_NUM_DATA_BYTES_MAX 24 +#define ADS131E08_NUM_DATA_BYTES(dr) (((dr) >= 32) ? 2 : 3) +#define ADS131E08_NUM_DATA_BITS(dr) (ADS131E08_NUM_DATA_BYTES(dr) * 8) +#define ADS131E08_NUM_STORAGE_BYTES 4 + +enum ads131e08_ids { + ads131e04, + ads131e06, + ads131e08, +}; + +struct ads131e08_info { + unsigned int max_channels; + const char *name; +}; + +struct ads131e08_channel_config { + unsigned int pga_gain; + unsigned int mux; +}; + +struct ads131e08_state { + const struct ads131e08_info *info; + struct spi_device *spi; + struct iio_trigger *trig; + struct clk *adc_clk; + struct regulator *vref_reg; + struct ads131e08_channel_config *channel_config; + unsigned int data_rate; + unsigned int vref_mv; + unsigned int sdecode_delay_us; + unsigned int reset_delay_us; + unsigned int readback_len; + struct completion completion; + struct { + u8 data[ADS131E08_NUM_DATA_BYTES_MAX]; + s64 ts __aligned(8); + } tmp_buf; + + u8 tx_buf[3] ____cacheline_aligned; + /* + * Add extra one padding byte to be able to access the last channel + * value using u32 pointer + */ + u8 rx_buf[ADS131E08_NUM_STATUS_BYTES + + ADS131E08_NUM_DATA_BYTES_MAX + 1]; +}; + +static const struct ads131e08_info ads131e08_info_tbl[] = { + [ads131e04] = { + .max_channels = 4, + .name = "ads131e04", + }, + [ads131e06] = { + .max_channels = 6, + .name = "ads131e06", + }, + [ads131e08] = { + .max_channels = 8, + .name = "ads131e08", + }, +}; + +struct ads131e08_data_rate_desc { + unsigned int rate; /* data rate in kSPS */ + u8 reg; /* reg value */ +}; + +static const struct ads131e08_data_rate_desc ads131e08_data_rate_tbl[] = { + { .rate = 64, .reg = 0x00 }, + { .rate = 32, .reg = 0x01 }, + { .rate = 16, .reg = 0x02 }, + { .rate = 8, .reg = 0x03 }, + { .rate = 4, .reg = 0x04 }, + { .rate = 2, .reg = 0x05 }, + { .rate = 1, .reg = 0x06 }, +}; + +struct ads131e08_pga_gain_desc { + unsigned int gain; /* PGA gain value */ + u8 reg; /* field value */ +}; + +static const struct ads131e08_pga_gain_desc ads131e08_pga_gain_tbl[] = { + { .gain = 1, .reg = 0x01 }, + { .gain = 2, .reg = 0x02 }, + { .gain = 4, .reg = 0x04 }, + { .gain = 8, .reg = 0x05 }, + { .gain = 12, .reg = 0x06 }, +}; + +static const u8 ads131e08_valid_channel_mux_values[] = { 0, 1, 3, 4 }; + +static int ads131e08_exec_cmd(struct ads131e08_state *st, u8 cmd) +{ + int ret; + + ret = spi_write_then_read(st->spi, &cmd, 1, NULL, 0); + if (ret) + dev_err(&st->spi->dev, "Exec cmd(%02x) failed\n", cmd); + + return ret; +} + +static int ads131e08_read_reg(struct ads131e08_state *st, u8 reg) +{ + int ret; + struct spi_transfer transfer[] = { + { + .tx_buf = &st->tx_buf, + .len = 2, + .delay_usecs = st->sdecode_delay_us, + }, { + .rx_buf = &st->rx_buf, + .len = 1, + }, + }; + + st->tx_buf[0] = ADS131E08_CMD_RREG(reg); + st->tx_buf[1] = 0; + + ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer)); + if (ret) { + dev_err(&st->spi->dev, "Read register failed\n"); + return ret; + } + + return st->rx_buf[0]; +} + +static int ads131e08_write_reg(struct ads131e08_state *st, u8 reg, u8 value) +{ + int ret; + struct spi_transfer transfer[] = { + { + .tx_buf = &st->tx_buf, + .len = 3, + .delay_usecs = st->sdecode_delay_us, + } + }; + + st->tx_buf[0] = ADS131E08_CMD_WREG(reg); + st->tx_buf[1] = 0; + st->tx_buf[2] = value; + + ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer)); + if (ret) + dev_err(&st->spi->dev, "Write register failed\n"); + + return ret; +} + +static int ads131e08_read_data(struct ads131e08_state *st, int rx_len) +{ + int ret; + struct spi_transfer transfer[] = { + { + .tx_buf = &st->tx_buf, + .len = 1, + }, { + .rx_buf = &st->rx_buf, + .len = rx_len, + }, + }; + + st->tx_buf[0] = ADS131E08_CMD_RDATA; + + ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer)); + if (ret) + dev_err(&st->spi->dev, "Read data failed\n"); + + return ret; +} + +static int ads131e08_set_data_rate(struct ads131e08_state *st, int data_rate) +{ + int i, reg, ret; + + for (i = 0; i < ARRAY_SIZE(ads131e08_data_rate_tbl); i++) { + if (ads131e08_data_rate_tbl[i].rate == data_rate) + break; + } + + if (i == ARRAY_SIZE(ads131e08_data_rate_tbl)) { + dev_err(&st->spi->dev, "invalid data rate value\n"); + return -EINVAL; + } + + reg = ads131e08_read_reg(st, ADS131E08_ADR_CFG1R); + if (reg < 0) + return reg; + + reg &= ~ADS131E08_CFG1R_DR_MASK; + reg |= FIELD_PREP(ADS131E08_CFG1R_DR_MASK, + ads131e08_data_rate_tbl[i].reg); + + ret = ads131e08_write_reg(st, ADS131E08_ADR_CFG1R, reg); + if (ret) + return ret; + + st->data_rate = data_rate; + st->readback_len = ADS131E08_NUM_STATUS_BYTES + + ADS131E08_NUM_DATA_BYTES(st->data_rate) * + st->info->max_channels; + + return 0; +} + +static int ads131e08_pga_gain_to_field_value(struct ads131e08_state *st, + unsigned int pga_gain) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ads131e08_pga_gain_tbl); i++) { + if (ads131e08_pga_gain_tbl[i].gain == pga_gain) + break; + } + + if (i == ARRAY_SIZE(ads131e08_pga_gain_tbl)) { + dev_err(&st->spi->dev, "invalid PGA gain value\n"); + return -EINVAL; + } + + return ads131e08_pga_gain_tbl[i].reg; +} + +static int ads131e08_set_pga_gain(struct ads131e08_state *st, + unsigned int channel, unsigned int pga_gain) +{ + int field_value, reg; + + field_value = ads131e08_pga_gain_to_field_value(st, pga_gain); + if (field_value < 0) + return field_value; + + reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel); + if (reg < 0) + return reg; + + reg &= ~ADS131E08_CHR_GAIN_MASK; + reg |= FIELD_PREP(ADS131E08_CHR_GAIN_MASK, field_value); + + return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg); +} + +static int ads131e08_validate_channel_mux(struct ads131e08_state *st, + unsigned int mux) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ads131e08_valid_channel_mux_values); i++) { + if (ads131e08_valid_channel_mux_values[i] == mux) + break; + } + + if (i == ARRAY_SIZE(ads131e08_valid_channel_mux_values)) { + dev_err(&st->spi->dev, "invalid channel mux value\n"); + return -EINVAL; + } + + return 0; +} + +static int ads131e08_set_channel_mux(struct ads131e08_state *st, + unsigned int channel, unsigned int mux) +{ + int reg; + + reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel); + if (reg < 0) + return reg; + + reg &= ~ADS131E08_CHR_MUX_MASK; + reg |= FIELD_PREP(ADS131E08_CHR_MUX_MASK, mux); + + return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg); +} + +static int ads131e08_power_down_channel(struct ads131e08_state *st, + unsigned int channel, bool value) +{ + int reg; + + reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel); + if (reg < 0) + return reg; + + reg &= ~ADS131E08_CHR_PWD_MASK; + reg |= FIELD_PREP(ADS131E08_CHR_PWD_MASK, value); + + return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg); +} + +static int ads131e08_config_reference_voltage(struct ads131e08_state *st) +{ + int reg; + + reg = ads131e08_read_reg(st, ADS131E08_ADR_CFG3R); + if (reg < 0) + return reg; + + reg &= ~ADS131E08_CFG3R_PDB_REFBUF_MASK; + if (!st->vref_reg) { + reg |= FIELD_PREP(ADS131E08_CFG3R_PDB_REFBUF_MASK, 1); + reg &= ~ADS131E08_CFG3R_VREF_4V_MASK; + reg |= FIELD_PREP(ADS131E08_CFG3R_VREF_4V_MASK, + st->vref_mv == ADS131E08_VREF_4V_mV); + } + + return ads131e08_write_reg(st, ADS131E08_ADR_CFG3R, reg); +} + +static int ads131e08_initial_config(struct iio_dev *indio_dev) +{ + const struct iio_chan_spec *channel = indio_dev->channels; + struct ads131e08_state *st = iio_priv(indio_dev); + unsigned long active_channels = 0; + int ret, i; + + ret = ads131e08_exec_cmd(st, ADS131E08_CMD_RESET); + if (ret) + return ret; + + udelay(st->reset_delay_us); + + /* Disable read data in continuous mode (enabled by default) */ + ret = ads131e08_exec_cmd(st, ADS131E08_CMD_SDATAC); + if (ret) + return ret; + + ret = ads131e08_set_data_rate(st, ADS131E08_DEFAULT_DATA_RATE); + if (ret) + return ret; + + ret = ads131e08_config_reference_voltage(st); + if (ret) + return ret; + + for (i = 0; i < indio_dev->num_channels; i++) { + ret = ads131e08_set_pga_gain(st, channel->channel, + st->channel_config[i].pga_gain); + if (ret) + return ret; + + ret = ads131e08_set_channel_mux(st, channel->channel, + st->channel_config[i].mux); + if (ret) + return ret; + + active_channels |= BIT(channel->channel); + channel++; + } + + /* Power down unused channels */ + for_each_clear_bit(i, &active_channels, st->info->max_channels) { + ret = ads131e08_power_down_channel(st, i, true); + if (ret) + return ret; + } + + /* Request channel offset calibration */ + ret = ads131e08_exec_cmd(st, ADS131E08_CMD_OFFSETCAL); + if (ret) + return ret; + + /* + * Channel offset calibration is triggered with the first START + * command. Since calibration takes more time than settling operation, + * this causes timeout error when command START is sent first + * time (e.g. first call of the ads131e08_read_direct method). + * To avoid this problem offset calibration is triggered here. + */ + ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START); + if (ret) + return ret; + + msleep(ADS131E08_WAIT_OFFSETCAL_MS); + + return ads131e08_exec_cmd(st, ADS131E08_CMD_STOP); +} + +static int ads131e08_pool_data(struct ads131e08_state *st) +{ + unsigned long timeout; + int ret; + + reinit_completion(&st->completion); + + ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START); + if (ret) + return ret; + + timeout = msecs_to_jiffies(ADS131E08_MAX_SETTLING_TIME_MS); + ret = wait_for_completion_timeout(&st->completion, timeout); + if (!ret) + return -ETIMEDOUT; + + ret = ads131e08_read_data(st, st->readback_len); + if (ret) + return ret; + + return ads131e08_exec_cmd(st, ADS131E08_CMD_STOP); +} + +static int ads131e08_read_direct(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *value) +{ + struct ads131e08_state *st = iio_priv(indio_dev); + u8 num_bits, *src; + int ret; + + ret = ads131e08_pool_data(st); + if (ret) + return ret; + + src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES + + channel->channel * ADS131E08_NUM_DATA_BYTES(st->data_rate); + + num_bits = ADS131E08_NUM_DATA_BITS(st->data_rate); + *value = sign_extend32(get_unaligned_be32(src) >> (32 - num_bits), num_bits - 1); + + return 0; +} + +static int ads131e08_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *value, + int *value2, long mask) +{ + struct ads131e08_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = ads131e08_read_direct(indio_dev, channel, value); + iio_device_release_direct_mode(indio_dev); + if (ret) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + if (st->vref_reg) { + ret = regulator_get_voltage(st->vref_reg); + if (ret < 0) + return ret; + + *value = ret / 1000; + } else { + *value = st->vref_mv; + } + + *value /= st->channel_config[channel->address].pga_gain; + *value2 = ADS131E08_NUM_DATA_BITS(st->data_rate) - 1; + + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *value = st->data_rate; + + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int ads131e08_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int value, + int value2, long mask) +{ + struct ads131e08_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = ads131e08_set_data_rate(st, value); + iio_device_release_direct_mode(indio_dev); + return ret; + + default: + return -EINVAL; + } +} + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1 2 4 8 16 32 64"); + +static struct attribute *ads131e08_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group ads131e08_attribute_group = { + .attrs = ads131e08_attributes, +}; + +static int ads131e08_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, unsigned int *readval) +{ + struct ads131e08_state *st = iio_priv(indio_dev); + + if (readval) { + int ret = ads131e08_read_reg(st, reg); + *readval = ret; + return ret; + } + + return ads131e08_write_reg(st, reg, writeval); +} + +static const struct iio_info ads131e08_iio_info = { + .read_raw = ads131e08_read_raw, + .write_raw = ads131e08_write_raw, + .attrs = &ads131e08_attribute_group, + .debugfs_reg_access = &ads131e08_debugfs_reg_access, +}; + +static int ads131e08_set_trigger_state(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct ads131e08_state *st = iio_priv(indio_dev); + u8 cmd = state ? ADS131E08_CMD_START : ADS131E08_CMD_STOP; + + return ads131e08_exec_cmd(st, cmd); +} + +static const struct iio_trigger_ops ads131e08_trigger_ops = { + .set_trigger_state = &ads131e08_set_trigger_state, + .validate_device = &iio_trigger_validate_own_device, +}; + +static irqreturn_t ads131e08_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct ads131e08_state *st = iio_priv(indio_dev); + unsigned int chn, i = 0; + u8 *src, *dest; + int ret; + + /* + * The number of data bits per channel depends on the data rate. + * For 32 and 64 ksps data rates, number of data bits per channel + * is 16. This case is not compliant with used (fixed) scan element + * type (be:s24/32>>8). So we use a little tweak to pack properly + * 16 bits of data into the buffer. + */ + unsigned int num_bytes = ADS131E08_NUM_DATA_BYTES(st->data_rate); + u8 tweek_offset = num_bytes == 2 ? 1 : 0; + + if (iio_trigger_using_own(indio_dev)) + ret = ads131e08_read_data(st, st->readback_len); + else + ret = ads131e08_pool_data(st); + + if (ret) + goto out; + + for_each_set_bit(chn, indio_dev->active_scan_mask, indio_dev->masklength) { + src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES + chn * num_bytes; + dest = st->tmp_buf.data + i * ADS131E08_NUM_STORAGE_BYTES; + + /* + * Tweek offset is 0: + * +---+---+---+---+ + * |D0 |D1 |D2 | X | (3 data bytes) + * +---+---+---+---+ + * a+0 a+1 a+2 a+3 + * + * Tweek offset is 1: + * +---+---+---+---+ + * |P0 |D0 |D1 | X | (one padding byte and 2 data bytes) + * +---+---+---+---+ + * a+0 a+1 a+2 a+3 + */ + memcpy(dest + tweek_offset, src, num_bytes); + + /* + * Data conversion from 16 bits of data to 24 bits of data + * is done by sign extension (properly filling padding byte). + */ + if (tweek_offset) + *dest = *src & BIT(7) ? 0xff : 0x00; + + i++; + } + + iio_push_to_buffers_with_timestamp(indio_dev, st->tmp_buf.data, + iio_get_time_ns(indio_dev)); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static irqreturn_t ads131e08_interrupt(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct ads131e08_state *st = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev)) + iio_trigger_poll(st->trig); + else + complete(&st->completion); + + return IRQ_HANDLED; +} + +static int ads131e08_alloc_channels(struct iio_dev *indio_dev) +{ + struct ads131e08_state *st = iio_priv(indio_dev); + struct ads131e08_channel_config *channel_config; + struct device *dev = &st->spi->dev; + struct iio_chan_spec *channels; + struct fwnode_handle *node; + unsigned int channel, tmp; + int num_channels, i, ret; + + ret = device_property_read_u32(dev, "ti,vref-internal", &tmp); + if (ret) + tmp = 0; + + switch (tmp) { + case 0: + st->vref_mv = ADS131E08_VREF_2V4_mV; + break; + case 1: + st->vref_mv = ADS131E08_VREF_4V_mV; + break; + default: + dev_err(&st->spi->dev, "invalid internal voltage reference\n"); + return -EINVAL; + } + + num_channels = device_get_child_node_count(dev); + if (num_channels == 0) { + dev_err(&st->spi->dev, "no channel children\n"); + return -ENODEV; + } + + if (num_channels > st->info->max_channels) { + dev_err(&st->spi->dev, "num of channel children out of range\n"); + return -EINVAL; + } + + channels = devm_kcalloc(&st->spi->dev, num_channels, + sizeof(*channels), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + channel_config = devm_kcalloc(&st->spi->dev, num_channels, + sizeof(*channel_config), GFP_KERNEL); + if (!channel_config) + return -ENOMEM; + + i = 0; + device_for_each_child_node(dev, node) { + ret = fwnode_property_read_u32(node, "reg", &channel); + if (ret) + return ret; + + ret = fwnode_property_read_u32(node, "ti,gain", &tmp); + if (ret) { + channel_config[i].pga_gain = ADS131E08_DEFAULT_PGA_GAIN; + } else { + ret = ads131e08_pga_gain_to_field_value(st, tmp); + if (ret < 0) + return ret; + + channel_config[i].pga_gain = tmp; + } + + ret = fwnode_property_read_u32(node, "ti,mux", &tmp); + if (ret) { + channel_config[i].mux = ADS131E08_DEFAULT_MUX; + } else { + ret = ads131e08_validate_channel_mux(st, tmp); + if (ret) + return ret; + + channel_config[i].mux = tmp; + } + + channels[i].type = IIO_VOLTAGE; + channels[i].indexed = 1; + channels[i].channel = channel; + channels[i].address = i; + channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE); + channels[i].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ); + channels[i].scan_index = channel; + channels[i].scan_type.sign = 's'; + channels[i].scan_type.realbits = 24; + channels[i].scan_type.storagebits = 32; + channels[i].scan_type.shift = 8; + channels[i].scan_type.endianness = IIO_BE; + i++; + } + + indio_dev->channels = channels; + indio_dev->num_channels = num_channels; + st->channel_config = channel_config; + + return 0; +} + +static void ads131e08_regulator_disable(void *data) +{ + struct ads131e08_state *st = data; + + regulator_disable(st->vref_reg); +} + +static void ads131e08_clk_disable(void *data) +{ + struct ads131e08_state *st = data; + + clk_disable_unprepare(st->adc_clk); +} + +static int ads131e08_probe(struct spi_device *spi) +{ + const struct ads131e08_info *info; + struct ads131e08_state *st; + struct iio_dev *indio_dev; + unsigned long adc_clk_hz; + unsigned long adc_clk_ns; + int ret; + + info = device_get_match_data(&spi->dev); + if (!info) { + dev_err(&spi->dev, "failed to get match data\n"); + return -ENODEV; + } + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) { + dev_err(&spi->dev, "failed to allocate IIO device\n"); + return -ENOMEM; + } + + st = iio_priv(indio_dev); + st->info = info; + st->spi = spi; + + ret = ads131e08_alloc_channels(indio_dev); + if (ret) + return ret; + + indio_dev->name = st->info->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &ads131e08_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + init_completion(&st->completion); + + if (spi->irq) { + ret = devm_request_irq(&spi->dev, spi->irq, + ads131e08_interrupt, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + spi->dev.driver->name, indio_dev); + if (ret) + return dev_err_probe(&spi->dev, ret, + "request irq failed\n"); + } else { + dev_err(&spi->dev, "data ready IRQ missing\n"); + return -ENODEV; + } + + st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d", + indio_dev->name, indio_dev->id); + if (!st->trig) { + dev_err(&spi->dev, "failed to allocate IIO trigger\n"); + return -ENOMEM; + } + + st->trig->ops = &ads131e08_trigger_ops; + st->trig->dev.parent = &spi->dev; + iio_trigger_set_drvdata(st->trig, indio_dev); + ret = devm_iio_trigger_register(&spi->dev, st->trig); + if (ret) { + dev_err(&spi->dev, "failed to register IIO trigger\n"); + return -ENOMEM; + } + + indio_dev->trig = iio_trigger_get(st->trig); + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + NULL, &ads131e08_trigger_handler, NULL); + if (ret) { + dev_err(&spi->dev, "failed to setup IIO buffer\n"); + return ret; + } + + st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref"); + if (!IS_ERR(st->vref_reg)) { + ret = regulator_enable(st->vref_reg); + if (ret) { + dev_err(&spi->dev, + "failed to enable external vref supply\n"); + return ret; + } + + ret = devm_add_action_or_reset(&spi->dev, ads131e08_regulator_disable, st); + if (ret) + return ret; + } else { + if (PTR_ERR(st->vref_reg) != -ENODEV) + return PTR_ERR(st->vref_reg); + + st->vref_reg = NULL; + } + + st->adc_clk = devm_clk_get(&spi->dev, "adc-clk"); + if (IS_ERR(st->adc_clk)) + return dev_err_probe(&spi->dev, PTR_ERR(st->adc_clk), + "failed to get the ADC clock\n"); + + ret = clk_prepare_enable(st->adc_clk); + if (ret) { + dev_err(&spi->dev, "failed to prepare/enable the ADC clock\n"); + return ret; + } + + ret = devm_add_action_or_reset(&spi->dev, ads131e08_clk_disable, st); + if (ret) + return ret; + + adc_clk_hz = clk_get_rate(st->adc_clk); + if (!adc_clk_hz) { + dev_err(&spi->dev, "failed to get the ADC clock rate\n"); + return -EINVAL; + } + + adc_clk_ns = NSEC_PER_SEC / adc_clk_hz; + st->sdecode_delay_us = DIV_ROUND_UP( + ADS131E08_WAIT_SDECODE_CYCLES * adc_clk_ns, NSEC_PER_USEC); + st->reset_delay_us = DIV_ROUND_UP( + ADS131E08_WAIT_RESET_CYCLES * adc_clk_ns, NSEC_PER_USEC); + + ret = ads131e08_initial_config(indio_dev); + if (ret) { + dev_err(&spi->dev, "initial configuration failed\n"); + return ret; + } + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct of_device_id ads131e08_of_match[] = { + { .compatible = "ti,ads131e04", + .data = &ads131e08_info_tbl[ads131e04], }, + { .compatible = "ti,ads131e06", + .data = &ads131e08_info_tbl[ads131e06], }, + { .compatible = "ti,ads131e08", + .data = &ads131e08_info_tbl[ads131e08], }, + {} +}; +MODULE_DEVICE_TABLE(of, ads131e08_of_match); + +static struct spi_driver ads131e08_driver = { + .driver = { + .name = "ads131e08", + .of_match_table = ads131e08_of_match, + }, + .probe = ads131e08_probe, +}; +module_spi_driver(ads131e08_driver); + +MODULE_AUTHOR("Tomislav Denis "); +MODULE_DESCRIPTION("Driver for ADS131E0x ADC family"); +MODULE_LICENSE("GPL v2"); From f3c52f01b4274239c1945aca1f27360b20b83f39 Mon Sep 17 00:00:00 2001 From: Tomislav Denis Date: Tue, 2 Feb 2021 09:41:07 +0100 Subject: [PATCH 012/119] bindings: iio: adc: Add documentation for ADS131E0x ADC driver Add a device tree binding documentation for Texas Instruments ADS131E0x ADC family driver. Signed-off-by: Tomislav Denis Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20210202084107.3260-3-tomislav.denis@avl.com Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/ti,ads131e08.yaml | 181 ++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 182 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/ti,ads131e08.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads131e08.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads131e08.yaml new file mode 100644 index 000000000000..e0670e3fbb72 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/ti,ads131e08.yaml @@ -0,0 +1,181 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/ti,ads131e08.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments ADS131E0x 4-, 6- and 8-Channel ADCs + +maintainers: + - Tomislav Denis + +description: | + The ADS131E0x are a family of multichannel, simultaneous sampling, + 24-bit, delta-sigma, analog-to-digital converters (ADCs) with a + built-in programmable gain amplifier (PGA), internal reference + and an onboard oscillator. + The communication with ADC chip is via the SPI bus (mode 1). + + https://www.ti.com/lit/ds/symlink/ads131e08.pdf + +properties: + compatible: + enum: + - ti,ads131e04 + - ti,ads131e06 + - ti,ads131e08 + + reg: + maxItems: 1 + + spi-max-frequency: true + + spi-cpha: true + + clocks: + description: | + Device tree identifier to the clock source (2.048 MHz). + Note: clock source is selected using CLKSEL pin. + maxItems: 1 + + clock-names: + items: + - const: adc-clk + + interrupts: + description: | + IRQ line for the ADC data ready. + maxItems: 1 + + vref-supply: + description: | + Optional external voltage reference. If not supplied, internal voltage + reference is used. + + ti,vref-internal: + description: | + Select the internal voltage reference value. + 0: 2.4V + 1: 4.0V + If this field is left empty, 2.4V is selected. + Note: internal voltage reference is used only if vref-supply is not supplied. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1] + default: 0 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +required: + - compatible + - reg + - spi-cpha + - clocks + - clock-names + - interrupts + +patternProperties: + "^channel@([0-7])$": + $ref: "adc.yaml" + type: object + description: | + Represents the external channels which are connected to the ADC. + + properties: + reg: + description: | + The channel number. + Up to 4 channels, numbered from 0 to 3 for ti,ads131e04. + Up to 6 channels, numbered from 0 to 5 for ti,ads131e06. + Up to 8 channels, numbered from 0 to 7 for ti,ads131e08. + items: + minimum: 0 + maximum: 7 + + ti,gain: + description: | + The PGA gain value for the channel. + If this field is left empty, PGA gain 1 is used. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [1, 2, 4, 8, 12] + default: 1 + + ti,mux: + description: | + Channel input selection(muliplexer). + 0: Normal input. + 1: Input shorted to (VREFP + VREFN) / 2 (for offset or noise measurements). + 3: MVDD (for supply measurement) + 4: Temperature sensor + If this field is left empty, normal input is selected. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 3, 4] + default: 0 + + required: + - reg + + additionalProperties: false + +additionalProperties: false + +examples: + - | + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "ti,ads131e08"; + reg = <0>; + spi-max-frequency = <1000000>; + spi-cpha; + clocks = <&clk2048k>; + clock-names = "adc-clk"; + interrupt-parent = <&gpio5>; + interrupts = <28 IRQ_TYPE_EDGE_FALLING>; + vref-supply = <&adc_vref>; + + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + }; + + channel@1 { + reg = <1>; + }; + + channel@2 { + reg = <2>; + ti,gain = <2>; + }; + + channel@3 { + reg = <3>; + }; + + channel@4 { + reg = <4>; + }; + + channel@5 { + reg = <5>; + }; + + channel@6 { + reg = <6>; + }; + + channel@7 { + reg = <7>; + ti,mux = <4>; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 180d13dab902..05279582917a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17830,6 +17830,7 @@ TI ADS131E0X ADC SERIES DRIVER M: Tomislav Denis L: linux-iio@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/iio/adc/ti,ads131e08.yaml F: drivers/iio/adc/ti-ads131e08.c TI AM437X VPFE DRIVER From 6f71bf1991b6f04dc87a4f5b9d6823535f51a50d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 7 Feb 2021 17:08:59 +0100 Subject: [PATCH 013/119] iio: core: Allow drivers to specify a label without it coming from of Only set indio_dev->label from of/dt if there actually is a label specified in of. This allows drivers to set a label without this being overwritten with NULL when there is no label specified in of. This is esp. useful on devices where of is not used at all, such as your typical x86/ACPI device. Signed-off-by: Hans de Goede Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210207160901.110643-2-hdegoede@redhat.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 7db761afa578..121d5b098367 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1788,6 +1788,7 @@ static const struct iio_buffer_setup_ops noop_ring_setup_ops; int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) { + const char *label; int ret; if (!indio_dev->info) @@ -1798,8 +1799,9 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) if (!indio_dev->dev.of_node && indio_dev->dev.parent) indio_dev->dev.of_node = indio_dev->dev.parent->of_node; - indio_dev->label = of_get_property(indio_dev->dev.of_node, "label", - NULL); + label = of_get_property(indio_dev->dev.of_node, "label", NULL); + if (label) + indio_dev->label = label; ret = iio_check_unique_scan_index(indio_dev); if (ret < 0) From 788348a5f788f976908231962c6d9ee9d1e6f2db Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 7 Feb 2021 17:09:00 +0100 Subject: [PATCH 014/119] iio: accel: bmc150: Set label based on accel-location on 2-accel yoga-style 2-in-1s MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some 2-in-1 laptops / convertibles with 360° (yoga-style) hinges, use 2 bmc150 accelerometers, defined by a single BOSC0200 ACPI device node (1 in their base and 1 in their display). Since in this case we know the location of each accelerometer, set the label for the accelerometers to the standardized "accel-display" resp. "accel-base" labels. This way userspace can use the labels to get the location. This was tested on a Lenovo ThinkPad Yoga 11e 4th gen (N3450 CPU). Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20210207160901.110643-3-hdegoede@redhat.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bmc150-accel-core.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 7e425ebcd7ea..b0dbd12cbf42 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -443,26 +443,32 @@ static bool bmc150_apply_acpi_orientation(struct device *dev, struct iio_mount_matrix *orientation) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct acpi_device *adev = ACPI_COMPANION(dev); + char *name, *alt_name, *label, *str; union acpi_object *obj, *elements; - char *name, *alt_name, *str; acpi_status status; int i, j, val[3]; if (!adev || !acpi_dev_hid_uid_match(adev, "BOSC0200", NULL)) return false; - if (strcmp(dev_name(dev), "i2c-BOSC0200:base") == 0) + if (strcmp(dev_name(dev), "i2c-BOSC0200:base") == 0) { alt_name = "ROMK"; - else + label = "accel-base"; + } else { alt_name = "ROMS"; + label = "accel-display"; + } - if (acpi_has_method(adev->handle, "ROTM")) + if (acpi_has_method(adev->handle, "ROTM")) { name = "ROTM"; - else if (acpi_has_method(adev->handle, alt_name)) + } else if (acpi_has_method(adev->handle, alt_name)) { name = alt_name; - else + indio_dev->label = label; + } else { return false; + } status = acpi_evaluate_object(adev->handle, name, NULL, &buffer); if (ACPI_FAILURE(status)) { From 30132fe46669a16846fa5412f92040a2fb9243ec Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 7 Feb 2021 17:09:01 +0100 Subject: [PATCH 015/119] iio: accel: kxcjk-1013: Set label based on accel-location on 2-accel yoga-style 2-in-1s MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some 2-in-1 laptops / convertibles with 360° (yoga-style) hinges, use 2 KXCJ91008 accelerometers: 1 in their display using an ACPI HID of "KIOX010A"; and 1 in their base using an ACPI HID of "KIOX020A" Since in this case we know the location of each accelerometer, set the label for the accelerometers to the standardized "accel-display" resp. "accel-base" labels. This way userspace can use the labels to get the location. This was tested on a Medion Akoya E2228T MD60250. Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20210207160901.110643-4-hdegoede@redhat.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/kxcjk-1013.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 2fadafc860fd..5c056165f473 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -1284,7 +1284,8 @@ static irqreturn_t kxcjk1013_data_rdy_trig_poll(int irq, void *private) static const char *kxcjk1013_match_acpi_device(struct device *dev, enum kx_chipset *chipset, - enum kx_acpi_type *acpi_type) + enum kx_acpi_type *acpi_type, + const char **label) { const struct acpi_device_id *id; @@ -1292,10 +1293,14 @@ static const char *kxcjk1013_match_acpi_device(struct device *dev, if (!id) return NULL; - if (strcmp(id->id, "SMO8500") == 0) + if (strcmp(id->id, "SMO8500") == 0) { *acpi_type = ACPI_SMO8500; - else if (strcmp(id->id, "KIOX010A") == 0) + } else if (strcmp(id->id, "KIOX010A") == 0) { *acpi_type = ACPI_KIOX010A; + *label = "accel-display"; + } else if (strcmp(id->id, "KIOX020A") == 0) { + *label = "accel-base"; + } *chipset = (enum kx_chipset)id->driver_data; @@ -1368,7 +1373,8 @@ static int kxcjk1013_probe(struct i2c_client *client, } else if (ACPI_HANDLE(&client->dev)) { name = kxcjk1013_match_acpi_device(&client->dev, &data->chipset, - &data->acpi_type); + &data->acpi_type, + &indio_dev->label); } else return -ENODEV; From a04e3db5146e49807bb6000302fd2d9efbd964c1 Mon Sep 17 00:00:00 2001 From: Bhaskar Chowdhury Date: Wed, 10 Feb 2021 14:27:04 +0530 Subject: [PATCH 016/119] iio: proximity: sx9500: Fix a spelling postive to positive s/postive/positive/ Signed-off-by: Bhaskar Chowdhury Acked-by: Randy Dunlap Link: https://lore.kernel.org/r/20210210085704.1228068-1-unixbhaskar@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/sx9500.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index acb821cbad46..67353d33af94 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -758,7 +758,7 @@ static const struct sx9500_reg_default sx9500_default_regs[] = { .reg = SX9500_REG_PROX_CTRL5, /* * Debouncer off, lowest average negative filter, - * highest average postive filter. + * highest average positive filter. */ .def = 0x0f, }, From 2646a95df94e5d2aaeb22fa912179831ebd0095b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 15 Dec 2020 20:17:41 +0100 Subject: [PATCH 017/119] iio: iio_format_value(): Use signed temporary for IIO_VAL_FRACTIONAL_LOG2 IIO_VAL_FRACTIONAL_LOG2 works with signed values, yet the temporary we use is unsigned. This works at the moment because the variable is implicitly cast to signed everywhere where it is used. But it will certainly be cleaner to use a signed variable in the first place. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20201215191743.2725-1-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 121d5b098367..023040ecd96f 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -625,7 +625,6 @@ EXPORT_SYMBOL(iio_read_mount_matrix); static ssize_t __iio_format_value(char *buf, size_t len, unsigned int type, int size, const int *vals) { - unsigned long long tmp; int tmp0, tmp1; s64 tmp2; bool scale_db = false; @@ -658,8 +657,8 @@ static ssize_t __iio_format_value(char *buf, size_t len, unsigned int type, else return snprintf(buf, len, "%d.%09u", tmp0, abs(tmp1)); case IIO_VAL_FRACTIONAL_LOG2: - tmp = shift_right((s64)vals[0] * 1000000000LL, vals[1]); - tmp0 = (int)div_s64_rem(tmp, 1000000000LL, &tmp1); + tmp2 = shift_right((s64)vals[0] * 1000000000LL, vals[1]); + tmp0 = (int)div_s64_rem(tmp2, 1000000000LL, &tmp1); return scnprintf(buf, len, "%d.%09u", tmp0, abs(tmp1)); case IIO_VAL_INT_MULTIPLE: { From 38a52cdef5b3ba2e4ac6b24a073a231e04d2c552 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 15 Dec 2020 20:17:42 +0100 Subject: [PATCH 018/119] iio: iio_format_value(): Fix IIO_VAL_FRACTIONAL_LOG2 values between -1.0 and 0.0 When formatting a value using IIO_VAL_FRACTIONAL_LOG2 and the values is between -1 and 0 the sign is omitted. We need the same trick as for IIO_VAL_FRACTIONAL to make sure this gets formatted correctly. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20201215191743.2725-2-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 023040ecd96f..1b40136f10ac 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -659,7 +659,10 @@ static ssize_t __iio_format_value(char *buf, size_t len, unsigned int type, case IIO_VAL_FRACTIONAL_LOG2: tmp2 = shift_right((s64)vals[0] * 1000000000LL, vals[1]); tmp0 = (int)div_s64_rem(tmp2, 1000000000LL, &tmp1); - return scnprintf(buf, len, "%d.%09u", tmp0, abs(tmp1)); + if (tmp0 == 0 && tmp2 < 0) + return snprintf(buf, len, "-0.%09u", abs(tmp1)); + else + return scnprintf(buf, len, "%d.%09u", tmp0, abs(tmp1)); case IIO_VAL_INT_MULTIPLE: { int i; From 0bf49ffbfe949df21e95b8f95b5f308db379ac74 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 15 Dec 2020 20:17:43 +0100 Subject: [PATCH 019/119] iio: Add basic unit test for iio_format_value() The IIO core provides a function to do formatting of fixedpoint numbers. In the past there have been some issues with the implementation of the function where for example negative numbers were not handled correctly. Introduce a basic unit test based on kunit that tests the function and ensures that the generated output matches the expected output. This gives us some confidence that future modifications to the function implementation will not break ABI compatibility. To run the unit tests follow the kunit documentation and add CONFIG_IIO=y CONFIG_IIO_TEST_FORMAT=y to the .kunitconfig and run > ./tools/testing/kunit/kunit.py run Configuring KUnit Kernel ... Building KUnit Kernel ... Starting KUnit Kernel ... ============================================================ ======== [PASSED] iio-format ======== [PASSED] iio_test_iio_format_value_integer [PASSED] iio_test_iio_format_value_fixedpoint [PASSED] iio_test_iio_format_value_fractional [PASSED] iio_test_iio_format_value_fractional_log2 [PASSED] iio_test_iio_format_value_multiple ============================================================ Testing complete. 21 tests run. 0 failed. 0 crashed. Elapsed time: 8.242s total, 0.001s configuring, 3.865s building, 0.000s running Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20201215191743.2725-3-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/Kconfig | 1 + drivers/iio/Makefile | 1 + drivers/iio/test/Kconfig | 9 ++ drivers/iio/test/Makefile | 7 + drivers/iio/test/iio-test-format.c | 198 +++++++++++++++++++++++++++++ 5 files changed, 216 insertions(+) create mode 100644 drivers/iio/test/Kconfig create mode 100644 drivers/iio/test/Makefile create mode 100644 drivers/iio/test/iio-test-format.c diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 267553386c71..b35e0c33b5e2 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -85,6 +85,7 @@ source "drivers/iio/light/Kconfig" source "drivers/iio/magnetometer/Kconfig" source "drivers/iio/multiplexer/Kconfig" source "drivers/iio/orientation/Kconfig" +source "drivers/iio/test/Kconfig" if IIO_TRIGGER source "drivers/iio/trigger/Kconfig" endif #IIO_TRIGGER diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 1712011c0f4a..2561325aaa74 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -38,4 +38,5 @@ obj-y += pressure/ obj-y += proximity/ obj-y += resolver/ obj-y += temperature/ +obj-y += test/ obj-y += trigger/ diff --git a/drivers/iio/test/Kconfig b/drivers/iio/test/Kconfig new file mode 100644 index 000000000000..679a7794af20 --- /dev/null +++ b/drivers/iio/test/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Industrial I/O subsystem unit tests configuration +# + +# Keep in alphabetical order +config IIO_TEST_FORMAT + bool "Test IIO formatting functions" + depends on KUNIT=y diff --git a/drivers/iio/test/Makefile b/drivers/iio/test/Makefile new file mode 100644 index 000000000000..f1099b495301 --- /dev/null +++ b/drivers/iio/test/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the industrial I/O unit tests. +# + +# Keep in alphabetical order +obj-$(CONFIG_IIO_TEST_FORMAT) += iio-test-format.o diff --git a/drivers/iio/test/iio-test-format.c b/drivers/iio/test/iio-test-format.c new file mode 100644 index 000000000000..55a0cfe9181d --- /dev/null +++ b/drivers/iio/test/iio-test-format.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Unit tests for IIO formatting functions + * + * Copyright (c) 2020 Lars-Peter Clausen + */ + +#include +#include + +#define IIO_TEST_FORMAT_EXPECT_EQ(_test, _buf, _ret, _val) do { \ + KUNIT_EXPECT_EQ(_test, (int)strlen(_buf), _ret); \ + KUNIT_EXPECT_STREQ(_test, (_buf), (_val)); \ + } while (0) + +static void iio_test_iio_format_value_integer(struct kunit *test) +{ + char *buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + int val; + int ret; + + val = 42; + ret = iio_format_value(buf, IIO_VAL_INT, 1, &val); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "42\n"); + + val = -23; + ret = iio_format_value(buf, IIO_VAL_INT, 1, &val); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-23\n"); + + val = 0; + ret = iio_format_value(buf, IIO_VAL_INT, 1, &val); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0\n"); + + val = INT_MAX; + ret = iio_format_value(buf, IIO_VAL_INT, 1, &val); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "2147483647\n"); + + val = INT_MIN; + ret = iio_format_value(buf, IIO_VAL_INT, 1, &val); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-2147483648\n"); +} + +static void iio_test_iio_format_value_fixedpoint(struct kunit *test) +{ + char *buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + int values[2]; + int ret; + + /* positive >= 1 */ + values[0] = 1; + values[1] = 10; + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1.000010\n"); + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1.000010 dB\n"); + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1.000000010\n"); + + /* positive < 1 */ + values[0] = 0; + values[1] = 12; + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000012\n"); + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000012 dB\n"); + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000000012\n"); + + /* negative <= -1 */ + values[0] = -1; + values[1] = 10; + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-1.000010\n"); + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-1.000010 dB\n"); + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-1.000000010\n"); + + /* negative > -1 */ + values[0] = 0; + values[1] = -123; + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.000123\n"); + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.000123 dB\n"); + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.000000123\n"); +} + +static void iio_test_iio_format_value_fractional(struct kunit *test) +{ + char *buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + int values[2]; + int ret; + + /* positive < 1 */ + values[0] = 1; + values[1] = 10; + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.100000000\n"); + + /* positive >= 1 */ + values[0] = 100; + values[1] = 3; + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "33.333333333\n"); + + /* negative > -1 */ + values[0] = -1; + values[1] = 1000000000; + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.000000001\n"); + + /* negative <= -1 */ + values[0] = -200; + values[1] = 3; + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-66.666666666\n"); + + /* Zero */ + values[0] = 0; + values[1] = -10; + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000000000\n"); +} + +static void iio_test_iio_format_value_fractional_log2(struct kunit *test) +{ + char *buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + int values[2]; + int ret; + + /* positive < 1 */ + values[0] = 123; + values[1] = 10; + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.120117187\n"); + + /* positive >= 1 */ + values[0] = 1234567; + values[1] = 10; + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1205.631835937\n"); + + /* negative > -1 */ + values[0] = -123; + values[1] = 10; + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.120117187\n"); + + /* negative <= -1 */ + values[0] = -1234567; + values[1] = 10; + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-1205.631835937\n"); + + /* Zero */ + values[0] = 0; + values[1] = 10; + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000000000\n"); +} + +static void iio_test_iio_format_value_multiple(struct kunit *test) +{ + char *buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + int values[] = {1, -2, 3, -4, 5}; + int ret; + + ret = iio_format_value(buf, IIO_VAL_INT_MULTIPLE, + ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1 -2 3 -4 5 \n"); +} + +static struct kunit_case iio_format_test_cases[] = { + KUNIT_CASE(iio_test_iio_format_value_integer), + KUNIT_CASE(iio_test_iio_format_value_fixedpoint), + KUNIT_CASE(iio_test_iio_format_value_fractional), + KUNIT_CASE(iio_test_iio_format_value_fractional_log2), + KUNIT_CASE(iio_test_iio_format_value_multiple), + {} +}; + +static struct kunit_suite iio_format_test_suite = { + .name = "iio-format", + .test_cases = iio_format_test_cases, +}; +kunit_test_suite(iio_format_test_suite); From e36db6a06937c6fce3291f0c362d4f757b8ec703 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:21 +0200 Subject: [PATCH 020/119] iio: kfifo: add devm_iio_kfifo_buffer_setup() helper This change adds the devm_iio_kfifo_buffer_setup() helper/short-hand, which groups the simple routine of allocating a kfifo buffers via devm_iio_kfifo_allocate() and calling iio_device_attach_buffer(). The mode_flags parameter is required, as the IIO kfifo supports 2 modes: INDIO_BUFFER_SOFTWARE & INDIO_BUFFER_TRIGGERED. The setup_ops parameter is optional. This function will be a bit more useful when needing to define multiple buffers per IIO device. The naming for this function has been inspired from iio_triggered_buffer_setup() since that one does a kfifo alloc + a pollfunc alloc. So, this should have a more familiar ring to what it is. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-3-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- .../driver-api/driver-model/devres.rst | 1 + drivers/iio/buffer/kfifo_buf.c | 39 +++++++++++++++++++ include/linux/iio/kfifo_buf.h | 7 ++++ 3 files changed, 47 insertions(+) diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index cd8b6e657b94..562f5722281e 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -286,6 +286,7 @@ IIO devm_iio_device_alloc() devm_iio_device_register() devm_iio_kfifo_allocate() + devm_iio_kfifo_buffer_setup() devm_iio_triggered_buffer_setup() devm_iio_trigger_alloc() devm_iio_trigger_register() diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c index 1359abed3b31..6472c9fa1937 100644 --- a/drivers/iio/buffer/kfifo_buf.c +++ b/drivers/iio/buffer/kfifo_buf.c @@ -206,4 +206,43 @@ struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev) } EXPORT_SYMBOL(devm_iio_kfifo_allocate); +/** + * devm_iio_kfifo_buffer_setup - Allocate a kfifo buffer & attach it to an IIO device + * @dev: Device object to which to attach the life-time of this kfifo buffer + * @indio_dev: The device the buffer should be attached to + * @mode_flags: The mode flags for this buffer (INDIO_BUFFER_SOFTWARE and/or + * INDIO_BUFFER_TRIGGERED). + * @setup_ops: The setup_ops required to configure the HW part of the buffer (optional) + * + * This function allocates a kfifo buffer via devm_iio_kfifo_allocate() and + * attaches it to the IIO device via iio_device_attach_buffer(). + * This is meant to be a bit of a short-hand/helper function as there are a few + * drivers that seem to do this. + */ +int devm_iio_kfifo_buffer_setup(struct device *dev, + struct iio_dev *indio_dev, + int mode_flags, + const struct iio_buffer_setup_ops *setup_ops) +{ + struct iio_buffer *buffer; + + if (mode_flags) + mode_flags &= kfifo_access_funcs.modes; + + if (!mode_flags) + return -EINVAL; + + buffer = devm_iio_kfifo_allocate(dev); + if (!buffer) + return -ENOMEM; + + iio_device_attach_buffer(indio_dev, buffer); + + indio_dev->modes |= mode_flags; + indio_dev->setup_ops = setup_ops; + + return 0; +} +EXPORT_SYMBOL_GPL(devm_iio_kfifo_buffer_setup); + MODULE_LICENSE("GPL"); diff --git a/include/linux/iio/kfifo_buf.h b/include/linux/iio/kfifo_buf.h index 1fc1efa7799d..92c411b9ac26 100644 --- a/include/linux/iio/kfifo_buf.h +++ b/include/linux/iio/kfifo_buf.h @@ -3,6 +3,8 @@ #define __LINUX_IIO_KFIFO_BUF_H__ struct iio_buffer; +struct iio_buffer_setup_ops; +struct iio_dev; struct device; struct iio_buffer *iio_kfifo_allocate(void); @@ -10,4 +12,9 @@ void iio_kfifo_free(struct iio_buffer *r); struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev); +int devm_iio_kfifo_buffer_setup(struct device *dev, + struct iio_dev *indio_dev, + int mode_flags, + const struct iio_buffer_setup_ops *setup_ops); + #endif From 17395ce299211a8148ee45d1d71eb740a3fec48d Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:22 +0200 Subject: [PATCH 021/119] iio: make use of devm_iio_kfifo_buffer_setup() helper All drivers that already call devm_iio_kfifo_allocate() & iio_device_attach_buffer() are simple to convert to iio_device_attach_kfifo_buffer() in a single go. This change does that; the unwind order is preserved. What is important, is that the devm_iio_kfifo_buffer_setup() be called after the indio_dev->modes is assigned, to make sure that INDIO_BUFFER_SOFTWARE flag is set and not overridden by the assignment to indio_dev->modes. Also, the INDIO_BUFFER_SOFTWARE has been removed from the assignments of 'indio_dev->modes' because it is set by devm_iio_kfifo_buffer_setup(). Signed-off-by: Alexandru Ardelean Reviewed-by: Gwendal Grignou Reviewed-by: Matt Ranostay x Link: https://lore.kernel.org/r/20210215104043.91251-4-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/ssp_accel_sensor.c | 14 ++++------- drivers/iio/adc/ina2xx-adc.c | 14 +++++------ drivers/iio/adc/ti_am335x_adc.c | 18 ++++----------- .../cros_ec_sensors/cros_ec_sensors_core.c | 13 ++++------- drivers/iio/gyro/ssp_gyro_sensor.c | 14 ++++------- drivers/iio/health/max30100.c | 16 ++++++------- drivers/iio/health/max30102.c | 16 ++++++------- .../iio/imu/inv_icm42600/inv_icm42600_accel.c | 14 +++++------ .../iio/imu/inv_icm42600/inv_icm42600_gyro.c | 13 +++++------ .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 15 +++++------- drivers/iio/light/acpi-als.c | 12 ++++------ drivers/iio/light/apds9960.c | 16 ++++++------- .../staging/iio/impedance-analyzer/ad5933.c | 23 ++++--------------- 13 files changed, 74 insertions(+), 124 deletions(-) diff --git a/drivers/iio/accel/ssp_accel_sensor.c b/drivers/iio/accel/ssp_accel_sensor.c index 474477e91b5e..04dcb2b657ee 100644 --- a/drivers/iio/accel/ssp_accel_sensor.c +++ b/drivers/iio/accel/ssp_accel_sensor.c @@ -96,7 +96,6 @@ static int ssp_accel_probe(struct platform_device *pdev) int ret; struct iio_dev *indio_dev; struct ssp_sensor_data *spd; - struct iio_buffer *buffer; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd)); if (!indio_dev) @@ -109,18 +108,15 @@ static int ssp_accel_probe(struct platform_device *pdev) indio_dev->name = ssp_accel_device_name; indio_dev->info = &ssp_accel_iio_info; - indio_dev->modes = INDIO_BUFFER_SOFTWARE; indio_dev->channels = ssp_acc_channels; indio_dev->num_channels = ARRAY_SIZE(ssp_acc_channels); indio_dev->available_scan_masks = ssp_accel_scan_mask; - buffer = devm_iio_kfifo_allocate(&pdev->dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); - - indio_dev->setup_ops = &ssp_accel_buffer_ops; + ret = devm_iio_kfifo_buffer_setup(&pdev->dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &ssp_accel_buffer_ops); + if (ret) + return ret; platform_set_drvdata(pdev, indio_dev); diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index b573ec60a8b8..2ae54258b221 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -953,7 +953,6 @@ static int ina2xx_probe(struct i2c_client *client, { struct ina2xx_chip_info *chip; struct iio_dev *indio_dev; - struct iio_buffer *buffer; unsigned int val; enum ina2xx_ids type; int ret; @@ -1017,7 +1016,7 @@ static int ina2xx_probe(struct i2c_client *client, return ret; } - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + indio_dev->modes = INDIO_DIRECT_MODE; if (id->driver_data == ina226) { indio_dev->channels = ina226_channels; indio_dev->num_channels = ARRAY_SIZE(ina226_channels); @@ -1028,13 +1027,12 @@ static int ina2xx_probe(struct i2c_client *client, indio_dev->info = &ina219_info; } indio_dev->name = id->name; - indio_dev->setup_ops = &ina2xx_setup_ops; - buffer = devm_iio_kfifo_allocate(&indio_dev->dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &ina2xx_setup_ops); + if (ret) + return ret; return iio_device_register(indio_dev); } diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index e946903b0993..855cc2d64ac8 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -385,24 +385,16 @@ static int tiadc_iio_buffered_hardware_setup(struct device *dev, unsigned long flags, const struct iio_buffer_setup_ops *setup_ops) { - struct iio_buffer *buffer; int ret; - buffer = devm_iio_kfifo_allocate(dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); - - ret = devm_request_threaded_irq(dev, irq, pollfunc_th, pollfunc_bh, - flags, indio_dev->name, indio_dev); + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + setup_ops); if (ret) return ret; - indio_dev->setup_ops = setup_ops; - indio_dev->modes |= INDIO_BUFFER_SOFTWARE; - - return 0; + return devm_request_threaded_irq(dev, irq, pollfunc_th, pollfunc_bh, + flags, indio_dev->name, indio_dev); } static const char * const chan_name_ain[] = { diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c index c833ec0ef214..f8824afe595e 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c @@ -334,14 +334,11 @@ int cros_ec_sensors_core_init(struct platform_device *pdev, * We can not use trigger here, as events are generated * as soon as sample_frequency is set. */ - struct iio_buffer *buffer; - - buffer = devm_iio_kfifo_allocate(dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); - indio_dev->modes = INDIO_BUFFER_SOFTWARE; + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + NULL); + if (ret) + return ret; ret = cros_ec_sensorhub_register_push_data( sensor_hub, sensor_platform->sensor_num, diff --git a/drivers/iio/gyro/ssp_gyro_sensor.c b/drivers/iio/gyro/ssp_gyro_sensor.c index ac7c170a20de..46ed12771d2f 100644 --- a/drivers/iio/gyro/ssp_gyro_sensor.c +++ b/drivers/iio/gyro/ssp_gyro_sensor.c @@ -96,7 +96,6 @@ static int ssp_gyro_probe(struct platform_device *pdev) int ret; struct iio_dev *indio_dev; struct ssp_sensor_data *spd; - struct iio_buffer *buffer; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd)); if (!indio_dev) @@ -109,18 +108,15 @@ static int ssp_gyro_probe(struct platform_device *pdev) indio_dev->name = ssp_gyro_name; indio_dev->info = &ssp_gyro_iio_info; - indio_dev->modes = INDIO_BUFFER_SOFTWARE; indio_dev->channels = ssp_gyro_channels; indio_dev->num_channels = ARRAY_SIZE(ssp_gyro_channels); indio_dev->available_scan_masks = ssp_gyro_scan_mask; - buffer = devm_iio_kfifo_allocate(&pdev->dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); - - indio_dev->setup_ops = &ssp_gyro_buffer_ops; + ret = devm_iio_kfifo_buffer_setup(&pdev->dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &ssp_gyro_buffer_ops); + if (ret) + return ret; platform_set_drvdata(pdev, indio_dev); diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c index 38aa2030f3c6..36ba7611d9ce 100644 --- a/drivers/iio/health/max30100.c +++ b/drivers/iio/health/max30100.c @@ -418,7 +418,6 @@ static int max30100_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct max30100_data *data; - struct iio_buffer *buffer; struct iio_dev *indio_dev; int ret; @@ -426,19 +425,18 @@ static int max30100_probe(struct i2c_client *client, if (!indio_dev) return -ENOMEM; - buffer = devm_iio_kfifo_allocate(&client->dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); - indio_dev->name = MAX30100_DRV_NAME; indio_dev->channels = max30100_channels; indio_dev->info = &max30100_info; indio_dev->num_channels = ARRAY_SIZE(max30100_channels); indio_dev->available_scan_masks = max30100_scan_masks; - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE); - indio_dev->setup_ops = &max30100_buffer_setup_ops; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &max30100_buffer_setup_ops); + if (ret) + return ret; data = iio_priv(indio_dev); data->indio_dev = indio_dev; diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index b35557a54ee2..2292876c55e2 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -506,7 +506,6 @@ static int max30102_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct max30102_data *data; - struct iio_buffer *buffer; struct iio_dev *indio_dev; int ret; unsigned int reg; @@ -515,16 +514,9 @@ static int max30102_probe(struct i2c_client *client, if (!indio_dev) return -ENOMEM; - buffer = devm_iio_kfifo_allocate(&client->dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); - indio_dev->name = MAX30102_DRV_NAME; indio_dev->info = &max30102_info; - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE); - indio_dev->setup_ops = &max30102_buffer_setup_ops; + indio_dev->modes = INDIO_DIRECT_MODE; data = iio_priv(indio_dev); data->indio_dev = indio_dev; @@ -549,6 +541,12 @@ static int max30102_probe(struct i2c_client *client, return -ENODEV; } + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &max30102_buffer_setup_ops); + if (ret) + return ret; + data->regmap = devm_regmap_init_i2c(client, &max30102_regmap_config); if (IS_ERR(data->regmap)) { dev_err(&client->dev, "regmap initialization failed\n"); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index 3441b0d61c5d..383cc3250342 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -709,7 +709,6 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) const char *name; struct inv_icm42600_timestamp *ts; struct iio_dev *indio_dev; - struct iio_buffer *buffer; int ret; name = devm_kasprintf(dev, GFP_KERNEL, "%s-accel", st->name); @@ -720,23 +719,22 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) if (!indio_dev) return ERR_PTR(-ENOMEM); - buffer = devm_iio_kfifo_allocate(dev); - if (!buffer) - return ERR_PTR(-ENOMEM); - ts = iio_priv(indio_dev); inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.accel.odr)); iio_device_set_drvdata(indio_dev, st); indio_dev->name = name; indio_dev->info = &inv_icm42600_accel_info; - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = inv_icm42600_accel_channels; indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_accel_channels); indio_dev->available_scan_masks = inv_icm42600_accel_scan_masks; - indio_dev->setup_ops = &inv_icm42600_buffer_ops; - iio_device_attach_buffer(indio_dev, buffer); + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &inv_icm42600_buffer_ops); + if (ret) + return ERR_PTR(ret); ret = devm_iio_device_register(dev, indio_dev); if (ret) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c index aee7b9ff4bf4..cec1dd0e0464 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c @@ -720,7 +720,6 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st) const char *name; struct inv_icm42600_timestamp *ts; struct iio_dev *indio_dev; - struct iio_buffer *buffer; int ret; name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->name); @@ -731,23 +730,23 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st) if (!indio_dev) return ERR_PTR(-ENOMEM); - buffer = devm_iio_kfifo_allocate(dev); - if (!buffer) - return ERR_PTR(-ENOMEM); - ts = iio_priv(indio_dev); inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.gyro.odr)); iio_device_set_drvdata(indio_dev, st); indio_dev->name = name; indio_dev->info = &inv_icm42600_gyro_info; - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = inv_icm42600_gyro_channels; indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_gyro_channels); indio_dev->available_scan_masks = inv_icm42600_gyro_scan_masks; indio_dev->setup_ops = &inv_icm42600_buffer_ops; - iio_device_attach_buffer(indio_dev, buffer); + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &inv_icm42600_buffer_ops); + if (ret) + return ERR_PTR(ret); ret = devm_iio_device_register(dev, indio_dev); if (ret) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index f1103ecedd64..16730a780964 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -739,20 +739,17 @@ static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = { int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw) { - struct iio_buffer *buffer; - int i; + int i, ret; for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { if (!hw->iio_devs[i]) continue; - buffer = devm_iio_kfifo_allocate(hw->dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(hw->iio_devs[i], buffer); - hw->iio_devs[i]->modes |= INDIO_BUFFER_SOFTWARE; - hw->iio_devs[i]->setup_ops = &st_lsm6dsx_buffer_ops; + ret = devm_iio_kfifo_buffer_setup(hw->dev, hw->iio_devs[i], + INDIO_BUFFER_SOFTWARE, + &st_lsm6dsx_buffer_ops); + if (ret) + return ret; } return 0; diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c index 2be7180e2cbf..f8e547fd35e7 100644 --- a/drivers/iio/light/acpi-als.c +++ b/drivers/iio/light/acpi-als.c @@ -165,7 +165,7 @@ static int acpi_als_add(struct acpi_device *device) { struct acpi_als *als; struct iio_dev *indio_dev; - struct iio_buffer *buffer; + int ret; indio_dev = devm_iio_device_alloc(&device->dev, sizeof(*als)); if (!indio_dev) @@ -179,15 +179,13 @@ static int acpi_als_add(struct acpi_device *device) indio_dev->name = ACPI_ALS_DEVICE_NAME; indio_dev->info = &acpi_als_info; - indio_dev->modes = INDIO_BUFFER_SOFTWARE; indio_dev->channels = acpi_als_channels; indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels); - buffer = devm_iio_kfifo_allocate(&device->dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); + ret = devm_iio_kfifo_buffer_setup(&device->dev, indio_dev, + INDIO_BUFFER_SOFTWARE, NULL); + if (ret) + return ret; return devm_iio_device_register(&device->dev, indio_dev); } diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c index df0647856e5d..4141c0fa7bc4 100644 --- a/drivers/iio/light/apds9960.c +++ b/drivers/iio/light/apds9960.c @@ -988,7 +988,6 @@ static int apds9960_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct apds9960_data *data; - struct iio_buffer *buffer; struct iio_dev *indio_dev; int ret; @@ -996,19 +995,18 @@ static int apds9960_probe(struct i2c_client *client, if (!indio_dev) return -ENOMEM; - buffer = devm_iio_kfifo_allocate(&client->dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); - indio_dev->info = &apds9960_info; indio_dev->name = APDS9960_DRV_NAME; indio_dev->channels = apds9960_channels; indio_dev->num_channels = ARRAY_SIZE(apds9960_channels); indio_dev->available_scan_masks = apds9960_scan_masks; - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE); - indio_dev->setup_ops = &apds9960_buffer_setup_ops; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &apds9960_buffer_setup_ops); + if (ret) + return ret; data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c index dba78896ea8f..793918e1c45f 100644 --- a/drivers/staging/iio/impedance-analyzer/ad5933.c +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -602,23 +602,6 @@ static const struct iio_buffer_setup_ops ad5933_ring_setup_ops = { .postdisable = ad5933_ring_postdisable, }; -static int ad5933_register_ring_funcs_and_init(struct device *dev, - struct iio_dev *indio_dev) -{ - struct iio_buffer *buffer; - - buffer = devm_iio_kfifo_allocate(dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); - - /* Ring buffer functions - here trigger setup related */ - indio_dev->setup_ops = &ad5933_ring_setup_ops; - - return 0; -} - static void ad5933_work(struct work_struct *work) { struct ad5933_state *st = container_of(work, @@ -761,11 +744,13 @@ static int ad5933_probe(struct i2c_client *client, indio_dev->info = &ad5933_info; indio_dev->name = id->name; - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE); + indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = ad5933_channels; indio_dev->num_channels = ARRAY_SIZE(ad5933_channels); - ret = ad5933_register_ring_funcs_and_init(&client->dev, indio_dev); + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &ad5933_ring_setup_ops); if (ret) return ret; From e03ed893e2cf7305183b3314ae80daa1e97667b2 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:23 +0200 Subject: [PATCH 022/119] iio: accel: sca3000: use devm_iio_kfifo_buffer_setup() helper This change makes use of the devm_iio_kfifo_buffer_setup() helper, however the unwind order is changed. The life-time of the kfifo object is attached to the parent device object. This is to make the driver a bit more consistent with the other IIO drivers, even though (as it is now before this change) it shouldn't be a problem. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-5-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/sca3000.c | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index 194738660523..467b5fcb81db 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -1272,20 +1272,6 @@ static int sca3000_write_event_config(struct iio_dev *indio_dev, return ret; } -static int sca3000_configure_ring(struct iio_dev *indio_dev) -{ - struct iio_buffer *buffer; - - buffer = devm_iio_kfifo_allocate(&indio_dev->dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); - indio_dev->modes |= INDIO_BUFFER_SOFTWARE; - - return 0; -} - static inline int __sca3000_hw_ring_state_set(struct iio_dev *indio_dev, bool state) { @@ -1479,7 +1465,9 @@ static int sca3000_probe(struct spi_device *spi) } indio_dev->modes = INDIO_DIRECT_MODE; - ret = sca3000_configure_ring(indio_dev); + ret = devm_iio_kfifo_buffer_setup(&spi->dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &sca3000_ring_setup_ops); if (ret) return ret; @@ -1493,7 +1481,6 @@ static int sca3000_probe(struct spi_device *spi) if (ret) return ret; } - indio_dev->setup_ops = &sca3000_ring_setup_ops; ret = sca3000_clean_setup(st); if (ret) goto error_free_irq; From 99f6e8215b627730e2e6d371430a1f81e8ed6c27 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:24 +0200 Subject: [PATCH 023/119] iio: kfifo: un-export devm_iio_kfifo_allocate() function At this point all drivers should use devm_iio_kfifo_buffer_setup() instead of manually allocating via devm_iio_kfifo_allocate() and assigning ops and modes. With this change, the devm_iio_kfifo_allocate() will be made private to the IIO core, since all drivers should call either devm_iio_kfifo_buffer_setup() or devm_iio_triggered_buffer_setup() to create a kfifo buffer. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-6-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- Documentation/driver-api/driver-model/devres.rst | 1 - drivers/iio/buffer/kfifo_buf.c | 3 +-- include/linux/iio/kfifo_buf.h | 2 -- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index 562f5722281e..4b15b3e9358b 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -285,7 +285,6 @@ I2C IIO devm_iio_device_alloc() devm_iio_device_register() - devm_iio_kfifo_allocate() devm_iio_kfifo_buffer_setup() devm_iio_triggered_buffer_setup() devm_iio_trigger_alloc() diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c index 6472c9fa1937..c35a625280b1 100644 --- a/drivers/iio/buffer/kfifo_buf.c +++ b/drivers/iio/buffer/kfifo_buf.c @@ -186,7 +186,7 @@ static void devm_iio_kfifo_release(struct device *dev, void *res) * RETURNS: * Pointer to allocated iio_buffer on success, NULL on failure. */ -struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev) +static struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev) { struct iio_buffer **ptr, *r; @@ -204,7 +204,6 @@ struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev) return r; } -EXPORT_SYMBOL(devm_iio_kfifo_allocate); /** * devm_iio_kfifo_buffer_setup - Allocate a kfifo buffer & attach it to an IIO device diff --git a/include/linux/iio/kfifo_buf.h b/include/linux/iio/kfifo_buf.h index 92c411b9ac26..1522896e1daf 100644 --- a/include/linux/iio/kfifo_buf.h +++ b/include/linux/iio/kfifo_buf.h @@ -10,8 +10,6 @@ struct device; struct iio_buffer *iio_kfifo_allocate(void); void iio_kfifo_free(struct iio_buffer *r); -struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev); - int devm_iio_kfifo_buffer_setup(struct device *dev, struct iio_dev *indio_dev, int mode_flags, From a02c09e42b3ed6cefae671e302835f1f04bf474e Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:25 +0200 Subject: [PATCH 024/119] iio: buffer-dma,adi-axi-adc: introduce devm_iio_dmaengine_buffer_setup() This change does a conversion of the devm_iio_dmaengine_buffer_alloc() to devm_iio_dmaengine_buffer_setup(). This will allocate an IIO DMA buffer and attach it to the IIO device, similar to devm_iio_triggered_buffer_setup() (though the underlying code is different, the final logic is the same). Since the only user of the devm_iio_dmaengine_buffer_alloc() was the adi-axi-adc driver, this change does the replacement in a single go in the driver. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-7-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- .../driver-api/driver-model/devres.rst | 1 + drivers/iio/adc/adi-axi-adc.c | 12 ++----- .../buffer/industrialio-buffer-dmaengine.c | 33 +++++++++++++++++-- include/linux/iio/buffer-dmaengine.h | 7 ++-- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index 4b15b3e9358b..5f8c6c303ff2 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -285,6 +285,7 @@ I2C IIO devm_iio_device_alloc() devm_iio_device_register() + devm_iio_dmaengine_buffer_setup() devm_iio_kfifo_buffer_setup() devm_iio_triggered_buffer_setup() devm_iio_trigger_alloc() diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index 9109da2d2e15..2e84623f732e 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -104,7 +104,6 @@ static unsigned int adi_axi_adc_read(struct adi_axi_adc_state *st, static int adi_axi_adc_config_dma_buffer(struct device *dev, struct iio_dev *indio_dev) { - struct iio_buffer *buffer; const char *dma_name; if (!device_property_present(dev, "dmas")) @@ -113,15 +112,8 @@ static int adi_axi_adc_config_dma_buffer(struct device *dev, if (device_property_read_string(dev, "dma-names", &dma_name)) dma_name = "rx"; - buffer = devm_iio_dmaengine_buffer_alloc(indio_dev->dev.parent, - dma_name); - if (IS_ERR(buffer)) - return PTR_ERR(buffer); - - indio_dev->modes |= INDIO_BUFFER_HARDWARE; - iio_device_attach_buffer(indio_dev, buffer); - - return 0; + return devm_iio_dmaengine_buffer_setup(indio_dev->dev.parent, + indio_dev, dma_name); } static int adi_axi_adc_read_raw(struct iio_dev *indio_dev, diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index b0cb9a35f5cd..9981896e1495 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -244,7 +244,7 @@ static void __devm_iio_dmaengine_buffer_free(struct device *dev, void *res) * * The buffer will be automatically de-allocated once the device gets destroyed. */ -struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev, +static struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev, const char *channel) { struct iio_buffer **bufferp, *buffer; @@ -265,7 +265,36 @@ struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev, return buffer; } -EXPORT_SYMBOL_GPL(devm_iio_dmaengine_buffer_alloc); + +/** + * devm_iio_dmaengine_buffer_setup() - Setup a DMA buffer for an IIO device + * @dev: Parent device for the buffer + * @indio_dev: IIO device to which to attach this buffer. + * @channel: DMA channel name, typically "rx". + * + * This allocates a new IIO buffer with devm_iio_dmaengine_buffer_alloc() + * and attaches it to an IIO device with iio_device_attach_buffer(). + * It also appends the INDIO_BUFFER_HARDWARE mode to the supported modes of the + * IIO device. + */ +int devm_iio_dmaengine_buffer_setup(struct device *dev, + struct iio_dev *indio_dev, + const char *channel) +{ + struct iio_buffer *buffer; + + buffer = devm_iio_dmaengine_buffer_alloc(indio_dev->dev.parent, + channel); + if (IS_ERR(buffer)) + return PTR_ERR(buffer); + + indio_dev->modes |= INDIO_BUFFER_HARDWARE; + + iio_device_attach_buffer(indio_dev, buffer); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_iio_dmaengine_buffer_setup); MODULE_AUTHOR("Lars-Peter Clausen "); MODULE_DESCRIPTION("DMA buffer for the IIO framework"); diff --git a/include/linux/iio/buffer-dmaengine.h b/include/linux/iio/buffer-dmaengine.h index 5b502291d6a4..5c355be89814 100644 --- a/include/linux/iio/buffer-dmaengine.h +++ b/include/linux/iio/buffer-dmaengine.h @@ -7,10 +7,11 @@ #ifndef __IIO_DMAENGINE_H__ #define __IIO_DMAENGINE_H__ -struct iio_buffer; +struct iio_dev; struct device; -struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev, - const char *channel); +int devm_iio_dmaengine_buffer_setup(struct device *dev, + struct iio_dev *indio_dev, + const char *channel); #endif From c127161865bc21a3fc6adad07b38ce8276ba2f8e Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:26 +0200 Subject: [PATCH 025/119] docs: ioctl-number.rst: reserve IIO subsystem ioctl() space Currently, only the 'i' 0x90 ioctl() actually exists and is defined in 'include/uapi/linux/iio/events.h'. It's the IIO_GET_EVENT_FD_IOCTL, which is used to retrieve and FD for reading events from an IIO device. We will want to add more ioct() numbers, so with this change the 'i' 0x90-0x9F space is reserved for IIO ioctl() calls. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-8-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- Documentation/userspace-api/ioctl/ioctl-number.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index 599bd4493944..96b2ae9f277f 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -245,6 +245,7 @@ Code Seq# Include File Comments 'i' 00-3F linux/i2o-dev.h conflict! 'i' 0B-1F linux/ipmi.h conflict! 'i' 80-8F linux/i8k.h +'i' 90-9F `linux/iio/*.h` IIO 'j' 00-3F linux/joystick.h 'k' 00-0F linux/spi/spidev.h conflict! 'k' 00-05 video/kyro.h conflict! From 8ebaa3ff1e71d428912423f7f4f8001bfb368cc5 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:27 +0200 Subject: [PATCH 026/119] iio: core: register chardev only if needed We only need a chardev if we need to support buffers and/or events. With this change, a chardev will be created only if an IIO buffer is attached OR an event_interface is configured. Otherwise, no chardev will be created, and the IIO device will get registered with the 'device_add()' call. Quite a lot of IIO devices don't really need a chardev, so this is a minor improvement to the IIO core, as the IIO device will take up (slightly) fewer resources. In order to not create a chardev, we mostly just need to not initialize the indio_dev->dev.devt field. If that is un-initialized, cdev_device_add() behaves like device_add(). This change has a small chance of breaking some userspace ABI, because it removes un-needed chardevs. While these chardevs (that are being removed) have always been unusable, it is likely that some scripts may check their existence (for whatever logic). And we also hope that before opening these chardevs, userspace would have already checked for some pre-conditions to make sure that opening these chardevs makes sense. For the most part, there is also the hope that it would be easier to change userspace code than revert this. But in the case that reverting this is required, it should be easy enough to do it. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-9-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 1b40136f10ac..92efbc245098 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1763,6 +1763,15 @@ static const struct file_operations iio_buffer_fileops = { .release = iio_chrdev_release, }; +static const struct file_operations iio_event_fileops = { + .owner = THIS_MODULE, + .llseek = noop_llseek, + .unlocked_ioctl = iio_ioctl, + .compat_ioctl = compat_ptr_ioctl, + .open = iio_chrdev_open, + .release = iio_chrdev_release, +}; + static int iio_check_unique_scan_index(struct iio_dev *indio_dev) { int i, j; @@ -1790,6 +1799,7 @@ static const struct iio_buffer_setup_ops noop_ring_setup_ops; int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); const char *label; int ret; @@ -1809,9 +1819,6 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) if (ret < 0) return ret; - /* configure elements for the chrdev */ - indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id); - iio_device_register_debugfs(indio_dev); ret = iio_buffer_alloc_sysfs_and_mask(indio_dev); @@ -1840,9 +1847,15 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) indio_dev->setup_ops == NULL) indio_dev->setup_ops = &noop_ring_setup_ops; - cdev_init(&indio_dev->chrdev, &iio_buffer_fileops); + if (indio_dev->buffer) + cdev_init(&indio_dev->chrdev, &iio_buffer_fileops); + else if (iio_dev_opaque->event_interface) + cdev_init(&indio_dev->chrdev, &iio_event_fileops); - indio_dev->chrdev.owner = this_mod; + if (indio_dev->buffer || iio_dev_opaque->event_interface) { + indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id); + indio_dev->chrdev.owner = this_mod; + } ret = cdev_device_add(&indio_dev->chrdev, &indio_dev->dev); if (ret < 0) From e64506bf69b680975c47dcbca2a33fc672b368b5 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:28 +0200 Subject: [PATCH 027/119] iio: core-trigger: make iio_device_register_trigger_consumer() an int return Oddly enough the noop function is an int-return. This one seems to be void. This change converts it to int, because we want to change how groups are registered. With that change this function could error out with -ENOMEM. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-10-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/iio_core_trigger.h | 4 +++- drivers/iio/industrialio-trigger.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/iio/iio_core_trigger.h b/drivers/iio/iio_core_trigger.h index 374816bc3e73..e1a56824e07f 100644 --- a/drivers/iio/iio_core_trigger.h +++ b/drivers/iio/iio_core_trigger.h @@ -9,8 +9,10 @@ /** * iio_device_register_trigger_consumer() - set up an iio_dev to use triggers * @indio_dev: iio_dev associated with the device that will consume the trigger + * + * Return 0 if successful, negative otherwise **/ -void iio_device_register_trigger_consumer(struct iio_dev *indio_dev); +int iio_device_register_trigger_consumer(struct iio_dev *indio_dev); /** * iio_device_unregister_trigger_consumer() - reverse the registration process diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index ea3c9859b258..438d5012e8b8 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -692,10 +692,12 @@ int iio_trigger_validate_own_device(struct iio_trigger *trig, } EXPORT_SYMBOL(iio_trigger_validate_own_device); -void iio_device_register_trigger_consumer(struct iio_dev *indio_dev) +int iio_device_register_trigger_consumer(struct iio_dev *indio_dev) { indio_dev->groups[indio_dev->groupcounter++] = &iio_trigger_consumer_attr_group; + + return 0; } void iio_device_unregister_trigger_consumer(struct iio_dev *indio_dev) From 32f171724e5cbecc80594fb6eced057cfdd6eb6f Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:29 +0200 Subject: [PATCH 028/119] iio: core: rework iio device group creation Up until now, the device groups that an IIO device had were limited to 6. Two of these groups would account for buffer attributes (the buffer/ and scan_elements/ directories). Since we want to add multiple buffers per IIO device, this number may not be enough, when adding a second buffer. So, this change reallocates the groups array whenever an IIO device group is added, via a iio_device_register_sysfs_group() helper. This also means that the groups array should be assigned to 'indio_dev.dev.groups' really late, right before {cdev_}device_add() is called to do the entire setup. And we also must take care to free this array when the sysfs resources are being cleaned up. With this change we can also move the 'groups' & 'groupcounter' fields to the iio_dev_opaque object. Up until now, this didn't make a whole lot of sense (especially since we weren't sure how multibuffer support would look like in the end). But doing it now kills one birds with one stone. An alternative, would be to add a configurable Kconfig symbol CONFIG_IIO_MAX_BUFFERS_PER_DEVICE (or something like that) and compute a static maximum of the groups we can support per IIO device. But that would probably annoy a few people since that would make the system less configurable. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-11-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/iio_core.h | 3 +++ drivers/iio/industrialio-buffer.c | 12 ++++++++++-- drivers/iio/industrialio-core.c | 30 +++++++++++++++++++++++++++--- drivers/iio/industrialio-event.c | 5 ++++- drivers/iio/industrialio-trigger.c | 6 ++---- include/linux/iio/iio-opaque.h | 4 ++++ include/linux/iio/iio.h | 5 ----- 7 files changed, 50 insertions(+), 15 deletions(-) diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index fced02cadcc3..7d5b179c1fe7 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -46,6 +46,9 @@ int __iio_add_chan_devattr(const char *postfix, struct list_head *attr_list); void iio_free_chan_devattr_list(struct list_head *attr_list); +int iio_device_register_sysfs_group(struct iio_dev *indio_dev, + const struct attribute_group *group); + ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals); /* Event interface flags */ diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 2f7426a2f47c..cc846988fdb9 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1287,7 +1287,9 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, buffer->buffer_group.name = "buffer"; buffer->buffer_group.attrs = attr; - indio_dev->groups[indio_dev->groupcounter++] = &buffer->buffer_group; + ret = iio_device_register_sysfs_group(indio_dev, &buffer->buffer_group); + if (ret) + goto error_free_buffer_attrs; attrcount = 0; INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list); @@ -1330,14 +1332,20 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l) buffer->scan_el_group.attrs[attrn++] = &p->dev_attr.attr; - indio_dev->groups[indio_dev->groupcounter++] = &buffer->scan_el_group; + + ret = iio_device_register_sysfs_group(indio_dev, &buffer->scan_el_group); + if (ret) + goto error_free_scan_el_attrs; return 0; +error_free_scan_el_attrs: + kfree(buffer->scan_el_group.attrs); error_free_scan_mask: bitmap_free(buffer->scan_mask); error_cleanup_dynamic: iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list); +error_free_buffer_attrs: kfree(buffer->buffer_group.attrs); return ret; diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 92efbc245098..924f3a167125 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1454,6 +1454,25 @@ static ssize_t iio_store_timestamp_clock(struct device *dev, return len; } +int iio_device_register_sysfs_group(struct iio_dev *indio_dev, + const struct attribute_group *group) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + const struct attribute_group **new, **old = iio_dev_opaque->groups; + unsigned int cnt = iio_dev_opaque->groupcounter; + + new = krealloc(old, sizeof(*new) * (cnt + 2), GFP_KERNEL); + if (!new) + return -ENOMEM; + + new[iio_dev_opaque->groupcounter++] = group; + new[iio_dev_opaque->groupcounter] = NULL; + + iio_dev_opaque->groups = new; + + return 0; +} + static DEVICE_ATTR(current_timestamp_clock, S_IRUGO | S_IWUSR, iio_show_timestamp_clock, iio_store_timestamp_clock); @@ -1527,8 +1546,10 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev) if (clk) iio_dev_opaque->chan_attr_group.attrs[attrn++] = clk; - indio_dev->groups[indio_dev->groupcounter++] = - &iio_dev_opaque->chan_attr_group; + ret = iio_device_register_sysfs_group(indio_dev, + &iio_dev_opaque->chan_attr_group); + if (ret) + goto error_clear_attrs; return 0; @@ -1545,6 +1566,7 @@ static void iio_device_unregister_sysfs(struct iio_dev *indio_dev) iio_free_chan_devattr_list(&iio_dev_opaque->channel_attr_list); kfree(iio_dev_opaque->chan_attr_group.attrs); iio_dev_opaque->chan_attr_group.attrs = NULL; + kfree(iio_dev_opaque->groups); } static void iio_dev_release(struct device *device) @@ -1594,7 +1616,6 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv) ALIGN(sizeof(struct iio_dev_opaque), IIO_ALIGN); dev->dev.parent = parent; - dev->dev.groups = dev->groups; dev->dev.type = &iio_device_type; dev->dev.bus = &iio_bus_type; device_initialize(&dev->dev); @@ -1857,6 +1878,9 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) indio_dev->chrdev.owner = this_mod; } + /* assign device groups now; they should be all registered now */ + indio_dev->dev.groups = iio_dev_opaque->groups; + ret = cdev_device_add(&indio_dev->chrdev, &indio_dev->dev); if (ret < 0) goto error_unreg_eventset; diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index 7e532117ac55..ea8947cc21e4 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -544,7 +544,10 @@ int iio_device_register_eventset(struct iio_dev *indio_dev) /* Add all elements from the list. */ list_for_each_entry(p, &ev_int->dev_attr_list, l) ev_int->group.attrs[attrn++] = &p->dev_attr.attr; - indio_dev->groups[indio_dev->groupcounter++] = &ev_int->group; + + ret = iio_device_register_sysfs_group(indio_dev, &ev_int->group); + if (ret) + goto error_free_setup_event_lines; ev_int->ioctl_handler.ioctl = iio_event_ioctl; iio_device_ioctl_handler_register(&iio_dev_opaque->indio_dev, diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index 438d5012e8b8..a035d5c2a445 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -694,10 +694,8 @@ EXPORT_SYMBOL(iio_trigger_validate_own_device); int iio_device_register_trigger_consumer(struct iio_dev *indio_dev) { - indio_dev->groups[indio_dev->groupcounter++] = - &iio_trigger_consumer_attr_group; - - return 0; + return iio_device_register_sysfs_group(indio_dev, + &iio_trigger_consumer_attr_group); } void iio_device_unregister_trigger_consumer(struct iio_dev *indio_dev) diff --git a/include/linux/iio/iio-opaque.h b/include/linux/iio/iio-opaque.h index 07c5a8e52ca8..8ba13a5c7af6 100644 --- a/include/linux/iio/iio-opaque.h +++ b/include/linux/iio/iio-opaque.h @@ -12,6 +12,8 @@ * attributes * @chan_attr_group: group for all attrs in base directory * @ioctl_handlers: ioctl handlers registered with the core handler + * @groups: attribute groups + * @groupcounter: index of next attribute group * @debugfs_dentry: device specific debugfs dentry * @cached_reg_addr: cached register address for debugfs reads * @read_buf: read buffer to be used for the initial reg read @@ -24,6 +26,8 @@ struct iio_dev_opaque { struct list_head channel_attr_list; struct attribute_group chan_attr_group; struct list_head ioctl_handlers; + const struct attribute_group **groups; + int groupcounter; #if defined(CONFIG_DEBUG_FS) struct dentry *debugfs_dentry; unsigned cached_reg_addr; diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index e4a9822e6495..f8585d01fc76 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -518,8 +518,6 @@ struct iio_buffer_setup_ops { * @setup_ops: [DRIVER] callbacks to call before and after buffer * enable/disable * @chrdev: [INTERN] associated character device - * @groups: [INTERN] attribute groups - * @groupcounter: [INTERN] index of next attribute group * @flags: [INTERN] file ops related flags including busy flag. * @priv: [DRIVER] reference to driver's private information * **MUST** be accessed **ONLY** via iio_priv() helper @@ -556,9 +554,6 @@ struct iio_dev { struct mutex info_exist_lock; const struct iio_buffer_setup_ops *setup_ops; struct cdev chrdev; -#define IIO_MAX_GROUPS 6 - const struct attribute_group *groups[IIO_MAX_GROUPS + 1]; - int groupcounter; unsigned long flags; void *priv; From e2b4d7aca9db5c1f0789e5a8e0fd1f4953d0be9e Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:30 +0200 Subject: [PATCH 029/119] iio: buffer: group attr count and attr alloc If we want to merge the attributes of the buffer/ and scan_elements/ directories, we'll need to count all attributes first, then (depending on the attribute group) either allocate 2 attribute groups, or a single one. Historically an IIO buffer was described by 2 subdirectories under /sys/bus/iio/iio:devicesX (i.e. buffer/ and scan_elements/); these subdirs were actually 2 separate attribute groups on the iio_buffer object. Moving forward, if we want to allow more than one buffer per IIO device, keeping 2 subdirectories for each IIO buffer is a bit cumbersome (especially for userpace ABI). So, we will merge the attributes of these 2 subdirs under a /sys/bus/iio/iio:devicesX/bufferY subdirectory. To do this, we need to count all attributes first, and then distribute them based on which buffer this is. For the first buffer, we'll need to also allocate the legacy 2 attribute groups (for buffer/ and scan_elements/), and also a /sys/bus/iio/iio:devicesX/buffer0 attribute group. For buffer1 and above, just a single attribute group will be allocated (the merged one). Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-12-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 71 ++++++++++++++++--------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index cc846988fdb9..01ab3dd0726a 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1257,41 +1257,16 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, { struct iio_dev_attr *p; struct attribute **attr; - int ret, i, attrn, attrcount; + int ret, i, attrn, scan_el_attrcount, buffer_attrcount; const struct iio_chan_spec *channels; - attrcount = 0; + buffer_attrcount = 0; if (buffer->attrs) { - while (buffer->attrs[attrcount] != NULL) - attrcount++; + while (buffer->attrs[buffer_attrcount] != NULL) + buffer_attrcount++; } - attr = kcalloc(attrcount + ARRAY_SIZE(iio_buffer_attrs) + 1, - sizeof(struct attribute *), GFP_KERNEL); - if (!attr) - return -ENOMEM; - - memcpy(attr, iio_buffer_attrs, sizeof(iio_buffer_attrs)); - if (!buffer->access->set_length) - attr[0] = &dev_attr_length_ro.attr; - - if (buffer->access->flags & INDIO_BUFFER_FLAG_FIXED_WATERMARK) - attr[2] = &dev_attr_watermark_ro.attr; - - if (buffer->attrs) - memcpy(&attr[ARRAY_SIZE(iio_buffer_attrs)], buffer->attrs, - sizeof(struct attribute *) * attrcount); - - attr[attrcount + ARRAY_SIZE(iio_buffer_attrs)] = NULL; - - buffer->buffer_group.name = "buffer"; - buffer->buffer_group.attrs = attr; - - ret = iio_device_register_sysfs_group(indio_dev, &buffer->buffer_group); - if (ret) - goto error_free_buffer_attrs; - - attrcount = 0; + scan_el_attrcount = 0; INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list); channels = indio_dev->channels; if (channels) { @@ -1304,7 +1279,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, &channels[i]); if (ret < 0) goto error_cleanup_dynamic; - attrcount += ret; + scan_el_attrcount += ret; if (channels[i].type == IIO_TIMESTAMP) indio_dev->scan_index_timestamp = channels[i].scan_index; @@ -1319,9 +1294,37 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, } } + attr = kcalloc(buffer_attrcount + ARRAY_SIZE(iio_buffer_attrs) + 1, + sizeof(*attr), GFP_KERNEL); + if (!attr) { + ret = -ENOMEM; + goto error_free_scan_mask; + } + + memcpy(attr, iio_buffer_attrs, sizeof(iio_buffer_attrs)); + if (!buffer->access->set_length) + attr[0] = &dev_attr_length_ro.attr; + + if (buffer->access->flags & INDIO_BUFFER_FLAG_FIXED_WATERMARK) + attr[2] = &dev_attr_watermark_ro.attr; + + if (buffer->attrs) + memcpy(&attr[ARRAY_SIZE(iio_buffer_attrs)], buffer->attrs, + sizeof(struct attribute *) * buffer_attrcount); + + buffer_attrcount += ARRAY_SIZE(iio_buffer_attrs); + attr[buffer_attrcount] = NULL; + + buffer->buffer_group.name = "buffer"; + buffer->buffer_group.attrs = attr; + + ret = iio_device_register_sysfs_group(indio_dev, &buffer->buffer_group); + if (ret) + goto error_free_buffer_attrs; + buffer->scan_el_group.name = iio_scan_elements_group_name; - buffer->scan_el_group.attrs = kcalloc(attrcount + 1, + buffer->scan_el_group.attrs = kcalloc(scan_el_attrcount + 1, sizeof(buffer->scan_el_group.attrs[0]), GFP_KERNEL); if (buffer->scan_el_group.attrs == NULL) { @@ -1341,12 +1344,12 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, error_free_scan_el_attrs: kfree(buffer->scan_el_group.attrs); +error_free_buffer_attrs: + kfree(buffer->buffer_group.attrs); error_free_scan_mask: bitmap_free(buffer->scan_mask); error_cleanup_dynamic: iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list); -error_free_buffer_attrs: - kfree(buffer->buffer_group.attrs); return ret; } From d9a625744ed0e452f5c495cd8c51eed4b6623a4c Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:31 +0200 Subject: [PATCH 030/119] iio: core: merge buffer/ & scan_elements/ attributes With this change, we create a new directory for the IIO device called buffer0, under which both the old buffer/ and scan_elements/ are stored. This is done to simplify the addition of multiple IIO buffers per IIO device. Otherwise we would need to add a bufferX/ and scan_elementsX/ directory for each IIO buffer. With the current way of storing attribute groups, we can't have directories stored under each other (i.e. scan_elements/ under buffer/), so the best approach moving forward is to merge their attributes. The old/legacy buffer/ & scan_elements/ groups are not stored on the opaque IIO device object. This way the IIO buffer can have just a single attribute_group object, saving a bit of memory when adding multiple IIO buffers. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-13-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 115 +++++++++++++++++++++++------- include/linux/iio/buffer_impl.h | 9 +-- include/linux/iio/iio-opaque.h | 4 ++ 3 files changed, 95 insertions(+), 33 deletions(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 01ab3dd0726a..76f0f6a61ebc 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1175,8 +1175,6 @@ done: return (ret < 0) ? ret : len; } -static const char * const iio_scan_elements_group_name = "scan_elements"; - static ssize_t iio_buffer_show_watermark(struct device *dev, struct device_attribute *attr, char *buf) @@ -1252,8 +1250,68 @@ static struct attribute *iio_buffer_attrs[] = { &dev_attr_data_available.attr, }; +static int iio_buffer_register_legacy_sysfs_groups(struct iio_dev *indio_dev, + struct attribute **buffer_attrs, + int buffer_attrcount, + int scan_el_attrcount) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + struct attribute_group *group; + struct attribute **attrs; + int ret; + + attrs = kcalloc(buffer_attrcount + 1, sizeof(*attrs), GFP_KERNEL); + if (!attrs) + return -ENOMEM; + + memcpy(attrs, buffer_attrs, buffer_attrcount * sizeof(*attrs)); + + group = &iio_dev_opaque->legacy_buffer_group; + group->attrs = attrs; + group->name = "buffer"; + + ret = iio_device_register_sysfs_group(indio_dev, group); + if (ret) + goto error_free_buffer_attrs; + + attrs = kcalloc(scan_el_attrcount + 1, sizeof(*attrs), GFP_KERNEL); + if (!attrs) { + ret = -ENOMEM; + goto error_free_buffer_attrs; + } + + memcpy(attrs, &buffer_attrs[buffer_attrcount], + scan_el_attrcount * sizeof(*attrs)); + + group = &iio_dev_opaque->legacy_scan_el_group; + group->attrs = attrs; + group->name = "scan_elements"; + + ret = iio_device_register_sysfs_group(indio_dev, group); + if (ret) + goto error_free_scan_el_attrs; + + return 0; + +error_free_buffer_attrs: + kfree(iio_dev_opaque->legacy_buffer_group.attrs); +error_free_scan_el_attrs: + kfree(iio_dev_opaque->legacy_scan_el_group.attrs); + + return ret; +} + +static void iio_buffer_unregister_legacy_sysfs_groups(struct iio_dev *indio_dev) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + + kfree(iio_dev_opaque->legacy_buffer_group.attrs); + kfree(iio_dev_opaque->legacy_scan_el_group.attrs); +} + static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, - struct iio_dev *indio_dev) + struct iio_dev *indio_dev, + int index) { struct iio_dev_attr *p; struct attribute **attr; @@ -1294,8 +1352,8 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, } } - attr = kcalloc(buffer_attrcount + ARRAY_SIZE(iio_buffer_attrs) + 1, - sizeof(*attr), GFP_KERNEL); + attrn = buffer_attrcount + scan_el_attrcount + ARRAY_SIZE(iio_buffer_attrs); + attr = kcalloc(attrn + 1, sizeof(* attr), GFP_KERNEL); if (!attr) { ret = -ENOMEM; goto error_free_scan_mask; @@ -1313,37 +1371,38 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, sizeof(struct attribute *) * buffer_attrcount); buffer_attrcount += ARRAY_SIZE(iio_buffer_attrs); - attr[buffer_attrcount] = NULL; - buffer->buffer_group.name = "buffer"; + attrn = buffer_attrcount; + + list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l) + attr[attrn++] = &p->dev_attr.attr; + + buffer->buffer_group.name = kasprintf(GFP_KERNEL, "buffer%d", index); + if (!buffer->buffer_group.name) { + ret = -ENOMEM; + goto error_free_buffer_attrs; + } + buffer->buffer_group.attrs = attr; ret = iio_device_register_sysfs_group(indio_dev, &buffer->buffer_group); if (ret) - goto error_free_buffer_attrs; + goto error_free_buffer_attr_group_name; - buffer->scan_el_group.name = iio_scan_elements_group_name; + /* we only need to register the legacy groups for the first buffer */ + if (index > 0) + return 0; - buffer->scan_el_group.attrs = kcalloc(scan_el_attrcount + 1, - sizeof(buffer->scan_el_group.attrs[0]), - GFP_KERNEL); - if (buffer->scan_el_group.attrs == NULL) { - ret = -ENOMEM; - goto error_free_scan_mask; - } - attrn = 0; - - list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l) - buffer->scan_el_group.attrs[attrn++] = &p->dev_attr.attr; - - ret = iio_device_register_sysfs_group(indio_dev, &buffer->scan_el_group); + ret = iio_buffer_register_legacy_sysfs_groups(indio_dev, attr, + buffer_attrcount, + scan_el_attrcount); if (ret) - goto error_free_scan_el_attrs; + goto error_free_buffer_attr_group_name; return 0; -error_free_scan_el_attrs: - kfree(buffer->scan_el_group.attrs); +error_free_buffer_attr_group_name: + kfree(buffer->buffer_group.name); error_free_buffer_attrs: kfree(buffer->buffer_group.attrs); error_free_scan_mask: @@ -1372,14 +1431,14 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) if (!buffer) return 0; - return __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev); + return __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev, 0); } static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer) { bitmap_free(buffer->scan_mask); + kfree(buffer->buffer_group.name); kfree(buffer->buffer_group.attrs); - kfree(buffer->scan_el_group.attrs); iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list); } @@ -1390,6 +1449,8 @@ void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev) if (!buffer) return; + iio_buffer_unregister_legacy_sysfs_groups(indio_dev); + __iio_buffer_free_sysfs_and_mask(buffer); } diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h index a63dc07b7350..3e555e58475b 100644 --- a/include/linux/iio/buffer_impl.h +++ b/include/linux/iio/buffer_impl.h @@ -100,14 +100,11 @@ struct iio_buffer { /* @scan_el_dev_attr_list: List of scan element related attributes. */ struct list_head scan_el_dev_attr_list; - /* @buffer_group: Attributes of the buffer group. */ - struct attribute_group buffer_group; - /* - * @scan_el_group: Attribute group for those attributes not - * created from the iio_chan_info array. + * @buffer_group: Attributes of the new buffer group. + * Includes scan elements attributes. */ - struct attribute_group scan_el_group; + struct attribute_group buffer_group; /* @attrs: Standard attributes of the buffer. */ const struct attribute **attrs; diff --git a/include/linux/iio/iio-opaque.h b/include/linux/iio/iio-opaque.h index 8ba13a5c7af6..1a9310b0145f 100644 --- a/include/linux/iio/iio-opaque.h +++ b/include/linux/iio/iio-opaque.h @@ -14,6 +14,8 @@ * @ioctl_handlers: ioctl handlers registered with the core handler * @groups: attribute groups * @groupcounter: index of next attribute group + * @legacy_scan_el_group: attribute group for legacy scan elements attribute group + * @legacy_buffer_group: attribute group for legacy buffer attributes group * @debugfs_dentry: device specific debugfs dentry * @cached_reg_addr: cached register address for debugfs reads * @read_buf: read buffer to be used for the initial reg read @@ -28,6 +30,8 @@ struct iio_dev_opaque { struct list_head ioctl_handlers; const struct attribute_group **groups; int groupcounter; + struct attribute_group legacy_scan_el_group; + struct attribute_group legacy_buffer_group; #if defined(CONFIG_DEBUG_FS) struct dentry *debugfs_dentry; unsigned cached_reg_addr; From 3e3d11b2e43b9a967d98261250c19636b893b7ed Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:32 +0200 Subject: [PATCH 031/119] iio: add reference to iio buffer on iio_dev_attr This change adds a reference to a 'struct iio_buffer' object on the iio_dev_attr object. This way, we can use the created iio_dev_attr objects on per-buffer basis (since they're allocated anyway). A minor downside of this change is that the number of parameters on __iio_add_chan_devattr() grows by 1. This looks like it could do with a bit of a re-think. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-14-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/iio_core.h | 2 ++ drivers/iio/industrialio-buffer.c | 4 ++++ drivers/iio/industrialio-core.c | 6 ++++++ drivers/iio/industrialio-event.c | 1 + include/linux/iio/sysfs.h | 3 +++ 5 files changed, 16 insertions(+) diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index 7d5b179c1fe7..731f5170d5b9 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -12,6 +12,7 @@ #include #include +struct iio_buffer; struct iio_chan_spec; struct iio_dev; @@ -43,6 +44,7 @@ int __iio_add_chan_devattr(const char *postfix, u64 mask, enum iio_shared_by shared_by, struct device *dev, + struct iio_buffer *buffer, struct list_head *attr_list); void iio_free_chan_devattr_list(struct list_head *attr_list); diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 76f0f6a61ebc..e6edec3bcb73 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -447,6 +447,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, 0, IIO_SEPARATE, &indio_dev->dev, + buffer, &buffer->scan_el_dev_attr_list); if (ret) return ret; @@ -458,6 +459,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, 0, 0, &indio_dev->dev, + buffer, &buffer->scan_el_dev_attr_list); if (ret) return ret; @@ -470,6 +472,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, chan->scan_index, 0, &indio_dev->dev, + buffer, &buffer->scan_el_dev_attr_list); else ret = __iio_add_chan_devattr("en", @@ -479,6 +482,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, chan->scan_index, 0, &indio_dev->dev, + buffer, &buffer->scan_el_dev_attr_list); if (ret) return ret; diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 924f3a167125..f4e5a08bbcd5 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1116,6 +1116,7 @@ int __iio_add_chan_devattr(const char *postfix, u64 mask, enum iio_shared_by shared_by, struct device *dev, + struct iio_buffer *buffer, struct list_head *attr_list) { int ret; @@ -1131,6 +1132,7 @@ int __iio_add_chan_devattr(const char *postfix, goto error_iio_dev_attr_free; iio_attr->c = chan; iio_attr->address = mask; + iio_attr->buffer = buffer; list_for_each_entry(t, attr_list, l) if (strcmp(t->dev_attr.attr.name, iio_attr->dev_attr.attr.name) == 0) { @@ -1167,6 +1169,7 @@ static int iio_device_add_channel_label(struct iio_dev *indio_dev, 0, IIO_SEPARATE, &indio_dev->dev, + NULL, &iio_dev_opaque->channel_attr_list); if (ret < 0) return ret; @@ -1192,6 +1195,7 @@ static int iio_device_add_info_mask_type(struct iio_dev *indio_dev, i, shared_by, &indio_dev->dev, + NULL, &iio_dev_opaque->channel_attr_list); if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE)) continue; @@ -1228,6 +1232,7 @@ static int iio_device_add_info_mask_type_avail(struct iio_dev *indio_dev, i, shared_by, &indio_dev->dev, + NULL, &iio_dev_opaque->channel_attr_list); kfree(avail_postfix); if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE)) @@ -1324,6 +1329,7 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, i, ext_info->shared, &indio_dev->dev, + NULL, &iio_dev_opaque->channel_attr_list); i++; if (ret == -EBUSY && ext_info->shared) diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index ea8947cc21e4..a30e289fc362 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -385,6 +385,7 @@ static int iio_device_add_event(struct iio_dev *indio_dev, ret = __iio_add_chan_devattr(postfix, chan, show, store, (i << 16) | spec_index, shared_by, &indio_dev->dev, + NULL, &iio_dev_opaque->event_interface->dev_attr_list); kfree(postfix); diff --git a/include/linux/iio/sysfs.h b/include/linux/iio/sysfs.h index b532c875bc24..e51fba66de4b 100644 --- a/include/linux/iio/sysfs.h +++ b/include/linux/iio/sysfs.h @@ -9,6 +9,7 @@ #ifndef _INDUSTRIAL_IO_SYSFS_H_ #define _INDUSTRIAL_IO_SYSFS_H_ +struct iio_buffer; struct iio_chan_spec; /** @@ -17,12 +18,14 @@ struct iio_chan_spec; * @address: associated register address * @l: list head for maintaining list of dynamically created attrs * @c: specification for the underlying channel + * @buffer: the IIO buffer to which this attribute belongs to (if any) */ struct iio_dev_attr { struct device_attribute dev_attr; u64 address; struct list_head l; struct iio_chan_spec const *c; + struct iio_buffer *buffer; }; #define to_iio_dev_attr(_dev_attr) \ From 15097c7a1adc0554ce8eb6f5fd6758d063bfea44 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:33 +0200 Subject: [PATCH 032/119] iio: buffer: wrap all buffer attributes into iio_dev_attr This change wraps all buffer attributes into iio_dev_attr objects, and assigns a reference to the IIO buffer they belong to. With the addition of multiple IIO buffers per one IIO device, we need a way to know which IIO buffer is being enabled/disabled/controlled. We know that all buffer attributes are device_attributes. So we can wrap them with a iio_dev_attr types. In the iio_dev_attr type, we can also hold a reference to an IIO buffer. So, we end up being able to allocate wrapped attributes for all buffer attributes (even the one from other drivers). The neat part with this mechanism, is that we don't need to add any extra cleanup, because these attributes are being added to a dynamic list that will get cleaned up via iio_free_chan_devattr_list(). With this change, the 'buffer->scan_el_dev_attr_list' list is being renamed to 'buffer->buffer_attr_list', effectively merging (or finalizing the merge) of the buffer/ & scan_elements/ attributes internally. Accessing these new buffer attributes can now be done via 'to_iio_dev_attr(attr)->buffer' inside the show/store handlers. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-15-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 77 ++++++++++++++++++++----------- drivers/iio/industrialio-core.c | 2 +- include/linux/iio/buffer_impl.h | 4 +- 3 files changed, 54 insertions(+), 29 deletions(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index e6edec3bcb73..ea91f9578dee 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -253,8 +253,7 @@ static ssize_t iio_scan_el_show(struct device *dev, char *buf) { int ret; - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; /* Ensure ret is 0 or 1. */ ret = !!test_bit(to_iio_dev_attr(attr)->address, @@ -367,8 +366,8 @@ static ssize_t iio_scan_el_store(struct device *dev, int ret; bool state; struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + struct iio_buffer *buffer = this_attr->buffer; ret = strtobool(buf, &state); if (ret < 0) @@ -402,8 +401,7 @@ static ssize_t iio_scan_el_ts_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; return sprintf(buf, "%d\n", buffer->scan_timestamp); } @@ -415,7 +413,7 @@ static ssize_t iio_scan_el_ts_store(struct device *dev, { int ret; struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; bool state; ret = strtobool(buf, &state); @@ -448,7 +446,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, IIO_SEPARATE, &indio_dev->dev, buffer, - &buffer->scan_el_dev_attr_list); + &buffer->buffer_attr_list); if (ret) return ret; attrcount++; @@ -460,7 +458,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, 0, &indio_dev->dev, buffer, - &buffer->scan_el_dev_attr_list); + &buffer->buffer_attr_list); if (ret) return ret; attrcount++; @@ -473,7 +471,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, 0, &indio_dev->dev, buffer, - &buffer->scan_el_dev_attr_list); + &buffer->buffer_attr_list); else ret = __iio_add_chan_devattr("en", chan, @@ -483,7 +481,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, 0, &indio_dev->dev, buffer, - &buffer->scan_el_dev_attr_list); + &buffer->buffer_attr_list); if (ret) return ret; attrcount++; @@ -495,8 +493,7 @@ static ssize_t iio_buffer_read_length(struct device *dev, struct device_attribute *attr, char *buf) { - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; return sprintf(buf, "%d\n", buffer->length); } @@ -506,7 +503,7 @@ static ssize_t iio_buffer_write_length(struct device *dev, const char *buf, size_t len) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; unsigned int val; int ret; @@ -538,8 +535,7 @@ static ssize_t iio_buffer_show_enable(struct device *dev, struct device_attribute *attr, char *buf) { - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; return sprintf(buf, "%d\n", iio_buffer_is_active(buffer)); } @@ -1154,7 +1150,7 @@ static ssize_t iio_buffer_store_enable(struct device *dev, int ret; bool requested_state; struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; bool inlist; ret = strtobool(buf, &requested_state); @@ -1183,8 +1179,7 @@ static ssize_t iio_buffer_show_watermark(struct device *dev, struct device_attribute *attr, char *buf) { - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; return sprintf(buf, "%u\n", buffer->watermark); } @@ -1195,7 +1190,7 @@ static ssize_t iio_buffer_store_watermark(struct device *dev, size_t len) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; unsigned int val; int ret; @@ -1228,8 +1223,7 @@ static ssize_t iio_dma_show_data_available(struct device *dev, struct device_attribute *attr, char *buf) { - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; return sprintf(buf, "%zu\n", iio_buffer_data_available(buffer)); } @@ -1254,6 +1248,27 @@ static struct attribute *iio_buffer_attrs[] = { &dev_attr_data_available.attr, }; +#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr) + +static struct attribute *iio_buffer_wrap_attr(struct iio_buffer *buffer, + struct attribute *attr) +{ + struct device_attribute *dattr = to_dev_attr(attr); + struct iio_dev_attr *iio_attr; + + iio_attr = kzalloc(sizeof(*iio_attr), GFP_KERNEL); + if (!iio_attr) + return NULL; + + iio_attr->buffer = buffer; + memcpy(&iio_attr->dev_attr, dattr, sizeof(iio_attr->dev_attr)); + iio_attr->dev_attr.attr.name = kstrdup_const(attr->name, GFP_KERNEL); + + list_add(&iio_attr->l, &buffer->buffer_attr_list); + + return &iio_attr->dev_attr.attr; +} + static int iio_buffer_register_legacy_sysfs_groups(struct iio_dev *indio_dev, struct attribute **buffer_attrs, int buffer_attrcount, @@ -1329,7 +1344,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, } scan_el_attrcount = 0; - INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list); + INIT_LIST_HEAD(&buffer->buffer_attr_list); channels = indio_dev->channels; if (channels) { /* new magic */ @@ -1376,9 +1391,19 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, buffer_attrcount += ARRAY_SIZE(iio_buffer_attrs); - attrn = buffer_attrcount; + for (i = 0; i < buffer_attrcount; i++) { + struct attribute *wrapped; - list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l) + wrapped = iio_buffer_wrap_attr(buffer, attr[i]); + if (!wrapped) { + ret = -ENOMEM; + goto error_free_scan_mask; + } + attr[i] = wrapped; + } + + attrn = 0; + list_for_each_entry(p, &buffer->buffer_attr_list, l) attr[attrn++] = &p->dev_attr.attr; buffer->buffer_group.name = kasprintf(GFP_KERNEL, "buffer%d", index); @@ -1412,7 +1437,7 @@ error_free_buffer_attrs: error_free_scan_mask: bitmap_free(buffer->scan_mask); error_cleanup_dynamic: - iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list); + iio_free_chan_devattr_list(&buffer->buffer_attr_list); return ret; } @@ -1443,7 +1468,7 @@ static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer) bitmap_free(buffer->scan_mask); kfree(buffer->buffer_group.name); kfree(buffer->buffer_group.attrs); - iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list); + iio_free_chan_devattr_list(&buffer->buffer_attr_list); } void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index f4e5a08bbcd5..b8b149ccecef 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1357,7 +1357,7 @@ void iio_free_chan_devattr_list(struct list_head *attr_list) struct iio_dev_attr *p, *n; list_for_each_entry_safe(p, n, attr_list, l) { - kfree(p->dev_attr.attr.name); + kfree_const(p->dev_attr.attr.name); list_del(&p->l); kfree(p); } diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h index 3e555e58475b..41044320e581 100644 --- a/include/linux/iio/buffer_impl.h +++ b/include/linux/iio/buffer_impl.h @@ -97,8 +97,8 @@ struct iio_buffer { /* @scan_timestamp: Does the scan mode include a timestamp. */ bool scan_timestamp; - /* @scan_el_dev_attr_list: List of scan element related attributes. */ - struct list_head scan_el_dev_attr_list; + /* @buffer_attr_list: List of buffer attributes. */ + struct list_head buffer_attr_list; /* * @buffer_group: Attributes of the new buffer group. From 4991f3ea2aec5dd8c5abdb40a360258dd71eddf4 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:34 +0200 Subject: [PATCH 033/119] iio: buffer: dmaengine: obtain buffer object from attribute The reference to the IIO buffer object is stored on the attribute object. So we need to unwind it to obtain it. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-16-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/industrialio-buffer-dmaengine.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index 9981896e1495..a64b222289be 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -132,9 +132,9 @@ static const struct iio_dma_buffer_ops iio_dmaengine_default_ops = { static ssize_t iio_dmaengine_buffer_get_length_align(struct device *dev, struct device_attribute *attr, char *buf) { - struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; struct dmaengine_buffer *dmaengine_buffer = - iio_buffer_to_dmaengine_buffer(indio_dev->buffer); + iio_buffer_to_dmaengine_buffer(buffer); return sprintf(buf, "%zu\n", dmaengine_buffer->align); } From be24dcb113675f72c2b95c96a55d8aae4964cdc6 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:35 +0200 Subject: [PATCH 034/119] iio: core: wrap iio device & buffer into struct for character devices In order to keep backwards compatibility with the current chardev mechanism, and in order to add support for multiple buffers per IIO device, we need to pass both the IIO device & IIO buffer to the chardev. This is particularly needed for the iio_buffer_read_outer() function, where we need to pass another buffer object than 'indio_dev->buffer'. Since we'll also open some chardevs via anon inodes, we can pass extra buffers in that function by assigning another object to the iio_dev_buffer_pair object. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-17-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/iio_core.h | 5 +++++ drivers/iio/industrialio-buffer.c | 10 ++++++---- drivers/iio/industrialio-core.c | 18 ++++++++++++++++-- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index 731f5170d5b9..87868fff7d37 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -18,6 +18,11 @@ struct iio_dev; extern struct device_type iio_device_type; +struct iio_dev_buffer_pair { + struct iio_dev *indio_dev; + struct iio_buffer *buffer; +}; + #define IIO_IOCTL_UNHANDLED 1 struct iio_ioctl_handler { struct list_head entry; diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index ea91f9578dee..232a60fe57d2 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -104,8 +104,9 @@ static bool iio_buffer_ready(struct iio_dev *indio_dev, struct iio_buffer *buf, ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf, size_t n, loff_t *f_ps) { - struct iio_dev *indio_dev = filp->private_data; - struct iio_buffer *rb = indio_dev->buffer; + struct iio_dev_buffer_pair *ib = filp->private_data; + struct iio_buffer *rb = ib->buffer; + struct iio_dev *indio_dev = ib->indio_dev; DEFINE_WAIT_FUNC(wait, woken_wake_function); size_t datum_size; size_t to_wait; @@ -170,8 +171,9 @@ ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf, __poll_t iio_buffer_poll(struct file *filp, struct poll_table_struct *wait) { - struct iio_dev *indio_dev = filp->private_data; - struct iio_buffer *rb = indio_dev->buffer; + struct iio_dev_buffer_pair *ib = filp->private_data; + struct iio_buffer *rb = ib->buffer; + struct iio_dev *indio_dev = ib->indio_dev; if (!indio_dev->info || rb == NULL) return 0; diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index b8b149ccecef..b96acfe13e92 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1705,13 +1705,24 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp) { struct iio_dev *indio_dev = container_of(inode->i_cdev, struct iio_dev, chrdev); + struct iio_dev_buffer_pair *ib; if (test_and_set_bit(IIO_BUSY_BIT_POS, &indio_dev->flags)) return -EBUSY; iio_device_get(indio_dev); - filp->private_data = indio_dev; + ib = kmalloc(sizeof(*ib), GFP_KERNEL); + if (!ib) { + iio_device_put(indio_dev); + clear_bit(IIO_BUSY_BIT_POS, &indio_dev->flags); + return -ENOMEM; + } + + ib->indio_dev = indio_dev; + ib->buffer = indio_dev->buffer; + + filp->private_data = ib; return 0; } @@ -1725,8 +1736,10 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp) */ static int iio_chrdev_release(struct inode *inode, struct file *filp) { + struct iio_dev_buffer_pair *ib = filp->private_data; struct iio_dev *indio_dev = container_of(inode->i_cdev, struct iio_dev, chrdev); + kfree(ib); clear_bit(IIO_BUSY_BIT_POS, &indio_dev->flags); iio_device_put(indio_dev); @@ -1748,7 +1761,8 @@ void iio_device_ioctl_handler_unregister(struct iio_ioctl_handler *h) static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - struct iio_dev *indio_dev = filp->private_data; + struct iio_dev_buffer_pair *ib = filp->private_data; + struct iio_dev *indio_dev = ib->indio_dev; struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); struct iio_ioctl_handler *h; int ret = -ENODEV; From 0224af85a77be1ebdf5256f0d6738b0f7250f41d Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:36 +0200 Subject: [PATCH 035/119] iio: buffer: move __iio_buffer_free_sysfs_and_mask() before alloc The __iio_buffer_free_sysfs_and_mask() function will be used in iio_buffer_alloc_sysfs_and_mask() when multiple buffers will be attached to the IIO device. This will need to be used to cleanup resources on each buffer, when the buffers cleanup unwind will occur on the error path. The move is done in this patch to make the patch that adds multiple buffers per IIO device a bit cleaner. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-18-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 232a60fe57d2..349c0c011745 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1444,6 +1444,14 @@ error_cleanup_dynamic: return ret; } +static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer) +{ + bitmap_free(buffer->scan_mask); + kfree(buffer->buffer_group.name); + kfree(buffer->buffer_group.attrs); + iio_free_chan_devattr_list(&buffer->buffer_attr_list); +} + int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) { struct iio_buffer *buffer = indio_dev->buffer; @@ -1465,14 +1473,6 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) return __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev, 0); } -static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer) -{ - bitmap_free(buffer->scan_mask); - kfree(buffer->buffer_group.name); - kfree(buffer->buffer_group.attrs); - iio_free_chan_devattr_list(&buffer->buffer_attr_list); -} - void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev) { struct iio_buffer *buffer = indio_dev->buffer; From 738f6ba1180046904402ffbe210fbd2fa2c4b050 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:37 +0200 Subject: [PATCH 036/119] iio: dummy: iio_simple_dummy_buffer: use triggered buffer core calls The iio_simple_dummy_configure_buffer() function is essentially a re-implementation of the iio_triggered_buffer_setup() function. This change makes use of the iio_triggered_buffer_setup() function. The reason is so that we don't have to modify the iio_device_attach_buffer() function in this driver as well. One minor drawback is that the pollfunc name may not be 100% identical with the one in the original code, but since it's an example, it should be a big problem. This change does a minor re-arranging of the included iio headers, as a minor tidy-up. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-19-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/dummy/iio_simple_dummy_buffer.c | 68 ++------------------- 1 file changed, 6 insertions(+), 62 deletions(-) diff --git a/drivers/iio/dummy/iio_simple_dummy_buffer.c b/drivers/iio/dummy/iio_simple_dummy_buffer.c index 5512d5edc707..59aa60d4ca37 100644 --- a/drivers/iio/dummy/iio_simple_dummy_buffer.c +++ b/drivers/iio/dummy/iio_simple_dummy_buffer.c @@ -16,9 +16,9 @@ #include #include -#include #include -#include +#include +#include #include "iio_simple_dummy.h" @@ -103,64 +103,9 @@ static const struct iio_buffer_setup_ops iio_simple_dummy_buffer_setup_ops = { int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev) { - int ret; - struct iio_buffer *buffer; - - /* Allocate a buffer to use - here a kfifo */ - buffer = iio_kfifo_allocate(); - if (!buffer) { - ret = -ENOMEM; - goto error_ret; - } - - iio_device_attach_buffer(indio_dev, buffer); - - /* - * Tell the core what device type specific functions should - * be run on either side of buffer capture enable / disable. - */ - indio_dev->setup_ops = &iio_simple_dummy_buffer_setup_ops; - - /* - * Configure a polling function. - * When a trigger event with this polling function connected - * occurs, this function is run. Typically this grabs data - * from the device. - * - * NULL for the bottom half. This is normally implemented only if we - * either want to ping a capture now pin (no sleeping) or grab - * a timestamp as close as possible to a data ready trigger firing. - * - * IRQF_ONESHOT ensures irqs are masked such that only one instance - * of the handler can run at a time. - * - * "iio_simple_dummy_consumer%d" formatting string for the irq 'name' - * as seen under /proc/interrupts. Remaining parameters as per printk. - */ - indio_dev->pollfunc = iio_alloc_pollfunc(NULL, - &iio_simple_dummy_trigger_h, - IRQF_ONESHOT, - indio_dev, - "iio_simple_dummy_consumer%d", - indio_dev->id); - - if (!indio_dev->pollfunc) { - ret = -ENOMEM; - goto error_free_buffer; - } - - /* - * Notify the core that this device is capable of buffered capture - * driven by a trigger. - */ - indio_dev->modes |= INDIO_BUFFER_TRIGGERED; - - return 0; - -error_free_buffer: - iio_kfifo_free(indio_dev->buffer); -error_ret: - return ret; + return iio_triggered_buffer_setup(indio_dev, NULL, + iio_simple_dummy_trigger_h, + &iio_simple_dummy_buffer_setup_ops); } /** @@ -169,6 +114,5 @@ error_ret: */ void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev) { - iio_dealloc_pollfunc(indio_dev->pollfunc); - iio_kfifo_free(indio_dev->buffer); + iio_triggered_buffer_cleanup(indio_dev); } From ee708e6baacd3afdace9b721c25fbbe106cebb94 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:38 +0200 Subject: [PATCH 037/119] iio: buffer: introduce support for attaching more IIO buffers With this change, calling iio_device_attach_buffer() will actually attach more buffers. Right now this doesn't do any validation of whether a buffer is attached twice; maybe that can be added later (if needed). Attaching a buffer more than once should yield noticeably bad results. The first buffer is the legacy buffer, so a reference is kept to it. At this point, accessing the data for the extra buffers (that are added after the first one) isn't possible yet. The iio_device_attach_buffer() is also changed to return an error code, which for now is -ENOMEM if the array could not be realloc-ed for more buffers. To adapt to this new change iio_device_attach_buffer() is called last in all place where it's called. The realloc failure is a bit difficult to handle during un-managed calls when unwinding, so it's better to have this as the last error in the setup_buffer calls. At this point, no driver should call iio_device_attach_buffer() directly, it should call one of the {devm_}iio_triggered_buffer_setup() or devm_iio_kfifo_buffer_setup() or devm_iio_dmaengine_buffer_setup() functions. This makes iio_device_attach_buffer() a bit easier to handle. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-20-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- .../buffer/industrialio-buffer-dmaengine.c | 4 +- .../buffer/industrialio-triggered-buffer.c | 10 +- drivers/iio/buffer/kfifo_buf.c | 4 +- drivers/iio/iio_core.h | 10 +- drivers/iio/industrialio-buffer.c | 100 ++++++++++++++---- drivers/iio/industrialio-core.c | 12 +-- include/linux/iio/buffer.h | 4 +- include/linux/iio/buffer_impl.h | 3 + include/linux/iio/iio-opaque.h | 4 + 9 files changed, 111 insertions(+), 40 deletions(-) diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index a64b222289be..d76179878ff9 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -290,9 +290,7 @@ int devm_iio_dmaengine_buffer_setup(struct device *dev, indio_dev->modes |= INDIO_BUFFER_HARDWARE; - iio_device_attach_buffer(indio_dev, buffer); - - return 0; + return iio_device_attach_buffer(indio_dev, buffer); } EXPORT_SYMBOL_GPL(devm_iio_dmaengine_buffer_setup); diff --git a/drivers/iio/buffer/industrialio-triggered-buffer.c b/drivers/iio/buffer/industrialio-triggered-buffer.c index 92b8aea3e063..b2b1b7d27af4 100644 --- a/drivers/iio/buffer/industrialio-triggered-buffer.c +++ b/drivers/iio/buffer/industrialio-triggered-buffer.c @@ -50,8 +50,6 @@ int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev, goto error_ret; } - iio_device_attach_buffer(indio_dev, buffer); - indio_dev->pollfunc = iio_alloc_pollfunc(h, thread, IRQF_ONESHOT, @@ -72,10 +70,16 @@ int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev, buffer->attrs = buffer_attrs; + ret = iio_device_attach_buffer(indio_dev, buffer); + if (ret < 0) + goto error_dealloc_pollfunc; + return 0; +error_dealloc_pollfunc: + iio_dealloc_pollfunc(indio_dev->pollfunc); error_kfifo_free: - iio_kfifo_free(indio_dev->buffer); + iio_kfifo_free(buffer); error_ret: return ret; } diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c index c35a625280b1..34289ce12f20 100644 --- a/drivers/iio/buffer/kfifo_buf.c +++ b/drivers/iio/buffer/kfifo_buf.c @@ -235,12 +235,10 @@ int devm_iio_kfifo_buffer_setup(struct device *dev, if (!buffer) return -ENOMEM; - iio_device_attach_buffer(indio_dev, buffer); - indio_dev->modes |= mode_flags; indio_dev->setup_ops = setup_ops; - return 0; + return iio_device_attach_buffer(indio_dev, buffer); } EXPORT_SYMBOL_GPL(devm_iio_kfifo_buffer_setup); diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index 87868fff7d37..7990c759f1f5 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -69,29 +69,31 @@ __poll_t iio_buffer_poll(struct file *filp, ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf, size_t n, loff_t *f_ps); -int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev); -void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev); +int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev); +void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev); #define iio_buffer_poll_addr (&iio_buffer_poll) #define iio_buffer_read_outer_addr (&iio_buffer_read_outer) void iio_disable_all_buffers(struct iio_dev *indio_dev); void iio_buffer_wakeup_poll(struct iio_dev *indio_dev); +void iio_buffers_put(struct iio_dev *indio_dev); #else #define iio_buffer_poll_addr NULL #define iio_buffer_read_outer_addr NULL -static inline int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) +static inline int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev) { return 0; } -static inline void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev) {} +static inline void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev) {} static inline void iio_disable_all_buffers(struct iio_dev *indio_dev) {} static inline void iio_buffer_wakeup_poll(struct iio_dev *indio_dev) {} +static inline void iio_buffers_put(struct iio_dev *indio_dev) {} #endif diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 349c0c011745..b60c2e66bd1c 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -193,12 +193,14 @@ __poll_t iio_buffer_poll(struct file *filp, */ void iio_buffer_wakeup_poll(struct iio_dev *indio_dev) { - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + struct iio_buffer *buffer; + unsigned int i; - if (!buffer) - return; - - wake_up(&buffer->pollq); + for (i = 0; i < iio_dev_opaque->attached_buffers_cnt; i++) { + buffer = iio_dev_opaque->attached_buffers[i]; + wake_up(&buffer->pollq); + } } void iio_buffer_init(struct iio_buffer *buffer) @@ -212,6 +214,18 @@ void iio_buffer_init(struct iio_buffer *buffer) } EXPORT_SYMBOL(iio_buffer_init); +void iio_buffers_put(struct iio_dev *indio_dev) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + struct iio_buffer *buffer; + unsigned int i; + + for (i = 0; i < iio_dev_opaque->attached_buffers_cnt; i++) { + buffer = iio_dev_opaque->attached_buffers[i]; + iio_buffer_put(buffer); + } +} + static ssize_t iio_show_scan_index(struct device *dev, struct device_attribute *attr, char *buf) @@ -1452,11 +1466,13 @@ static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer) iio_free_chan_devattr_list(&buffer->buffer_attr_list); } -int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) +int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev) { - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); const struct iio_chan_spec *channels; - int i; + struct iio_buffer *buffer; + int unwind_idx; + int ret, i; channels = indio_dev->channels; if (channels) { @@ -1467,22 +1483,46 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) indio_dev->masklength = ml; } - if (!buffer) + if (!iio_dev_opaque->attached_buffers_cnt) return 0; - return __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev, 0); + for (i = 0; i < iio_dev_opaque->attached_buffers_cnt; i++) { + buffer = iio_dev_opaque->attached_buffers[i]; + ret = __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev, i); + if (ret) { + unwind_idx = i; + goto error_unwind_sysfs_and_mask; + } + } + + return 0; + +error_unwind_sysfs_and_mask: + for (; unwind_idx >= 0; unwind_idx--) { + buffer = iio_dev_opaque->attached_buffers[unwind_idx]; + __iio_buffer_free_sysfs_and_mask(buffer); + } + kfree(iio_dev_opaque->attached_buffers); + return ret; } -void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev) +void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev) { - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + struct iio_buffer *buffer; + int i; - if (!buffer) + if (!iio_dev_opaque->attached_buffers_cnt) return; iio_buffer_unregister_legacy_sysfs_groups(indio_dev); - __iio_buffer_free_sysfs_and_mask(buffer); + for (i = iio_dev_opaque->attached_buffers_cnt - 1; i >= 0; i--) { + buffer = iio_dev_opaque->attached_buffers[i]; + __iio_buffer_free_sysfs_and_mask(buffer); + } + + kfree(iio_dev_opaque->attached_buffers); } /** @@ -1600,13 +1640,35 @@ EXPORT_SYMBOL_GPL(iio_buffer_put); * @indio_dev: The device the buffer should be attached to * @buffer: The buffer to attach to the device * + * Return 0 if successful, negative if error. + * * This function attaches a buffer to a IIO device. The buffer stays attached to - * the device until the device is freed. The function should only be called at - * most once per device. + * the device until the device is freed. For legacy reasons, the first attached + * buffer will also be assigned to 'indio_dev->buffer'. */ -void iio_device_attach_buffer(struct iio_dev *indio_dev, - struct iio_buffer *buffer) +int iio_device_attach_buffer(struct iio_dev *indio_dev, + struct iio_buffer *buffer) { - indio_dev->buffer = iio_buffer_get(buffer); + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + struct iio_buffer **new, **old = iio_dev_opaque->attached_buffers; + unsigned int cnt = iio_dev_opaque->attached_buffers_cnt; + + cnt++; + + new = krealloc(old, sizeof(*new) * cnt, GFP_KERNEL); + if (!new) + return -ENOMEM; + iio_dev_opaque->attached_buffers = new; + + buffer = iio_buffer_get(buffer); + + /* first buffer is legacy; attach it to the IIO device directly */ + if (!indio_dev->buffer) + indio_dev->buffer = buffer; + + iio_dev_opaque->attached_buffers[cnt - 1] = buffer; + iio_dev_opaque->attached_buffers_cnt = cnt; + + return 0; } EXPORT_SYMBOL_GPL(iio_device_attach_buffer); diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index b96acfe13e92..1645c739c44f 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1585,7 +1585,7 @@ static void iio_dev_release(struct device *device) iio_device_unregister_eventset(indio_dev); iio_device_unregister_sysfs(indio_dev); - iio_buffer_put(indio_dev->buffer); + iio_buffers_put(indio_dev); ida_simple_remove(&iio_ida, indio_dev->id); kfree(iio_dev_opaque); @@ -1862,7 +1862,7 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) iio_device_register_debugfs(indio_dev); - ret = iio_buffer_alloc_sysfs_and_mask(indio_dev); + ret = iio_buffers_alloc_sysfs_and_mask(indio_dev); if (ret) { dev_err(indio_dev->dev.parent, "Failed to create buffer sysfs interfaces\n"); @@ -1888,12 +1888,12 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) indio_dev->setup_ops == NULL) indio_dev->setup_ops = &noop_ring_setup_ops; - if (indio_dev->buffer) + if (iio_dev_opaque->attached_buffers_cnt) cdev_init(&indio_dev->chrdev, &iio_buffer_fileops); else if (iio_dev_opaque->event_interface) cdev_init(&indio_dev->chrdev, &iio_event_fileops); - if (indio_dev->buffer || iio_dev_opaque->event_interface) { + if (iio_dev_opaque->attached_buffers_cnt || iio_dev_opaque->event_interface) { indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id); indio_dev->chrdev.owner = this_mod; } @@ -1912,7 +1912,7 @@ error_unreg_eventset: error_free_sysfs: iio_device_unregister_sysfs(indio_dev); error_buffer_free_sysfs: - iio_buffer_free_sysfs_and_mask(indio_dev); + iio_buffers_free_sysfs_and_mask(indio_dev); error_unreg_debugfs: iio_device_unregister_debugfs(indio_dev); return ret; @@ -1946,7 +1946,7 @@ void iio_device_unregister(struct iio_dev *indio_dev) mutex_unlock(&indio_dev->info_exist_lock); - iio_buffer_free_sysfs_and_mask(indio_dev); + iio_buffers_free_sysfs_and_mask(indio_dev); } EXPORT_SYMBOL(iio_device_unregister); diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index 8febc23f5f26..b6928ac5c63d 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -41,7 +41,7 @@ static inline int iio_push_to_buffers_with_timestamp(struct iio_dev *indio_dev, bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev, const unsigned long *mask); -void iio_device_attach_buffer(struct iio_dev *indio_dev, - struct iio_buffer *buffer); +int iio_device_attach_buffer(struct iio_dev *indio_dev, + struct iio_buffer *buffer); #endif /* _IIO_BUFFER_GENERIC_H_ */ diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h index 41044320e581..768b90c64412 100644 --- a/include/linux/iio/buffer_impl.h +++ b/include/linux/iio/buffer_impl.h @@ -112,6 +112,9 @@ struct iio_buffer { /* @demux_bounce: Buffer for doing gather from incoming scan. */ void *demux_bounce; + /* @attached_entry: Entry in the devices list of buffers attached by the driver. */ + struct list_head attached_entry; + /* @buffer_list: Entry in the devices list of current buffers. */ struct list_head buffer_list; diff --git a/include/linux/iio/iio-opaque.h b/include/linux/iio/iio-opaque.h index 1a9310b0145f..b6ebc04af3e7 100644 --- a/include/linux/iio/iio-opaque.h +++ b/include/linux/iio/iio-opaque.h @@ -7,6 +7,8 @@ * struct iio_dev_opaque - industrial I/O device opaque information * @indio_dev: public industrial I/O device information * @event_interface: event chrdevs associated with interrupt lines + * @attached_buffers: array of buffers statically attached by the driver + * @attached_buffers_cnt: number of buffers in the array of statically attached buffers * @buffer_list: list of all buffers currently attached * @channel_attr_list: keep track of automatically created channel * attributes @@ -24,6 +26,8 @@ struct iio_dev_opaque { struct iio_dev indio_dev; struct iio_event_interface *event_interface; + struct iio_buffer **attached_buffers; + unsigned int attached_buffers_cnt; struct list_head buffer_list; struct list_head channel_attr_list; struct attribute_group chan_attr_group; From f73f7f4da581875f9b1f2fb8ebd1ab15ed634488 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:39 +0200 Subject: [PATCH 038/119] iio: buffer: add ioctl() to support opening extra buffers for IIO device With this change, an ioctl() call is added to open a character device for a buffer. The ioctl() number is 'i' 0x91, which follows the IIO_GET_EVENT_FD_IOCTL ioctl. The ioctl() will return an FD for the requested buffer index. The indexes are the same from the /sys/iio/devices/iio:deviceX/bufferY (i.e. the Y variable). Since there doesn't seem to be a sane way to return the FD for buffer0 to be the same FD for the /dev/iio:deviceX, this ioctl() will return another FD for buffer0 (or the first buffer). This duplicate FD will be able to access the same buffer object (for buffer0) as accessing directly the /dev/iio:deviceX chardev. Also, there is no IIO_BUFFER_GET_BUFFER_COUNT ioctl() implemented, as the index for each buffer (and the count) can be deduced from the '/sys/bus/iio/devices/iio:deviceX/bufferY' folders (i.e the number of bufferY folders). Used following C code to test this: ------------------------------------------------------------------- #include #include #include #include #include #define IIO_BUFFER_GET_FD_IOCTL _IOWR('i', 0x91, int) int main(int argc, char *argv[]) { int fd; int fd1; int ret; if ((fd = open("/dev/iio:device0", O_RDWR))<0) { fprintf(stderr, "Error open() %d errno %d\n",fd, errno); return -1; } fprintf(stderr, "Using FD %d\n", fd); fd1 = atoi(argv[1]); ret = ioctl(fd, IIO_BUFFER_GET_FD_IOCTL, &fd1); if (ret < 0) { fprintf(stderr, "Error for buffer %d ioctl() %d errno %d\n", fd1, ret, errno); close(fd); return -1; } fprintf(stderr, "Got FD %d\n", fd1); close(fd1); close(fd); return 0; } ------------------------------------------------------------------- Results are: ------------------------------------------------------------------- # ./test 0 Using FD 3 Got FD 4 # ./test 1 Using FD 3 Got FD 4 # ./test 2 Using FD 3 Got FD 4 # ./test 3 Using FD 3 Got FD 4 # ls /sys/bus/iio/devices/iio\:device0 buffer buffer0 buffer1 buffer2 buffer3 dev in_voltage_sampling_frequency in_voltage_scale in_voltage_scale_available name of_node power scan_elements subsystem uevent ------------------------------------------------------------------- iio:device0 has some fake kfifo buffers attached to an IIO device. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-21-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/iio_core.h | 12 +-- drivers/iio/industrialio-buffer.c | 144 ++++++++++++++++++++++++++++-- include/linux/iio/buffer_impl.h | 5 ++ include/linux/iio/iio-opaque.h | 2 + include/uapi/linux/iio/buffer.h | 10 +++ 5 files changed, 162 insertions(+), 11 deletions(-) create mode 100644 include/uapi/linux/iio/buffer.h diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index 7990c759f1f5..062fe16c6c49 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -64,16 +64,16 @@ ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals); #ifdef CONFIG_IIO_BUFFER struct poll_table_struct; -__poll_t iio_buffer_poll(struct file *filp, - struct poll_table_struct *wait); -ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf, - size_t n, loff_t *f_ps); +__poll_t iio_buffer_poll_wrapper(struct file *filp, + struct poll_table_struct *wait); +ssize_t iio_buffer_read_wrapper(struct file *filp, char __user *buf, + size_t n, loff_t *f_ps); int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev); void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev); -#define iio_buffer_poll_addr (&iio_buffer_poll) -#define iio_buffer_read_outer_addr (&iio_buffer_read_outer) +#define iio_buffer_poll_addr (&iio_buffer_poll_wrapper) +#define iio_buffer_read_outer_addr (&iio_buffer_read_wrapper) void iio_disable_all_buffers(struct iio_dev *indio_dev); void iio_buffer_wakeup_poll(struct iio_dev *indio_dev); diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index b60c2e66bd1c..a48e494a9fbb 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -9,9 +9,11 @@ * - Better memory allocation techniques? * - Alternative access techniques? */ +#include #include #include #include +#include #include #include #include @@ -89,7 +91,7 @@ static bool iio_buffer_ready(struct iio_dev *indio_dev, struct iio_buffer *buf, } /** - * iio_buffer_read_outer() - chrdev read for buffer access + * iio_buffer_read() - chrdev read for buffer access * @filp: File structure pointer for the char device * @buf: Destination buffer for iio buffer read * @n: First n bytes to read @@ -101,8 +103,8 @@ static bool iio_buffer_ready(struct iio_dev *indio_dev, struct iio_buffer *buf, * Return: negative values corresponding to error codes or ret != 0 * for ending the reading activity **/ -ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf, - size_t n, loff_t *f_ps) +static ssize_t iio_buffer_read(struct file *filp, char __user *buf, + size_t n, loff_t *f_ps) { struct iio_dev_buffer_pair *ib = filp->private_data; struct iio_buffer *rb = ib->buffer; @@ -168,8 +170,8 @@ ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf, * Return: (EPOLLIN | EPOLLRDNORM) if data is available for reading * or 0 for other cases */ -__poll_t iio_buffer_poll(struct file *filp, - struct poll_table_struct *wait) +static __poll_t iio_buffer_poll(struct file *filp, + struct poll_table_struct *wait) { struct iio_dev_buffer_pair *ib = filp->private_data; struct iio_buffer *rb = ib->buffer; @@ -184,6 +186,32 @@ __poll_t iio_buffer_poll(struct file *filp, return 0; } +ssize_t iio_buffer_read_wrapper(struct file *filp, char __user *buf, + size_t n, loff_t *f_ps) +{ + struct iio_dev_buffer_pair *ib = filp->private_data; + struct iio_buffer *rb = ib->buffer; + + /* check if buffer was opened through new API */ + if (test_bit(IIO_BUSY_BIT_POS, &rb->flags)) + return -EBUSY; + + return iio_buffer_read(filp, buf, n, f_ps); +} + +__poll_t iio_buffer_poll_wrapper(struct file *filp, + struct poll_table_struct *wait) +{ + struct iio_dev_buffer_pair *ib = filp->private_data; + struct iio_buffer *rb = ib->buffer; + + /* check if buffer was opened through new API */ + if (test_bit(IIO_BUSY_BIT_POS, &rb->flags)) + return 0; + + return iio_buffer_poll(filp, wait); +} + /** * iio_buffer_wakeup_poll - Wakes up the buffer waitqueue * @indio_dev: The IIO device @@ -1344,6 +1372,96 @@ static void iio_buffer_unregister_legacy_sysfs_groups(struct iio_dev *indio_dev) kfree(iio_dev_opaque->legacy_scan_el_group.attrs); } +static int iio_buffer_chrdev_release(struct inode *inode, struct file *filep) +{ + struct iio_dev_buffer_pair *ib = filep->private_data; + struct iio_dev *indio_dev = ib->indio_dev; + struct iio_buffer *buffer = ib->buffer; + + wake_up(&buffer->pollq); + + kfree(ib); + clear_bit(IIO_BUSY_BIT_POS, &buffer->flags); + iio_device_put(indio_dev); + + return 0; +} + +static const struct file_operations iio_buffer_chrdev_fileops = { + .owner = THIS_MODULE, + .llseek = noop_llseek, + .read = iio_buffer_read, + .poll = iio_buffer_poll, + .release = iio_buffer_chrdev_release, +}; + +static long iio_device_buffer_getfd(struct iio_dev *indio_dev, unsigned long arg) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + int __user *ival = (int __user *)arg; + struct iio_dev_buffer_pair *ib; + struct iio_buffer *buffer; + int fd, idx, ret; + + if (copy_from_user(&idx, ival, sizeof(idx))) + return -EFAULT; + + if (idx >= iio_dev_opaque->attached_buffers_cnt) + return -ENODEV; + + iio_device_get(indio_dev); + + buffer = iio_dev_opaque->attached_buffers[idx]; + + if (test_and_set_bit(IIO_BUSY_BIT_POS, &buffer->flags)) { + ret = -EBUSY; + goto error_iio_dev_put; + } + + ib = kzalloc(sizeof(*ib), GFP_KERNEL); + if (!ib) { + ret = -ENOMEM; + goto error_clear_busy_bit; + } + + ib->indio_dev = indio_dev; + ib->buffer = buffer; + + fd = anon_inode_getfd("iio:buffer", &iio_buffer_chrdev_fileops, + ib, O_RDWR | O_CLOEXEC); + if (fd < 0) { + ret = fd; + goto error_free_ib; + } + + if (copy_to_user(ival, &fd, sizeof(fd))) { + put_unused_fd(fd); + ret = -EFAULT; + goto error_free_ib; + } + + return fd; + +error_free_ib: + kfree(ib); +error_clear_busy_bit: + clear_bit(IIO_BUSY_BIT_POS, &buffer->flags); +error_iio_dev_put: + iio_device_put(indio_dev); + return ret; +} + +static long iio_device_buffer_ioctl(struct iio_dev *indio_dev, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case IIO_BUFFER_GET_FD_IOCTL: + return iio_device_buffer_getfd(indio_dev, arg); + default: + return IIO_IOCTL_UNHANDLED; + } +} + static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, struct iio_dev *indio_dev, int index) @@ -1473,6 +1591,7 @@ int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev) struct iio_buffer *buffer; int unwind_idx; int ret, i; + size_t sz; channels = indio_dev->channels; if (channels) { @@ -1494,6 +1613,18 @@ int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev) goto error_unwind_sysfs_and_mask; } } + unwind_idx = iio_dev_opaque->attached_buffers_cnt - 1; + + sz = sizeof(*(iio_dev_opaque->buffer_ioctl_handler)); + iio_dev_opaque->buffer_ioctl_handler = kzalloc(sz, GFP_KERNEL); + if (!iio_dev_opaque->buffer_ioctl_handler) { + ret = -ENOMEM; + goto error_unwind_sysfs_and_mask; + } + + iio_dev_opaque->buffer_ioctl_handler->ioctl = iio_device_buffer_ioctl; + iio_device_ioctl_handler_register(indio_dev, + iio_dev_opaque->buffer_ioctl_handler); return 0; @@ -1515,6 +1646,9 @@ void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev) if (!iio_dev_opaque->attached_buffers_cnt) return; + iio_device_ioctl_handler_unregister(iio_dev_opaque->buffer_ioctl_handler); + kfree(iio_dev_opaque->buffer_ioctl_handler); + iio_buffer_unregister_legacy_sysfs_groups(indio_dev); for (i = iio_dev_opaque->attached_buffers_cnt - 1; i >= 0; i--) { diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h index 768b90c64412..245b32918ae1 100644 --- a/include/linux/iio/buffer_impl.h +++ b/include/linux/iio/buffer_impl.h @@ -6,6 +6,8 @@ #ifdef CONFIG_IIO_BUFFER +#include + struct iio_dev; struct iio_buffer; @@ -72,6 +74,9 @@ struct iio_buffer { /** @length: Number of datums in buffer. */ unsigned int length; + /** @flags: File ops flags including busy flag. */ + unsigned long flags; + /** @bytes_per_datum: Size of individual datum including timestamp. */ size_t bytes_per_datum; diff --git a/include/linux/iio/iio-opaque.h b/include/linux/iio/iio-opaque.h index b6ebc04af3e7..32addd5e790e 100644 --- a/include/linux/iio/iio-opaque.h +++ b/include/linux/iio/iio-opaque.h @@ -9,6 +9,7 @@ * @event_interface: event chrdevs associated with interrupt lines * @attached_buffers: array of buffers statically attached by the driver * @attached_buffers_cnt: number of buffers in the array of statically attached buffers + * @buffer_ioctl_handler: ioctl() handler for this IIO device's buffer interface * @buffer_list: list of all buffers currently attached * @channel_attr_list: keep track of automatically created channel * attributes @@ -28,6 +29,7 @@ struct iio_dev_opaque { struct iio_event_interface *event_interface; struct iio_buffer **attached_buffers; unsigned int attached_buffers_cnt; + struct iio_ioctl_handler *buffer_ioctl_handler; struct list_head buffer_list; struct list_head channel_attr_list; struct attribute_group chan_attr_group; diff --git a/include/uapi/linux/iio/buffer.h b/include/uapi/linux/iio/buffer.h new file mode 100644 index 000000000000..13939032b3f6 --- /dev/null +++ b/include/uapi/linux/iio/buffer.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* industrial I/O buffer definitions needed both in and out of kernel + */ + +#ifndef _UAPI_IIO_BUFFER_H_ +#define _UAPI_IIO_BUFFER_H_ + +#define IIO_BUFFER_GET_FD_IOCTL _IOWR('i', 0x91, int) + +#endif /* _UAPI_IIO_BUFFER_H_ */ From 0d596bb2ad0dbefc7c3e6072acb64b379888ee3d Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:40 +0200 Subject: [PATCH 039/119] iio: core: rename 'dev' -> 'indio_dev' in iio_device_alloc() The 'dev' variable name usually refers to 'struct device' types. However in iio_device_alloc() this was used for the 'struct iio_dev' type, which was sometimes causing minor confusions. This change renames the variable to 'indio_dev', which is the usual name used around IIO for 'struct iio_dev' type objects. It makes grepping a bit easier as well. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-22-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 1645c739c44f..57e1cb376e22 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1604,7 +1604,7 @@ struct device_type iio_device_type = { struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv) { struct iio_dev_opaque *iio_dev_opaque; - struct iio_dev *dev; + struct iio_dev *indio_dev; size_t alloc_size; alloc_size = sizeof(struct iio_dev_opaque); @@ -1617,31 +1617,31 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv) if (!iio_dev_opaque) return NULL; - dev = &iio_dev_opaque->indio_dev; - dev->priv = (char *)iio_dev_opaque + + indio_dev = &iio_dev_opaque->indio_dev; + indio_dev->priv = (char *)iio_dev_opaque + ALIGN(sizeof(struct iio_dev_opaque), IIO_ALIGN); - dev->dev.parent = parent; - dev->dev.type = &iio_device_type; - dev->dev.bus = &iio_bus_type; - device_initialize(&dev->dev); - dev_set_drvdata(&dev->dev, (void *)dev); - mutex_init(&dev->mlock); - mutex_init(&dev->info_exist_lock); + indio_dev->dev.parent = parent; + indio_dev->dev.type = &iio_device_type; + indio_dev->dev.bus = &iio_bus_type; + device_initialize(&indio_dev->dev); + dev_set_drvdata(&indio_dev->dev, (void *)indio_dev); + mutex_init(&indio_dev->mlock); + mutex_init(&indio_dev->info_exist_lock); INIT_LIST_HEAD(&iio_dev_opaque->channel_attr_list); - dev->id = ida_simple_get(&iio_ida, 0, 0, GFP_KERNEL); - if (dev->id < 0) { + indio_dev->id = ida_simple_get(&iio_ida, 0, 0, GFP_KERNEL); + if (indio_dev->id < 0) { /* cannot use a dev_err as the name isn't available */ pr_err("failed to get device id\n"); kfree(iio_dev_opaque); return NULL; } - dev_set_name(&dev->dev, "iio:device%d", dev->id); + dev_set_name(&indio_dev->dev, "iio:device%d", indio_dev->id); INIT_LIST_HEAD(&iio_dev_opaque->buffer_list); INIT_LIST_HEAD(&iio_dev_opaque->ioctl_handlers); - return dev; + return indio_dev; } EXPORT_SYMBOL(iio_device_alloc); From a605c8f4e71c35fddb9b13785f4ea5c24e273aa2 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:41 +0200 Subject: [PATCH 040/119] tools: iio: make iioutils_get_type() private in iio_utils This is a bit of a tidy-up, but also helps with extending the iioutils_get_type() function a bit, as we don't need to use it outside of the iio_utils.c file. So, we'll need to update it only in one place. With this change, the 'unsigned' types are updated to 'unsigned int' in the iioutils_get_type() function definition. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-23-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- tools/iio/iio_utils.c | 9 +++++---- tools/iio/iio_utils.h | 4 ---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index 7399eb7f1378..a96002f2c2d5 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c @@ -82,10 +82,11 @@ int iioutils_break_up_name(const char *full_name, char **generic_name) * * Returns a value >= 0 on success, otherwise a negative error code. **/ -int iioutils_get_type(unsigned *is_signed, unsigned *bytes, unsigned *bits_used, - unsigned *shift, uint64_t *mask, unsigned *be, - const char *device_dir, const char *name, - const char *generic_name) +static int iioutils_get_type(unsigned int *is_signed, unsigned int *bytes, + unsigned int *bits_used, unsigned int *shift, + uint64_t *mask, unsigned int *be, + const char *device_dir, const char *name, + const char *generic_name) { FILE *sysfsfp; int ret; diff --git a/tools/iio/iio_utils.h b/tools/iio/iio_utils.h index 74bde4fde2c8..a5d0aa8a57d3 100644 --- a/tools/iio/iio_utils.h +++ b/tools/iio/iio_utils.h @@ -57,10 +57,6 @@ static inline int iioutils_check_suffix(const char *str, const char *suffix) } int iioutils_break_up_name(const char *full_name, char **generic_name); -int iioutils_get_type(unsigned *is_signed, unsigned *bytes, unsigned *bits_used, - unsigned *shift, uint64_t *mask, unsigned *be, - const char *device_dir, const char *name, - const char *generic_name); int iioutils_get_param_float(float *output, const char *param_name, const char *device_dir, const char *name, const char *generic_name); From ebe5112535b5cf389ca7d337cf6a0c1d885f9880 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:42 +0200 Subject: [PATCH 041/119] tools: iio: privatize globals and functions in iio_generic_buffer.c file Mostly a tidy-up. But also helps to understand the limits of scope of these functions and globals. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-24-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- tools/iio/iio_generic_buffer.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/tools/iio/iio_generic_buffer.c b/tools/iio/iio_generic_buffer.c index 34d63bcebcd2..7c7240553777 100644 --- a/tools/iio/iio_generic_buffer.c +++ b/tools/iio/iio_generic_buffer.c @@ -49,7 +49,7 @@ enum autochan { * Has the side effect of filling the channels[i].location values used * in processing the buffer output. **/ -int size_from_channelarray(struct iio_channel_info *channels, int num_channels) +static int size_from_channelarray(struct iio_channel_info *channels, int num_channels) { int bytes = 0; int i = 0; @@ -68,7 +68,7 @@ int size_from_channelarray(struct iio_channel_info *channels, int num_channels) return bytes; } -void print1byte(uint8_t input, struct iio_channel_info *info) +static void print1byte(uint8_t input, struct iio_channel_info *info) { /* * Shift before conversion to avoid sign extension @@ -85,7 +85,7 @@ void print1byte(uint8_t input, struct iio_channel_info *info) } } -void print2byte(uint16_t input, struct iio_channel_info *info) +static void print2byte(uint16_t input, struct iio_channel_info *info) { /* First swap if incorrect endian */ if (info->be) @@ -108,7 +108,7 @@ void print2byte(uint16_t input, struct iio_channel_info *info) } } -void print4byte(uint32_t input, struct iio_channel_info *info) +static void print4byte(uint32_t input, struct iio_channel_info *info) { /* First swap if incorrect endian */ if (info->be) @@ -131,7 +131,7 @@ void print4byte(uint32_t input, struct iio_channel_info *info) } } -void print8byte(uint64_t input, struct iio_channel_info *info) +static void print8byte(uint64_t input, struct iio_channel_info *info) { /* First swap if incorrect endian */ if (info->be) @@ -167,9 +167,8 @@ void print8byte(uint64_t input, struct iio_channel_info *info) * to fill the location offsets. * @num_channels: number of channels **/ -void process_scan(char *data, - struct iio_channel_info *channels, - int num_channels) +static void process_scan(char *data, struct iio_channel_info *channels, + int num_channels) { int k; @@ -238,7 +237,7 @@ static int enable_disable_all_channels(char *dev_dir_name, int enable) return 0; } -void print_usage(void) +static void print_usage(void) { fprintf(stderr, "Usage: generic_buffer [options]...\n" "Capture, convert and output data from IIO device buffer\n" @@ -257,12 +256,12 @@ void print_usage(void) " -w Set delay between reads in us (event-less mode)\n"); } -enum autochan autochannels = AUTOCHANNELS_DISABLED; -char *dev_dir_name = NULL; -char *buf_dir_name = NULL; -bool current_trigger_set = false; +static enum autochan autochannels = AUTOCHANNELS_DISABLED; +static char *dev_dir_name = NULL; +static char *buf_dir_name = NULL; +static bool current_trigger_set = false; -void cleanup(void) +static void cleanup(void) { int ret; @@ -294,14 +293,14 @@ void cleanup(void) } } -void sig_handler(int signum) +static void sig_handler(int signum) { fprintf(stderr, "Caught signal %d\n", signum); cleanup(); exit(-signum); } -void register_cleanup(void) +static void register_cleanup(void) { struct sigaction sa = { .sa_handler = sig_handler }; const int signums[] = { SIGINT, SIGTERM, SIGABRT }; From 8827faab2c8b52e848071a039a945db6f3ae8365 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 15 Feb 2021 12:40:43 +0200 Subject: [PATCH 042/119] tools: iio: convert iio_generic_buffer to use new IIO buffer API This change makes use of the new IIO buffer API to read data from an IIO buffer. It doesn't read the /sys/bus/iio/devices/iio:deviceX/scan_elements dir anymore, it reads /sys/bus/iio/devices/iio:deviceX/bufferY, where all the scan_elements have been merged together with the old/classical buffer attributes. And it makes use of the new IIO_BUFFER_GET_FD_IOCTL ioctl to get an FD for the IIO buffer for which to read data from. It also does a quick sanity check to see that -EBUSY is returned if reading the chardev after the ioctl() has succeeded. This was tested with the following cases: 1. Tested buffer0 works with ioctl() 2. Tested that buffer0 can't be opened via /dev/iio:deviceX after ioctl() This check should be omitted under normal operation; it's being done here to check that the driver change is sane 3. Moved valid buffer0 to be buffer1, and tested that data comes from it Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210215104043.91251-25-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- tools/iio/Makefile | 1 + tools/iio/iio_generic_buffer.c | 122 ++++++++++++++++++++++++++------- tools/iio/iio_utils.c | 13 ++-- tools/iio/iio_utils.h | 4 +- 4 files changed, 107 insertions(+), 33 deletions(-) diff --git a/tools/iio/Makefile b/tools/iio/Makefile index 3de763d9ab70..5d12ac4e7f8f 100644 --- a/tools/iio/Makefile +++ b/tools/iio/Makefile @@ -27,6 +27,7 @@ include $(srctree)/tools/build/Makefile.include # $(OUTPUT)include/linux/iio: ../../include/uapi/linux/iio mkdir -p $(OUTPUT)include/linux/iio 2>&1 || true + ln -sf $(CURDIR)/../../include/uapi/linux/iio/buffer.h $@ ln -sf $(CURDIR)/../../include/uapi/linux/iio/events.h $@ ln -sf $(CURDIR)/../../include/uapi/linux/iio/types.h $@ diff --git a/tools/iio/iio_generic_buffer.c b/tools/iio/iio_generic_buffer.c index 7c7240553777..2491c54a5e4f 100644 --- a/tools/iio/iio_generic_buffer.c +++ b/tools/iio/iio_generic_buffer.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include "iio_utils.h" /** @@ -197,7 +199,7 @@ static void process_scan(char *data, struct iio_channel_info *channels, printf("\n"); } -static int enable_disable_all_channels(char *dev_dir_name, int enable) +static int enable_disable_all_channels(char *dev_dir_name, int buffer_idx, int enable) { const struct dirent *ent; char scanelemdir[256]; @@ -205,7 +207,7 @@ static int enable_disable_all_channels(char *dev_dir_name, int enable) int ret; snprintf(scanelemdir, sizeof(scanelemdir), - FORMAT_SCAN_ELEMENTS_DIR, dev_dir_name); + FORMAT_SCAN_ELEMENTS_DIR, dev_dir_name, buffer_idx); scanelemdir[sizeof(scanelemdir)-1] = '\0'; dp = opendir(scanelemdir); @@ -243,6 +245,7 @@ static void print_usage(void) "Capture, convert and output data from IIO device buffer\n" " -a Auto-activate all available channels\n" " -A Force-activate ALL channels\n" + " -b The buffer which to open (by index), default 0\n" " -c Do n conversions, or loop forever if n < 0\n" " -e Disable wait for event (new data)\n" " -g Use trigger-less mode\n" @@ -259,6 +262,7 @@ static void print_usage(void) static enum autochan autochannels = AUTOCHANNELS_DISABLED; static char *dev_dir_name = NULL; static char *buf_dir_name = NULL; +static int buffer_idx = 0; static bool current_trigger_set = false; static void cleanup(void) @@ -286,7 +290,7 @@ static void cleanup(void) /* Disable channels if auto-enabled */ if (dev_dir_name && autochannels == AUTOCHANNELS_ACTIVE) { - ret = enable_disable_all_channels(dev_dir_name, 0); + ret = enable_disable_all_channels(dev_dir_name, buffer_idx, 0); if (ret) fprintf(stderr, "Failed to disable all channels\n"); autochannels = AUTOCHANNELS_DISABLED; @@ -333,7 +337,9 @@ int main(int argc, char **argv) unsigned long long j; unsigned long toread; int ret, c; - int fp = -1; + struct stat st; + int fd = -1; + int buf_fd = -1; int num_channels = 0; char *trigger_name = NULL, *device_name = NULL; @@ -352,7 +358,7 @@ int main(int argc, char **argv) register_cleanup(); - while ((c = getopt_long(argc, argv, "aAc:egl:n:N:t:T:w:?", longopts, + while ((c = getopt_long(argc, argv, "aAb:c:egl:n:N:t:T:w:?", longopts, NULL)) != -1) { switch (c) { case 'a': @@ -361,7 +367,20 @@ int main(int argc, char **argv) case 'A': autochannels = AUTOCHANNELS_ENABLED; force_autochannels = true; - break; + break; + case 'b': + errno = 0; + buffer_idx = strtoll(optarg, &dummy, 10); + if (errno) { + ret = -errno; + goto error; + } + if (buffer_idx < 0) { + ret = -ERANGE; + goto error; + } + + break; case 'c': errno = 0; num_loops = strtoll(optarg, &dummy, 10); @@ -518,7 +537,7 @@ int main(int argc, char **argv) * Parse the files in scan_elements to identify what channels are * present */ - ret = build_channel_array(dev_dir_name, &channels, &num_channels); + ret = build_channel_array(dev_dir_name, buffer_idx, &channels, &num_channels); if (ret) { fprintf(stderr, "Problem reading scan element information\n" "diag %s\n", dev_dir_name); @@ -535,7 +554,7 @@ int main(int argc, char **argv) (autochannels == AUTOCHANNELS_ENABLED && force_autochannels)) { fprintf(stderr, "Enabling all channels\n"); - ret = enable_disable_all_channels(dev_dir_name, 1); + ret = enable_disable_all_channels(dev_dir_name, buffer_idx, 1); if (ret) { fprintf(stderr, "Failed to enable all channels\n"); goto error; @@ -544,7 +563,7 @@ int main(int argc, char **argv) /* This flags that we need to disable the channels again */ autochannels = AUTOCHANNELS_ACTIVE; - ret = build_channel_array(dev_dir_name, &channels, + ret = build_channel_array(dev_dir_name, buffer_idx, &channels, &num_channels); if (ret) { fprintf(stderr, "Problem reading scan element " @@ -565,7 +584,7 @@ int main(int argc, char **argv) fprintf(stderr, "Enable channels manually in " FORMAT_SCAN_ELEMENTS_DIR "/*_en or pass -a to autoenable channels and " - "try again.\n", dev_dir_name); + "try again.\n", dev_dir_name, buffer_idx); ret = -ENOENT; goto error; } @@ -576,12 +595,25 @@ int main(int argc, char **argv) * be built rather than found. */ ret = asprintf(&buf_dir_name, - "%siio:device%d/buffer", iio_dir, dev_num); + "%siio:device%d/buffer%d", iio_dir, dev_num, buffer_idx); if (ret < 0) { ret = -ENOMEM; goto error; } + if (stat(buf_dir_name, &st)) { + fprintf(stderr, "Could not stat() '%s', got error %d: %s\n", + buf_dir_name, errno, strerror(errno)); + ret = -errno; + goto error; + } + + if (!S_ISDIR(st.st_mode)) { + fprintf(stderr, "File '%s' is not a directory\n", buf_dir_name); + ret = -EFAULT; + goto error; + } + if (!notrigger) { printf("%s %s\n", dev_dir_name, trigger_name); /* @@ -598,6 +630,35 @@ int main(int argc, char **argv) } } + ret = asprintf(&buffer_access, "/dev/iio:device%d", dev_num); + if (ret < 0) { + ret = -ENOMEM; + goto error; + } + + /* Attempt to open non blocking the access dev */ + fd = open(buffer_access, O_RDONLY | O_NONBLOCK); + if (fd == -1) { /* TODO: If it isn't there make the node */ + ret = -errno; + fprintf(stderr, "Failed to open %s\n", buffer_access); + goto error; + } + + /* specify for which buffer index we want an FD */ + buf_fd = buffer_idx; + + ret = ioctl(fd, IIO_BUFFER_GET_FD_IOCTL, &buf_fd); + if (ret == -1 || buf_fd == -1) { + ret = -errno; + if (ret == -ENODEV || ret == -EINVAL) + fprintf(stderr, + "Device does not have this many buffers\n"); + else + fprintf(stderr, "Failed to retrieve buffer fd\n"); + + goto error; + } + /* Setup ring buffer parameters */ ret = write_sysfs_int("length", buf_dir_name, buf_len); if (ret < 0) @@ -607,7 +668,8 @@ int main(int argc, char **argv) ret = write_sysfs_int("enable", buf_dir_name, 1); if (ret < 0) { fprintf(stderr, - "Failed to enable buffer: %s\n", strerror(-ret)); + "Failed to enable buffer '%s': %s\n", + buf_dir_name, strerror(-ret)); goto error; } @@ -618,24 +680,30 @@ int main(int argc, char **argv) goto error; } - ret = asprintf(&buffer_access, "/dev/iio:device%d", dev_num); - if (ret < 0) { - ret = -ENOMEM; - goto error; + /** + * This check is being done here for sanity reasons, however it + * should be omitted under normal operation. + * If this is buffer0, we check that we get EBUSY after this point. + */ + if (buffer_idx == 0) { + errno = 0; + read_size = read(fd, data, 1); + if (read_size > -1 || errno != EBUSY) { + ret = -EFAULT; + perror("Reading from '%s' should not be possible after ioctl()"); + goto error; + } } - /* Attempt to open non blocking the access dev */ - fp = open(buffer_access, O_RDONLY | O_NONBLOCK); - if (fp == -1) { /* TODO: If it isn't there make the node */ - ret = -errno; - fprintf(stderr, "Failed to open %s\n", buffer_access); - goto error; - } + /* close now the main chardev FD and let the buffer FD work */ + if (close(fd) == -1) + perror("Failed to close character device file"); + fd = -1; for (j = 0; j < num_loops || num_loops < 0; j++) { if (!noevents) { struct pollfd pfd = { - .fd = fp, + .fd = buf_fd, .events = POLLIN, }; @@ -653,7 +721,7 @@ int main(int argc, char **argv) toread = 64; } - read_size = read(fp, data, toread * scan_size); + read_size = read(buf_fd, data, toread * scan_size); if (read_size < 0) { if (errno == EAGAIN) { fprintf(stderr, "nothing available\n"); @@ -670,7 +738,9 @@ int main(int argc, char **argv) error: cleanup(); - if (fp >= 0 && close(fp) == -1) + if (fd >= 0 && close(fd) == -1) + perror("Failed to close character device"); + if (buf_fd >= 0 && close(buf_fd) == -1) perror("Failed to close buffer"); free(buffer_access); free(data); diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index a96002f2c2d5..aadee6d34c74 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c @@ -77,6 +77,7 @@ int iioutils_break_up_name(const char *full_name, char **generic_name) * @mask: output a bit mask for the raw data * @be: output if data in big endian * @device_dir: the IIO device directory + * @buffer_idx: the IIO buffer index * @name: the channel name * @generic_name: the channel type name * @@ -85,8 +86,8 @@ int iioutils_break_up_name(const char *full_name, char **generic_name) static int iioutils_get_type(unsigned int *is_signed, unsigned int *bytes, unsigned int *bits_used, unsigned int *shift, uint64_t *mask, unsigned int *be, - const char *device_dir, const char *name, - const char *generic_name) + const char *device_dir, int buffer_idx, + const char *name, const char *generic_name) { FILE *sysfsfp; int ret; @@ -96,7 +97,7 @@ static int iioutils_get_type(unsigned int *is_signed, unsigned int *bytes, unsigned padint; const struct dirent *ent; - ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir); + ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir, buffer_idx); if (ret < 0) return -ENOMEM; @@ -304,12 +305,13 @@ void bsort_channel_array_by_index(struct iio_channel_info *ci_array, int cnt) /** * build_channel_array() - function to figure out what channels are present * @device_dir: the IIO device directory in sysfs + * @buffer_idx: the IIO buffer for this channel array * @ci_array: output the resulting array of iio_channel_info * @counter: output the amount of array elements * * Returns 0 on success, otherwise a negative error code. **/ -int build_channel_array(const char *device_dir, +int build_channel_array(const char *device_dir, int buffer_idx, struct iio_channel_info **ci_array, int *counter) { DIR *dp; @@ -322,7 +324,7 @@ int build_channel_array(const char *device_dir, char *filename; *counter = 0; - ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir); + ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir, buffer_idx); if (ret < 0) return -ENOMEM; @@ -503,6 +505,7 @@ int build_channel_array(const char *device_dir, ¤t->mask, ¤t->be, device_dir, + buffer_idx, current->name, current->generic_name); if (ret < 0) diff --git a/tools/iio/iio_utils.h b/tools/iio/iio_utils.h index a5d0aa8a57d3..336752cade4f 100644 --- a/tools/iio/iio_utils.h +++ b/tools/iio/iio_utils.h @@ -12,7 +12,7 @@ /* Made up value to limit allocation sizes */ #define IIO_MAX_NAME_LENGTH 64 -#define FORMAT_SCAN_ELEMENTS_DIR "%s/scan_elements" +#define FORMAT_SCAN_ELEMENTS_DIR "%s/buffer%d" #define FORMAT_TYPE_FILE "%s_type" #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) @@ -61,7 +61,7 @@ int iioutils_get_param_float(float *output, const char *param_name, const char *device_dir, const char *name, const char *generic_name); void bsort_channel_array_by_index(struct iio_channel_info *ci_array, int cnt); -int build_channel_array(const char *device_dir, +int build_channel_array(const char *device_dir, int buffer_idx, struct iio_channel_info **ci_array, int *counter); int find_type_by_name(const char *name, const char *type); int write_sysfs_int(const char *filename, const char *basedir, int val); From b624fd14a9275bd546b63cf103cee6c9f57a4f55 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 9 Feb 2021 22:13:15 +0100 Subject: [PATCH 043/119] iio: use getter/setter functions Use getter and setter functions, for a variety of data types. Signed-off-by: Julia Lawall Link: https://lore.kernel.org/r/20210209211315.1261791-1-Julia.Lawall@inria.fr Signed-off-by: Jonathan Cameron --- drivers/iio/adc/palmas_gpadc.c | 2 +- drivers/iio/chemical/scd30_serial.c | 2 +- drivers/iio/industrialio-core.c | 2 +- drivers/iio/potentiometer/max5481.c | 4 ++-- drivers/iio/potentiometer/max5487.c | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index 889b88768b63..9ae0d7f73155 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -517,7 +517,7 @@ static int palmas_gpadc_probe(struct platform_device *pdev) adc->palmas = dev_get_drvdata(pdev->dev.parent); adc->adc_info = palmas_gpadc_info; init_completion(&adc->conv_completion); - dev_set_drvdata(&pdev->dev, indio_dev); + platform_set_drvdata(pdev, indio_dev); adc->auto_conversion_period = gpadc_pdata->auto_conversion_period_ms; adc->irq = palmas_irq_get_virq(adc->palmas, PALMAS_GPADC_EOC_SW_IRQ); diff --git a/drivers/iio/chemical/scd30_serial.c b/drivers/iio/chemical/scd30_serial.c index 06f85eb1a4dd..568b34486c44 100644 --- a/drivers/iio/chemical/scd30_serial.c +++ b/drivers/iio/chemical/scd30_serial.c @@ -177,7 +177,7 @@ static int scd30_serdev_command(struct scd30_state *state, enum scd30_cmd cmd, u static int scd30_serdev_receive_buf(struct serdev_device *serdev, const unsigned char *buf, size_t size) { - struct iio_dev *indio_dev = dev_get_drvdata(&serdev->dev); + struct iio_dev *indio_dev = serdev_device_get_drvdata(serdev); struct scd30_serdev_priv *priv; struct scd30_state *state; int num; diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 57e1cb376e22..2e2970f3e21c 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1625,7 +1625,7 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv) indio_dev->dev.type = &iio_device_type; indio_dev->dev.bus = &iio_bus_type; device_initialize(&indio_dev->dev); - dev_set_drvdata(&indio_dev->dev, (void *)indio_dev); + iio_device_set_drvdata(indio_dev, (void *)indio_dev); mutex_init(&indio_dev->mlock); mutex_init(&indio_dev->info_exist_lock); INIT_LIST_HEAD(&iio_dev_opaque->channel_attr_list); diff --git a/drivers/iio/potentiometer/max5481.c b/drivers/iio/potentiometer/max5481.c index a88ed0eb3adc..6e22b538091f 100644 --- a/drivers/iio/potentiometer/max5481.c +++ b/drivers/iio/potentiometer/max5481.c @@ -136,7 +136,7 @@ static int max5481_probe(struct spi_device *spi) if (!indio_dev) return -ENOMEM; - dev_set_drvdata(&spi->dev, indio_dev); + spi_set_drvdata(spi, indio_dev); data = iio_priv(indio_dev); data->spi = spi; @@ -163,7 +163,7 @@ static int max5481_probe(struct spi_device *spi) static int max5481_remove(struct spi_device *spi) { - struct iio_dev *indio_dev = dev_get_drvdata(&spi->dev); + struct iio_dev *indio_dev = spi_get_drvdata(spi); struct max5481_data *data = iio_priv(indio_dev); iio_device_unregister(indio_dev); diff --git a/drivers/iio/potentiometer/max5487.c b/drivers/iio/potentiometer/max5487.c index 7ec51976ec99..1c0d46a96200 100644 --- a/drivers/iio/potentiometer/max5487.c +++ b/drivers/iio/potentiometer/max5487.c @@ -92,7 +92,7 @@ static int max5487_spi_probe(struct spi_device *spi) if (!indio_dev) return -ENOMEM; - dev_set_drvdata(&spi->dev, indio_dev); + spi_set_drvdata(spi, indio_dev); data = iio_priv(indio_dev); data->spi = spi; @@ -114,7 +114,7 @@ static int max5487_spi_probe(struct spi_device *spi) static int max5487_spi_remove(struct spi_device *spi) { - struct iio_dev *indio_dev = dev_get_drvdata(&spi->dev); + struct iio_dev *indio_dev = spi_get_drvdata(spi); iio_device_unregister(indio_dev); From c1d82dbcb0a6b660bc44f7e9c1ba606b671f1b71 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Mon, 15 Feb 2021 15:35:07 +0100 Subject: [PATCH 044/119] docs: iio: fix example formatting This line is part of the code snippet, so it has to be nested in order to be rendered correctly. Signed-off-by: Luca Ceresoli Link: https://lore.kernel.org/r/20210215143511.25471-2-luca@lucaceresoli.net Signed-off-by: Jonathan Cameron --- Documentation/iio/iio_configfs.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/iio/iio_configfs.rst b/Documentation/iio/iio_configfs.rst index 3a5d76f9e2b9..dbc61f35ea05 100644 --- a/Documentation/iio/iio_configfs.rst +++ b/Documentation/iio/iio_configfs.rst @@ -71,7 +71,7 @@ kernel module following the interface in include/linux/iio/sw_trigger.h:: .ops = &iio_trig_sample_ops, }; -module_iio_sw_trigger_driver(iio_trig_sample); + module_iio_sw_trigger_driver(iio_trig_sample); Each trigger type has its own directory under /config/iio/triggers. Loading iio-trig-sample module will create 'trig-sample' trigger type directory From f2163c1e365bef19bb371933272f2327d286fbbb Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Mon, 15 Feb 2021 15:35:08 +0100 Subject: [PATCH 045/119] docs: iio: fix directory naming This directory is a, well, directory, not a file. Signed-off-by: Luca Ceresoli Link: https://lore.kernel.org/r/20210215143511.25471-3-luca@lucaceresoli.net Signed-off-by: Jonathan Cameron --- Documentation/driver-api/iio/buffers.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-api/iio/buffers.rst b/Documentation/driver-api/iio/buffers.rst index 3ddebddc02ca..9f7dc245d37a 100644 --- a/Documentation/driver-api/iio/buffers.rst +++ b/Documentation/driver-api/iio/buffers.rst @@ -28,7 +28,7 @@ IIO buffer setup The meta information associated with a channel reading placed in a buffer is called a scan element. The important bits configuring scan elements are exposed to userspace applications via the -:file:`/sys/bus/iio/iio:device{X}/scan_elements/*` directory. This file contains +:file:`/sys/bus/iio/iio:device{X}/scan_elements/` directory. This directory contains attributes of the following form: * :file:`enable`, used for enabling a channel. If and only if its attribute From bd9a013d2988321ef1122e41874f9cbd141e480a Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Mon, 15 Feb 2021 15:35:09 +0100 Subject: [PATCH 046/119] docs: iio: document the 'index' attribute too Two out of three attributes are documented, document the third one too. Signed-off-by: Luca Ceresoli Link: https://lore.kernel.org/r/20210215143511.25471-4-luca@lucaceresoli.net Signed-off-by: Jonathan Cameron --- Documentation/driver-api/iio/buffers.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/driver-api/iio/buffers.rst b/Documentation/driver-api/iio/buffers.rst index 9f7dc245d37a..9dad7c94034b 100644 --- a/Documentation/driver-api/iio/buffers.rst +++ b/Documentation/driver-api/iio/buffers.rst @@ -34,6 +34,7 @@ attributes of the following form: * :file:`enable`, used for enabling a channel. If and only if its attribute is non *zero*, then a triggered capture will contain data samples for this channel. +* :file:`index`, the scan_index of the channel. * :file:`type`, description of the scan element data storage within the buffer and hence the form in which it is read from user space. Format is [be|le]:[s|u]bits/storagebitsXrepeat[>>shift] . From 218977dcdb2aac1ec0492b51e8473ca6d3387c50 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Mon, 15 Feb 2021 15:35:10 +0100 Subject: [PATCH 047/119] docs: iio: fix bullet list formatting This 2nd-level bullet list is not properly ReST-formatted and thus it gets rendered as a unique paragraph quite unreadable. Fix by adding spaces as needed. While there also swap "shift" and "repeat" so they are in the correct order. Signed-off-by: Luca Ceresoli Link: https://lore.kernel.org/r/20210215143511.25471-5-luca@lucaceresoli.net Signed-off-by: Jonathan Cameron --- Documentation/driver-api/iio/buffers.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/driver-api/iio/buffers.rst b/Documentation/driver-api/iio/buffers.rst index 9dad7c94034b..5801caf216a6 100644 --- a/Documentation/driver-api/iio/buffers.rst +++ b/Documentation/driver-api/iio/buffers.rst @@ -38,15 +38,16 @@ attributes of the following form: * :file:`type`, description of the scan element data storage within the buffer and hence the form in which it is read from user space. Format is [be|le]:[s|u]bits/storagebitsXrepeat[>>shift] . + * *be* or *le*, specifies big or little endian. * *s* or *u*, specifies if signed (2's complement) or unsigned. * *bits*, is the number of valid data bits. * *storagebits*, is the number of bits (after padding) that it occupies in the - buffer. - * *shift*, if specified, is the shift that needs to be applied prior to - masking out unused bits. + buffer. * *repeat*, specifies the number of bits/storagebits repetitions. When the - repeat element is 0 or 1, then the repeat value is omitted. + repeat element is 0 or 1, then the repeat value is omitted. + * *shift*, if specified, is the shift that needs to be applied prior to + masking out unused bits. For example, a driver for a 3-axis accelerometer with 12 bit resolution where data is stored in two 8-bits registers as follows:: @@ -123,4 +124,3 @@ More details .. kernel-doc:: include/linux/iio/buffer.h .. kernel-doc:: drivers/iio/industrialio-buffer.c :export: - From 6bc5ebe8aa08637355cd891e596b5bce63497cb5 Mon Sep 17 00:00:00 2001 From: Luca Ceresoli Date: Mon, 15 Feb 2021 15:35:11 +0100 Subject: [PATCH 048/119] docs: iio: mark "repeat" sysfs attribute as optional Show that this field is optional, just like the shift value. Signed-off-by: Luca Ceresoli Link: https://lore.kernel.org/r/20210215143511.25471-6-luca@lucaceresoli.net Signed-off-by: Jonathan Cameron --- Documentation/driver-api/iio/buffers.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/driver-api/iio/buffers.rst b/Documentation/driver-api/iio/buffers.rst index 5801caf216a6..e83026aebe97 100644 --- a/Documentation/driver-api/iio/buffers.rst +++ b/Documentation/driver-api/iio/buffers.rst @@ -37,7 +37,7 @@ attributes of the following form: * :file:`index`, the scan_index of the channel. * :file:`type`, description of the scan element data storage within the buffer and hence the form in which it is read from user space. - Format is [be|le]:[s|u]bits/storagebitsXrepeat[>>shift] . + Format is [be|le]:[s|u]bits/storagebits[Xrepeat][>>shift] . * *be* or *le*, specifies big or little endian. * *s* or *u*, specifies if signed (2's complement) or unsigned. From 0e41fd515f94dcfcc24b6e510d29528431e46f60 Mon Sep 17 00:00:00 2001 From: Ye Xiang Date: Mon, 1 Feb 2021 13:49:20 +0800 Subject: [PATCH 049/119] iio: hid-sensors: Move get sensitivity attribute to hid-sensor-common No functional change has been made with this patch. The main intent here is to reduce code repetition of getting sensitivity attribute. In the current implementation, sensor_hub_input_get_attribute_info() is called from multiple drivers to get attribute info for sensitivity field. Moving this to common place will avoid code repetition. Signed-off-by: Ye Xiang Acked-by: Alexandre Belloni Link: https://lore.kernel.org/r/20210201054921.18214-2-xiang.ye@intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/hid-sensor-accel-3d.c | 23 ++++++------- .../hid-sensors/hid-sensor-attributes.c | 17 +++++++++- drivers/iio/gyro/hid-sensor-gyro-3d.c | 19 ++++------- drivers/iio/humidity/hid-sensor-humidity.c | 16 ++++------ drivers/iio/light/hid-sensor-als.c | 19 ++++------- drivers/iio/light/hid-sensor-prox.c | 27 +++++----------- drivers/iio/magnetometer/hid-sensor-magn-3d.c | 32 ++++++------------- drivers/iio/orientation/hid-sensor-incl-3d.c | 19 ++++------- drivers/iio/orientation/hid-sensor-rotation.c | 23 ++++++------- .../position/hid-sensor-custom-intel-hinge.c | 20 ++++-------- drivers/iio/pressure/hid-sensor-press.c | 19 ++++------- .../iio/temperature/hid-sensor-temperature.c | 16 ++++------ drivers/rtc/rtc-hid-sensor-time.c | 4 ++- include/linux/hid-sensor-hub.h | 4 ++- 14 files changed, 107 insertions(+), 151 deletions(-) diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index 5d63ed19e6e2..2f9465cb382f 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -43,6 +43,10 @@ static const u32 accel_3d_addresses[ACCEL_3D_CHANNEL_MAX] = { HID_USAGE_SENSOR_ACCEL_Z_AXIS }; +static const u32 accel_3d_sensitivity_addresses[] = { + HID_USAGE_SENSOR_DATA_ACCELERATION, +}; + /* Channel definitions */ static const struct iio_chan_spec accel_3d_channels[] = { { @@ -317,18 +321,6 @@ static int accel_3d_parse_report(struct platform_device *pdev, &st->accel[CHANNEL_SCAN_INDEX_X], &st->scale_pre_decml, &st->scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_ACCELERATION, - &st->common_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->common_attributes.sensitivity.index, - st->common_attributes.sensitivity.report_id); - } - return ret; } @@ -366,8 +358,11 @@ static int hid_accel_3d_probe(struct platform_device *pdev) channel_size = sizeof(gravity_channels); indio_dev->num_channels = ARRAY_SIZE(gravity_channels); } - ret = hid_sensor_parse_common_attributes(hsdev, hsdev->usage, - &accel_state->common_attributes); + ret = hid_sensor_parse_common_attributes(hsdev, + hsdev->usage, + &accel_state->common_attributes, + accel_3d_sensitivity_addresses, + ARRAY_SIZE(accel_3d_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index 5b822a4298a0..d349ace2e33f 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -448,12 +448,15 @@ EXPORT_SYMBOL(hid_sensor_batch_mode_supported); int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, u32 usage_id, - struct hid_sensor_common *st) + struct hid_sensor_common *st, + const u32 *sensitivity_addresses, + u32 sensitivity_addresses_len) { struct hid_sensor_hub_attribute_info timestamp; s32 value; int ret; + int i; hid_sensor_get_reporting_interval(hsdev, usage_id, st); @@ -475,6 +478,18 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS, &st->sensitivity); + /* + * Set Sensitivity field ids, when there is no individual modifier, will + * check absolute sensitivity of data field + */ + for (i = 0; i < sensitivity_addresses_len && st->sensitivity.index < 0; i++) { + sensor_hub_input_get_attribute_info(hsdev, + HID_FEATURE_REPORT, usage_id, + HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | + sensitivity_addresses[i], + &st->sensitivity); + } + st->raw_hystersis = -1; sensor_hub_input_get_attribute_info(hsdev, diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index fb0d678ece1a..dad26ee4fd1f 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -45,6 +45,10 @@ static const u32 gyro_3d_addresses[GYRO_3D_CHANNEL_MAX] = { HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS }; +static const u32 gryo_3d_sensitivity_addresses[] = { + HID_USAGE_SENSOR_DATA_ANGL_VELOCITY, +}; + /* Channel definitions */ static const struct iio_chan_spec gyro_3d_channels[] = { { @@ -271,17 +275,6 @@ static int gyro_3d_parse_report(struct platform_device *pdev, &st->gyro[CHANNEL_SCAN_INDEX_X], &st->scale_pre_decml, &st->scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_ANGL_VELOCITY, - &st->common_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->common_attributes.sensitivity.index, - st->common_attributes.sensitivity.report_id); - } return ret; } @@ -305,7 +298,9 @@ static int hid_gyro_3d_probe(struct platform_device *pdev) ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_GYRO_3D, - &gyro_state->common_attributes); + &gyro_state->common_attributes, + gryo_3d_sensitivity_addresses, + ARRAY_SIZE(gryo_3d_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; diff --git a/drivers/iio/humidity/hid-sensor-humidity.c b/drivers/iio/humidity/hid-sensor-humidity.c index 52f605114ef7..ec88ae3f233d 100644 --- a/drivers/iio/humidity/hid-sensor-humidity.c +++ b/drivers/iio/humidity/hid-sensor-humidity.c @@ -22,6 +22,10 @@ struct hid_humidity_state { int value_offset; }; +static const u32 humidity_sensitivity_addresses[] = { + HID_USAGE_SENSOR_ATMOSPHERIC_HUMIDITY, +}; + /* Channel definitions */ static const struct iio_chan_spec humidity_channels[] = { { @@ -174,14 +178,6 @@ static int humidity_parse_report(struct platform_device *pdev, &st->scale_pre_decml, &st->scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_ATMOSPHERIC_HUMIDITY, - &st->common_attributes.sensitivity); - return ret; } @@ -210,7 +206,9 @@ static int hid_humidity_probe(struct platform_device *pdev) ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_HUMIDITY, - &humid_st->common_attributes); + &humid_st->common_attributes, + humidity_sensitivity_addresses, + ARRAY_SIZE(humidity_sensitivity_addresses)); if (ret) return ret; diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index 4093f2353d95..8bf6e9e0a0e0 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -39,6 +39,10 @@ struct als_state { s64 timestamp; }; +static const u32 als_sensitivity_addresses[] = { + HID_USAGE_SENSOR_DATA_LIGHT, +}; + /* Channel definitions */ static const struct iio_chan_spec als_channels[] = { { @@ -252,17 +256,6 @@ static int als_parse_report(struct platform_device *pdev, &st->als_illum, &st->scale_pre_decml, &st->scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_LIGHT, - &st->common_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->common_attributes.sensitivity.index, - st->common_attributes.sensitivity.report_id); - } return ret; } @@ -285,7 +278,9 @@ static int hid_als_probe(struct platform_device *pdev) als_state->common_attributes.pdev = pdev; ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_ALS, - &als_state->common_attributes); + &als_state->common_attributes, + als_sensitivity_addresses, + ARRAY_SIZE(als_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c index 330cf359e0b8..4ab285a418d5 100644 --- a/drivers/iio/light/hid-sensor-prox.c +++ b/drivers/iio/light/hid-sensor-prox.c @@ -25,6 +25,11 @@ struct prox_state { u32 human_presence; }; +static const u32 prox_sensitivity_addresses[] = { + HID_USAGE_SENSOR_HUMAN_PRESENCE, + HID_USAGE_SENSOR_DATA_PRESENCE, +}; + /* Channel definitions */ static const struct iio_chan_spec prox_channels[] = { { @@ -216,24 +221,6 @@ static int prox_parse_report(struct platform_device *pdev, dev_dbg(&pdev->dev, "prox %x:%x\n", st->prox_attr.index, st->prox_attr.report_id); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_PRESENCE, - &st->common_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->common_attributes.sensitivity.index, - st->common_attributes.sensitivity.report_id); - } - if (st->common_attributes.sensitivity.index < 0) - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_HUMAN_PRESENCE, - &st->common_attributes.sensitivity); - return ret; } @@ -257,7 +244,9 @@ static int hid_prox_probe(struct platform_device *pdev) prox_state->common_attributes.pdev = pdev; ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_PROX, - &prox_state->common_attributes); + &prox_state->common_attributes, + prox_sensitivity_addresses, + ARRAY_SIZE(prox_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index fa48044b7f5b..b78691523dd4 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -62,6 +62,11 @@ static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = { HID_USAGE_SENSOR_TIME_TIMESTAMP, }; +static const u32 magn_3d_sensitivity_addresses[] = { + HID_USAGE_SENSOR_DATA_ORIENTATION, + HID_USAGE_SENSOR_ORIENT_MAGN_FLUX, +}; + /* Channel definitions */ static const struct iio_chan_spec magn_3d_channels[] = { { @@ -448,27 +453,6 @@ static int magn_3d_parse_report(struct platform_device *pdev, &st->rot_attr.scale_pre_decml, &st->rot_attr.scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->magn_flux_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_ORIENTATION, - &st->magn_flux_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->magn_flux_attributes.sensitivity.index, - st->magn_flux_attributes.sensitivity.report_id); - } - if (st->magn_flux_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_ORIENT_MAGN_FLUX, - &st->magn_flux_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->magn_flux_attributes.sensitivity.index, - st->magn_flux_attributes.sensitivity.report_id); - } if (st->rot_attributes.sensitivity.index < 0) { sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT, usage_id, @@ -507,12 +491,16 @@ static int hid_magn_3d_probe(struct platform_device *pdev) ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_COMPASS_3D, - &magn_state->magn_flux_attributes); + &magn_state->magn_flux_attributes, + magn_3d_sensitivity_addresses, + ARRAY_SIZE(magn_3d_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; } magn_state->rot_attributes = magn_state->magn_flux_attributes; + /* sensitivity of rot_attribute is not the same as magn_flux_attributes */ + magn_state->rot_attributes.sensitivity.index = -1; ret = magn_3d_parse_report(pdev, hsdev, &channels, &chan_count, diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c index 52ebef30f9be..6e69f6e673cc 100644 --- a/drivers/iio/orientation/hid-sensor-incl-3d.c +++ b/drivers/iio/orientation/hid-sensor-incl-3d.c @@ -47,6 +47,10 @@ static const u32 incl_3d_addresses[INCLI_3D_CHANNEL_MAX] = { HID_USAGE_SENSOR_ORIENT_TILT_Z }; +static const u32 incl_3d_sensitivity_addresses[] = { + HID_USAGE_SENSOR_DATA_ORIENTATION, +}; + /* Channel definitions */ static const struct iio_chan_spec incl_3d_channels[] = { { @@ -291,17 +295,6 @@ static int incl_3d_parse_report(struct platform_device *pdev, &st->incl[CHANNEL_SCAN_INDEX_X], &st->scale_pre_decml, &st->scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_ORIENTATION, - &st->common_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->common_attributes.sensitivity.index, - st->common_attributes.sensitivity.report_id); - } return ret; } @@ -327,7 +320,9 @@ static int hid_incl_3d_probe(struct platform_device *pdev) ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D, - &incl_state->common_attributes); + &incl_state->common_attributes, + incl_3d_sensitivity_addresses, + ARRAY_SIZE(incl_3d_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index c087d8f72a54..baa2ee353ba4 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -31,6 +31,10 @@ struct dev_rot_state { s64 timestamp; }; +static const u32 rotation_sensitivity_addresses[] = { + HID_USAGE_SENSOR_DATA_ORIENTATION, +}; + /* Channel definitions */ static const struct iio_chan_spec dev_rot_channels[] = { { @@ -221,18 +225,6 @@ static int dev_rot_parse_report(struct platform_device *pdev, &st->quaternion, &st->scale_pre_decml, &st->scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_ORIENTATION, - &st->common_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->common_attributes.sensitivity.index, - st->common_attributes.sensitivity.report_id); - } - return 0; } @@ -270,8 +262,11 @@ static int hid_dev_rot_probe(struct platform_device *pdev) return -EINVAL; } - ret = hid_sensor_parse_common_attributes(hsdev, hsdev->usage, - &rot_state->common_attributes); + ret = hid_sensor_parse_common_attributes(hsdev, + hsdev->usage, + &rot_state->common_attributes, + rotation_sensitivity_addresses, + ARRAY_SIZE(rotation_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; diff --git a/drivers/iio/position/hid-sensor-custom-intel-hinge.c b/drivers/iio/position/hid-sensor-custom-intel-hinge.c index 64a7fa7db6af..fd77e7ee87f3 100644 --- a/drivers/iio/position/hid-sensor-custom-intel-hinge.c +++ b/drivers/iio/position/hid-sensor-custom-intel-hinge.c @@ -47,6 +47,10 @@ struct hinge_state { u64 timestamp; }; +static const u32 hinge_sensitivity_addresses[] = { + HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(1), +}; + /* Channel definitions */ static const struct iio_chan_spec hinge_channels[] = { { @@ -251,18 +255,6 @@ static int hinge_parse_report(struct platform_device *pdev, &st->hinge[CHANNEL_SCAN_INDEX_HINGE_ANGLE], &st->scale_pre_decml, &st->scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(1), - &st->common_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->common_attributes.sensitivity.index, - st->common_attributes.sensitivity.report_id); - } - return ret; } @@ -289,7 +281,9 @@ static int hid_hinge_probe(struct platform_device *pdev) st->labels[i] = hinge_labels[i]; ret = hid_sensor_parse_common_attributes(hsdev, hsdev->usage, - &st->common_attributes); + &st->common_attributes, + hinge_sensitivity_addresses, + ARRAY_SIZE(hinge_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c index 5c458788f346..8cac2c94e75a 100644 --- a/drivers/iio/pressure/hid-sensor-press.c +++ b/drivers/iio/pressure/hid-sensor-press.c @@ -29,6 +29,10 @@ struct press_state { int value_offset; }; +static const u32 press_sensitivity_addresses[] = { + HID_USAGE_SENSOR_DATA_ATMOSPHERIC_PRESSURE, +}; + /* Channel definitions */ static const struct iio_chan_spec press_channels[] = { { @@ -225,17 +229,6 @@ static int press_parse_report(struct platform_device *pdev, &st->press_attr, &st->scale_pre_decml, &st->scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_ATMOSPHERIC_PRESSURE, - &st->common_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->common_attributes.sensitivity.index, - st->common_attributes.sensitivity.report_id); - } return ret; } @@ -260,7 +253,9 @@ static int hid_press_probe(struct platform_device *pdev) ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_PRESSURE, - &press_state->common_attributes); + &press_state->common_attributes, + press_sensitivity_addresses, + ARRAY_SIZE(press_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c index 81688f1b932f..e3d38cbcf354 100644 --- a/drivers/iio/temperature/hid-sensor-temperature.c +++ b/drivers/iio/temperature/hid-sensor-temperature.c @@ -22,6 +22,10 @@ struct temperature_state { int value_offset; }; +static const u32 temperature_sensitivity_addresses[] = { + HID_USAGE_SENSOR_DATA_ENVIRONMENTAL_TEMPERATURE, +}; + /* Channel definitions */ static const struct iio_chan_spec temperature_channels[] = { { @@ -171,14 +175,6 @@ static int temperature_parse_report(struct platform_device *pdev, &st->temperature_attr, &st->scale_pre_decml, &st->scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_ENVIRONMENTAL_TEMPERATURE, - &st->common_attributes.sensitivity); - return ret; } @@ -207,7 +203,9 @@ static int hid_temperature_probe(struct platform_device *pdev) ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_TEMPERATURE, - &temp_st->common_attributes); + &temp_st->common_attributes, + temperature_sensitivity_addresses, + ARRAY_SIZE(temperature_sensitivity_addresses)); if (ret) return ret; diff --git a/drivers/rtc/rtc-hid-sensor-time.c b/drivers/rtc/rtc-hid-sensor-time.c index 1b42ee0758d2..47cd12db2356 100644 --- a/drivers/rtc/rtc-hid-sensor-time.c +++ b/drivers/rtc/rtc-hid-sensor-time.c @@ -238,7 +238,9 @@ static int hid_time_probe(struct platform_device *pdev) ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_TIME, - &time_state->common_attributes); + &time_state->common_attributes, + NULL, + 0); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes!\n"); return ret; diff --git a/include/linux/hid-sensor-hub.h b/include/linux/hid-sensor-hub.h index 763802b2b8f9..637ec53a98a1 100644 --- a/include/linux/hid-sensor-hub.h +++ b/include/linux/hid-sensor-hub.h @@ -248,7 +248,9 @@ static inline int hid_sensor_convert_exponent(int unit_expo) int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, u32 usage_id, - struct hid_sensor_common *st); + struct hid_sensor_common *st, + const u32 *sensitivity_addresses, + u32 sensitivity_addresses_len); int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, int val1, int val2); int hid_sensor_read_raw_hyst_value(struct hid_sensor_common *st, From 4efd13c3c2bc9a5fc37fa34b7d4d2ec1bdf0d127 Mon Sep 17 00:00:00 2001 From: Ye Xiang Date: Mon, 1 Feb 2021 13:49:21 +0800 Subject: [PATCH 050/119] hid-sensors: Add more data fields for sensitivity checking Before, when reading/writing the hysteresis of als, incli-3d, press, and rotation sensor, we will get invalid argument error. This patch add more sensitivity data fields for these sensors, so that these sensors can get sensitivity index and return correct hysteresis value. Signed-off-by: Ye Xiang Link: https://lore.kernel.org/r/20210201054921.18214-3-xiang.ye@intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/hid-sensor-als.c | 1 + drivers/iio/orientation/hid-sensor-incl-3d.c | 1 + drivers/iio/orientation/hid-sensor-rotation.c | 1 + drivers/iio/pressure/hid-sensor-press.c | 1 + 4 files changed, 4 insertions(+) diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index 8bf6e9e0a0e0..afcdb424bfb8 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -41,6 +41,7 @@ struct als_state { static const u32 als_sensitivity_addresses[] = { HID_USAGE_SENSOR_DATA_LIGHT, + HID_USAGE_SENSOR_LIGHT_ILLUM, }; /* Channel definitions */ diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c index 6e69f6e673cc..7af48d336285 100644 --- a/drivers/iio/orientation/hid-sensor-incl-3d.c +++ b/drivers/iio/orientation/hid-sensor-incl-3d.c @@ -49,6 +49,7 @@ static const u32 incl_3d_addresses[INCLI_3D_CHANNEL_MAX] = { static const u32 incl_3d_sensitivity_addresses[] = { HID_USAGE_SENSOR_DATA_ORIENTATION, + HID_USAGE_SENSOR_ORIENT_TILT, }; /* Channel definitions */ diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index baa2ee353ba4..cf7f57a47681 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -33,6 +33,7 @@ struct dev_rot_state { static const u32 rotation_sensitivity_addresses[] = { HID_USAGE_SENSOR_DATA_ORIENTATION, + HID_USAGE_SENSOR_ORIENT_QUATERNION, }; /* Channel definitions */ diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c index 8cac2c94e75a..c416d261e3e3 100644 --- a/drivers/iio/pressure/hid-sensor-press.c +++ b/drivers/iio/pressure/hid-sensor-press.c @@ -31,6 +31,7 @@ struct press_state { static const u32 press_sensitivity_addresses[] = { HID_USAGE_SENSOR_DATA_ATMOSPHERIC_PRESSURE, + HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE }; /* Channel definitions */ From 1c71a2863a0c56123e5a67880cf658083c0a0b1e Mon Sep 17 00:00:00 2001 From: Ye Xiang Date: Sun, 7 Feb 2021 15:00:46 +0800 Subject: [PATCH 051/119] iio: Add relative sensitivity support Some hid sensors may use relative sensitivity such as als sensor. This patch adds relative sensitivity checking for all hid sensors. Signed-off-by: Ye Xiang Acked-by: Jiri Kosina Link: https://lore.kernel.org/r/20210207070048.23935-2-xiang.ye@intel.com Signed-off-by: Jonathan Cameron --- .../hid-sensors/hid-sensor-attributes.c | 74 ++++++++++++++++++- drivers/iio/industrialio-core.c | 1 + include/linux/hid-sensor-hub.h | 5 ++ include/linux/hid-sensor-ids.h | 1 + include/linux/iio/types.h | 1 + 5 files changed, 78 insertions(+), 4 deletions(-) diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index d349ace2e33f..cb52b4fd6bf7 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -263,6 +263,29 @@ int hid_sensor_read_raw_hyst_value(struct hid_sensor_common *st, } EXPORT_SYMBOL(hid_sensor_read_raw_hyst_value); +int hid_sensor_read_raw_hyst_rel_value(struct hid_sensor_common *st, int *val1, + int *val2) +{ + s32 value; + int ret; + + ret = sensor_hub_get_feature(st->hsdev, + st->sensitivity_rel.report_id, + st->sensitivity_rel.index, sizeof(value), + &value); + if (ret < 0 || value < 0) { + *val1 = *val2 = 0; + return -EINVAL; + } + + convert_from_vtf_format(value, st->sensitivity_rel.size, + st->sensitivity_rel.unit_expo, val1, val2); + + return IIO_VAL_INT_PLUS_MICRO; +} +EXPORT_SYMBOL(hid_sensor_read_raw_hyst_rel_value); + + int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, int val1, int val2) { @@ -294,6 +317,37 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, } EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value); +int hid_sensor_write_raw_hyst_rel_value(struct hid_sensor_common *st, + int val1, int val2) +{ + s32 value; + int ret; + + if (val1 < 0 || val2 < 0) + return -EINVAL; + + value = convert_to_vtf_format(st->sensitivity_rel.size, + st->sensitivity_rel.unit_expo, + val1, val2); + ret = sensor_hub_set_feature(st->hsdev, st->sensitivity_rel.report_id, + st->sensitivity_rel.index, sizeof(value), + &value); + if (ret < 0 || value < 0) + return -EINVAL; + + ret = sensor_hub_get_feature(st->hsdev, + st->sensitivity_rel.report_id, + st->sensitivity_rel.index, sizeof(value), + &value); + if (ret < 0 || value < 0) + return -EINVAL; + + st->raw_hystersis = value; + + return 0; +} +EXPORT_SYMBOL(hid_sensor_write_raw_hyst_rel_value); + /* * This fuction applies the unit exponent to the scale. * For example: @@ -478,16 +532,28 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS, &st->sensitivity); + sensor_hub_input_get_attribute_info(hsdev, + HID_FEATURE_REPORT, usage_id, + HID_USAGE_SENSOR_PROP_SENSITIVITY_REL_PCT, + &st->sensitivity_rel); /* * Set Sensitivity field ids, when there is no individual modifier, will - * check absolute sensitivity of data field + * check absolute sensitivity and relative sensitivity of data field */ - for (i = 0; i < sensitivity_addresses_len && st->sensitivity.index < 0; i++) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, + for (i = 0; i < sensitivity_addresses_len; i++) { + if (st->sensitivity.index < 0) + sensor_hub_input_get_attribute_info( + hsdev, HID_FEATURE_REPORT, usage_id, HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | sensitivity_addresses[i], &st->sensitivity); + + if (st->sensitivity_rel.index < 0) + sensor_hub_input_get_attribute_info( + hsdev, HID_FEATURE_REPORT, usage_id, + HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_REL_PCT | + sensitivity_addresses[i], + &st->sensitivity_rel); } st->raw_hystersis = -1; diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 2e2970f3e21c..cb2735d2ae4b 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -157,6 +157,7 @@ static const char * const iio_chan_info_postfix[] = { [IIO_CHAN_INFO_PHASE] = "phase", [IIO_CHAN_INFO_HARDWAREGAIN] = "hardwaregain", [IIO_CHAN_INFO_HYSTERESIS] = "hysteresis", + [IIO_CHAN_INFO_HYSTERESIS_RELATIVE] = "hysteresis_relative", [IIO_CHAN_INFO_INT_TIME] = "integration_time", [IIO_CHAN_INFO_ENABLE] = "en", [IIO_CHAN_INFO_CALIBHEIGHT] = "calibheight", diff --git a/include/linux/hid-sensor-hub.h b/include/linux/hid-sensor-hub.h index 637ec53a98a1..c27329e2a5ad 100644 --- a/include/linux/hid-sensor-hub.h +++ b/include/linux/hid-sensor-hub.h @@ -231,6 +231,7 @@ struct hid_sensor_common { struct hid_sensor_hub_attribute_info report_state; struct hid_sensor_hub_attribute_info power_state; struct hid_sensor_hub_attribute_info sensitivity; + struct hid_sensor_hub_attribute_info sensitivity_rel; struct hid_sensor_hub_attribute_info report_latency; struct work_struct work; }; @@ -253,8 +254,12 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, u32 sensitivity_addresses_len); int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, int val1, int val2); +int hid_sensor_write_raw_hyst_rel_value(struct hid_sensor_common *st, int val1, + int val2); int hid_sensor_read_raw_hyst_value(struct hid_sensor_common *st, int *val1, int *val2); +int hid_sensor_read_raw_hyst_rel_value(struct hid_sensor_common *st, + int *val1, int *val2); int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st, int val1, int val2); int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st, diff --git a/include/linux/hid-sensor-ids.h b/include/linux/hid-sensor-ids.h index 3bbdbccc5805..ac631159403a 100644 --- a/include/linux/hid-sensor-ids.h +++ b/include/linux/hid-sensor-ids.h @@ -149,6 +149,7 @@ /* Per data field properties */ #define HID_USAGE_SENSOR_DATA_MOD_NONE 0x00 #define HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS 0x1000 +#define HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_REL_PCT 0xE000 /* Power state enumerations */ #define HID_USAGE_SENSOR_PROP_POWER_STATE_UNDEFINED_ENUM 0x200850 diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 1e3ed6f55bca..5aa7f66d4345 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -50,6 +50,7 @@ enum iio_chan_info_enum { IIO_CHAN_INFO_PHASE, IIO_CHAN_INFO_HARDWAREGAIN, IIO_CHAN_INFO_HYSTERESIS, + IIO_CHAN_INFO_HYSTERESIS_RELATIVE, IIO_CHAN_INFO_INT_TIME, IIO_CHAN_INFO_ENABLE, IIO_CHAN_INFO_CALIBHEIGHT, From 84dbc231a6f56c810fdd1908c723c0c23c9e169b Mon Sep 17 00:00:00 2001 From: Ye Xiang Date: Sun, 7 Feb 2021 15:00:47 +0800 Subject: [PATCH 052/119] iio: hid-sensor-als: Add relative hysteresis support Hid sensor als use relative hysteresis, this patch adds the support. Signed-off-by: Ye Xiang Link: https://lore.kernel.org/r/20210207070048.23935-3-xiang.ye@intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/hid-sensor-als.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index afcdb424bfb8..85c8a05b73cb 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -54,7 +54,8 @@ static const struct iio_chan_spec als_channels[] = { .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ) | - BIT(IIO_CHAN_INFO_HYSTERESIS), + BIT(IIO_CHAN_INFO_HYSTERESIS) | + BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE), .scan_index = CHANNEL_SCAN_INDEX_INTENSITY, }, { @@ -63,7 +64,8 @@ static const struct iio_chan_spec als_channels[] = { .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ) | - BIT(IIO_CHAN_INFO_HYSTERESIS), + BIT(IIO_CHAN_INFO_HYSTERESIS) | + BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE), .scan_index = CHANNEL_SCAN_INDEX_ILLUM, }, IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP) @@ -141,6 +143,10 @@ static int als_read_raw(struct iio_dev *indio_dev, ret_type = hid_sensor_read_raw_hyst_value( &als_state->common_attributes, val, val2); break; + case IIO_CHAN_INFO_HYSTERESIS_RELATIVE: + ret_type = hid_sensor_read_raw_hyst_rel_value( + &als_state->common_attributes, val, val2); + break; default: ret_type = -EINVAL; break; @@ -168,6 +174,10 @@ static int als_write_raw(struct iio_dev *indio_dev, ret = hid_sensor_write_raw_hyst_value( &als_state->common_attributes, val, val2); break; + case IIO_CHAN_INFO_HYSTERESIS_RELATIVE: + ret = hid_sensor_write_raw_hyst_rel_value( + &als_state->common_attributes, val, val2); + break; default: ret = -EINVAL; } From 3a826f7c64080ec2789abc229d8012430fa092b7 Mon Sep 17 00:00:00 2001 From: Ye Xiang Date: Sun, 7 Feb 2021 15:00:48 +0800 Subject: [PATCH 053/119] iio: Add relative hysteresis in ABI documentation Add relative hysteresis in ABI documentation for als sensor. Signed-off-by: Ye Xiang Link: https://lore.kernel.org/r/20210207070048.23935-4-xiang.ye@intel.com Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index d957f5da5c04..0890bd113eea 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1823,3 +1823,12 @@ Description: hinge, keyboard, screen. It means the three channels each correspond respectively to hinge angle, keyboard angle, and screen angle. + +What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_hysteresis_relative +What: /sys/bus/iio/devices/iio:deviceX/in_intensity_hysteresis_relative +KernelVersion: 5.12 +Contact: linux-iio@vger.kernel.org +Description: + Specify the percent for light sensor relative to the channel + absolute value that a data field should change before an event + is generated. Units are a percentage of the prior reading. From 6505dfab33c519368e54ae7f3ea1bf4d9916fdc5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 15 Feb 2021 20:10:02 +0100 Subject: [PATCH 054/119] iio: documentation: Document proximity sensor label use Add an entry to Documentation/ABI/testing/sysfs-bus-iio for the new device label sysfs-attribute support. And document the standardized labels which may be used with proximity sensors to hint userspace about the intended use of the sensor. Using labels to differentiate between the multiple proximity sensors which a modern laptop/tablet may have was discussed in this thread: https://lore.kernel.org/linux-iio/9f9b0ff6-3bf1-63c4-eb36-901cecd7c4d9@redhat.com/ As mentioned there the "proximity-wifi*" labels are already being used in this manner on some chromebooks, see e.g.: arch/arm64/boot/dts/qcom/sc7180-trogdor.dtsi arch/arm64/boot/dts/qcom/sc7180-trogdor-lte-sku.dtsi And the "proximity-palmrest" and "proximity-lap" labels are intended to be used with the lap and palmrest sensors found in recent Lenovo ThinkPad models. Cc: Dmitry Torokhov Cc: Mark Pearson Cc: Bastien Nocera Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20210215191003.698888-1-hdegoede@redhat.com Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 39 +++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 0890bd113eea..c42fe980f395 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -33,6 +33,45 @@ Description: Description of the physical chip / device for device X. Typically a part number. +What: /sys/bus/iio/devices/iio:deviceX/label +KernelVersion: 5.8 +Contact: linux-iio@vger.kernel.org +Description: + Optional symbolic label for a device. + This is useful for userspace to be able to better identify an + individual device. + + The contents of the label are free-form, but there are some + standardized uses: + + For proximity sensors which give the proximity (of a person) to + a certain wlan or wwan antenna the following standardized labels + are used: + + * "proximity-wifi" + * "proximity-lte" + * "proximity-wifi-lte" + * "proximity-wifi-left" + * "proximity-wifi-right" + + These are used to indicate to userspace that these proximity + sensors may be used to tune transmit power to ensure that + Specific Absorption Rate (SAR) limits are honored. + The "-left" and "-right" labels are for devices with multiple + antennas. + + In some laptops/tablets the standardized proximity sensor labels + instead indicate proximity to a specific part of the device: + + * "proximity-palmrest" indicates proximity to the keyboard's palmrest + * "proximity-palmrest-left" indicates proximity to the left part of the palmrest + * "proximity-palmrest-right" indicates proximity to the right part of the palmrest + * "proximity-lap" indicates the device is being used on someone's lap + + Note "proximity-lap" is special in that its value may be + calculated by firmware from other sensor readings, rather then + being a raw sensor reading. + What: /sys/bus/iio/devices/iio:deviceX/current_timestamp_clock KernelVersion: 4.5 Contact: linux-iio@vger.kernel.org From 9a6df4b1ab0e467f23ccdcbb82700cfb3eaf44a3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 15 Feb 2021 20:10:03 +0100 Subject: [PATCH 055/119] iio: documentation: Document accelerometer label use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some 2-in-1 laptops / convertibles with 360° (yoga-style) hinges, have 2 accelerometers, 1 in their base and 1 in their display. In many cases the kernel can detect the location of each accelerometer based on e.g. information from the ACPI tables. It is important for userspace to know the location of the 2 accelerometers. Rather then adding a new sysfs-attribute for this we can relay this information to userspace by using standardized label strings for this. This mirrors how this is done for proximity sensors. This commit documents 2 new standardized label strings for this purpose: "accel-base" "accel-display" Note the "base" and "display" suffixes were chosen to match the values used for the systemd/udev hwdb.d/60-sensor.hwdb file's ACCEL_LOCATION property. Cc: Dmitry Torokhov Cc: Mark Pearson Cc: Bastien Nocera Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20210215191003.698888-2-hdegoede@redhat.com Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index c42fe980f395..d709a5181980 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -72,6 +72,13 @@ Description: calculated by firmware from other sensor readings, rather then being a raw sensor reading. + For accelerometers used in 2-in-1s with 360° (yoga-style) hinges, + which have an accelerometer in both their base and their display, + the following standardized labels are used: + + * "accel-base" + * "accel-display" + What: /sys/bus/iio/devices/iio:deviceX/current_timestamp_clock KernelVersion: 4.5 Contact: linux-iio@vger.kernel.org From 477bd010c20efc377c55e6077b0526201a7a33b3 Mon Sep 17 00:00:00 2001 From: Mircea Caprioru Date: Wed, 17 Feb 2021 09:41:02 +0200 Subject: [PATCH 056/119] iio: dac: ad5686: Add support for AD5673R/AD5677R The AD5673R/AD5677R are low power, 16-channel, 12-/16-bit buffered voltage output digital-to-analog converters (DACs). They include a 2.5 V internal reference (enabled by default). These devices are very similar to AD5674R/AD5679R, except that they have an i2c interface. Signed-off-by: Mircea Caprioru Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210217074102.23148-1-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 5 +++-- drivers/iio/dac/ad5686.c | 12 ++++++++++++ drivers/iio/dac/ad5686.h | 2 ++ drivers/iio/dac/ad5696-i2c.c | 6 ++++-- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index cea07b4cced1..75e1f2b48638 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -142,8 +142,9 @@ config AD5696_I2C select AD5686 help Say yes here to build support for Analog Devices AD5311R, AD5338R, - AD5671R, AD5675R, AD5691R, AD5692R, AD5693, AD5693R, AD5694, AD5694R, - AD5695R, AD5696, and AD5696R Digital to Analog converters. + AD5671R, AD5673R, AD5675R, AD5677R, AD5691R, AD5692R, AD5693, AD5693R, + AD5694, AD5694R, AD5695R, AD5696, and AD5696R Digital to Analog + converters. To compile this driver as a module, choose M here: the module will be called ad5696. diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 7d6792ac1020..99a95282ac57 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -301,6 +301,12 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { .num_channels = 8, .regmap_type = AD5686_REGMAP, }, + [ID_AD5673R] = { + .channels = ad5674r_channels, + .int_vref_mv = 2500, + .num_channels = 16, + .regmap_type = AD5686_REGMAP, + }, [ID_AD5674R] = { .channels = ad5674r_channels, .int_vref_mv = 2500, @@ -324,6 +330,12 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { .num_channels = 8, .regmap_type = AD5686_REGMAP, }, + [ID_AD5677R] = { + .channels = ad5679r_channels, + .int_vref_mv = 2500, + .num_channels = 16, + .regmap_type = AD5686_REGMAP, + }, [ID_AD5679R] = { .channels = ad5679r_channels, .int_vref_mv = 2500, diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index d9c8ba413fe9..f89a6f92b427 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -55,10 +55,12 @@ enum ad5686_supported_device_ids { ID_AD5338R, ID_AD5671R, ID_AD5672R, + ID_AD5673R, ID_AD5674R, ID_AD5675R, ID_AD5676, ID_AD5676R, + ID_AD5677R, ID_AD5679R, ID_AD5681R, ID_AD5682R, diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index a39eda7c02d2..24a6a4a5a2e0 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * AD5671R, AD5675R, AD5691R, AD5692R, AD5693, AD5693R, - * AD5694, AD5694R, AD5695R, AD5696, AD5696R + * AD5338R, AD5671R, AD5673R, AD5675R, AD5677R, AD5691R, AD5692R, AD5693, + * AD5693R, AD5694, AD5694R, AD5695R, AD5696, AD5696R * Digital to analog converters driver * * Copyright 2018 Analog Devices Inc. @@ -74,7 +74,9 @@ static const struct i2c_device_id ad5686_i2c_id[] = { {"ad5311r", ID_AD5311R}, {"ad5338r", ID_AD5338R}, {"ad5671r", ID_AD5671R}, + {"ad5673r", ID_AD5673R}, {"ad5675r", ID_AD5675R}, + {"ad5677r", ID_AD5677R}, {"ad5691r", ID_AD5691R}, {"ad5692r", ID_AD5692R}, {"ad5693", ID_AD5693}, From 63cd35f34d2e75dde3dd3ab51ae09b5daad755e7 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Wed, 17 Feb 2021 10:34:34 +0200 Subject: [PATCH 057/119] iio: Documentation: update definitions for bufferY and scan_elements Since the new change to the IIO buffer infrastructure, the buffer/ and scan_elements/ directories have been merged into bufferY/ to have some attributes available per-buffer. This change updates the ABI docs to reflect this change. The hwfifo attributes are not updated, as for now these should be used via the legacy buffer/ directory until they are moved into core. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210217083438.37865-2-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 85 +++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index d709a5181980..affd4ce871d7 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1164,12 +1164,16 @@ Description: What: /sys/bus/iio/devices/iio:deviceX/buffer/length KernelVersion: 2.6.35 +What: /sys/bus/iio/devices/iio:deviceX/bufferY/length +KernelVersion: 5.11 Contact: linux-iio@vger.kernel.org Description: Number of scans contained by the buffer. What: /sys/bus/iio/devices/iio:deviceX/buffer/enable KernelVersion: 2.6.35 +What: /sys/bus/iio/devices/iio:deviceX/bufferY/enable +KernelVersion: 5.11 Contact: linux-iio@vger.kernel.org Description: Actually start the buffer capture up. Will start trigger @@ -1177,11 +1181,16 @@ Description: What: /sys/bus/iio/devices/iio:deviceX/scan_elements KernelVersion: 2.6.37 +What: /sys/bus/iio/devices/iio:deviceX/bufferY +KernelVersion: 5.11 Contact: linux-iio@vger.kernel.org Description: Directory containing interfaces for elements that will be captured for a single triggered sample set in the buffer. + Since kernel 5.11 the scan_elements attributes are merged into + the bufferY directory, to be configurable per buffer. + What: /sys/.../iio:deviceX/scan_elements/in_accel_x_en What: /sys/.../iio:deviceX/scan_elements/in_accel_y_en What: /sys/.../iio:deviceX/scan_elements/in_accel_z_en @@ -1210,6 +1219,34 @@ What: /sys/.../iio:deviceX/scan_elements/in_pressure_en What: /sys/.../iio:deviceX/scan_elements/in_rot_quaternion_en What: /sys/.../iio:deviceX/scan_elements/in_proximity_en KernelVersion: 2.6.37 +What: /sys/.../iio:deviceX/bufferY/in_accel_x_en +What: /sys/.../iio:deviceX/bufferY/in_accel_y_en +What: /sys/.../iio:deviceX/bufferY/in_accel_z_en +What: /sys/.../iio:deviceX/bufferY/in_anglvel_x_en +What: /sys/.../iio:deviceX/bufferY/in_anglvel_y_en +What: /sys/.../iio:deviceX/bufferY/in_anglvel_z_en +What: /sys/.../iio:deviceX/bufferY/in_magn_x_en +What: /sys/.../iio:deviceX/bufferY/in_magn_y_en +What: /sys/.../iio:deviceX/bufferY/in_magn_z_en +What: /sys/.../iio:deviceX/bufferY/in_rot_from_north_magnetic_en +What: /sys/.../iio:deviceX/bufferY/in_rot_from_north_true_en +What: /sys/.../iio:deviceX/bufferY/in_rot_from_north_magnetic_tilt_comp_en +What: /sys/.../iio:deviceX/bufferY/in_rot_from_north_true_tilt_comp_en +What: /sys/.../iio:deviceX/bufferY/in_timestamp_en +What: /sys/.../iio:deviceX/bufferY/in_voltageY_supply_en +What: /sys/.../iio:deviceX/bufferY/in_voltageY_en +What: /sys/.../iio:deviceX/bufferY/in_voltageY-voltageZ_en +What: /sys/.../iio:deviceX/bufferY/in_voltageY_i_en +What: /sys/.../iio:deviceX/bufferY/in_voltageY_q_en +What: /sys/.../iio:deviceX/bufferY/in_voltage_i_en +What: /sys/.../iio:deviceX/bufferY/in_voltage_q_en +What: /sys/.../iio:deviceX/bufferY/in_incli_x_en +What: /sys/.../iio:deviceX/bufferY/in_incli_y_en +What: /sys/.../iio:deviceX/bufferY/in_pressureY_en +What: /sys/.../iio:deviceX/bufferY/in_pressure_en +What: /sys/.../iio:deviceX/bufferY/in_rot_quaternion_en +What: /sys/.../iio:deviceX/bufferY/in_proximity_en +KernelVersion: 5.11 Contact: linux-iio@vger.kernel.org Description: Scan element control for triggered data capture. @@ -1231,6 +1268,23 @@ What: /sys/.../iio:deviceX/scan_elements/in_pressure_type What: /sys/.../iio:deviceX/scan_elements/in_rot_quaternion_type What: /sys/.../iio:deviceX/scan_elements/in_proximity_type KernelVersion: 2.6.37 +What: /sys/.../iio:deviceX/bufferY/in_accel_type +What: /sys/.../iio:deviceX/bufferY/in_anglvel_type +What: /sys/.../iio:deviceX/bufferY/in_magn_type +What: /sys/.../iio:deviceX/bufferY/in_incli_type +What: /sys/.../iio:deviceX/bufferY/in_voltageY_type +What: /sys/.../iio:deviceX/bufferY/in_voltage_type +What: /sys/.../iio:deviceX/bufferY/in_voltageY_supply_type +What: /sys/.../iio:deviceX/bufferY/in_voltageY_i_type +What: /sys/.../iio:deviceX/bufferY/in_voltageY_q_type +What: /sys/.../iio:deviceX/bufferY/in_voltage_i_type +What: /sys/.../iio:deviceX/bufferY/in_voltage_q_type +What: /sys/.../iio:deviceX/bufferY/in_timestamp_type +What: /sys/.../iio:deviceX/bufferY/in_pressureY_type +What: /sys/.../iio:deviceX/bufferY/in_pressure_type +What: /sys/.../iio:deviceX/bufferY/in_rot_quaternion_type +What: /sys/.../iio:deviceX/bufferY/in_proximity_type +KernelVersion: 5.11 Contact: linux-iio@vger.kernel.org Description: Description of the scan element data storage within the buffer @@ -1287,6 +1341,33 @@ What: /sys/.../iio:deviceX/scan_elements/in_pressure_index What: /sys/.../iio:deviceX/scan_elements/in_rot_quaternion_index What: /sys/.../iio:deviceX/scan_elements/in_proximity_index KernelVersion: 2.6.37 +What: /sys/.../iio:deviceX/bufferY/in_voltageY_index +What: /sys/.../iio:deviceX/bufferY/in_voltageY_supply_index +What: /sys/.../iio:deviceX/bufferY/in_voltageY_i_index +What: /sys/.../iio:deviceX/bufferY/in_voltageY_q_index +What: /sys/.../iio:deviceX/bufferY/in_voltage_i_index +What: /sys/.../iio:deviceX/bufferY/in_voltage_q_index +What: /sys/.../iio:deviceX/bufferY/in_accel_x_index +What: /sys/.../iio:deviceX/bufferY/in_accel_y_index +What: /sys/.../iio:deviceX/bufferY/in_accel_z_index +What: /sys/.../iio:deviceX/bufferY/in_anglvel_x_index +What: /sys/.../iio:deviceX/bufferY/in_anglvel_y_index +What: /sys/.../iio:deviceX/bufferY/in_anglvel_z_index +What: /sys/.../iio:deviceX/bufferY/in_magn_x_index +What: /sys/.../iio:deviceX/bufferY/in_magn_y_index +What: /sys/.../iio:deviceX/bufferY/in_magn_z_index +What: /sys/.../iio:deviceX/bufferY/in_rot_from_north_magnetic_index +What: /sys/.../iio:deviceX/bufferY/in_rot_from_north_true_index +What: /sys/.../iio:deviceX/bufferY/in_rot_from_north_magnetic_tilt_comp_index +What: /sys/.../iio:deviceX/bufferY/in_rot_from_north_true_tilt_comp_index +What: /sys/.../iio:deviceX/bufferY/in_incli_x_index +What: /sys/.../iio:deviceX/bufferY/in_incli_y_index +What: /sys/.../iio:deviceX/bufferY/in_timestamp_index +What: /sys/.../iio:deviceX/bufferY/in_pressureY_index +What: /sys/.../iio:deviceX/bufferY/in_pressure_index +What: /sys/.../iio:deviceX/bufferY/in_rot_quaternion_index +What: /sys/.../iio:deviceX/bufferY/in_proximity_index +KernelVersion: 5.11 Contact: linux-iio@vger.kernel.org Description: A single positive integer specifying the position of this @@ -1501,6 +1582,8 @@ Description: What: /sys/bus/iio/devices/iio:deviceX/buffer/watermark KernelVersion: 4.2 +What: /sys/bus/iio/devices/iio:deviceX/bufferY/watermark +KernelVersion: 5.11 Contact: linux-iio@vger.kernel.org Description: A single positive integer specifying the maximum number of scan @@ -1519,6 +1602,8 @@ Description: What: /sys/bus/iio/devices/iio:deviceX/buffer/data_available KernelVersion: 4.16 +What: /sys/bus/iio/devices/iio:deviceX/bufferY/data_available +KernelVersion: 5.11 Contact: linux-iio@vger.kernel.org Description: A read-only value indicating the bytes of data available in the From aa29cf932fb345e111cd7fef04320846fa73e372 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 19 Feb 2021 11:01:34 +0200 Subject: [PATCH 058/119] iio: adc: adi-axi-adc: fix typo in doc-string The channels are of type iio_chan_spec, not axi_adc_chan_spec. They were in some earlier version, but forgot to rename in the doc-string. Fixes: ef04070692a21 ("iio: adc: adi-axi-adc: add support for AXI ADC IP core") Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210219090134.48057-1-alexandru.ardelean@analog.com Signed-off-by: Jonathan Cameron --- include/linux/iio/adc/adi-axi-adc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/iio/adc/adi-axi-adc.h b/include/linux/iio/adc/adi-axi-adc.h index c5d48e1c2d36..52620e5b8052 100644 --- a/include/linux/iio/adc/adi-axi-adc.h +++ b/include/linux/iio/adc/adi-axi-adc.h @@ -15,7 +15,7 @@ struct iio_chan_spec; * struct adi_axi_adc_chip_info - Chip specific information * @name Chip name * @id Chip ID (usually product ID) - * @channels Channel specifications of type @struct axi_adc_chan_spec + * @channels Channel specifications of type @struct iio_chan_spec * @num_channels Number of @channels * @scale_table Supported scales by the chip; tuples of 2 ints * @num_scales Number of scales in the table From 0463e60f087069adf25a815cd88753946aca2565 Mon Sep 17 00:00:00 2001 From: Nuno Sa Date: Thu, 18 Feb 2021 12:40:36 +0100 Subject: [PATCH 059/119] iio: adis16480: fix pps mode sampling frequency math When using PPS mode, the input clock needs to be scaled so that we have an IMU sample rate between (optimally) 4000 and 4250. After this, we can use the decimation filter to lower the sampling rate in order to get what the user wants. Optimally, the user sample rate is a multiple of both the IMU sample rate and the input clock. Hence, calculating the sync_scale dynamically gives us better chances of achieving a perfect/integer value for DEC_RATE. The math here is: 1. lcm of the input clock and the desired output rate. 2. get the highest multiple of the previous result lower than the adis max rate. 3. The last result becomes the IMU sample rate. Use that to calculate SYNC_SCALE and DEC_RATE (to get the user output rate). Fixes: 326e2357553d3 ("iio: imu: adis16480: Add support for external clock") Signed-off-by: Nuno Sa Link: https://lore.kernel.org/r/20210218114039.216091-2-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16480.c | 128 ++++++++++++++++++++++++++---------- 1 file changed, 94 insertions(+), 34 deletions(-) diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index dfe86c589325..c41b8ef1e250 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -17,6 +18,7 @@ #include #include #include +#include #include #include @@ -170,6 +172,11 @@ static const char * const adis16480_int_pin_names[4] = { [ADIS16480_PIN_DIO4] = "DIO4", }; +static bool low_rate_allow; +module_param(low_rate_allow, bool, 0444); +MODULE_PARM_DESC(low_rate_allow, + "Allow IMU rates below the minimum advisable when external clk is used in PPS mode (default: N)"); + #ifdef CONFIG_DEBUG_FS static ssize_t adis16480_show_firmware_revision(struct file *file, @@ -312,7 +319,8 @@ static int adis16480_debugfs_init(struct iio_dev *indio_dev) static int adis16480_set_freq(struct iio_dev *indio_dev, int val, int val2) { struct adis16480 *st = iio_priv(indio_dev); - unsigned int t, reg; + unsigned int t, sample_rate = st->clk_freq; + int ret; if (val < 0 || val2 < 0) return -EINVAL; @@ -321,28 +329,65 @@ static int adis16480_set_freq(struct iio_dev *indio_dev, int val, int val2) if (t == 0) return -EINVAL; + mutex_lock(&st->adis.state_lock); /* - * When using PPS mode, the rate of data collection is equal to the - * product of the external clock frequency and the scale factor in the - * SYNC_SCALE register. - * When using sync mode, or internal clock, the output data rate is - * equal with the clock frequency divided by DEC_RATE + 1. + * When using PPS mode, the input clock needs to be scaled so that we have an IMU + * sample rate between (optimally) 4000 and 4250. After this, we can use the + * decimation filter to lower the sampling rate in order to get what the user wants. + * Optimally, the user sample rate is a multiple of both the IMU sample rate and + * the input clock. Hence, calculating the sync_scale dynamically gives us better + * chances of achieving a perfect/integer value for DEC_RATE. The math here is: + * 1. lcm of the input clock and the desired output rate. + * 2. get the highest multiple of the previous result lower than the adis max rate. + * 3. The last result becomes the IMU sample rate. Use that to calculate SYNC_SCALE + * and DEC_RATE (to get the user output rate) */ if (st->clk_mode == ADIS16480_CLK_PPS) { - t = t / st->clk_freq; - reg = ADIS16495_REG_SYNC_SCALE; - } else { - t = st->clk_freq / t; - reg = ADIS16480_REG_DEC_RATE; + unsigned long scaled_rate = lcm(st->clk_freq, t); + int sync_scale; + + /* + * If lcm is bigger than the IMU maximum sampling rate there's no perfect + * solution. In this case, we get the highest multiple of the input clock + * lower than the IMU max sample rate. + */ + if (scaled_rate > st->chip_info->int_clk) + scaled_rate = st->chip_info->int_clk / st->clk_freq * st->clk_freq; + else + scaled_rate = st->chip_info->int_clk / scaled_rate * scaled_rate; + + /* + * This is not an hard requirement but it's not advised to run the IMU + * with a sample rate lower than 4000Hz due to possible undersampling + * issues. However, there are users that might really want to take the risk. + * Hence, we provide a module parameter for them. If set, we allow sample + * rates lower than 4KHz. By default, we won't allow this and we just roundup + * the rate to the next multiple of the input clock bigger than 4KHz. This + * is done like this as in some cases (when DEC_RATE is 0) might give + * us the closest value to the one desired by the user... + */ + if (scaled_rate < 4000000 && !low_rate_allow) + scaled_rate = roundup(4000000, st->clk_freq); + + sync_scale = scaled_rate / st->clk_freq; + ret = __adis_write_reg_16(&st->adis, ADIS16495_REG_SYNC_SCALE, sync_scale); + if (ret) + goto error; + + sample_rate = scaled_rate; } + t = DIV_ROUND_CLOSEST(sample_rate, t); + if (t) + t--; + if (t > st->chip_info->max_dec_rate) t = st->chip_info->max_dec_rate; - if ((t != 0) && (st->clk_mode != ADIS16480_CLK_PPS)) - t--; - - return adis_write_reg_16(&st->adis, reg, t); + ret = __adis_write_reg_16(&st->adis, ADIS16480_REG_DEC_RATE, t); +error: + mutex_unlock(&st->adis.state_lock); + return ret; } static int adis16480_get_freq(struct iio_dev *indio_dev, int *val, int *val2) @@ -350,34 +395,35 @@ static int adis16480_get_freq(struct iio_dev *indio_dev, int *val, int *val2) struct adis16480 *st = iio_priv(indio_dev); uint16_t t; int ret; - unsigned int freq; - unsigned int reg; + unsigned int freq, sample_rate = st->clk_freq; - if (st->clk_mode == ADIS16480_CLK_PPS) - reg = ADIS16495_REG_SYNC_SCALE; - else - reg = ADIS16480_REG_DEC_RATE; + mutex_lock(&st->adis.state_lock); - ret = adis_read_reg_16(&st->adis, reg, &t); + if (st->clk_mode == ADIS16480_CLK_PPS) { + u16 sync_scale; + + ret = __adis_read_reg_16(&st->adis, ADIS16495_REG_SYNC_SCALE, &sync_scale); + if (ret) + goto error; + + sample_rate = st->clk_freq * sync_scale; + } + + ret = __adis_read_reg_16(&st->adis, ADIS16480_REG_DEC_RATE, &t); if (ret) - return ret; + goto error; - /* - * When using PPS mode, the rate of data collection is equal to the - * product of the external clock frequency and the scale factor in the - * SYNC_SCALE register. - * When using sync mode, or internal clock, the output data rate is - * equal with the clock frequency divided by DEC_RATE + 1. - */ - if (st->clk_mode == ADIS16480_CLK_PPS) - freq = st->clk_freq * t; - else - freq = st->clk_freq / (t + 1); + mutex_unlock(&st->adis.state_lock); + + freq = DIV_ROUND_CLOSEST(sample_rate, (t + 1)); *val = freq / 1000; *val2 = (freq % 1000) * 1000; return IIO_VAL_INT_PLUS_MICRO; +error: + mutex_unlock(&st->adis.state_lock); + return ret; } enum { @@ -1278,6 +1324,20 @@ static int adis16480_probe(struct spi_device *spi) st->clk_freq = clk_get_rate(st->ext_clk); st->clk_freq *= 1000; /* micro */ + if (st->clk_mode == ADIS16480_CLK_PPS) { + u16 sync_scale; + + /* + * In PPS mode, the IMU sample rate is the clk_freq * sync_scale. Hence, + * default the IMU sample rate to the highest multiple of the input clock + * lower than the IMU max sample rate. The internal sample rate is the + * max... + */ + sync_scale = st->chip_info->int_clk / st->clk_freq; + ret = __adis_write_reg_16(&st->adis, ADIS16495_REG_SYNC_SCALE, sync_scale); + if (ret) + return ret; + } } else { st->clk_freq = st->chip_info->int_clk; } From 39c024b51b5607e9d2fc6c04c2573e4a778c728d Mon Sep 17 00:00:00 2001 From: Nuno Sa Date: Thu, 18 Feb 2021 12:40:37 +0100 Subject: [PATCH 060/119] iio: adis16475: improve sync scale mode handling With this patch, we don't force users to define the IMU scaled internal sampling rate once in the devicetree. Now it's dynamically calculated at runtime depending on the desired output rate given by users. Calculating the sync_scale dynamically gives us better chances of achieving a perfect/integer value for DEC_RATE (thus giving more flexibility). The math is: 1. lcm of the input clock and the desired output rate. 2. get the highest multiple of the previous result lower than the adis max rate. 3. The last result becomes the IMU sample rate. Use that to calculate SYNC_SCALE and DEC_RATE (to get the user output rate). Signed-off-by: Nuno Sa Link: https://lore.kernel.org/r/20210218114039.216091-3-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16475.c | 120 +++++++++++++++++++++++++++--------- 1 file changed, 91 insertions(+), 29 deletions(-) diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index 197d48240991..5759cd8ffbef 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include #include @@ -101,6 +103,7 @@ struct adis16475 { u32 clk_freq; bool burst32; unsigned long lsb_flag; + u16 sync_mode; /* Alignment needed for the timestamp */ __be16 data[ADIS16475_MAX_SCAN_DATA] __aligned(8); }; @@ -117,6 +120,11 @@ enum { ADIS16475_SCAN_CRC_FAILURE, }; +static bool low_rate_allow; +module_param(low_rate_allow, bool, 0444); +MODULE_PARM_DESC(low_rate_allow, + "Allow IMU rates below the minimum advisable when external clk is used in SCALED mode (default: N)"); + #ifdef CONFIG_DEBUG_FS static ssize_t adis16475_show_firmware_revision(struct file *file, char __user *userbuf, @@ -253,25 +261,92 @@ static int adis16475_get_freq(struct adis16475 *st, u32 *freq) { int ret; u16 dec; + u32 sample_rate = st->clk_freq; - ret = adis_read_reg_16(&st->adis, ADIS16475_REG_DEC_RATE, &dec); + mutex_lock(&st->adis.state_lock); + + if (st->sync_mode == ADIS16475_SYNC_SCALED) { + u16 sync_scale; + + ret = __adis_read_reg_16(&st->adis, ADIS16475_REG_UP_SCALE, &sync_scale); + if (ret) + goto error; + + sample_rate = st->clk_freq * sync_scale; + } + + ret = __adis_read_reg_16(&st->adis, ADIS16475_REG_DEC_RATE, &dec); if (ret) - return -EINVAL; + goto error; - *freq = DIV_ROUND_CLOSEST(st->clk_freq, dec + 1); + mutex_unlock(&st->adis.state_lock); + + *freq = DIV_ROUND_CLOSEST(sample_rate, dec + 1); return 0; +error: + mutex_unlock(&st->adis.state_lock); + return ret; } static int adis16475_set_freq(struct adis16475 *st, const u32 freq) { u16 dec; int ret; + u32 sample_rate = st->clk_freq; if (!freq) return -EINVAL; - dec = DIV_ROUND_CLOSEST(st->clk_freq, freq); + mutex_lock(&st->adis.state_lock); + /* + * When using sync scaled mode, the input clock needs to be scaled so that we have + * an IMU sample rate between (optimally) 1900 and 2100. After this, we can use the + * decimation filter to lower the sampling rate in order to get what the user wants. + * Optimally, the user sample rate is a multiple of both the IMU sample rate and + * the input clock. Hence, calculating the sync_scale dynamically gives us better + * chances of achieving a perfect/integer value for DEC_RATE. The math here is: + * 1. lcm of the input clock and the desired output rate. + * 2. get the highest multiple of the previous result lower than the adis max rate. + * 3. The last result becomes the IMU sample rate. Use that to calculate SYNC_SCALE + * and DEC_RATE (to get the user output rate) + */ + if (st->sync_mode == ADIS16475_SYNC_SCALED) { + unsigned long scaled_rate = lcm(st->clk_freq, freq); + int sync_scale; + + /* + * If lcm is bigger than the IMU maximum sampling rate there's no perfect + * solution. In this case, we get the highest multiple of the input clock + * lower than the IMU max sample rate. + */ + if (scaled_rate > 2100000) + scaled_rate = 2100000 / st->clk_freq * st->clk_freq; + else + scaled_rate = 2100000 / scaled_rate * scaled_rate; + + /* + * This is not an hard requirement but it's not advised to run the IMU + * with a sample rate lower than 4000Hz due to possible undersampling + * issues. However, there are users that might really want to take the risk. + * Hence, we provide a module parameter for them. If set, we allow sample + * rates lower than 4KHz. By default, we won't allow this and we just roundup + * the rate to the next multiple of the input clock bigger than 4KHz. This + * is done like this as in some cases (when DEC_RATE is 0) might give + * us the closest value to the one desired by the user... + */ + if (scaled_rate < 1900000 && !low_rate_allow) + scaled_rate = roundup(1900000, st->clk_freq); + + sync_scale = scaled_rate / st->clk_freq; + ret = __adis_write_reg_16(&st->adis, ADIS16475_REG_UP_SCALE, sync_scale); + if (ret) + goto error; + + sample_rate = scaled_rate; + } + + dec = DIV_ROUND_CLOSEST(sample_rate, freq); if (dec) dec--; @@ -281,7 +356,7 @@ static int adis16475_set_freq(struct adis16475 *st, const u32 freq) ret = adis_write_reg_16(&st->adis, ADIS16475_REG_DEC_RATE, dec); if (ret) - return ret; + goto error; /* * If decimation is used, then gyro and accel data will have meaningful @@ -290,6 +365,9 @@ static int adis16475_set_freq(struct adis16475 *st, const u32 freq) assign_bit(ADIS16475_LSB_DEC_MASK, &st->lsb_flag, dec); return 0; +error: + mutex_unlock(&st->adis.state_lock); + return ret; } /* The values are approximated. */ @@ -1085,6 +1163,7 @@ static int adis16475_config_sync_mode(struct adis16475 *st) } sync = &st->info->sync[sync_mode]; + st->sync_mode = sync->sync_mode; /* All the other modes require external input signal */ if (sync->sync_mode != ADIS16475_SYNC_OUTPUT) { @@ -1112,37 +1191,20 @@ static int adis16475_config_sync_mode(struct adis16475 *st) if (sync->sync_mode == ADIS16475_SYNC_SCALED) { u16 up_scale; - u32 scaled_out_freq = 0; - /* - * If we are in scaled mode, we must have an up_scale. - * In scaled mode the allowable input clock range is - * 1 Hz to 128 Hz, and the allowable output range is - * 1900 to 2100 Hz. Hence, a scale must be given to - * get the allowable output. - */ - ret = device_property_read_u32(dev, - "adi,scaled-output-hz", - &scaled_out_freq); - if (ret) { - dev_err(dev, "adi,scaled-output-hz must be given when in scaled sync mode"); - return -EINVAL; - } else if (scaled_out_freq < 1900 || - scaled_out_freq > 2100) { - dev_err(dev, "Invalid value: %u for adi,scaled-output-hz", - scaled_out_freq); - return -EINVAL; - } - up_scale = DIV_ROUND_CLOSEST(scaled_out_freq, - st->clk_freq); + /* + * In sync scaled mode, the IMU sample rate is the clk_freq * sync_scale. + * Hence, default the IMU sample rate to the highest multiple of the input + * clock lower than the IMU max sample rate. The optimal range is + * 1900-2100 sps... + */ + up_scale = 2100 / st->clk_freq; ret = __adis_write_reg_16(&st->adis, ADIS16475_REG_UP_SCALE, up_scale); if (ret) return ret; - - st->clk_freq = scaled_out_freq; } st->clk_freq *= 1000; From 3b15e6a532b30691e7c464e7d74c2d699c23d439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20S=C3=A1?= Date: Thu, 18 Feb 2021 12:40:38 +0100 Subject: [PATCH 061/119] dt-bindings: adis16475: remove property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `adi,scaled-output-hz` is no longer used by the driver. Acked-by: Rob Herring Signed-off-by: Nuno Sá Link: https://lore.kernel.org/r/20210218114039.216091-4-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/imu/adi,adis16475.yaml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/imu/adi,adis16475.yaml b/Documentation/devicetree/bindings/iio/imu/adi,adis16475.yaml index 79fba1508e89..a7574210175a 100644 --- a/Documentation/devicetree/bindings/iio/imu/adi,adis16475.yaml +++ b/Documentation/devicetree/bindings/iio/imu/adi,adis16475.yaml @@ -71,15 +71,6 @@ properties: minimum: 0 maximum: 3 - adi,scaled-output-hz: - description: - This property must be present if the clock mode is scaled-sync through - clock-names property. In this mode, the input clock can have a range - of 1Hz to 128HZ which must be scaled to originate an allowable sample - rate. This property specifies that rate. - minimum: 1900 - maximum: 2100 - required: - compatible - reg From 15aacc980dcb326ef33dfc32772faea1067f9178 Mon Sep 17 00:00:00 2001 From: Nuno Sa Date: Thu, 18 Feb 2021 12:40:39 +0100 Subject: [PATCH 062/119] iio: adis: add helpers for locking Add some helpers to lock and unlock the device. As this is such a simple change, we update all the users that were using the lock already in this patch. Signed-off-by: Nuno Sa Link: https://lore.kernel.org/r/20210218114039.216091-5-nuno.sa@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis16400.c | 22 ++++++++++------------ drivers/iio/imu/adis16475.c | 10 +++++----- drivers/iio/imu/adis16480.c | 15 +++++++-------- include/linux/iio/imu/adis.h | 10 ++++++++++ 4 files changed, 32 insertions(+), 25 deletions(-) diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c index 54af2ed664f6..b1ec14bf439f 100644 --- a/drivers/iio/imu/adis16400.c +++ b/drivers/iio/imu/adis16400.c @@ -505,7 +505,6 @@ static int adis16400_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long info) { struct adis16400_state *st = iio_priv(indio_dev); - struct mutex *slock = &st->adis.state_lock; int ret, sps; switch (info) { @@ -518,18 +517,18 @@ static int adis16400_write_raw(struct iio_dev *indio_dev, * Need to cache values so we can update if the frequency * changes. */ - mutex_lock(slock); + adis_dev_lock(&st->adis); st->filt_int = val; /* Work out update to current value */ sps = st->variant->get_freq(st); if (sps < 0) { - mutex_unlock(slock); + adis_dev_unlock(&st->adis); return sps; } ret = __adis16400_set_filter(indio_dev, sps, val * 1000 + val2 / 1000); - mutex_unlock(slock); + adis_dev_unlock(&st->adis); return ret; case IIO_CHAN_INFO_SAMP_FREQ: sps = val * 1000 + val2 / 1000; @@ -537,9 +536,9 @@ static int adis16400_write_raw(struct iio_dev *indio_dev, if (sps <= 0) return -EINVAL; - mutex_lock(slock); + adis_dev_lock(&st->adis); ret = st->variant->set_freq(st, sps); - mutex_unlock(slock); + adis_dev_unlock(&st->adis); return ret; default: return -EINVAL; @@ -550,7 +549,6 @@ static int adis16400_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long info) { struct adis16400_state *st = iio_priv(indio_dev); - struct mutex *slock = &st->adis.state_lock; int16_t val16; int ret; @@ -606,17 +604,17 @@ static int adis16400_read_raw(struct iio_dev *indio_dev, *val = st->variant->temp_offset; return IIO_VAL_INT; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: - mutex_lock(slock); + adis_dev_lock(&st->adis); /* Need both the number of taps and the sampling frequency */ ret = __adis_read_reg_16(&st->adis, ADIS16400_SENS_AVG, &val16); if (ret) { - mutex_unlock(slock); + adis_dev_unlock(&st->adis); return ret; } ret = st->variant->get_freq(st); - mutex_unlock(slock); + adis_dev_unlock(&st->adis); if (ret) return ret; ret /= adis16400_3db_divisors[val16 & 0x07]; @@ -624,9 +622,9 @@ static int adis16400_read_raw(struct iio_dev *indio_dev, *val2 = (ret % 1000) * 1000; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SAMP_FREQ: - mutex_lock(slock); + adis_dev_lock(&st->adis); ret = st->variant->get_freq(st); - mutex_unlock(slock); + adis_dev_unlock(&st->adis); if (ret) return ret; *val = ret / 1000; diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index 5759cd8ffbef..8f6bea4b6608 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -263,7 +263,7 @@ static int adis16475_get_freq(struct adis16475 *st, u32 *freq) u16 dec; u32 sample_rate = st->clk_freq; - mutex_lock(&st->adis.state_lock); + adis_dev_lock(&st->adis); if (st->sync_mode == ADIS16475_SYNC_SCALED) { u16 sync_scale; @@ -279,13 +279,13 @@ static int adis16475_get_freq(struct adis16475 *st, u32 *freq) if (ret) goto error; - mutex_unlock(&st->adis.state_lock); + adis_dev_unlock(&st->adis); *freq = DIV_ROUND_CLOSEST(sample_rate, dec + 1); return 0; error: - mutex_unlock(&st->adis.state_lock); + adis_dev_unlock(&st->adis); return ret; } @@ -298,7 +298,7 @@ static int adis16475_set_freq(struct adis16475 *st, const u32 freq) if (!freq) return -EINVAL; - mutex_lock(&st->adis.state_lock); + adis_dev_lock(&st->adis); /* * When using sync scaled mode, the input clock needs to be scaled so that we have * an IMU sample rate between (optimally) 1900 and 2100. After this, we can use the @@ -366,7 +366,7 @@ static int adis16475_set_freq(struct adis16475 *st, const u32 freq) return 0; error: - mutex_unlock(&st->adis.state_lock); + adis_dev_unlock(&st->adis); return ret; } diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index c41b8ef1e250..f81b86690b76 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -329,7 +329,7 @@ static int adis16480_set_freq(struct iio_dev *indio_dev, int val, int val2) if (t == 0) return -EINVAL; - mutex_lock(&st->adis.state_lock); + adis_dev_lock(&st->adis); /* * When using PPS mode, the input clock needs to be scaled so that we have an IMU * sample rate between (optimally) 4000 and 4250. After this, we can use the @@ -386,7 +386,7 @@ static int adis16480_set_freq(struct iio_dev *indio_dev, int val, int val2) ret = __adis_write_reg_16(&st->adis, ADIS16480_REG_DEC_RATE, t); error: - mutex_unlock(&st->adis.state_lock); + adis_dev_unlock(&st->adis); return ret; } @@ -397,7 +397,7 @@ static int adis16480_get_freq(struct iio_dev *indio_dev, int *val, int *val2) int ret; unsigned int freq, sample_rate = st->clk_freq; - mutex_lock(&st->adis.state_lock); + adis_dev_lock(&st->adis); if (st->clk_mode == ADIS16480_CLK_PPS) { u16 sync_scale; @@ -413,7 +413,7 @@ static int adis16480_get_freq(struct iio_dev *indio_dev, int *val, int *val2) if (ret) goto error; - mutex_unlock(&st->adis.state_lock); + adis_dev_unlock(&st->adis); freq = DIV_ROUND_CLOSEST(sample_rate, (t + 1)); @@ -422,7 +422,7 @@ static int adis16480_get_freq(struct iio_dev *indio_dev, int *val, int *val2) return IIO_VAL_INT_PLUS_MICRO; error: - mutex_unlock(&st->adis.state_lock); + adis_dev_unlock(&st->adis); return ret; } @@ -598,7 +598,6 @@ static int adis16480_set_filter_freq(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, unsigned int freq) { struct adis16480 *st = iio_priv(indio_dev); - struct mutex *slock = &st->adis.state_lock; unsigned int enable_mask, offset, reg; unsigned int diff, best_diff; unsigned int i, best_freq; @@ -609,7 +608,7 @@ static int adis16480_set_filter_freq(struct iio_dev *indio_dev, offset = ad16480_filter_data[chan->scan_index][1]; enable_mask = BIT(offset + 2); - mutex_lock(slock); + adis_dev_lock(&st->adis); ret = __adis_read_reg_16(&st->adis, reg, &val); if (ret) @@ -637,7 +636,7 @@ static int adis16480_set_filter_freq(struct iio_dev *indio_dev, ret = __adis_write_reg_16(&st->adis, reg, val); out_unlock: - mutex_unlock(slock); + adis_dev_unlock(&st->adis); return ret; } diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index 04e96d688ba9..f9b728d490b1 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -428,6 +428,16 @@ static inline int adis_initial_startup(struct adis *adis) return ret; } +static inline void adis_dev_lock(struct adis *adis) +{ + mutex_lock(&adis->state_lock); +} + +static inline void adis_dev_unlock(struct adis *adis) +{ + mutex_unlock(&adis->state_lock); +} + int adis_single_conversion(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, unsigned int error_mask, int *val); From b923561fabc33664b25470b101857712339e1021 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 15 Feb 2021 16:34:47 +0100 Subject: [PATCH 063/119] iio: adc: mt6360: Include right header To get access to the big endian byte order parsing helpers drivers need to include and nothing else. Cc: Gene Chen Suggested-by: Harvey Harrison Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20210215153447.48457-1-linus.walleij@linaro.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mt6360-adc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/mt6360-adc.c b/drivers/iio/adc/mt6360-adc.c index f57db3056fbe..6b39a139ce28 100644 --- a/drivers/iio/adc/mt6360-adc.c +++ b/drivers/iio/adc/mt6360-adc.c @@ -9,13 +9,14 @@ #include #include #include -#include #include #include #include #include +#include + #define MT6360_REG_PMUCHGCTRL3 0x313 #define MT6360_REG_PMUADCCFG 0x356 #define MT6360_REG_PMUADCIDLET 0x358 From f774117c96f94c7c4d2f076e4cacc80218b0df48 Mon Sep 17 00:00:00 2001 From: Jyoti Bhayana Date: Tue, 9 Mar 2021 23:12:59 +0000 Subject: [PATCH 064/119] iio/scmi: Adding support for IIO SCMI Based Sensors This change provides ARM SCMI Protocol based IIO device. This driver provides support for Accelerometer and Gyroscope using SCMI Sensor Protocol extensions added in the SCMIv3.0 ARM specification Reported-by: kernel test robot Signed-off-by: Jyoti Bhayana Link: https://lore.kernel.org/r/20210212172235.507028-2-jbhayana@google.com Signed-off-by: Jonathan Cameron Link: https://lore.kernel.org/r/20210309231259.78050-2-jbhayana@google.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 6 + drivers/firmware/arm_scmi/driver.c | 2 +- drivers/iio/common/Kconfig | 1 + drivers/iio/common/Makefile | 1 + drivers/iio/common/scmi_sensors/Kconfig | 18 + drivers/iio/common/scmi_sensors/Makefile | 5 + drivers/iio/common/scmi_sensors/scmi_iio.c | 683 +++++++++++++++++++++ 7 files changed, 715 insertions(+), 1 deletion(-) create mode 100644 drivers/iio/common/scmi_sensors/Kconfig create mode 100644 drivers/iio/common/scmi_sensors/Makefile create mode 100644 drivers/iio/common/scmi_sensors/scmi_iio.c diff --git a/MAINTAINERS b/MAINTAINERS index d92f85ca831d..14227980f3d2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8692,6 +8692,12 @@ S: Maintained F: Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt F: drivers/iio/multiplexer/iio-mux.c +IIO SCMI BASED DRIVER +M: Jyoti Bhayana +L: linux-iio@vger.kernel.org +S: Maintained +F: drivers/iio/common/scmi_sensors/scmi_iio.c + IIO SUBSYSTEM AND DRIVERS M: Jonathan Cameron R: Lars-Peter Clausen diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index cacdf1589b10..3e748e57deab 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -741,7 +741,7 @@ static struct scmi_prot_devnames devnames[] = { { SCMI_PROTOCOL_SYSTEM, { "syspower" },}, { SCMI_PROTOCOL_PERF, { "cpufreq" },}, { SCMI_PROTOCOL_CLOCK, { "clocks" },}, - { SCMI_PROTOCOL_SENSOR, { "hwmon" },}, + { SCMI_PROTOCOL_SENSOR, { "hwmon", "iiodev" },}, { SCMI_PROTOCOL_RESET, { "reset" },}, { SCMI_PROTOCOL_VOLTAGE, { "regulator" },}, }; diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig index 2b9ee9161abd..0334b4954773 100644 --- a/drivers/iio/common/Kconfig +++ b/drivers/iio/common/Kconfig @@ -6,5 +6,6 @@ source "drivers/iio/common/cros_ec_sensors/Kconfig" source "drivers/iio/common/hid-sensors/Kconfig" source "drivers/iio/common/ms_sensors/Kconfig" +source "drivers/iio/common/scmi_sensors/Kconfig" source "drivers/iio/common/ssp_sensors/Kconfig" source "drivers/iio/common/st_sensors/Kconfig" diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile index 4bc30bb548e2..fad40e1e1718 100644 --- a/drivers/iio/common/Makefile +++ b/drivers/iio/common/Makefile @@ -11,5 +11,6 @@ obj-y += cros_ec_sensors/ obj-y += hid-sensors/ obj-y += ms_sensors/ +obj-y += scmi_sensors/ obj-y += ssp_sensors/ obj-y += st_sensors/ diff --git a/drivers/iio/common/scmi_sensors/Kconfig b/drivers/iio/common/scmi_sensors/Kconfig new file mode 100644 index 000000000000..67e084cbb1ab --- /dev/null +++ b/drivers/iio/common/scmi_sensors/Kconfig @@ -0,0 +1,18 @@ +# +# IIO over SCMI +# +# When adding new entries keep the list in alphabetical order + +menu "IIO SCMI Sensors" + +config IIO_SCMI + tristate "IIO SCMI" + depends on ARM_SCMI_PROTOCOL + select IIO_BUFFER + select IIO_KFIFO_BUF + help + Say yes here to build support for IIO SCMI Driver. + This provides ARM SCMI Protocol based IIO device. + This driver provides support for accelerometer and gyroscope + sensors available on SCMI based platforms. +endmenu diff --git a/drivers/iio/common/scmi_sensors/Makefile b/drivers/iio/common/scmi_sensors/Makefile new file mode 100644 index 000000000000..f13140a2575a --- /dev/null +++ b/drivers/iio/common/scmi_sensors/Makefile @@ -0,0 +1,5 @@ +# SPDX - License - Identifier : GPL - 2.0 - only +# +# Makefile for the IIO over SCMI +# +obj-$(CONFIG_IIO_SCMI) += scmi_iio.o diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c new file mode 100644 index 000000000000..872d87ca6256 --- /dev/null +++ b/drivers/iio/common/scmi_sensors/scmi_iio.c @@ -0,0 +1,683 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * System Control and Management Interface(SCMI) based IIO sensor driver + * + * Copyright (C) 2021 Google LLC + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SCMI_IIO_NUM_OF_AXIS 3 + +struct scmi_iio_priv { + struct scmi_handle *handle; + const struct scmi_sensor_info *sensor_info; + struct iio_dev *indio_dev; + /* adding one additional channel for timestamp */ + s64 iio_buf[SCMI_IIO_NUM_OF_AXIS + 1]; + struct notifier_block sensor_update_nb; + u32 *freq_avail; +}; + +static int scmi_iio_sensor_update_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct scmi_sensor_update_report *sensor_update = data; + struct iio_dev *scmi_iio_dev; + struct scmi_iio_priv *sensor; + s8 tstamp_scale; + u64 time, time_ns; + int i; + + if (sensor_update->readings_count == 0) + return NOTIFY_DONE; + + sensor = container_of(nb, struct scmi_iio_priv, sensor_update_nb); + + for (i = 0; i < sensor_update->readings_count; i++) + sensor->iio_buf[i] = sensor_update->readings[i].value; + + if (!sensor->sensor_info->timestamped) { + time_ns = ktime_to_ns(sensor_update->timestamp); + } else { + /* + * All the axes are supposed to have the same value for timestamp. + * We are just using the values from the Axis 0 here. + */ + time = sensor_update->readings[0].timestamp; + + /* + * Timestamp returned by SCMI is in seconds and is equal to + * time * power-of-10 multiplier(tstamp_scale) seconds. + * Converting the timestamp to nanoseconds below. + */ + tstamp_scale = sensor->sensor_info->tstamp_scale + + const_ilog2(NSEC_PER_SEC) / const_ilog2(10); + if (tstamp_scale < 0) { + do_div(time, int_pow(10, abs(tstamp_scale))); + time_ns = time; + } else { + time_ns = time * int_pow(10, tstamp_scale); + } + } + + scmi_iio_dev = sensor->indio_dev; + iio_push_to_buffers_with_timestamp(scmi_iio_dev, sensor->iio_buf, + time_ns); + return NOTIFY_OK; +} + +static int scmi_iio_buffer_preenable(struct iio_dev *iio_dev) +{ + struct scmi_iio_priv *sensor = iio_priv(iio_dev); + u32 sensor_id = sensor->sensor_info->id; + u32 sensor_config = 0; + int err; + + if (sensor->sensor_info->timestamped) + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_TSTAMP_ENABLED_MASK, + SCMI_SENS_CFG_TSTAMP_ENABLE); + + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK, + SCMI_SENS_CFG_SENSOR_ENABLE); + + err = sensor->handle->notify_ops->register_event_notifier(sensor->handle, + SCMI_PROTOCOL_SENSOR, SCMI_EVENT_SENSOR_UPDATE, + &sensor_id, &sensor->sensor_update_nb); + if (err) { + dev_err(&iio_dev->dev, + "Error in registering sensor update notifier for sensor %s err %d", + sensor->sensor_info->name, err); + return err; + } + + err = sensor->handle->sensor_ops->config_set(sensor->handle, + sensor->sensor_info->id, sensor_config); + if (err) { + sensor->handle->notify_ops->unregister_event_notifier(sensor->handle, + SCMI_PROTOCOL_SENSOR, + SCMI_EVENT_SENSOR_UPDATE, &sensor_id, + &sensor->sensor_update_nb); + dev_err(&iio_dev->dev, "Error in enabling sensor %s err %d", + sensor->sensor_info->name, err); + } + + return err; +} + +static int scmi_iio_buffer_postdisable(struct iio_dev *iio_dev) +{ + struct scmi_iio_priv *sensor = iio_priv(iio_dev); + u32 sensor_id = sensor->sensor_info->id; + u32 sensor_config = 0; + int err; + + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK, + SCMI_SENS_CFG_SENSOR_DISABLE); + + err = sensor->handle->notify_ops->unregister_event_notifier(sensor->handle, + SCMI_PROTOCOL_SENSOR, SCMI_EVENT_SENSOR_UPDATE, + &sensor_id, &sensor->sensor_update_nb); + if (err) { + dev_err(&iio_dev->dev, + "Error in unregistering sensor update notifier for sensor %s err %d", + sensor->sensor_info->name, err); + return err; + } + + err = sensor->handle->sensor_ops->config_set(sensor->handle, sensor_id, + sensor_config); + if (err) { + dev_err(&iio_dev->dev, + "Error in disabling sensor %s with err %d", + sensor->sensor_info->name, err); + } + + return err; +} + +static const struct iio_buffer_setup_ops scmi_iio_buffer_ops = { + .preenable = scmi_iio_buffer_preenable, + .postdisable = scmi_iio_buffer_postdisable, +}; + +static int scmi_iio_set_odr_val(struct iio_dev *iio_dev, int val, int val2) +{ + struct scmi_iio_priv *sensor = iio_priv(iio_dev); + const unsigned long UHZ_PER_HZ = 1000000UL; + u64 sec, mult, uHz, sf; + u32 sensor_config; + char buf[32]; + + int err = sensor->handle->sensor_ops->config_get(sensor->handle, + sensor->sensor_info->id, &sensor_config); + if (err) { + dev_err(&iio_dev->dev, + "Error in getting sensor config for sensor %s err %d", + sensor->sensor_info->name, err); + return err; + } + + uHz = val * UHZ_PER_HZ + val2; + + /* + * The seconds field in the sensor interval in SCMI is 16 bits long + * Therefore seconds = 1/Hz <= 0xFFFF. As floating point calculations are + * discouraged in the kernel driver code, to calculate the scale factor (sf) + * (1* 1000000 * sf)/uHz <= 0xFFFF. Therefore, sf <= (uHz * 0xFFFF)/1000000 + * To calculate the multiplier,we convert the sf into char string and + * count the number of characters + */ + sf = (u64)uHz * 0xFFFF; + do_div(sf, UHZ_PER_HZ); + mult = scnprintf(buf, sizeof(buf), "%llu", sf) - 1; + + sec = int_pow(10, mult) * UHZ_PER_HZ; + do_div(sec, uHz); + if (sec == 0) { + dev_err(&iio_dev->dev, + "Trying to set invalid sensor update value for sensor %s", + sensor->sensor_info->name); + return -EINVAL; + } + + sensor_config &= ~SCMI_SENS_CFG_UPDATE_SECS_MASK; + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_UPDATE_SECS_MASK, sec); + sensor_config &= ~SCMI_SENS_CFG_UPDATE_EXP_MASK; + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_UPDATE_EXP_MASK, -mult); + + if (sensor->sensor_info->timestamped) { + sensor_config &= ~SCMI_SENS_CFG_TSTAMP_ENABLED_MASK; + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_TSTAMP_ENABLED_MASK, + SCMI_SENS_CFG_TSTAMP_ENABLE); + } + + sensor_config &= ~SCMI_SENS_CFG_ROUND_MASK; + sensor_config |= + FIELD_PREP(SCMI_SENS_CFG_ROUND_MASK, SCMI_SENS_CFG_ROUND_AUTO); + + err = sensor->handle->sensor_ops->config_set(sensor->handle, + sensor->sensor_info->id, sensor_config); + if (err) + dev_err(&iio_dev->dev, + "Error in setting sensor update interval for sensor %s value %u err %d", + sensor->sensor_info->name, sensor_config, err); + + return err; +} + +static int scmi_iio_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + int err; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + mutex_lock(&iio_dev->mlock); + err = scmi_iio_set_odr_val(iio_dev, val, val2); + mutex_unlock(&iio_dev->mlock); + return err; + default: + return -EINVAL; + } +} + +static int scmi_iio_read_avail(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct scmi_iio_priv *sensor = iio_priv(iio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = sensor->freq_avail; + *type = IIO_VAL_INT_PLUS_MICRO; + *length = sensor->sensor_info->intervals.count * 2; + if (sensor->sensor_info->intervals.segmented) + return IIO_AVAIL_RANGE; + else + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static void convert_ns_to_freq(u64 interval_ns, u64 *hz, u64 *uhz) +{ + u64 rem, freq; + + freq = NSEC_PER_SEC; + rem = do_div(freq, interval_ns); + *hz = freq; + *uhz = rem * 1000000UL; + do_div(*uhz, interval_ns); +} + +static int scmi_iio_get_odr_val(struct iio_dev *iio_dev, int *val, int *val2) +{ + u64 sensor_update_interval, sensor_interval_mult, hz, uhz; + struct scmi_iio_priv *sensor = iio_priv(iio_dev); + u32 sensor_config; + int mult; + + int err = sensor->handle->sensor_ops->config_get(sensor->handle, + sensor->sensor_info->id, &sensor_config); + if (err) { + dev_err(&iio_dev->dev, + "Error in getting sensor config for sensor %s err %d", + sensor->sensor_info->name, err); + return err; + } + + sensor_update_interval = + SCMI_SENS_CFG_GET_UPDATE_SECS(sensor_config) * NSEC_PER_SEC; + + mult = SCMI_SENS_CFG_GET_UPDATE_EXP(sensor_config); + if (mult < 0) { + sensor_interval_mult = int_pow(10, abs(mult)); + do_div(sensor_update_interval, sensor_interval_mult); + } else { + sensor_interval_mult = int_pow(10, mult); + sensor_update_interval = + sensor_update_interval * sensor_interval_mult; + } + + convert_ns_to_freq(sensor_update_interval, &hz, &uhz); + *val = hz; + *val2 = uhz; + return 0; +} + +static int scmi_iio_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *ch, int *val, + int *val2, long mask) +{ + struct scmi_iio_priv *sensor = iio_priv(iio_dev); + s8 scale; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + scale = sensor->sensor_info->axis[ch->scan_index].scale; + if (scale < 0) { + *val = 1; + *val2 = int_pow(10, abs(scale)); + return IIO_VAL_FRACTIONAL; + } + *val = int_pow(10, scale); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = scmi_iio_get_odr_val(iio_dev, val, val2); + return ret ? ret : IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static const struct iio_info scmi_iio_info = { + .read_raw = scmi_iio_read_raw, + .read_avail = scmi_iio_read_avail, + .write_raw = scmi_iio_write_raw, +}; + +static ssize_t scmi_iio_get_raw_available(struct iio_dev *iio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct scmi_iio_priv *sensor = iio_priv(iio_dev); + u64 resolution, rem; + s64 min_range, max_range; + s8 exponent, scale; + int len = 0; + + /* + * All the axes are supposed to have the same value for range and resolution. + * We are just using the values from the Axis 0 here. + */ + if (sensor->sensor_info->axis[0].extended_attrs) { + min_range = sensor->sensor_info->axis[0].attrs.min_range; + max_range = sensor->sensor_info->axis[0].attrs.max_range; + resolution = sensor->sensor_info->axis[0].resolution; + exponent = sensor->sensor_info->axis[0].exponent; + scale = sensor->sensor_info->axis[0].scale; + + /* + * To provide the raw value for the resolution to the userspace, + * need to divide the resolution exponent by the sensor scale + */ + exponent = exponent - scale; + if (exponent < 0) { + rem = do_div(resolution, + int_pow(10, abs(exponent)) + ); + len = scnprintf(buf, PAGE_SIZE, + "[%lld %llu.%llu %lld]\n", min_range, + resolution, rem, max_range); + } else { + resolution = resolution * int_pow(10, exponent); + len = scnprintf(buf, PAGE_SIZE, "[%lld %llu %lld]\n", + min_range, resolution, max_range); + } + } + return len; +} + +static const struct iio_chan_spec_ext_info scmi_iio_ext_info[] = { + { + .name = "raw_available", + .read = scmi_iio_get_raw_available, + .shared = IIO_SHARED_BY_TYPE, + }, + {}, +}; + +static void scmi_iio_set_timestamp_channel(struct iio_chan_spec *iio_chan, + int scan_index) +{ + iio_chan->type = IIO_TIMESTAMP; + iio_chan->channel = -1; + iio_chan->scan_index = scan_index; + iio_chan->scan_type.sign = 'u'; + iio_chan->scan_type.realbits = 64; + iio_chan->scan_type.storagebits = 64; +} + +static void scmi_iio_set_data_channel(struct iio_chan_spec *iio_chan, + enum iio_chan_type type, + enum iio_modifier mod, int scan_index) +{ + iio_chan->type = type; + iio_chan->modified = 1; + iio_chan->channel2 = mod; + iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_SCALE); + iio_chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ); + iio_chan->info_mask_shared_by_type_available = + BIT(IIO_CHAN_INFO_SAMP_FREQ); + iio_chan->scan_index = scan_index; + iio_chan->scan_type.sign = 's'; + iio_chan->scan_type.realbits = 64; + iio_chan->scan_type.storagebits = 64; + iio_chan->scan_type.endianness = IIO_LE; + iio_chan->ext_info = scmi_iio_ext_info; +} + +static int scmi_iio_get_chan_modifier(const char *name, + enum iio_modifier *modifier) +{ + char *pch, mod; + + if (!name) + return -EINVAL; + + pch = strrchr(name, '_'); + if (!pch) + return -EINVAL; + + mod = *(pch + 1); + switch (mod) { + case 'X': + *modifier = IIO_MOD_X; + return 0; + case 'Y': + *modifier = IIO_MOD_Y; + return 0; + case 'Z': + *modifier = IIO_MOD_Z; + return 0; + default: + return -EINVAL; + } +} + +static int scmi_iio_get_chan_type(u8 scmi_type, enum iio_chan_type *iio_type) +{ + switch (scmi_type) { + case METERS_SEC_SQUARED: + *iio_type = IIO_ACCEL; + return 0; + case RADIANS_SEC: + *iio_type = IIO_ANGL_VEL; + return 0; + default: + return -EINVAL; + } +} + +static u64 scmi_iio_convert_interval_to_ns(u32 val) +{ + u64 sensor_update_interval = + SCMI_SENS_INTVL_GET_SECS(val) * NSEC_PER_SEC; + u64 sensor_interval_mult; + int mult; + + mult = SCMI_SENS_INTVL_GET_EXP(val); + if (mult < 0) { + sensor_interval_mult = int_pow(10, abs(mult)); + do_div(sensor_update_interval, sensor_interval_mult); + } else { + sensor_interval_mult = int_pow(10, mult); + sensor_update_interval = + sensor_update_interval * sensor_interval_mult; + } + return sensor_update_interval; +} + +static int scmi_iio_set_sampling_freq_avail(struct iio_dev *iio_dev) +{ + u64 cur_interval_ns, low_interval_ns, high_interval_ns, step_size_ns, + hz, uhz; + unsigned int cur_interval, low_interval, high_interval, step_size; + struct scmi_iio_priv *sensor = iio_priv(iio_dev); + int i; + + sensor->freq_avail = + devm_kzalloc(&iio_dev->dev, + sizeof(*sensor->freq_avail) * + (sensor->sensor_info->intervals.count * 2), + GFP_KERNEL); + if (!sensor->freq_avail) + return -ENOMEM; + + if (sensor->sensor_info->intervals.segmented) { + low_interval = sensor->sensor_info->intervals + .desc[SCMI_SENS_INTVL_SEGMENT_LOW]; + low_interval_ns = scmi_iio_convert_interval_to_ns(low_interval); + convert_ns_to_freq(low_interval_ns, &hz, &uhz); + sensor->freq_avail[0] = hz; + sensor->freq_avail[1] = uhz; + + step_size = sensor->sensor_info->intervals + .desc[SCMI_SENS_INTVL_SEGMENT_STEP]; + step_size_ns = scmi_iio_convert_interval_to_ns(step_size); + convert_ns_to_freq(step_size_ns, &hz, &uhz); + sensor->freq_avail[2] = hz; + sensor->freq_avail[3] = uhz; + + high_interval = sensor->sensor_info->intervals + .desc[SCMI_SENS_INTVL_SEGMENT_HIGH]; + high_interval_ns = + scmi_iio_convert_interval_to_ns(high_interval); + convert_ns_to_freq(high_interval_ns, &hz, &uhz); + sensor->freq_avail[4] = hz; + sensor->freq_avail[5] = uhz; + } else { + for (i = 0; i < sensor->sensor_info->intervals.count; i++) { + cur_interval = sensor->sensor_info->intervals.desc[i]; + cur_interval_ns = + scmi_iio_convert_interval_to_ns(cur_interval); + convert_ns_to_freq(cur_interval_ns, &hz, &uhz); + sensor->freq_avail[i * 2] = hz; + sensor->freq_avail[i * 2 + 1] = uhz; + } + } + return 0; +} + +static int scmi_iio_buffers_setup(struct iio_dev *scmi_iiodev) +{ + struct iio_buffer *buffer; + + buffer = devm_iio_kfifo_allocate(&scmi_iiodev->dev); + if (!buffer) + return -ENOMEM; + + iio_device_attach_buffer(scmi_iiodev, buffer); + scmi_iiodev->modes |= INDIO_BUFFER_SOFTWARE; + scmi_iiodev->setup_ops = &scmi_iio_buffer_ops; + return 0; +} + +static struct iio_dev *scmi_alloc_iiodev(struct device *dev, + struct scmi_handle *handle, + const struct scmi_sensor_info *sensor_info) +{ + struct iio_chan_spec *iio_channels; + struct scmi_iio_priv *sensor; + enum iio_modifier modifier; + enum iio_chan_type type; + struct iio_dev *iiodev; + int i, ret; + + iiodev = devm_iio_device_alloc(dev, sizeof(*sensor)); + if (!iiodev) + return ERR_PTR(-ENOMEM); + + iiodev->modes = INDIO_DIRECT_MODE; + iiodev->dev.parent = dev; + sensor = iio_priv(iiodev); + sensor->handle = handle; + sensor->sensor_info = sensor_info; + sensor->sensor_update_nb.notifier_call = scmi_iio_sensor_update_cb; + sensor->indio_dev = iiodev; + + /* adding one additional channel for timestamp */ + iiodev->num_channels = sensor_info->num_axis + 1; + iiodev->name = sensor_info->name; + iiodev->info = &scmi_iio_info; + + iio_channels = + devm_kzalloc(dev, + sizeof(*iio_channels) * (iiodev->num_channels), + GFP_KERNEL); + if (!iio_channels) + return ERR_PTR(-ENOMEM); + + ret = scmi_iio_set_sampling_freq_avail(iiodev); + if (ret < 0) + return ERR_PTR(ret); + + for (i = 0; i < sensor_info->num_axis; i++) { + ret = scmi_iio_get_chan_type(sensor_info->axis[i].type, &type); + if (ret < 0) + return ERR_PTR(ret); + + ret = scmi_iio_get_chan_modifier(sensor_info->axis[i].name, + &modifier); + if (ret < 0) + return ERR_PTR(ret); + + scmi_iio_set_data_channel(&iio_channels[i], type, modifier, + sensor_info->axis[i].id); + } + + scmi_iio_set_timestamp_channel(&iio_channels[i], i); + iiodev->channels = iio_channels; + return iiodev; +} + +static int scmi_iio_dev_probe(struct scmi_device *sdev) +{ + const struct scmi_sensor_info *sensor_info; + struct scmi_handle *handle = sdev->handle; + struct device *dev = &sdev->dev; + struct iio_dev *scmi_iio_dev; + u16 nr_sensors; + int err = -ENODEV, i; + + if (!handle || !handle->sensor_ops) { + dev_err(dev, "SCMI device has no sensor interface\n"); + return -EINVAL; + } + + nr_sensors = handle->sensor_ops->count_get(handle); + if (!nr_sensors) { + dev_dbg(dev, "0 sensors found via SCMI bus\n"); + return -ENODEV; + } + + for (i = 0; i < nr_sensors; i++) { + sensor_info = handle->sensor_ops->info_get(handle, i); + if (!sensor_info) { + dev_err(dev, "SCMI sensor %d has missing info\n", i); + return -EINVAL; + } + + /* This driver only supports 3-axis accel and gyro, skipping other sensors */ + if (sensor_info->num_axis != SCMI_IIO_NUM_OF_AXIS) + continue; + + /* This driver only supports 3-axis accel and gyro, skipping other sensors */ + if (sensor_info->axis[0].type != METERS_SEC_SQUARED && + sensor_info->axis[0].type != RADIANS_SEC) + continue; + + scmi_iio_dev = scmi_alloc_iiodev(dev, handle, sensor_info); + if (IS_ERR(scmi_iio_dev)) { + dev_err(dev, + "failed to allocate IIO device for sensor %s: %ld\n", + sensor_info->name, PTR_ERR(scmi_iio_dev)); + return PTR_ERR(scmi_iio_dev); + } + + err = scmi_iio_buffers_setup(scmi_iio_dev); + if (err < 0) { + dev_err(dev, + "IIO buffer setup error at sensor %s: %d\n", + sensor_info->name, err); + return err; + } + + err = devm_iio_device_register(dev, scmi_iio_dev); + if (err) { + dev_err(dev, + "IIO device registration failed at sensor %s: %d\n", + sensor_info->name, err); + return err; + } + } + return err; +} + +static const struct scmi_device_id scmi_id_table[] = { + { SCMI_PROTOCOL_SENSOR, "iiodev" }, + {}, +}; + +MODULE_DEVICE_TABLE(scmi, scmi_id_table); + +static struct scmi_driver scmi_iiodev_driver = { + .name = "scmi-sensor-iiodev", + .probe = scmi_iio_dev_probe, + .id_table = scmi_id_table, +}; + +module_scmi_driver(scmi_iiodev_driver); + +MODULE_AUTHOR("Jyoti Bhayana "); +MODULE_DESCRIPTION("SCMI IIO Driver"); +MODULE_LICENSE("GPL v2"); From 4abbaf29dfd8336334c1791ae0e96095ae1cf438 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 17 Jan 2021 15:38:14 +0000 Subject: [PATCH 065/119] iio:ABI docs: Combine sysfs-bus-iio-humidity-hdc2010/hdc100x into one file These contain only one entry for out_current_heater_raw (_available). Document this in a new sysfs-bus-iio-humidity file, and make it a little more generic by allowing for non 0/1 values. Fixes $ scripts/get_abi.pl validate Warning: /sys/bus/iio/devices/iio:deviceX/out_current_heater_raw is defined 2 times: ./Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc2010:0 ./Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc100x:0 Warning: /sys/bus/iio/devices/iio:deviceX/out_current_heater_raw_available is defined 2 times: ./Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc2010:1 ./Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc100x:1 Cc: Eugene Zaikonnikov Cc: Matt Ranostay Reported-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Cameron Reviewed-by: Matt Ranostay Link: https://lore.kernel.org/r/20210117153816.696693-6-jic23@kernel.org --- ...s-bus-iio-humidity-hdc2010 => sysfs-bus-iio-humidity} | 3 ++- Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc100x | 9 --------- 2 files changed, 2 insertions(+), 10 deletions(-) rename Documentation/ABI/testing/{sysfs-bus-iio-humidity-hdc2010 => sysfs-bus-iio-humidity} (79%) delete mode 100644 Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc100x diff --git a/Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc2010 b/Documentation/ABI/testing/sysfs-bus-iio-humidity similarity index 79% rename from Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc2010 rename to Documentation/ABI/testing/sysfs-bus-iio-humidity index 5b78af5f341d..cb0d7e75d297 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc2010 +++ b/Documentation/ABI/testing/sysfs-bus-iio-humidity @@ -6,4 +6,5 @@ Description: Controls the heater device within the humidity sensor to get rid of excess condensation. - Valid control values are 0 = OFF, and 1 = ON. + In some devices, this is just a switch in which case 0 = OFF, + and 1 = ON. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc100x b/Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc100x deleted file mode 100644 index b72bb62552cf..000000000000 --- a/Documentation/ABI/testing/sysfs-bus-iio-humidity-hdc100x +++ /dev/null @@ -1,9 +0,0 @@ -What: /sys/bus/iio/devices/iio:deviceX/out_current_heater_raw -What: /sys/bus/iio/devices/iio:deviceX/out_current_heater_raw_available -KernelVersion: 4.3 -Contact: linux-iio@vger.kernel.org -Description: - Controls the heater device within the humidity sensor to get - rid of excess condensation. - - Valid control values are 0 = OFF, and 1 = ON. From 2a94469f736c264a2647abbb9a1862c3fe4d9f66 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 17 Jan 2021 15:38:15 +0000 Subject: [PATCH 066/119] iio:ABI docs: Combine the two instances of docs for sensor_sensitivity This control on the gain of a measurement used for time of flight sensing is standard but the expected values for different enviroments may not be. As we cannot have the same ABI element documented in two files, add a generic version to sysfs-bus-iio-proximity and a note on the expected value vs measuring environment for the as3935. Fixes $ scripts/get_abi.pl validate Warning: /sys/bus/iio/devices/iio:deviceX/sensor_sensitivity is defined 2 times: ./Documentation/ABI/testing/sysfs-bus-iio-distance-srf08:0 ./Documentation/ABI/testing/sysfs-bus-iio-proximity-as3935:8 Cc: Matt Ranostay Reported-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Cameron Reviewed-by: Matt Ranostay Link: https://lore.kernel.org/r/20210117153816.696693-7-jic23@kernel.org Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-iio-distance-srf08 | 8 -------- Documentation/ABI/testing/sysfs-bus-iio-proximity | 14 ++++++++++++++ .../ABI/testing/sysfs-bus-iio-proximity-as3935 | 9 --------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio-distance-srf08 b/Documentation/ABI/testing/sysfs-bus-iio-distance-srf08 index 40df5c9fef99..9dae94aa880b 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-distance-srf08 +++ b/Documentation/ABI/testing/sysfs-bus-iio-distance-srf08 @@ -1,11 +1,3 @@ -What: /sys/bus/iio/devices/iio:deviceX/sensor_sensitivity -Date: January 2017 -KernelVersion: 4.11 -Contact: linux-iio@vger.kernel.org -Description: - Show or set the gain boost of the amp, from 0-31 range. - default 31 - What: /sys/bus/iio/devices/iio:deviceX/sensor_max_range Date: January 2017 KernelVersion: 4.11 diff --git a/Documentation/ABI/testing/sysfs-bus-iio-proximity b/Documentation/ABI/testing/sysfs-bus-iio-proximity index 2172f3bb9c64..3aac6dab8775 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-proximity +++ b/Documentation/ABI/testing/sysfs-bus-iio-proximity @@ -8,3 +8,17 @@ Description: considered close to the device. If the value read from the sensor is above or equal to the value in this file an object should typically be considered near. + +What: /sys/bus/iio/devices/iio:deviceX/sensor_sensitivity +Date: March 2014 +KernelVersion: 3.15 +Contact: linux-iio@vger.kernel.org +Description: + Proximity sensors sometimes have a controllable amplifier + on the signal from which time of flight measurements are + taken. + The appropriate values to take is dependent on both the + sensor and it's operating environment: + * as3935 (0-31 range) + 18 = indoors (default) + 14 = outdoors diff --git a/Documentation/ABI/testing/sysfs-bus-iio-proximity-as3935 b/Documentation/ABI/testing/sysfs-bus-iio-proximity-as3935 index c59d95346341..1e5c40775a6c 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-proximity-as3935 +++ b/Documentation/ABI/testing/sysfs-bus-iio-proximity-as3935 @@ -6,15 +6,6 @@ Description: Get the current distance in meters of storm (1km steps) 1000-40000 = distance in meters -What: /sys/bus/iio/devices/iio:deviceX/sensor_sensitivity -Date: March 2014 -KernelVersion: 3.15 -Contact: Matt Ranostay -Description: - Show or set the gain boost of the amp, from 0-31 range. - 18 = indoors (default) - 14 = outdoors - What /sys/bus/iio/devices/iio:deviceX/noise_level_tripped Date: May 2017 KernelVersion: 4.13 From 31e2d42a86201d32c345bebd4d324b8d8424f4c2 Mon Sep 17 00:00:00 2001 From: Sergiu Cuciurean Date: Mon, 28 Sep 2020 16:13:29 +0300 Subject: [PATCH 067/119] iio: adc: spear_adc: Replace indio_dev->mlock with own device lock As part of the general cleanup of indio_dev->mlock, this change replaces it with a local lock on the device's state structure. This is part of a bigger cleanup. Link: https://lore.kernel.org/linux-iio/CA+U=Dsoo6YABe5ODLp+eFNPGFDjk5ZeQEceGkqjxXcVEhLWubw@mail.gmail.com/ Signed-off-by: Sergiu Cuciurean Signed-off-by: Mircea Caprioru Link: https://lore.kernel.org/r/20200928131333.36646-1-mircea.caprioru@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/spear_adc.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c index 1bc986a7009d..d93e580b3dc5 100644 --- a/drivers/iio/adc/spear_adc.c +++ b/drivers/iio/adc/spear_adc.c @@ -75,6 +75,15 @@ struct spear_adc_state { struct adc_regs_spear6xx __iomem *adc_base_spear6xx; struct clk *clk; struct completion completion; + /* + * Lock to protect the device state during a potential concurrent + * read access from userspace. Reading a raw value requires a sequence + * of register writes, then a wait for a completion callback, + * and finally a register read, during which userspace could issue + * another read request. This lock protects a read access from + * ocurring before another one has finished. + */ + struct mutex lock; u32 current_clk; u32 sampling_freq; u32 avg_samples; @@ -146,7 +155,7 @@ static int spear_adc_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); status = SPEAR_ADC_STATUS_CHANNEL_NUM(chan->channel) | SPEAR_ADC_STATUS_AVG_SAMPLE(st->avg_samples) | @@ -159,7 +168,7 @@ static int spear_adc_read_raw(struct iio_dev *indio_dev, wait_for_completion(&st->completion); /* set by ISR */ *val = st->value; - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return IIO_VAL_INT; @@ -187,7 +196,7 @@ static int spear_adc_write_raw(struct iio_dev *indio_dev, if (mask != IIO_CHAN_INFO_SAMP_FREQ) return -EINVAL; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); if ((val < SPEAR_ADC_CLK_MIN) || (val > SPEAR_ADC_CLK_MAX) || @@ -199,7 +208,7 @@ static int spear_adc_write_raw(struct iio_dev *indio_dev, spear_adc_set_clk(st, val); out: - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -271,6 +280,9 @@ static int spear_adc_probe(struct platform_device *pdev) } st = iio_priv(indio_dev); + + mutex_init(&st->lock); + st->np = np; /* From 3a27d11fbf58e7b29930376b0b869882c780dd5b Mon Sep 17 00:00:00 2001 From: Sergiu Cuciurean Date: Mon, 28 Sep 2020 16:13:30 +0300 Subject: [PATCH 068/119] iio: adc: palmas_gpadc: Replace indio_dev->mlock with own device lock As part of the general cleanup of indio_dev->mlock, this change replaces it with a local lock on the device's state structure. This is part of a bigger cleanup. Link: https://lore.kernel.org/linux-iio/CA+U=Dsoo6YABe5ODLp+eFNPGFDjk5ZeQEceGkqjxXcVEhLWubw@mail.gmail.com/ Signed-off-by: Sergiu Cuciurean Signed-off-by: Mircea Caprioru Link: https://lore.kernel.org/r/20200928131333.36646-2-mircea.caprioru@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/palmas_gpadc.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index 9ae0d7f73155..6ef09609be9f 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -90,6 +90,12 @@ static struct palmas_gpadc_info palmas_gpadc_info[] = { * 3: 800 uA * @extended_delay: enable the gpadc extended delay mode * @auto_conversion_period: define the auto_conversion_period + * @lock: Lock to protect the device state during a potential concurrent + * read access from userspace. Reading a raw value requires a sequence + * of register writes, then a wait for a completion callback, + * and finally a register read, during which userspace could issue + * another read request. This lock protects a read access from + * ocurring before another one has finished. * * This is the palmas_gpadc structure to store run-time information * and pointers for this driver instance. @@ -110,6 +116,7 @@ struct palmas_gpadc { bool wakeup1_enable; bool wakeup2_enable; int auto_conversion_period; + struct mutex lock; }; /* @@ -388,7 +395,7 @@ static int palmas_gpadc_read_raw(struct iio_dev *indio_dev, if (adc_chan > PALMAS_ADC_CH_MAX) return -EINVAL; - mutex_lock(&indio_dev->mlock); + mutex_lock(&adc->lock); switch (mask) { case IIO_CHAN_INFO_RAW: @@ -414,12 +421,12 @@ static int palmas_gpadc_read_raw(struct iio_dev *indio_dev, goto out; } - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&adc->lock); return ret; out: palmas_gpadc_read_done(adc, adc_chan); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&adc->lock); return ret; } @@ -516,6 +523,9 @@ static int palmas_gpadc_probe(struct platform_device *pdev) adc->dev = &pdev->dev; adc->palmas = dev_get_drvdata(pdev->dev.parent); adc->adc_info = palmas_gpadc_info; + + mutex_init(&adc->lock); + init_completion(&adc->conv_completion); platform_set_drvdata(pdev, indio_dev); From 08dfc6f8aa5d443c81347f6ea876d833f5aedcf6 Mon Sep 17 00:00:00 2001 From: Sergiu Cuciurean Date: Mon, 28 Sep 2020 16:13:31 +0300 Subject: [PATCH 069/119] iio: adc: npcm_adc: Replace indio_dev->mlock with own device lock As part of the general cleanup of indio_dev->mlock, this change replaces it with a local lock on the device's state structure. This is part of a bigger cleanup. Link: https://lore.kernel.org/linux-iio/CA+U=Dsoo6YABe5ODLp+eFNPGFDjk5ZeQEceGkqjxXcVEhLWubw@mail.gmail.com/ Signed-off-by: Sergiu Cuciurean Signed-off-by: Mircea Caprioru Link: https://lore.kernel.org/r/20200928131333.36646-3-mircea.caprioru@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/npcm_adc.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c index d9d105920001..f7bc0bb7f112 100644 --- a/drivers/iio/adc/npcm_adc.c +++ b/drivers/iio/adc/npcm_adc.c @@ -25,6 +25,15 @@ struct npcm_adc { wait_queue_head_t wq; struct regulator *vref; struct reset_control *reset; + /* + * Lock to protect the device state during a potential concurrent + * read access from userspace. Reading a raw value requires a sequence + * of register writes, then a wait for a event and finally a register + * read, during which userspace could issue another read request. + * This lock protects a read access from ocurring before another one + * has finished. + */ + struct mutex lock; }; /* ADC registers */ @@ -135,9 +144,9 @@ static int npcm_adc_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); + mutex_lock(&info->lock); ret = npcm_adc_read(info, val, chan->channel); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&info->lock); if (ret) { dev_err(info->dev, "NPCM ADC read failed\n"); return ret; @@ -187,6 +196,8 @@ static int npcm_adc_probe(struct platform_device *pdev) return -ENOMEM; info = iio_priv(indio_dev); + mutex_init(&info->lock); + info->dev = &pdev->dev; info->regs = devm_platform_ioremap_resource(pdev, 0); From 6a878e70e88b53c015cae1a3898c17db4b4cb199 Mon Sep 17 00:00:00 2001 From: Ivan Mikhaylov Date: Thu, 25 Feb 2021 23:14:44 +0300 Subject: [PATCH 070/119] iio: proximity: vcnl3020: add proximity rate Add the proximity rate optional option and handling of it for vishay vcnl3020. Signed-off-by: Ivan Mikhaylov Link: https://lore.kernel.org/r/20210225201444.12983-2-i.mikhaylov@yadro.com Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/vcnl3020.c | 97 +++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/drivers/iio/proximity/vcnl3020.c b/drivers/iio/proximity/vcnl3020.c index 37264f801ad0..43817f6b3086 100644 --- a/drivers/iio/proximity/vcnl3020.c +++ b/drivers/iio/proximity/vcnl3020.c @@ -40,6 +40,17 @@ #define VCNL_ON_DEMAND_TIMEOUT_US 100000 #define VCNL_POLL_US 20000 +static const int vcnl3020_prox_sampling_frequency[][2] = { + {1, 950000}, + {3, 906250}, + {7, 812500}, + {16, 625000}, + {31, 250000}, + {62, 500000}, + {125, 0}, + {250, 0}, +}; + /** * struct vcnl3020_data - vcnl3020 specific data. * @regmap: device register map. @@ -165,10 +176,51 @@ err_unlock: return rc; } +static int vcnl3020_read_proxy_samp_freq(struct vcnl3020_data *data, int *val, + int *val2) +{ + int rc; + unsigned int prox_rate; + + rc = regmap_read(data->regmap, VCNL_PROXIMITY_RATE, &prox_rate); + if (rc) + return rc; + + if (prox_rate >= ARRAY_SIZE(vcnl3020_prox_sampling_frequency)) + return -EINVAL; + + *val = vcnl3020_prox_sampling_frequency[prox_rate][0]; + *val2 = vcnl3020_prox_sampling_frequency[prox_rate][1]; + + return 0; +} + +static int vcnl3020_write_proxy_samp_freq(struct vcnl3020_data *data, int val, + int val2) +{ + unsigned int i; + int index = -1; + + for (i = 0; i < ARRAY_SIZE(vcnl3020_prox_sampling_frequency); i++) { + if (val == vcnl3020_prox_sampling_frequency[i][0] && + val2 == vcnl3020_prox_sampling_frequency[i][1]) { + index = i; + break; + } + } + + if (index < 0) + return -EINVAL; + + return regmap_write(data->regmap, VCNL_PROXIMITY_RATE, index); +} + static const struct iio_chan_spec vcnl3020_channels[] = { { .type = IIO_PROXIMITY, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), }, }; @@ -185,6 +237,47 @@ static int vcnl3020_read_raw(struct iio_dev *indio_dev, if (rc) return rc; return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + rc = vcnl3020_read_proxy_samp_freq(data, val, val2); + if (rc < 0) + return rc; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int vcnl3020_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int rc; + struct vcnl3020_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + rc = iio_device_claim_direct_mode(indio_dev); + if (rc) + return rc; + rc = vcnl3020_write_proxy_samp_freq(data, val, val2); + iio_device_release_direct_mode(indio_dev); + return rc; + default: + return -EINVAL; + } +} + +static int vcnl3020_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (int *)vcnl3020_prox_sampling_frequency; + *type = IIO_VAL_INT_PLUS_MICRO; + *length = 2 * ARRAY_SIZE(vcnl3020_prox_sampling_frequency); + return IIO_AVAIL_LIST; default: return -EINVAL; } @@ -192,6 +285,8 @@ static int vcnl3020_read_raw(struct iio_dev *indio_dev, static const struct iio_info vcnl3020_info = { .read_raw = vcnl3020_read_raw, + .write_raw = vcnl3020_write_raw, + .read_avail = vcnl3020_read_avail, }; static const struct regmap_config vcnl3020_regmap_config = { From 9013b1d970455d50154e8dec31b7c0e3040f01c3 Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Mon, 1 Mar 2021 09:00:28 +0100 Subject: [PATCH 071/119] iio: accel: mma8452: fix indentation Improve readability by fixing indentation. Signed-off-by: Sean Nyekjaer Link: https://lore.kernel.org/r/20210301080029.1974797-1-sean@geanix.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 46 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index b0176d936423..33f0c419d8ff 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -58,7 +58,7 @@ #define MMA8452_FF_MT_THS 0x17 #define MMA8452_FF_MT_THS_MASK 0x7f #define MMA8452_FF_MT_COUNT 0x18 -#define MMA8452_FF_MT_CHAN_SHIFT 3 +#define MMA8452_FF_MT_CHAN_SHIFT 3 #define MMA8452_TRANSIENT_CFG 0x1d #define MMA8452_TRANSIENT_CFG_CHAN(chan) BIT(chan + 1) #define MMA8452_TRANSIENT_CFG_HPF_BYP BIT(0) @@ -70,7 +70,7 @@ #define MMA8452_TRANSIENT_THS 0x1f #define MMA8452_TRANSIENT_THS_MASK GENMASK(6, 0) #define MMA8452_TRANSIENT_COUNT 0x20 -#define MMA8452_TRANSIENT_CHAN_SHIFT 1 +#define MMA8452_TRANSIENT_CHAN_SHIFT 1 #define MMA8452_CTRL_REG1 0x2a #define MMA8452_CTRL_ACTIVE BIT(0) #define MMA8452_CTRL_DR_MASK GENMASK(5, 3) @@ -134,33 +134,33 @@ struct mma8452_data { * used for different chips and the relevant registers are included here. */ struct mma8452_event_regs { - u8 ev_cfg; - u8 ev_cfg_ele; - u8 ev_cfg_chan_shift; - u8 ev_src; - u8 ev_ths; - u8 ev_ths_mask; - u8 ev_count; + u8 ev_cfg; + u8 ev_cfg_ele; + u8 ev_cfg_chan_shift; + u8 ev_src; + u8 ev_ths; + u8 ev_ths_mask; + u8 ev_count; }; static const struct mma8452_event_regs ff_mt_ev_regs = { - .ev_cfg = MMA8452_FF_MT_CFG, - .ev_cfg_ele = MMA8452_FF_MT_CFG_ELE, - .ev_cfg_chan_shift = MMA8452_FF_MT_CHAN_SHIFT, - .ev_src = MMA8452_FF_MT_SRC, - .ev_ths = MMA8452_FF_MT_THS, - .ev_ths_mask = MMA8452_FF_MT_THS_MASK, - .ev_count = MMA8452_FF_MT_COUNT + .ev_cfg = MMA8452_FF_MT_CFG, + .ev_cfg_ele = MMA8452_FF_MT_CFG_ELE, + .ev_cfg_chan_shift = MMA8452_FF_MT_CHAN_SHIFT, + .ev_src = MMA8452_FF_MT_SRC, + .ev_ths = MMA8452_FF_MT_THS, + .ev_ths_mask = MMA8452_FF_MT_THS_MASK, + .ev_count = MMA8452_FF_MT_COUNT }; static const struct mma8452_event_regs trans_ev_regs = { - .ev_cfg = MMA8452_TRANSIENT_CFG, - .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE, - .ev_cfg_chan_shift = MMA8452_TRANSIENT_CHAN_SHIFT, - .ev_src = MMA8452_TRANSIENT_SRC, - .ev_ths = MMA8452_TRANSIENT_THS, - .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK, - .ev_count = MMA8452_TRANSIENT_COUNT, + .ev_cfg = MMA8452_TRANSIENT_CFG, + .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE, + .ev_cfg_chan_shift = MMA8452_TRANSIENT_CHAN_SHIFT, + .ev_src = MMA8452_TRANSIENT_SRC, + .ev_ths = MMA8452_TRANSIENT_THS, + .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK, + .ev_count = MMA8452_TRANSIENT_COUNT, }; /** From 7b3589f49b824e718c319dbd31535c608a410a55 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Tue, 23 Feb 2021 14:12:23 +0200 Subject: [PATCH 072/119] staging: iio: remove mention of defunct list The ADI device-drivers-devel list no longer exists. Cc: Mike Frysinger Signed-off-by: Baruch Siach Link: https://lore.kernel.org/r/69b3e45e7666a1dd74a83df0b84ef8a63bf090ea.1614082343.git.baruch@tkos.co.il Signed-off-by: Jonathan Cameron --- drivers/staging/iio/TODO | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/staging/iio/TODO b/drivers/staging/iio/TODO index 4d469016a13a..0fa6a5500bdb 100644 --- a/drivers/staging/iio/TODO +++ b/drivers/staging/iio/TODO @@ -1,9 +1,5 @@ 2020-02-25 -ADI Drivers: -CC the device-drivers-devel@blackfin.uclinux.org mailing list when -e-mailing the normal IIO list (see below). - Contact: Jonathan Cameron . Mailing list: linux-iio@vger.kernel.org From dafcf4ed8392476099c9e95642d2daa6e61ee1b6 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 25 Feb 2021 17:47:33 -0800 Subject: [PATCH 073/119] iio: hrtimer: Allow sub Hz granularity Allow setting frequency below 1Hz or sub 1Hz precision. Useful for slow sensors like ALS. Test frequency is set properly: modprobe iio-trig-hrtimer && \ mkdir /sys/kernel/config/iio/triggers/hrtimer/t1 && \ cd /sys/bus/iio/devices/triggerX ; for i in 1 .1 .01 .001 ; do echo $i > sampling_frequency cat sampling_frequency done Signed-off-by: Gwendal Grignou Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210226014733.2108544-1-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- Documentation/iio/iio_configfs.rst | 1 + drivers/iio/trigger/iio-trig-hrtimer.c | 33 ++++++++++++++++++-------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/Documentation/iio/iio_configfs.rst b/Documentation/iio/iio_configfs.rst index dbc61f35ea05..b276397af797 100644 --- a/Documentation/iio/iio_configfs.rst +++ b/Documentation/iio/iio_configfs.rst @@ -99,3 +99,4 @@ Each trigger can have one or more attributes specific to the trigger type. "hrtimer" trigger type doesn't have any configurable attribute from /config dir. It does introduce the sampling_frequency attribute to trigger directory. +That attribute sets the polling frequency in Hz, with mHz precision. diff --git a/drivers/iio/trigger/iio-trig-hrtimer.c b/drivers/iio/trigger/iio-trig-hrtimer.c index 410de837d041..aa8cc7391a60 100644 --- a/drivers/iio/trigger/iio-trig-hrtimer.c +++ b/drivers/iio/trigger/iio-trig-hrtimer.c @@ -16,13 +16,16 @@ #include #include +/* Defined locally, not in time64.h yet. */ +#define PSEC_PER_SEC 1000000000000LL + /* default sampling frequency - 100Hz */ #define HRTIMER_DEFAULT_SAMPLING_FREQUENCY 100 struct iio_hrtimer_info { struct iio_sw_trigger swt; struct hrtimer timer; - unsigned long sampling_frequency; + int sampling_frequency[2]; ktime_t period; }; @@ -38,7 +41,9 @@ ssize_t iio_hrtimer_show_sampling_frequency(struct device *dev, struct iio_trigger *trig = to_iio_trigger(dev); struct iio_hrtimer_info *info = iio_trigger_get_drvdata(trig); - return snprintf(buf, PAGE_SIZE, "%lu\n", info->sampling_frequency); + return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, + ARRAY_SIZE(info->sampling_frequency), + info->sampling_frequency); } static @@ -48,18 +53,26 @@ ssize_t iio_hrtimer_store_sampling_frequency(struct device *dev, { struct iio_trigger *trig = to_iio_trigger(dev); struct iio_hrtimer_info *info = iio_trigger_get_drvdata(trig); - unsigned long val; - int ret; + unsigned long long val; + u64 period; + int integer, fract, ret; - ret = kstrtoul(buf, 10, &val); + ret = iio_str_to_fixpoint(buf, 100, &integer, &fract); if (ret) return ret; + if (integer < 0 || fract < 0) + return -ERANGE; - if (!val || val > NSEC_PER_SEC) + val = fract + 1000 * integer; /* mHz */ + + if (!val || val > UINT_MAX) return -EINVAL; - info->sampling_frequency = val; - info->period = NSEC_PER_SEC / val; + info->sampling_frequency[0] = integer; /* Hz */ + info->sampling_frequency[1] = fract * 1000; /* uHz */ + period = PSEC_PER_SEC; + do_div(period, val); + info->period = period; /* nS */ return len; } @@ -135,8 +148,8 @@ static struct iio_sw_trigger *iio_trig_hrtimer_probe(const char *name) hrtimer_init(&trig_info->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD); trig_info->timer.function = iio_hrtimer_trig_handler; - trig_info->sampling_frequency = HRTIMER_DEFAULT_SAMPLING_FREQUENCY; - trig_info->period = NSEC_PER_SEC / trig_info->sampling_frequency; + trig_info->sampling_frequency[0] = HRTIMER_DEFAULT_SAMPLING_FREQUENCY; + trig_info->period = NSEC_PER_SEC / trig_info->sampling_frequency[0]; ret = iio_trigger_register(trig_info->swt.trigger); if (ret) From bbc1308de79368879580635ded6df51d232845ef Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Sat, 6 Mar 2021 18:28:34 +0200 Subject: [PATCH 074/119] iio: kfifo: mask flags without zero-check in devm_iio_kfifo_buffer_setup() As pointed by Lars, this doesn't require a zero-check. Also, while looking at this a little closer at it (again), the masking can be done later, as there is a zero-check for 'mode_flags' anyway, which returns -EINVAL. And we only need the 'mode_flags' later in the logic. This change is more of a tweak. Fixes: e36db6a06937 ("iio: kfifo: add devm_iio_kfifo_buffer_setup() helper") Cc: Lars-Peter Clausen Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210306162834.7339-1-ardeleanalex@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/kfifo_buf.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c index 34289ce12f20..4ecfa0ec3016 100644 --- a/drivers/iio/buffer/kfifo_buf.c +++ b/drivers/iio/buffer/kfifo_buf.c @@ -225,9 +225,6 @@ int devm_iio_kfifo_buffer_setup(struct device *dev, { struct iio_buffer *buffer; - if (mode_flags) - mode_flags &= kfifo_access_funcs.modes; - if (!mode_flags) return -EINVAL; @@ -235,6 +232,8 @@ int devm_iio_kfifo_buffer_setup(struct device *dev, if (!buffer) return -ENOMEM; + mode_flags &= kfifo_access_funcs.modes; + indio_dev->modes |= mode_flags; indio_dev->setup_ops = setup_ops; From b9d453a53d5e3a47790675bc972de99bd163052e Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Tue, 9 Mar 2021 15:43:14 -0800 Subject: [PATCH 075/119] iio: Remove kernel-doc keyword in file header comment Remove kernel-doc keyword from function header comment. It fixes issues spotted by scripts/kernel-doc like: drivers/iio/.c:3: info: Scanning doc for function drivers/iio/.c:X: warning: expecting prototype for . Prototype was for () instead To reproduce the errors: scripts/kernel-doc -v -none $(find drivers/iio/ -name \*.c \ -exec head -2 {} \+ | grep -B2 -e '\*\*' | grep '==' | cut -d ' ' -f 2) After, confirm these errors are gone with: scripts/kernel-doc -v -none $(git show --name-only | grep -e "^driver") Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20210309234314.2208256-1-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bma220_spi.c | 2 +- drivers/iio/accel/da280.c | 2 +- drivers/iio/accel/da311.c | 2 +- drivers/iio/accel/dmard10.c | 2 +- drivers/iio/accel/mc3230.c | 2 +- drivers/iio/accel/mma7660.c | 2 +- drivers/iio/accel/stk8312.c | 2 +- drivers/iio/accel/stk8ba50.c | 2 +- drivers/iio/adc/ti-adc084s021.c | 2 +- drivers/iio/humidity/am2315.c | 2 +- drivers/iio/light/opt3001.c | 2 +- drivers/iio/light/stk3310.c | 2 +- drivers/iio/trigger/iio-trig-hrtimer.c | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c index 3c9b0c6954e6..36fc9876dbca 100644 --- a/drivers/iio/accel/bma220_spi.c +++ b/drivers/iio/accel/bma220_spi.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * BMA220 Digital triaxial acceleration sensor driver * * Copyright (c) 2016,2020 Intel Corporation. diff --git a/drivers/iio/accel/da280.c b/drivers/iio/accel/da280.c index 4472dde6899e..5edff9ba72da 100644 --- a/drivers/iio/accel/da280.c +++ b/drivers/iio/accel/da280.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * IIO driver for the MiraMEMS DA280 3-axis accelerometer and * IIO driver for the MiraMEMS DA226 2-axis accelerometer * diff --git a/drivers/iio/accel/da311.c b/drivers/iio/accel/da311.c index 3b3df620ba27..92593a1cd1aa 100644 --- a/drivers/iio/accel/da311.c +++ b/drivers/iio/accel/da311.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * IIO driver for the MiraMEMS DA311 3-axis accelerometer * * Copyright (c) 2016 Hans de Goede diff --git a/drivers/iio/accel/dmard10.c b/drivers/iio/accel/dmard10.c index 90206f015857..e84bf8db1e89 100644 --- a/drivers/iio/accel/dmard10.c +++ b/drivers/iio/accel/dmard10.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * IIO driver for the 3-axis accelerometer Domintech ARD10. * * Copyright (c) 2016 Hans de Goede diff --git a/drivers/iio/accel/mc3230.c b/drivers/iio/accel/mc3230.c index 46e4283fc037..735002b716f3 100644 --- a/drivers/iio/accel/mc3230.c +++ b/drivers/iio/accel/mc3230.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/** +/* * mCube MC3230 3-Axis Accelerometer * * Copyright (c) 2016 Hans de Goede diff --git a/drivers/iio/accel/mma7660.c b/drivers/iio/accel/mma7660.c index b3c9136d51ec..47f5cd66e996 100644 --- a/drivers/iio/accel/mma7660.c +++ b/drivers/iio/accel/mma7660.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * Freescale MMA7660FC 3-Axis Accelerometer * * Copyright (c) 2016, Intel Corporation. diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c index 3b59887a8581..1a8a1a0c3ffb 100644 --- a/drivers/iio/accel/stk8312.c +++ b/drivers/iio/accel/stk8312.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * Sensortek STK8312 3-Axis Accelerometer * * Copyright (c) 2015, Intel Corporation. diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c index 3ead378b02c9..7a06904455eb 100644 --- a/drivers/iio/accel/stk8ba50.c +++ b/drivers/iio/accel/stk8ba50.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * Sensortek STK8BA50 3-Axis Accelerometer * * Copyright (c) 2015, Intel Corporation. diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c index fb14b92fa6e7..d6e1bf3de755 100644 --- a/drivers/iio/adc/ti-adc084s021.c +++ b/drivers/iio/adc/ti-adc084s021.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * Copyright (C) 2017 Axis Communications AB * * Driver for Texas Instruments' ADC084S021 ADC chip. diff --git a/drivers/iio/humidity/am2315.c b/drivers/iio/humidity/am2315.c index 02ad1767c845..23bc9c784ef4 100644 --- a/drivers/iio/humidity/am2315.c +++ b/drivers/iio/humidity/am2315.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * Aosong AM2315 relative humidity and temperature * * Copyright (c) 2016, Intel Corporation. diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index 2d48d61909a4..52963da401a7 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * opt3001.c - Texas Instruments OPT3001 Light Sensor * * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index a2827d03ab0f..07e91846307c 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * Sensortek STK3310/STK3311 Ambient Light and Proximity Sensor * * Copyright (c) 2015, Intel Corporation. diff --git a/drivers/iio/trigger/iio-trig-hrtimer.c b/drivers/iio/trigger/iio-trig-hrtimer.c index aa8cc7391a60..e68a2c56d459 100644 --- a/drivers/iio/trigger/iio-trig-hrtimer.c +++ b/drivers/iio/trigger/iio-trig-hrtimer.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * The industrial I/O periodic hrtimer trigger driver * * Copyright (C) Intuitive Aerial AB From 374be283ad429bf703d9036b799331dda793aeb7 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 10 Feb 2021 18:45:59 -0800 Subject: [PATCH 076/119] platform/chrome: cros_ec: Add SW_FRONT_PROXIMITY MKBP define Some cros ECs support a front proximity MKBP event via 'EC_MKBP_FRONT_PROXIMITY'. Add this define so it can be used in a future patch. Cc: Dmitry Torokhov Cc: Benson Leung Cc: Guenter Roeck Cc: Douglas Anderson Cc: Gwendal Grignou Acked-by: Enric Balletbo i Serra Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20210211024601.1963379-2-swboyd@chromium.org Signed-off-by: Jonathan Cameron --- include/linux/platform_data/cros_ec_commands.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 5ff8597ceabd..6035d9a98fb8 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -3467,6 +3467,7 @@ struct ec_response_get_next_event_v1 { #define EC_MKBP_LID_OPEN 0 #define EC_MKBP_TABLET_MODE 1 #define EC_MKBP_BASE_ATTACHED 2 +#define EC_MKBP_FRONT_PROXIMITY 3 /* Run keyboard factory test scanning */ #define EC_CMD_KEYBOARD_FACTORY_TEST 0x0068 From 19ad93bc82e7d0ea7b02ada623cddfeab20c9046 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 10 Feb 2021 18:46:00 -0800 Subject: [PATCH 077/119] dt-bindings: iio: Add cros ec proximity yaml doc Some cros ECs support a front proximity MKBP event via 'EC_MKBP_FRONT_PROXIMITY'. Add a DT binding to document this feature via a node that is a child of the main cros_ec device node. Devices that have this ability will describe this in firmware. Cc: Dmitry Torokhov Cc: Benson Leung Cc: Guenter Roeck Cc: Douglas Anderson Cc: Gwendal Grignou Cc: Reviewed-by: Rob Herring Cc: Enric Balletbo i Serra Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20210211024601.1963379-3-swboyd@chromium.org Signed-off-by: Jonathan Cameron --- .../google,cros-ec-mkbp-proximity.yaml | 37 +++++++++++++++++++ .../bindings/mfd/google,cros-ec.yaml | 7 ++++ 2 files changed, 44 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/proximity/google,cros-ec-mkbp-proximity.yaml diff --git a/Documentation/devicetree/bindings/iio/proximity/google,cros-ec-mkbp-proximity.yaml b/Documentation/devicetree/bindings/iio/proximity/google,cros-ec-mkbp-proximity.yaml new file mode 100644 index 000000000000..099b4be927d4 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/proximity/google,cros-ec-mkbp-proximity.yaml @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- + +$id: http://devicetree.org/schemas/iio/proximity/google,cros-ec-mkbp-proximity.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ChromeOS EC MKBP Proximity Sensor + +maintainers: + - Stephen Boyd + - Benson Leung + - Enric Balletbo i Serra + +description: | + Google's ChromeOS EC sometimes has the ability to detect user proximity. + This is implemented on the EC as near/far logic and exposed to the OS + via an MKBP switch bit. + +properties: + compatible: + const: google,cros-ec-mkbp-proximity + + label: + description: Name for proximity sensor + +required: + - compatible + +additionalProperties: false + +examples: + - | + proximity { + compatible = "google,cros-ec-mkbp-proximity"; + label = "proximity-wifi-lte"; + }; diff --git a/Documentation/devicetree/bindings/mfd/google,cros-ec.yaml b/Documentation/devicetree/bindings/mfd/google,cros-ec.yaml index 76bf16ee27ec..4dfa70a013ae 100644 --- a/Documentation/devicetree/bindings/mfd/google,cros-ec.yaml +++ b/Documentation/devicetree/bindings/mfd/google,cros-ec.yaml @@ -94,6 +94,9 @@ properties: keyboard-controller: $ref: "/schemas/input/google,cros-ec-keyb.yaml#" + proximity: + $ref: "/schemas/iio/proximity/google,cros-ec-mkbp-proximity.yaml#" + codecs: type: object additionalProperties: false @@ -180,6 +183,10 @@ examples: interrupts = <99 0>; interrupt-parent = <&gpio7>; spi-max-frequency = <5000000>; + + proximity { + compatible = "google,cros-ec-mkbp-proximity"; + }; }; }; From 7792225b7b671800d1c9b562ace8e167a3d0e2e7 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 10 Feb 2021 18:46:01 -0800 Subject: [PATCH 078/119] iio: proximity: Add a ChromeOS EC MKBP proximity driver Add support for a ChromeOS EC proximity driver that exposes a "front" proximity sensor via the IIO subsystem. The EC decides when front proximity is near and sets an MKBP switch 'EC_MKBP_FRONT_PROXIMITY' to notify the kernel of proximity. Similarly, when proximity detects something far away it sets the switch bit to 0. For now this driver exposes a single sensor, but it could be expanded in the future via more MKBP bits if desired. Cc: Dmitry Torokhov Cc: Benson Leung Cc: Guenter Roeck Cc: Douglas Anderson Cc: Gwendal Grignou Reviewed-by: Enric Balletbo i Serra Signed-off-by: Stephen Boyd Reviewed-by: Gwendal Grignou Link: https://lore.kernel.org/r/20210211024601.1963379-4-swboyd@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/Kconfig | 11 + drivers/iio/proximity/Makefile | 1 + .../iio/proximity/cros_ec_mkbp_proximity.c | 271 ++++++++++++++++++ 3 files changed, 283 insertions(+) create mode 100644 drivers/iio/proximity/cros_ec_mkbp_proximity.c diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig index 12672a0e89ed..7c7203ca3ac6 100644 --- a/drivers/iio/proximity/Kconfig +++ b/drivers/iio/proximity/Kconfig @@ -21,6 +21,17 @@ endmenu menu "Proximity and distance sensors" +config CROS_EC_MKBP_PROXIMITY + tristate "ChromeOS EC MKBP Proximity sensor" + depends on CROS_EC + help + Say Y here to enable the proximity sensor implemented via the ChromeOS EC MKBP + switches protocol. You must enable one bus option (CROS_EC_I2C or CROS_EC_SPI) + to use this. + + To compile this driver as a module, choose M here: the + module will be called cros_ec_mkbp_proximity. + config ISL29501 tristate "Intersil ISL29501 Time Of Flight sensor" depends on I2C diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile index 9c1aca1a8b79..cbdac09433eb 100644 --- a/drivers/iio/proximity/Makefile +++ b/drivers/iio/proximity/Makefile @@ -5,6 +5,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AS3935) += as3935.o +obj-$(CONFIG_CROS_EC_MKBP_PROXIMITY) += cros_ec_mkbp_proximity.o obj-$(CONFIG_ISL29501) += isl29501.o obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o obj-$(CONFIG_MB1232) += mb1232.o diff --git a/drivers/iio/proximity/cros_ec_mkbp_proximity.c b/drivers/iio/proximity/cros_ec_mkbp_proximity.c new file mode 100644 index 000000000000..8213b0081713 --- /dev/null +++ b/drivers/iio/proximity/cros_ec_mkbp_proximity.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for cros-ec proximity sensor exposed through MKBP switch + * + * Copyright 2021 Google LLC. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +struct cros_ec_mkbp_proximity_data { + struct cros_ec_device *ec; + struct iio_dev *indio_dev; + struct mutex lock; + struct notifier_block notifier; + int last_proximity; + bool enabled; +}; + +static const struct iio_event_spec cros_ec_mkbp_proximity_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_chan_spec cros_ec_mkbp_proximity_chan_spec[] = { + { + .type = IIO_PROXIMITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .event_spec = cros_ec_mkbp_proximity_events, + .num_event_specs = ARRAY_SIZE(cros_ec_mkbp_proximity_events), + }, +}; + +static int cros_ec_mkbp_proximity_parse_state(const void *data) +{ + u32 switches = get_unaligned_le32(data); + + return !!(switches & BIT(EC_MKBP_FRONT_PROXIMITY)); +} + +static int cros_ec_mkbp_proximity_query(struct cros_ec_device *ec_dev, + int *state) +{ + struct { + struct cros_ec_command msg; + union { + struct ec_params_mkbp_info params; + u32 switches; + }; + } __packed buf = { }; + struct ec_params_mkbp_info *params = &buf.params; + struct cros_ec_command *msg = &buf.msg; + u32 *switches = &buf.switches; + size_t insize = sizeof(*switches); + int ret; + + msg->command = EC_CMD_MKBP_INFO; + msg->version = 1; + msg->outsize = sizeof(*params); + msg->insize = insize; + + params->info_type = EC_MKBP_INFO_CURRENT; + params->event_type = EC_MKBP_EVENT_SWITCH; + + ret = cros_ec_cmd_xfer_status(ec_dev, msg); + if (ret < 0) + return ret; + + if (ret != insize) { + dev_warn(ec_dev->dev, "wrong result size: %d != %zu\n", ret, + insize); + return -EPROTO; + } + + *state = cros_ec_mkbp_proximity_parse_state(switches); + return IIO_VAL_INT; +} + +static void cros_ec_mkbp_proximity_push_event(struct cros_ec_mkbp_proximity_data *data, int state) +{ + s64 timestamp; + u64 ev; + int dir; + struct iio_dev *indio_dev = data->indio_dev; + struct cros_ec_device *ec = data->ec; + + mutex_lock(&data->lock); + if (state != data->last_proximity) { + if (data->enabled) { + timestamp = ktime_to_ns(ec->last_event_time); + if (iio_device_get_clock(indio_dev) != CLOCK_BOOTTIME) + timestamp = iio_get_time_ns(indio_dev); + + dir = state ? IIO_EV_DIR_FALLING : IIO_EV_DIR_RISING; + ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, + IIO_EV_TYPE_THRESH, dir); + iio_push_event(indio_dev, ev, timestamp); + } + data->last_proximity = state; + } + mutex_unlock(&data->lock); +} + +static int cros_ec_mkbp_proximity_notify(struct notifier_block *nb, + unsigned long queued_during_suspend, + void *_ec) +{ + struct cros_ec_mkbp_proximity_data *data; + struct cros_ec_device *ec = _ec; + u8 event_type = ec->event_data.event_type & EC_MKBP_EVENT_TYPE_MASK; + void *switches; + int state; + + if (event_type == EC_MKBP_EVENT_SWITCH) { + data = container_of(nb, struct cros_ec_mkbp_proximity_data, + notifier); + + switches = &ec->event_data.data.switches; + state = cros_ec_mkbp_proximity_parse_state(switches); + cros_ec_mkbp_proximity_push_event(data, state); + } + + return NOTIFY_OK; +} + +static int cros_ec_mkbp_proximity_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, + int *val2, long mask) +{ + struct cros_ec_mkbp_proximity_data *data = iio_priv(indio_dev); + struct cros_ec_device *ec = data->ec; + + if (chan->type == IIO_PROXIMITY && mask == IIO_CHAN_INFO_RAW) + return cros_ec_mkbp_proximity_query(ec, val); + + return -EINVAL; +} + +static int cros_ec_mkbp_proximity_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct cros_ec_mkbp_proximity_data *data = iio_priv(indio_dev); + + return data->enabled; +} + +static int cros_ec_mkbp_proximity_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + struct cros_ec_mkbp_proximity_data *data = iio_priv(indio_dev); + + mutex_lock(&data->lock); + data->enabled = state; + mutex_unlock(&data->lock); + + return 0; +} + +static const struct iio_info cros_ec_mkbp_proximity_info = { + .read_raw = cros_ec_mkbp_proximity_read_raw, + .read_event_config = cros_ec_mkbp_proximity_read_event_config, + .write_event_config = cros_ec_mkbp_proximity_write_event_config, +}; + +static __maybe_unused int cros_ec_mkbp_proximity_resume(struct device *dev) +{ + struct cros_ec_mkbp_proximity_data *data = dev_get_drvdata(dev); + struct cros_ec_device *ec = data->ec; + int ret, state; + + ret = cros_ec_mkbp_proximity_query(ec, &state); + if (ret < 0) { + dev_warn(dev, "failed to fetch proximity state on resume: %d\n", + ret); + } else { + cros_ec_mkbp_proximity_push_event(data, state); + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(cros_ec_mkbp_proximity_pm_ops, NULL, + cros_ec_mkbp_proximity_resume); + +static int cros_ec_mkbp_proximity_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cros_ec_device *ec = dev_get_drvdata(dev->parent); + struct iio_dev *indio_dev; + struct cros_ec_mkbp_proximity_data *data; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->ec = ec; + data->indio_dev = indio_dev; + data->last_proximity = -1; /* Unknown to start */ + mutex_init(&data->lock); + platform_set_drvdata(pdev, data); + + indio_dev->name = dev->driver->name; + indio_dev->info = &cros_ec_mkbp_proximity_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = cros_ec_mkbp_proximity_chan_spec; + indio_dev->num_channels = ARRAY_SIZE(cros_ec_mkbp_proximity_chan_spec); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return ret; + + data->notifier.notifier_call = cros_ec_mkbp_proximity_notify; + blocking_notifier_chain_register(&ec->event_notifier, &data->notifier); + + return 0; +} + +static int cros_ec_mkbp_proximity_remove(struct platform_device *pdev) +{ + struct cros_ec_mkbp_proximity_data *data = platform_get_drvdata(pdev); + struct cros_ec_device *ec = data->ec; + + blocking_notifier_chain_unregister(&ec->event_notifier, + &data->notifier); + + return 0; +} + +static const struct of_device_id cros_ec_mkbp_proximity_of_match[] = { + { .compatible = "google,cros-ec-mkbp-proximity" }, + {} +}; +MODULE_DEVICE_TABLE(of, cros_ec_mkbp_proximity_of_match); + +static struct platform_driver cros_ec_mkbp_proximity_driver = { + .driver = { + .name = "cros-ec-mkbp-proximity", + .of_match_table = cros_ec_mkbp_proximity_of_match, + .pm = &cros_ec_mkbp_proximity_pm_ops, + }, + .probe = cros_ec_mkbp_proximity_probe, + .remove = cros_ec_mkbp_proximity_remove, +}; +module_platform_driver(cros_ec_mkbp_proximity_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ChromeOS EC MKBP proximity sensor driver"); From d612eb13ba9f407d38c9f040e5691abcee763dfe Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 12 Mar 2021 15:43:49 +0200 Subject: [PATCH 079/119] iio: chemical: bmp680: Drop unneeded explicit castings In few places the unnecessary explicit castings are being used. Drop them for good. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210312134349.3472-1-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/bme680_i2c.c | 3 +-- drivers/iio/chemical/bme680_spi.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/chemical/bme680_i2c.c b/drivers/iio/chemical/bme680_i2c.c index de9c9e3d23ea..29c0dfa4702b 100644 --- a/drivers/iio/chemical/bme680_i2c.c +++ b/drivers/iio/chemical/bme680_i2c.c @@ -26,8 +26,7 @@ static int bme680_i2c_probe(struct i2c_client *client, regmap = devm_regmap_init_i2c(client, &bme680_regmap_config); if (IS_ERR(regmap)) { - dev_err(&client->dev, "Failed to register i2c regmap %d\n", - (int)PTR_ERR(regmap)); + dev_err(&client->dev, "Failed to register i2c regmap %ld\n", PTR_ERR(regmap)); return PTR_ERR(regmap); } diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c index 3b838068a7e4..6f56ad48cc40 100644 --- a/drivers/iio/chemical/bme680_spi.c +++ b/drivers/iio/chemical/bme680_spi.c @@ -132,8 +132,7 @@ static int bme680_spi_probe(struct spi_device *spi) regmap = devm_regmap_init(&spi->dev, &bme680_regmap_bus, bus_context, &bme680_regmap_config); if (IS_ERR(regmap)) { - dev_err(&spi->dev, "Failed to register spi regmap %d\n", - (int)PTR_ERR(regmap)); + dev_err(&spi->dev, "Failed to register spi regmap %ld\n", PTR_ERR(regmap)); return PTR_ERR(regmap); } From 897cd10a962c0c4bacd88885e41610a776877384 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 12 Mar 2021 15:45:38 +0200 Subject: [PATCH 080/119] iio: imu: fxos8700: Drop unneeded explicit castings In a few places the unnecessary explicit castings are being used. Drop them for good. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210312134538.3759-1-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/imu/fxos8700_i2c.c | 3 +-- drivers/iio/imu/fxos8700_spi.c | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/imu/fxos8700_i2c.c b/drivers/iio/imu/fxos8700_i2c.c index 3ceb76366313..40a570325b0a 100644 --- a/drivers/iio/imu/fxos8700_i2c.c +++ b/drivers/iio/imu/fxos8700_i2c.c @@ -26,8 +26,7 @@ static int fxos8700_i2c_probe(struct i2c_client *client, regmap = devm_regmap_init_i2c(client, &fxos8700_regmap_config); if (IS_ERR(regmap)) { - dev_err(&client->dev, "Failed to register i2c regmap %d\n", - (int)PTR_ERR(regmap)); + dev_err(&client->dev, "Failed to register i2c regmap %ld\n", PTR_ERR(regmap)); return PTR_ERR(regmap); } diff --git a/drivers/iio/imu/fxos8700_spi.c b/drivers/iio/imu/fxos8700_spi.c index 57e7bb6444e7..27e694cce173 100644 --- a/drivers/iio/imu/fxos8700_spi.c +++ b/drivers/iio/imu/fxos8700_spi.c @@ -17,8 +17,7 @@ static int fxos8700_spi_probe(struct spi_device *spi) regmap = devm_regmap_init_spi(spi, &fxos8700_regmap_config); if (IS_ERR(regmap)) { - dev_err(&spi->dev, "Failed to register spi regmap %d\n", - (int)PTR_ERR(regmap)); + dev_err(&spi->dev, "Failed to register spi regmap %ld\n", PTR_ERR(regmap)); return PTR_ERR(regmap); } From 9e301ea77ddfc3e8607fa21ebcd61397c828ab0a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 12 Mar 2021 15:47:43 +0200 Subject: [PATCH 081/119] iio: imu: st_lsm6dsx: Drop unneeded explicit castings In a few places the unnecessary explicit castings are being used. Drop them for good. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210312134743.4055-1-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c | 3 +-- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c | 3 +-- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c index ec8d4351390a..8b4fc2c15622 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c @@ -29,8 +29,7 @@ static int st_lsm6dsx_i2c_probe(struct i2c_client *client, regmap = devm_regmap_init_i2c(client, &st_lsm6dsx_i2c_regmap_config); if (IS_ERR(regmap)) { - dev_err(&client->dev, "Failed to register i2c regmap %d\n", - (int)PTR_ERR(regmap)); + dev_err(&client->dev, "Failed to register i2c regmap %ld\n", PTR_ERR(regmap)); return PTR_ERR(regmap); } diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c index 57e633121bdc..8d4201b86e87 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c @@ -34,8 +34,7 @@ static int st_lsm6dsx_i3c_probe(struct i3c_device *i3cdev) regmap = devm_regmap_init_i3c(i3cdev, &st_lsm6dsx_i3c_regmap_config); if (IS_ERR(regmap)) { - dev_err(&i3cdev->dev, "Failed to register i3c regmap %d\n", - (int)PTR_ERR(regmap)); + dev_err(&i3cdev->dev, "Failed to register i3c regmap %ld\n", PTR_ERR(regmap)); return PTR_ERR(regmap); } diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c index 349ec9c1890d..e80110b6b280 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c @@ -29,8 +29,7 @@ static int st_lsm6dsx_spi_probe(struct spi_device *spi) regmap = devm_regmap_init_spi(spi, &st_lsm6dsx_spi_regmap_config); if (IS_ERR(regmap)) { - dev_err(&spi->dev, "Failed to register spi regmap %d\n", - (int)PTR_ERR(regmap)); + dev_err(&spi->dev, "Failed to register spi regmap %ld\n", PTR_ERR(regmap)); return PTR_ERR(regmap); } From 941f66765a32e9a1985b0815102423cf63fc343e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 12 Mar 2021 15:49:03 +0200 Subject: [PATCH 082/119] iio: light: gp2ap002: Drop unneeded explicit casting The unnecessary explicit casting is being used. Drop it for good. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210312134903.4296-1-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap002.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index 7ba7aa59437c..d048ae257c51 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -465,8 +465,7 @@ static int gp2ap002_probe(struct i2c_client *client, regmap = devm_regmap_init(dev, &gp2ap002_regmap_bus, dev, &config); if (IS_ERR(regmap)) { - dev_err(dev, "Failed to register i2c regmap %d\n", - (int)PTR_ERR(regmap)); + dev_err(dev, "Failed to register i2c regmap %ld\n", PTR_ERR(regmap)); return PTR_ERR(regmap); } gp2ap002->map = regmap; From 7b8d045e497a04dd88546da51f34fa3b102778d2 Mon Sep 17 00:00:00 2001 From: Alexandru Tachici Date: Thu, 11 Mar 2021 11:11:54 +0200 Subject: [PATCH 083/119] iio: adc: ad7124: allow more than 8 channels Currently AD7124-8 driver cannot use more than 8 IIO channels because it was assigning the channel configurations bijectively to channels specified in the device-tree. This is not possible to do when using more than 8 channels as AD7124-8 has only 8 configuration registers. To allow the user to use all channels at once the driver will keep in memory configurations for all channels but will program only 8 of them at a time on the device. If multiple channels have the same configuration, only one configuration register will be used. If there are more configurations than available registers only the last 8 used configurations will be allowed to exist on the device in a LRU fashion. Signed-off-by: Alexandru Tachici Reported-by: kernel test robot Link: https://lore.kernel.org/r/20210311091154.47785-2-alexandru.tachici@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7124.c | 461 +++++++++++++++++++++++++-------------- 1 file changed, 302 insertions(+), 159 deletions(-) diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index 766c73333604..9d3952b4674f 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -5,12 +5,14 @@ * Copyright 2018 Analog Devices Inc. */ #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -86,6 +88,10 @@ #define AD7124_SINC3_FILTER 2 #define AD7124_SINC4_FILTER 0 +#define AD7124_CONF_ADDR_OFFSET 20 +#define AD7124_MAX_CONFIGS 8 +#define AD7124_MAX_CHANNELS 16 + enum ad7124_ids { ID_AD7124_4, ID_AD7124_8, @@ -136,25 +142,37 @@ struct ad7124_chip_info { }; struct ad7124_channel_config { + bool live; + unsigned int cfg_slot; enum ad7124_ref_sel refsel; bool bipolar; bool buf_positive; bool buf_negative; - unsigned int ain; unsigned int vref_mv; unsigned int pga_bits; unsigned int odr; + unsigned int odr_sel_bits; unsigned int filter_type; }; +struct ad7124_channel { + unsigned int nr; + struct ad7124_channel_config cfg; + unsigned int ain; + unsigned int slot; +}; + struct ad7124_state { const struct ad7124_chip_info *chip_info; struct ad_sigma_delta sd; - struct ad7124_channel_config *channel_config; + struct ad7124_channel *channels; struct regulator *vref[4]; struct clk *mclk; unsigned int adc_control; unsigned int num_channels; + struct mutex cfgs_lock; /* lock for configs access */ + unsigned long cfg_slots_status; /* bitmap with slot status (1 means it is used) */ + DECLARE_KFIFO(live_cfgs_fifo, struct ad7124_channel_config *, AD7124_MAX_CONFIGS); }; static const struct iio_chan_spec ad7124_channel_template = { @@ -238,33 +256,9 @@ static int ad7124_set_mode(struct ad_sigma_delta *sd, return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control); } -static int ad7124_set_channel(struct ad_sigma_delta *sd, unsigned int channel) -{ - struct ad7124_state *st = container_of(sd, struct ad7124_state, sd); - unsigned int val; - - val = st->channel_config[channel].ain | AD7124_CHANNEL_EN(1) | - AD7124_CHANNEL_SETUP(channel); - - return ad_sd_write_reg(&st->sd, AD7124_CHANNEL(channel), 2, val); -} - -static const struct ad_sigma_delta_info ad7124_sigma_delta_info = { - .set_channel = ad7124_set_channel, - .set_mode = ad7124_set_mode, - .has_registers = true, - .addr_shift = 0, - .read_mask = BIT(6), - .data_reg = AD7124_DATA, - .irq_flags = IRQF_TRIGGER_FALLING, -}; - -static int ad7124_set_channel_odr(struct ad7124_state *st, - unsigned int channel, - unsigned int odr) +static void ad7124_set_channel_odr(struct ad7124_state *st, unsigned int channel, unsigned int odr) { unsigned int fclk, odr_sel_bits; - int ret; fclk = clk_get_rate(st->mclk); /* @@ -280,36 +274,12 @@ static int ad7124_set_channel_odr(struct ad7124_state *st, else if (odr_sel_bits > 2047) odr_sel_bits = 2047; - ret = ad7124_spi_write_mask(st, AD7124_FILTER(channel), - AD7124_FILTER_FS_MSK, - AD7124_FILTER_FS(odr_sel_bits), 3); - if (ret < 0) - return ret; + if (odr_sel_bits != st->channels[channel].cfg.odr_sel_bits) + st->channels[channel].cfg.live = false; + /* fADC = fCLK / (FS[10:0] x 32) */ - st->channel_config[channel].odr = - DIV_ROUND_CLOSEST(fclk, odr_sel_bits * 32); - - return 0; -} - -static int ad7124_set_channel_gain(struct ad7124_state *st, - unsigned int channel, - unsigned int gain) -{ - unsigned int res; - int ret; - - res = ad7124_find_closest_match(ad7124_gain, - ARRAY_SIZE(ad7124_gain), gain); - ret = ad7124_spi_write_mask(st, AD7124_CONFIG(channel), - AD7124_CONFIG_PGA_MSK, - AD7124_CONFIG_PGA(res), 2); - if (ret < 0) - return ret; - - st->channel_config[channel].pga_bits = res; - - return 0; + st->channels[channel].cfg.odr = DIV_ROUND_CLOSEST(fclk, odr_sel_bits * 32); + st->channels[channel].cfg.odr_sel_bits = odr_sel_bits; } static int ad7124_get_3db_filter_freq(struct ad7124_state *st, @@ -317,9 +287,9 @@ static int ad7124_get_3db_filter_freq(struct ad7124_state *st, { unsigned int fadc; - fadc = st->channel_config[channel].odr; + fadc = st->channels[channel].cfg.odr; - switch (st->channel_config[channel].filter_type) { + switch (st->channels[channel].cfg.filter_type) { case AD7124_SINC3_FILTER: return DIV_ROUND_CLOSEST(fadc * 230, 1000); case AD7124_SINC4_FILTER: @@ -329,9 +299,8 @@ static int ad7124_get_3db_filter_freq(struct ad7124_state *st, } } -static int ad7124_set_3db_filter_freq(struct ad7124_state *st, - unsigned int channel, - unsigned int freq) +static void ad7124_set_3db_filter_freq(struct ad7124_state *st, unsigned int channel, + unsigned int freq) { unsigned int sinc4_3db_odr; unsigned int sinc3_3db_odr; @@ -349,21 +318,211 @@ static int ad7124_set_3db_filter_freq(struct ad7124_state *st, new_odr = sinc3_3db_odr; } - if (st->channel_config[channel].filter_type != new_filter) { - int ret; + if (new_odr != st->channels[channel].cfg.odr) + st->channels[channel].cfg.live = false; - st->channel_config[channel].filter_type = new_filter; - ret = ad7124_spi_write_mask(st, AD7124_FILTER(channel), - AD7124_FILTER_TYPE_MSK, - AD7124_FILTER_TYPE_SEL(new_filter), - 3); - if (ret < 0) - return ret; + st->channels[channel].cfg.filter_type = new_filter; + st->channels[channel].cfg.odr = new_odr; +} + +static struct ad7124_channel_config *ad7124_find_similar_live_cfg(struct ad7124_state *st, + struct ad7124_channel_config *cfg) +{ + struct ad7124_channel_config *cfg_aux; + ptrdiff_t cmp_size; + int i; + + cmp_size = (u8 *)&cfg->live - (u8 *)cfg; + for (i = 0; i < st->num_channels; i++) { + cfg_aux = &st->channels[i].cfg; + + if (cfg_aux->live && !memcmp(cfg, cfg_aux, cmp_size)) + return cfg_aux; } - return ad7124_set_channel_odr(st, channel, new_odr); + return NULL; } +static int ad7124_find_free_config_slot(struct ad7124_state *st) +{ + unsigned int free_cfg_slot; + + free_cfg_slot = find_next_zero_bit(&st->cfg_slots_status, AD7124_MAX_CONFIGS, 0); + if (free_cfg_slot == AD7124_MAX_CONFIGS) + return -1; + + return free_cfg_slot; +} + +static int ad7124_init_config_vref(struct ad7124_state *st, struct ad7124_channel_config *cfg) +{ + unsigned int refsel = cfg->refsel; + + switch (refsel) { + case AD7124_REFIN1: + case AD7124_REFIN2: + case AD7124_AVDD_REF: + if (IS_ERR(st->vref[refsel])) { + dev_err(&st->sd.spi->dev, + "Error, trying to use external voltage reference without a %s regulator.\n", + ad7124_ref_names[refsel]); + return PTR_ERR(st->vref[refsel]); + } + cfg->vref_mv = regulator_get_voltage(st->vref[refsel]); + /* Conversion from uV to mV */ + cfg->vref_mv /= 1000; + return 0; + case AD7124_INT_REF: + cfg->vref_mv = 2500; + st->adc_control &= ~AD7124_ADC_CTRL_REF_EN_MSK; + st->adc_control |= AD7124_ADC_CTRL_REF_EN(1); + return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, + 2, st->adc_control); + default: + dev_err(&st->sd.spi->dev, "Invalid reference %d\n", refsel); + return -EINVAL; + } +} + +static int ad7124_write_config(struct ad7124_state *st, struct ad7124_channel_config *cfg, + unsigned int cfg_slot) +{ + unsigned int tmp; + unsigned int val; + int ret; + + cfg->cfg_slot = cfg_slot; + + tmp = (cfg->buf_positive << 1) + cfg->buf_negative; + val = AD7124_CONFIG_BIPOLAR(cfg->bipolar) | AD7124_CONFIG_REF_SEL(cfg->refsel) | + AD7124_CONFIG_IN_BUFF(tmp); + ret = ad_sd_write_reg(&st->sd, AD7124_CONFIG(cfg->cfg_slot), 2, val); + if (ret < 0) + return ret; + + tmp = AD7124_FILTER_TYPE_SEL(cfg->filter_type); + ret = ad7124_spi_write_mask(st, AD7124_FILTER(cfg->cfg_slot), AD7124_FILTER_TYPE_MSK, + tmp, 3); + if (ret < 0) + return ret; + + ret = ad7124_spi_write_mask(st, AD7124_FILTER(cfg->cfg_slot), AD7124_FILTER_FS_MSK, + AD7124_FILTER_FS(cfg->odr_sel_bits), 3); + if (ret < 0) + return ret; + + return ad7124_spi_write_mask(st, AD7124_CONFIG(cfg->cfg_slot), AD7124_CONFIG_PGA_MSK, + AD7124_CONFIG_PGA(cfg->pga_bits), 2); +} + +static struct ad7124_channel_config *ad7124_pop_config(struct ad7124_state *st) +{ + struct ad7124_channel_config *lru_cfg; + struct ad7124_channel_config *cfg; + int ret; + int i; + + /* + * Pop least recently used config from the fifo + * in order to make room for the new one + */ + ret = kfifo_get(&st->live_cfgs_fifo, &lru_cfg); + if (ret <= 0) + return NULL; + + lru_cfg->live = false; + + /* mark slot as free */ + assign_bit(lru_cfg->cfg_slot, &st->cfg_slots_status, 0); + + /* invalidate all other configs that pointed to this one */ + for (i = 0; i < st->num_channels; i++) { + cfg = &st->channels[i].cfg; + + if (cfg->cfg_slot == lru_cfg->cfg_slot) + cfg->live = false; + } + + return lru_cfg; +} + +static int ad7124_push_config(struct ad7124_state *st, struct ad7124_channel_config *cfg) +{ + struct ad7124_channel_config *lru_cfg; + int free_cfg_slot; + + free_cfg_slot = ad7124_find_free_config_slot(st); + if (free_cfg_slot >= 0) { + /* push the new config in configs queue */ + kfifo_put(&st->live_cfgs_fifo, cfg); + } else { + /* pop one config to make room for the new one */ + lru_cfg = ad7124_pop_config(st); + if (!lru_cfg) + return -EINVAL; + + /* push the new config in configs queue */ + free_cfg_slot = lru_cfg->cfg_slot; + kfifo_put(&st->live_cfgs_fifo, cfg); + } + + /* mark slot as used */ + assign_bit(free_cfg_slot, &st->cfg_slots_status, 1); + + return ad7124_write_config(st, cfg, free_cfg_slot); +} + +static int ad7124_enable_channel(struct ad7124_state *st, struct ad7124_channel *ch) +{ + ch->cfg.live = true; + return ad_sd_write_reg(&st->sd, AD7124_CHANNEL(ch->nr), 2, ch->ain | + AD7124_CHANNEL_SETUP(ch->cfg.cfg_slot) | AD7124_CHANNEL_EN(1)); +} + +static int ad7124_prepare_read(struct ad7124_state *st, int address) +{ + struct ad7124_channel_config *cfg = &st->channels[address].cfg; + struct ad7124_channel_config *live_cfg; + + /* + * Before doing any reads assign the channel a configuration. + * Check if channel's config is on the device + */ + if (!cfg->live) { + /* check if config matches another one */ + live_cfg = ad7124_find_similar_live_cfg(st, cfg); + if (!live_cfg) + ad7124_push_config(st, cfg); + else + cfg->cfg_slot = live_cfg->cfg_slot; + } + + /* point channel to the config slot and enable */ + return ad7124_enable_channel(st, &st->channels[address]); +} + +static int ad7124_set_channel(struct ad_sigma_delta *sd, unsigned int channel) +{ + struct ad7124_state *st = container_of(sd, struct ad7124_state, sd); + int ret; + + mutex_lock(&st->cfgs_lock); + ret = ad7124_prepare_read(st, channel); + mutex_unlock(&st->cfgs_lock); + + return ret; +} + +static const struct ad_sigma_delta_info ad7124_sigma_delta_info = { + .set_channel = ad7124_set_channel, + .set_mode = ad7124_set_mode, + .has_registers = true, + .addr_shift = 0, + .read_mask = BIT(6), + .data_reg = AD7124_DATA, + .irq_flags = IRQF_TRIGGER_FALLING +}; + static int ad7124_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long info) @@ -378,36 +537,44 @@ static int ad7124_read_raw(struct iio_dev *indio_dev, return ret; /* After the conversion is performed, disable the channel */ - ret = ad_sd_write_reg(&st->sd, - AD7124_CHANNEL(chan->address), 2, - st->channel_config[chan->address].ain | - AD7124_CHANNEL_EN(0)); + ret = ad_sd_write_reg(&st->sd, AD7124_CHANNEL(chan->address), 2, + st->channels[chan->address].ain | AD7124_CHANNEL_EN(0)); if (ret < 0) return ret; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - idx = st->channel_config[chan->address].pga_bits; - *val = st->channel_config[chan->address].vref_mv; - if (st->channel_config[chan->address].bipolar) + mutex_lock(&st->cfgs_lock); + + idx = st->channels[chan->address].cfg.pga_bits; + *val = st->channels[chan->address].cfg.vref_mv; + if (st->channels[chan->address].cfg.bipolar) *val2 = chan->scan_type.realbits - 1 + idx; else *val2 = chan->scan_type.realbits + idx; + mutex_unlock(&st->cfgs_lock); return IIO_VAL_FRACTIONAL_LOG2; case IIO_CHAN_INFO_OFFSET: - if (st->channel_config[chan->address].bipolar) + mutex_lock(&st->cfgs_lock); + if (st->channels[chan->address].cfg.bipolar) *val = -(1 << (chan->scan_type.realbits - 1)); else *val = 0; + mutex_unlock(&st->cfgs_lock); return IIO_VAL_INT; case IIO_CHAN_INFO_SAMP_FREQ: - *val = st->channel_config[chan->address].odr; + mutex_lock(&st->cfgs_lock); + *val = st->channels[chan->address].cfg.odr; + mutex_unlock(&st->cfgs_lock); return IIO_VAL_INT; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + mutex_lock(&st->cfgs_lock); *val = ad7124_get_3db_filter_freq(st, chan->scan_index); + mutex_unlock(&st->cfgs_lock); + return IIO_VAL_INT; default: return -EINVAL; @@ -420,35 +587,54 @@ static int ad7124_write_raw(struct iio_dev *indio_dev, { struct ad7124_state *st = iio_priv(indio_dev); unsigned int res, gain, full_scale, vref; + int ret = 0; + + mutex_lock(&st->cfgs_lock); switch (info) { case IIO_CHAN_INFO_SAMP_FREQ: - if (val2 != 0) - return -EINVAL; + if (val2 != 0) { + ret = -EINVAL; + break; + } - return ad7124_set_channel_odr(st, chan->address, val); + ad7124_set_channel_odr(st, chan->address, val); + break; case IIO_CHAN_INFO_SCALE: - if (val != 0) - return -EINVAL; + if (val != 0) { + ret = -EINVAL; + break; + } - if (st->channel_config[chan->address].bipolar) + if (st->channels[chan->address].cfg.bipolar) full_scale = 1 << (chan->scan_type.realbits - 1); else full_scale = 1 << chan->scan_type.realbits; - vref = st->channel_config[chan->address].vref_mv * 1000000LL; + vref = st->channels[chan->address].cfg.vref_mv * 1000000LL; res = DIV_ROUND_CLOSEST(vref, full_scale); gain = DIV_ROUND_CLOSEST(res, val2); + res = ad7124_find_closest_match(ad7124_gain, ARRAY_SIZE(ad7124_gain), gain); - return ad7124_set_channel_gain(st, chan->address, gain); + if (st->channels[chan->address].cfg.pga_bits != res) + st->channels[chan->address].cfg.live = false; + + st->channels[chan->address].cfg.pga_bits = res; + break; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: - if (val2 != 0) - return -EINVAL; + if (val2 != 0) { + ret = -EINVAL; + break; + } - return ad7124_set_3db_filter_freq(st, chan->address, val); + ad7124_set_3db_filter_freq(st, chan->address, val); + break; default: - return -EINVAL; + ret = -EINVAL; } + + mutex_unlock(&st->cfgs_lock); + return ret; } static int ad7124_reg_access(struct iio_dev *indio_dev, @@ -547,47 +733,14 @@ static int ad7124_check_chip_id(struct ad7124_state *st) return 0; } -static int ad7124_init_channel_vref(struct ad7124_state *st, - unsigned int channel_number) -{ - unsigned int refsel = st->channel_config[channel_number].refsel; - - switch (refsel) { - case AD7124_REFIN1: - case AD7124_REFIN2: - case AD7124_AVDD_REF: - if (IS_ERR(st->vref[refsel])) { - dev_err(&st->sd.spi->dev, - "Error, trying to use external voltage reference without a %s regulator.\n", - ad7124_ref_names[refsel]); - return PTR_ERR(st->vref[refsel]); - } - st->channel_config[channel_number].vref_mv = - regulator_get_voltage(st->vref[refsel]); - /* Conversion from uV to mV */ - st->channel_config[channel_number].vref_mv /= 1000; - break; - case AD7124_INT_REF: - st->channel_config[channel_number].vref_mv = 2500; - st->adc_control &= ~AD7124_ADC_CTRL_REF_EN_MSK; - st->adc_control |= AD7124_ADC_CTRL_REF_EN(1); - return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, - 2, st->adc_control); - default: - dev_err(&st->sd.spi->dev, "Invalid reference %d\n", refsel); - return -EINVAL; - } - - return 0; -} - static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev, struct device_node *np) { struct ad7124_state *st = iio_priv(indio_dev); + struct ad7124_channel_config *cfg; + struct ad7124_channel *channels; struct device_node *child; struct iio_chan_spec *chan; - struct ad7124_channel_config *chan_config; unsigned int ain[2], channel = 0, tmp; int ret; @@ -602,16 +755,18 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev, if (!chan) return -ENOMEM; - chan_config = devm_kcalloc(indio_dev->dev.parent, st->num_channels, - sizeof(*chan_config), GFP_KERNEL); - if (!chan_config) + channels = devm_kcalloc(indio_dev->dev.parent, st->num_channels, sizeof(*channels), + GFP_KERNEL); + if (!channels) return -ENOMEM; indio_dev->channels = chan; indio_dev->num_channels = st->num_channels; - st->channel_config = chan_config; + st->channels = channels; for_each_available_child_of_node(np, child) { + cfg = &st->channels[channel].cfg; + ret = of_property_read_u32(child, "reg", &channel); if (ret) goto err; @@ -621,21 +776,20 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev, if (ret) goto err; - st->channel_config[channel].ain = AD7124_CHANNEL_AINP(ain[0]) | + st->channels[channel].nr = channel; + st->channels[channel].ain = AD7124_CHANNEL_AINP(ain[0]) | AD7124_CHANNEL_AINM(ain[1]); - st->channel_config[channel].bipolar = - of_property_read_bool(child, "bipolar"); + + cfg->bipolar = of_property_read_bool(child, "bipolar"); ret = of_property_read_u32(child, "adi,reference-select", &tmp); if (ret) - st->channel_config[channel].refsel = AD7124_INT_REF; + cfg->refsel = AD7124_INT_REF; else - st->channel_config[channel].refsel = tmp; + cfg->refsel = tmp; - st->channel_config[channel].buf_positive = - of_property_read_bool(child, "adi,buffered-positive"); - st->channel_config[channel].buf_negative = - of_property_read_bool(child, "adi,buffered-negative"); + cfg->buf_positive = of_property_read_bool(child, "adi,buffered-positive"); + cfg->buf_negative = of_property_read_bool(child, "adi,buffered-negative"); chan[channel] = ad7124_channel_template; chan[channel].address = channel; @@ -653,8 +807,8 @@ err: static int ad7124_setup(struct ad7124_state *st) { - unsigned int val, fclk, power_mode; - int i, ret, tmp; + unsigned int fclk, power_mode; + int i, ret; fclk = clk_get_rate(st->mclk); if (!fclk) @@ -677,31 +831,20 @@ static int ad7124_setup(struct ad7124_state *st) if (ret < 0) return ret; + mutex_init(&st->cfgs_lock); + INIT_KFIFO(st->live_cfgs_fifo); for (i = 0; i < st->num_channels; i++) { - val = st->channel_config[i].ain | AD7124_CHANNEL_SETUP(i); - ret = ad_sd_write_reg(&st->sd, AD7124_CHANNEL(i), 2, val); + + ret = ad7124_init_config_vref(st, &st->channels[i].cfg); if (ret < 0) return ret; - ret = ad7124_init_channel_vref(st, i); - if (ret < 0) - return ret; - - tmp = (st->channel_config[i].buf_positive << 1) + - st->channel_config[i].buf_negative; - - val = AD7124_CONFIG_BIPOLAR(st->channel_config[i].bipolar) | - AD7124_CONFIG_REF_SEL(st->channel_config[i].refsel) | - AD7124_CONFIG_IN_BUFF(tmp); - ret = ad_sd_write_reg(&st->sd, AD7124_CONFIG(i), 2, val); - if (ret < 0) - return ret; /* * 9.38 SPS is the minimum output data rate supported * regardless of the selected power mode. Round it up to 10 and - * set all the enabled channels to this default value. + * set all channels to this default value. */ - ret = ad7124_set_channel_odr(st, i, 10); + ad7124_set_channel_odr(st, i, 10); } return ret; From 995071d36bb9804b644265450142fcb91c427ee8 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Tue, 9 Mar 2021 11:36:13 -0800 Subject: [PATCH 084/119] iio: set default trig->dev.parent When allocated with [devm_]iio_trigger_alloc(), set trig device parent to the device the trigger is allocated for by default. It can always be reassigned in the probe routine. Change iio_trigger_alloc() API to add the device pointer to be coherent with devm_iio_trigger_alloc, using similar interface to iio_device_alloc(). Signed-off-by: Gwendal Grignou Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210309193620.2176163-2-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bma180.c | 3 +- drivers/iio/adc/ad_sigma_delta.c | 6 ++-- drivers/iio/adc/at91_adc.c | 3 +- .../common/hid-sensors/hid-sensor-trigger.c | 4 +-- .../common/st_sensors/st_sensors_trigger.c | 4 +-- drivers/iio/gyro/itg3200_buffer.c | 3 +- drivers/iio/industrialio-trigger.c | 34 +++++++++++++------ drivers/iio/trigger/iio-trig-hrtimer.c | 2 +- drivers/iio/trigger/iio-trig-interrupt.c | 2 +- drivers/iio/trigger/iio-trig-loop.c | 2 +- drivers/iio/trigger/iio-trig-sysfs.c | 3 +- include/linux/iio/iio.h | 2 +- include/linux/iio/trigger.h | 3 +- 13 files changed, 40 insertions(+), 31 deletions(-) diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 71f85a3e525b..b8a7469cdae4 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -1044,7 +1044,7 @@ static int bma180_probe(struct i2c_client *client, indio_dev->info = &bma180_info; if (client->irq > 0) { - data->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name, + data->trig = iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, indio_dev->id); if (!data->trig) { ret = -ENOMEM; @@ -1059,7 +1059,6 @@ static int bma180_probe(struct i2c_client *client, goto err_trigger_free; } - data->trig->dev.parent = dev; data->trig->ops = &bma180_trigger_ops; iio_trigger_set_drvdata(data->trig, indio_dev); indio_dev->trig = iio_trigger_get(data->trig); diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 3a6f239d4acc..9289812c0a94 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -475,8 +475,9 @@ static int ad_sd_probe_trigger(struct iio_dev *indio_dev) struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); int ret; - sigma_delta->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name, - indio_dev->id); + sigma_delta->trig = iio_trigger_alloc(&sigma_delta->spi->dev, + "%s-dev%d", indio_dev->name, + indio_dev->id); if (sigma_delta->trig == NULL) { ret = -ENOMEM; goto error_ret; @@ -496,7 +497,6 @@ static int ad_sd_probe_trigger(struct iio_dev *indio_dev) sigma_delta->irq_dis = true; disable_irq_nosync(sigma_delta->spi->irq); } - sigma_delta->trig->dev.parent = &sigma_delta->spi->dev; iio_trigger_set_drvdata(sigma_delta->trig, sigma_delta); ret = iio_trigger_register(sigma_delta->trig); diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 70750abb5dea..0b5f0c91d0d7 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -625,12 +625,11 @@ static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *idev, struct iio_trigger *trig; int ret; - trig = iio_trigger_alloc("%s-dev%d-%s", idev->name, + trig = iio_trigger_alloc(idev->dev.parent, "%s-dev%d-%s", idev->name, idev->id, trigger->name); if (trig == NULL) return NULL; - trig->dev.parent = idev->dev.parent; iio_trigger_set_drvdata(trig, idev); trig->ops = &at91_adc_trigger_ops; diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index 064c32bec9c7..95ddccb44f1c 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -255,14 +255,14 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, return ret; } - trig = iio_trigger_alloc("%s-dev%d", name, indio_dev->id); + trig = iio_trigger_alloc(indio_dev->dev.parent, + "%s-dev%d", name, indio_dev->id); if (trig == NULL) { dev_err(&indio_dev->dev, "Trigger Allocate Failed\n"); ret = -ENOMEM; goto error_triggered_buffer_cleanup; } - trig->dev.parent = indio_dev->dev.parent; iio_trigger_set_drvdata(trig, attrb); trig->ops = &hid_sensor_trigger_ops; ret = iio_trigger_register(trig); diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index 2dbd2646e44e..0b511665dee5 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -123,7 +123,8 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, unsigned long irq_trig; int err; - sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name); + sdata->trig = iio_trigger_alloc(sdata->dev, "%s-trigger", + indio_dev->name); if (sdata->trig == NULL) { dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n"); return -ENOMEM; @@ -131,7 +132,6 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, iio_trigger_set_drvdata(sdata->trig, indio_dev); sdata->trig->ops = trigger_ops; - sdata->trig->dev.parent = sdata->dev; irq_trig = irqd_get_trigger_type(irq_get_irq_data(sdata->irq)); /* diff --git a/drivers/iio/gyro/itg3200_buffer.c b/drivers/iio/gyro/itg3200_buffer.c index 1c3c1bd53374..af0aaa146f0c 100644 --- a/drivers/iio/gyro/itg3200_buffer.c +++ b/drivers/iio/gyro/itg3200_buffer.c @@ -113,7 +113,7 @@ int itg3200_probe_trigger(struct iio_dev *indio_dev) int ret; struct itg3200 *st = iio_priv(indio_dev); - st->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name, + st->trig = iio_trigger_alloc(&st->i2c->dev, "%s-dev%d", indio_dev->name, indio_dev->id); if (!st->trig) return -ENOMEM; @@ -127,7 +127,6 @@ int itg3200_probe_trigger(struct iio_dev *indio_dev) goto error_free_trig; - st->trig->dev.parent = &st->i2c->dev; st->trig->ops = &itg3200_trigger_ops; iio_trigger_set_drvdata(st->trig, indio_dev); ret = iio_trigger_register(st->trig); diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index a035d5c2a445..32ac1bec25e3 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -514,8 +514,10 @@ static void iio_trig_subirqunmask(struct irq_data *d) trig->subirqs[d->irq - trig->subirq_base].enabled = true; } -static __printf(1, 0) -struct iio_trigger *viio_trigger_alloc(const char *fmt, va_list vargs) +static __printf(2, 0) +struct iio_trigger *viio_trigger_alloc(struct device *parent, + const char *fmt, + va_list vargs) { struct iio_trigger *trig; int i; @@ -524,6 +526,7 @@ struct iio_trigger *viio_trigger_alloc(const char *fmt, va_list vargs) if (!trig) return NULL; + trig->dev.parent = parent; trig->dev.type = &iio_trig_type; trig->dev.bus = &iio_bus_type; device_initialize(&trig->dev); @@ -559,13 +562,23 @@ free_trig: return NULL; } -struct iio_trigger *iio_trigger_alloc(const char *fmt, ...) +/** + * iio_trigger_alloc - Allocate a trigger + * @parent: Device to allocate iio_trigger for + * @fmt: trigger name format. If it includes format + * specifiers, the additional arguments following + * format are formatted and inserted in the resulting + * string replacing their respective specifiers. + * RETURNS: + * Pointer to allocated iio_trigger on success, NULL on failure. + */ +struct iio_trigger *iio_trigger_alloc(struct device *parent, const char *fmt, ...) { struct iio_trigger *trig; va_list vargs; va_start(vargs, fmt); - trig = viio_trigger_alloc(fmt, vargs); + trig = viio_trigger_alloc(parent, fmt, vargs); va_end(vargs); return trig; @@ -586,20 +599,19 @@ static void devm_iio_trigger_release(struct device *dev, void *res) /** * devm_iio_trigger_alloc - Resource-managed iio_trigger_alloc() - * @dev: Device to allocate iio_trigger for + * Managed iio_trigger_alloc. iio_trigger allocated with this function is + * automatically freed on driver detach. + * @parent: Device to allocate iio_trigger for * @fmt: trigger name format. If it includes format * specifiers, the additional arguments following * format are formatted and inserted in the resulting * string replacing their respective specifiers. * - * Managed iio_trigger_alloc. iio_trigger allocated with this function is - * automatically freed on driver detach. * * RETURNS: * Pointer to allocated iio_trigger on success, NULL on failure. */ -struct iio_trigger *devm_iio_trigger_alloc(struct device *dev, - const char *fmt, ...) +struct iio_trigger *devm_iio_trigger_alloc(struct device *parent, const char *fmt, ...) { struct iio_trigger **ptr, *trig; va_list vargs; @@ -611,11 +623,11 @@ struct iio_trigger *devm_iio_trigger_alloc(struct device *dev, /* use raw alloc_dr for kmalloc caller tracing */ va_start(vargs, fmt); - trig = viio_trigger_alloc(fmt, vargs); + trig = viio_trigger_alloc(parent, fmt, vargs); va_end(vargs); if (trig) { *ptr = trig; - devres_add(dev, ptr); + devres_add(parent, ptr); } else { devres_free(ptr); } diff --git a/drivers/iio/trigger/iio-trig-hrtimer.c b/drivers/iio/trigger/iio-trig-hrtimer.c index e68a2c56d459..51e362f091c2 100644 --- a/drivers/iio/trigger/iio-trig-hrtimer.c +++ b/drivers/iio/trigger/iio-trig-hrtimer.c @@ -135,7 +135,7 @@ static struct iio_sw_trigger *iio_trig_hrtimer_probe(const char *name) if (!trig_info) return ERR_PTR(-ENOMEM); - trig_info->swt.trigger = iio_trigger_alloc("%s", name); + trig_info->swt.trigger = iio_trigger_alloc(NULL, "%s", name); if (!trig_info->swt.trigger) { ret = -ENOMEM; goto err_free_trig_info; diff --git a/drivers/iio/trigger/iio-trig-interrupt.c b/drivers/iio/trigger/iio-trig-interrupt.c index 94a487caf421..f746c460bf2a 100644 --- a/drivers/iio/trigger/iio-trig-interrupt.c +++ b/drivers/iio/trigger/iio-trig-interrupt.c @@ -45,7 +45,7 @@ static int iio_interrupt_trigger_probe(struct platform_device *pdev) irq = irq_res->start; - trig = iio_trigger_alloc("irqtrig%d", irq); + trig = iio_trigger_alloc(NULL, "irqtrig%d", irq); if (!trig) { ret = -ENOMEM; goto error_ret; diff --git a/drivers/iio/trigger/iio-trig-loop.c b/drivers/iio/trigger/iio-trig-loop.c index 4a00668e3258..96ec06bbe546 100644 --- a/drivers/iio/trigger/iio-trig-loop.c +++ b/drivers/iio/trigger/iio-trig-loop.c @@ -84,7 +84,7 @@ static struct iio_sw_trigger *iio_trig_loop_probe(const char *name) if (!trig_info) return ERR_PTR(-ENOMEM); - trig_info->swt.trigger = iio_trigger_alloc("%s", name); + trig_info->swt.trigger = iio_trigger_alloc(NULL, "%s", name); if (!trig_info->swt.trigger) { ret = -ENOMEM; goto err_free_trig_info; diff --git a/drivers/iio/trigger/iio-trig-sysfs.c b/drivers/iio/trigger/iio-trig-sysfs.c index 0f6b512a5c37..e9adfff45b39 100644 --- a/drivers/iio/trigger/iio-trig-sysfs.c +++ b/drivers/iio/trigger/iio-trig-sysfs.c @@ -149,7 +149,7 @@ static int iio_sysfs_trigger_probe(int id) goto out1; } t->id = id; - t->trig = iio_trigger_alloc("sysfstrig%d", id); + t->trig = iio_trigger_alloc(&iio_sysfs_trig_dev, "sysfstrig%d", id); if (!t->trig) { ret = -ENOMEM; goto free_t; @@ -157,7 +157,6 @@ static int iio_sysfs_trigger_probe(int id) t->trig->dev.groups = iio_sysfs_trigger_attr_groups; t->trig->ops = &iio_sysfs_trigger_ops; - t->trig->dev.parent = &iio_sysfs_trig_dev; iio_trigger_set_drvdata(t->trig, t); t->work = IRQ_WORK_INIT_HARD(iio_sysfs_trigger_work); diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index f8585d01fc76..f2d65e2e88b6 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -693,7 +693,7 @@ static inline void *iio_priv(const struct iio_dev *indio_dev) void iio_device_free(struct iio_dev *indio_dev); struct iio_dev *devm_iio_device_alloc(struct device *parent, int sizeof_priv); __printf(2, 3) -struct iio_trigger *devm_iio_trigger_alloc(struct device *dev, +struct iio_trigger *devm_iio_trigger_alloc(struct device *parent, const char *fmt, ...); /** * iio_buffer_enabled() - helper function to test if the buffer is enabled diff --git a/include/linux/iio/trigger.h b/include/linux/iio/trigger.h index 055890b6ffcf..096f68dd2e0c 100644 --- a/include/linux/iio/trigger.h +++ b/include/linux/iio/trigger.h @@ -161,7 +161,8 @@ void iio_trigger_poll_chained(struct iio_trigger *trig); irqreturn_t iio_trigger_generic_data_rdy_poll(int irq, void *private); -__printf(1, 2) struct iio_trigger *iio_trigger_alloc(const char *fmt, ...); +__printf(2, 3) +struct iio_trigger *iio_trigger_alloc(struct device *parent, const char *fmt, ...); void iio_trigger_free(struct iio_trigger *trig); /** From 8a2252201656a147a9d7b1892028cef15869032e Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Tue, 9 Mar 2021 11:36:14 -0800 Subject: [PATCH 085/119] iio: fix devm_iio_trigger_alloc with parent.cocci Use cocci semantic patch: @@ expression trigger, P; @@ trigger = devm_iio_trigger_alloc(P, ...); ... - trigger->dev.parent = P; To remove trigger->dev.parent, since it is set by default. Signed-off-by: Gwendal Grignou Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210309193620.2176163-3-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl372.c | 2 -- drivers/iio/accel/bmc150-accel-core.c | 1 - drivers/iio/accel/kxcjk-1013.c | 2 -- drivers/iio/accel/mma8452.c | 1 - drivers/iio/accel/mxc4005.c | 1 - drivers/iio/accel/stk8312.c | 1 - drivers/iio/accel/stk8ba50.c | 1 - drivers/iio/adc/ad7606.c | 1 - drivers/iio/adc/ad7766.c | 1 - drivers/iio/adc/ad7768-1.c | 1 - drivers/iio/adc/max1027.c | 1 - drivers/iio/chemical/ccs811.c | 1 - drivers/iio/chemical/scd30_core.c | 1 - drivers/iio/gyro/adxrs290.c | 1 - drivers/iio/gyro/bmg160_core.c | 2 -- drivers/iio/gyro/fxas21002c_core.c | 1 - drivers/iio/health/afe4403.c | 1 - drivers/iio/health/afe4404.c | 1 - drivers/iio/humidity/hts221_buffer.c | 1 - drivers/iio/imu/kmx61.c | 1 - drivers/iio/light/rpr0521.c | 1 - drivers/iio/light/si1145.c | 1 - drivers/iio/light/st_uvis25_core.c | 1 - drivers/iio/light/vcnl4000.c | 1 - drivers/iio/light/vcnl4035.c | 1 - drivers/iio/magnetometer/bmc150_magn.c | 1 - drivers/iio/magnetometer/rm3100-core.c | 1 - drivers/iio/pressure/zpa2326.c | 1 - drivers/iio/proximity/sx9310.c | 1 - drivers/iio/proximity/sx9500.c | 1 - 30 files changed, 33 deletions(-) diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index 8ba1453b8dbf..9c9a896a872a 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -1236,8 +1236,6 @@ int adxl372_probe(struct device *dev, struct regmap *regmap, st->dready_trig->ops = &adxl372_trigger_ops; st->peak_datardy_trig->ops = &adxl372_peak_data_trigger_ops; - st->dready_trig->dev.parent = dev; - st->peak_datardy_trig->dev.parent = dev; iio_trigger_set_drvdata(st->dready_trig, indio_dev); iio_trigger_set_drvdata(st->peak_datardy_trig, indio_dev); ret = devm_iio_trigger_register(dev, st->dready_trig); diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index b0dbd12cbf42..04d85ce34e9f 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -1478,7 +1478,6 @@ static int bmc150_accel_triggers_setup(struct iio_dev *indio_dev, break; } - t->indio_trig->dev.parent = dev; t->indio_trig->ops = &bmc150_accel_trigger_ops; t->intr = bmc150_accel_triggers[i].intr; t->data = data; diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 5c056165f473..ff724bc17a45 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -1419,7 +1419,6 @@ static int kxcjk1013_probe(struct i2c_client *client, goto err_poweroff; } - data->dready_trig->dev.parent = &client->dev; data->dready_trig->ops = &kxcjk1013_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); indio_dev->trig = data->dready_trig; @@ -1428,7 +1427,6 @@ static int kxcjk1013_probe(struct i2c_client *client, if (ret) goto err_poweroff; - data->motion_trig->dev.parent = &client->dev; data->motion_trig->ops = &kxcjk1013_trigger_ops; iio_trigger_set_drvdata(data->motion_trig, indio_dev); ret = iio_trigger_register(data->motion_trig); diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 33f0c419d8ff..4d307dfb9169 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -1465,7 +1465,6 @@ static int mma8452_trigger_setup(struct iio_dev *indio_dev) if (!trig) return -ENOMEM; - trig->dev.parent = &data->client->dev; trig->ops = &mma8452_trigger_ops; iio_trigger_set_drvdata(trig, indio_dev); diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c index 0f8fd687866d..fb3cbaa62bd8 100644 --- a/drivers/iio/accel/mxc4005.c +++ b/drivers/iio/accel/mxc4005.c @@ -450,7 +450,6 @@ static int mxc4005_probe(struct i2c_client *client, return ret; } - data->dready_trig->dev.parent = &client->dev; data->dready_trig->ops = &mxc4005_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); indio_dev->trig = data->dready_trig; diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c index 1a8a1a0c3ffb..157d8faefb9e 100644 --- a/drivers/iio/accel/stk8312.c +++ b/drivers/iio/accel/stk8312.c @@ -558,7 +558,6 @@ static int stk8312_probe(struct i2c_client *client, goto err_power_off; } - data->dready_trig->dev.parent = &client->dev; data->dready_trig->ops = &stk8312_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); ret = iio_trigger_register(data->dready_trig); diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c index 7a06904455eb..7cf9cb7e8666 100644 --- a/drivers/iio/accel/stk8ba50.c +++ b/drivers/iio/accel/stk8ba50.c @@ -454,7 +454,6 @@ static int stk8ba50_probe(struct i2c_client *client, goto err_power_off; } - data->dready_trig->dev.parent = &client->dev; data->dready_trig->ops = &stk8ba50_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); ret = iio_trigger_register(data->dready_trig); diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c index ee7b108688b3..0af0bb4d5a7f 100644 --- a/drivers/iio/adc/ad7606.c +++ b/drivers/iio/adc/ad7606.c @@ -668,7 +668,6 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, return -ENOMEM; st->trig->ops = &ad7606_trigger_ops; - st->trig->dev.parent = dev; iio_trigger_set_drvdata(st->trig, indio_dev); ret = devm_iio_trigger_register(dev, st->trig); if (ret) diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c index b6b6765be7b4..829a3426f235 100644 --- a/drivers/iio/adc/ad7766.c +++ b/drivers/iio/adc/ad7766.c @@ -253,7 +253,6 @@ static int ad7766_probe(struct spi_device *spi) return -ENOMEM; ad7766->trig->ops = &ad7766_trigger_ops; - ad7766->trig->dev.parent = &spi->dev; iio_trigger_set_drvdata(ad7766->trig, ad7766); ret = devm_request_irq(&spi->dev, spi->irq, ad7766_irq, diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 5c0cbee03230..c945f1349623 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -631,7 +631,6 @@ static int ad7768_probe(struct spi_device *spi) return -ENOMEM; st->trig->ops = &ad7768_trigger_ops; - st->trig->dev.parent = &spi->dev; iio_trigger_set_drvdata(st->trig, indio_dev); ret = devm_iio_trigger_register(&spi->dev, st->trig); if (ret) diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index ca1dff3924ff..e3c8ec107722 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -473,7 +473,6 @@ static int max1027_probe(struct spi_device *spi) } st->trig->ops = &max1027_trigger_ops; - st->trig->dev.parent = &spi->dev; iio_trigger_set_drvdata(st->trig, indio_dev); ret = devm_iio_trigger_register(&indio_dev->dev, st->trig); diff --git a/drivers/iio/chemical/ccs811.c b/drivers/iio/chemical/ccs811.c index 60dd87e96f5f..886e96496dbf 100644 --- a/drivers/iio/chemical/ccs811.c +++ b/drivers/iio/chemical/ccs811.c @@ -497,7 +497,6 @@ static int ccs811_probe(struct i2c_client *client, goto err_poweroff; } - data->drdy_trig->dev.parent = &client->dev; data->drdy_trig->ops = &ccs811_trigger_ops; iio_trigger_set_drvdata(data->drdy_trig, indio_dev); indio_dev->trig = data->drdy_trig; diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index 4d0d798c7cd3..261c277ac4a5 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -646,7 +646,6 @@ static int scd30_setup_trigger(struct iio_dev *indio_dev) return -ENOMEM; } - trig->dev.parent = dev; trig->ops = &scd30_trigger_ops; iio_trigger_set_drvdata(trig, indio_dev); diff --git a/drivers/iio/gyro/adxrs290.c b/drivers/iio/gyro/adxrs290.c index c45d8226cc2b..cec5e1f17c22 100644 --- a/drivers/iio/gyro/adxrs290.c +++ b/drivers/iio/gyro/adxrs290.c @@ -593,7 +593,6 @@ static int adxrs290_probe_trigger(struct iio_dev *indio_dev) if (!st->dready_trig) return -ENOMEM; - st->dready_trig->dev.parent = &st->spi->dev; st->dready_trig->ops = &adxrs290_trigger_ops; iio_trigger_set_drvdata(st->dready_trig, indio_dev); diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c index 029ef4c34604..b11ebd9bb7a4 100644 --- a/drivers/iio/gyro/bmg160_core.c +++ b/drivers/iio/gyro/bmg160_core.c @@ -1148,14 +1148,12 @@ int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq, if (!data->motion_trig) return -ENOMEM; - data->dready_trig->dev.parent = dev; data->dready_trig->ops = &bmg160_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); ret = iio_trigger_register(data->dready_trig); if (ret) return ret; - data->motion_trig->dev.parent = dev; data->motion_trig->ops = &bmg160_trigger_ops; iio_trigger_set_drvdata(data->motion_trig, indio_dev); ret = iio_trigger_register(data->motion_trig); diff --git a/drivers/iio/gyro/fxas21002c_core.c b/drivers/iio/gyro/fxas21002c_core.c index 129eead8febc..1a20c6b88e7d 100644 --- a/drivers/iio/gyro/fxas21002c_core.c +++ b/drivers/iio/gyro/fxas21002c_core.c @@ -875,7 +875,6 @@ static int fxas21002c_trigger_probe(struct fxas21002c_data *data) if (ret < 0) return ret; - data->dready_trig->dev.parent = dev; data->dready_trig->ops = &fxas21002c_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index 38734e4ce360..1fa8d51d5080 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -531,7 +531,6 @@ static int afe4403_probe(struct spi_device *spi) iio_trigger_set_drvdata(afe->trig, indio_dev); afe->trig->ops = &afe4403_trigger_ops; - afe->trig->dev.parent = afe->dev; ret = iio_trigger_register(afe->trig); if (ret) { diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 61fe4932d81d..e1476bf79fe2 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -538,7 +538,6 @@ static int afe4404_probe(struct i2c_client *client, iio_trigger_set_drvdata(afe->trig, indio_dev); afe->trig->ops = &afe4404_trigger_ops; - afe->trig->dev.parent = afe->dev; ret = iio_trigger_register(afe->trig); if (ret) { diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c index 95e56917677f..f29692b9d2db 100644 --- a/drivers/iio/humidity/hts221_buffer.c +++ b/drivers/iio/humidity/hts221_buffer.c @@ -135,7 +135,6 @@ int hts221_allocate_trigger(struct iio_dev *iio_dev) iio_trigger_set_drvdata(hw->trig, iio_dev); hw->trig->ops = &hts221_trigger_ops; - hw->trig->dev.parent = hw->dev; iio_dev->trig = iio_trigger_get(hw->trig); return devm_iio_trigger_register(hw->dev, hw->trig); diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c index 4377047d503a..fc5a60fcfec0 100644 --- a/drivers/iio/imu/kmx61.c +++ b/drivers/iio/imu/kmx61.c @@ -1268,7 +1268,6 @@ static struct iio_trigger *kmx61_trigger_setup(struct kmx61_data *data, if (!trig) return ERR_PTR(-ENOMEM); - trig->dev.parent = &data->client->dev; trig->ops = &kmx61_trigger_ops; iio_trigger_set_drvdata(trig, indio_dev); diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c index 31224a33bade..033578f444e4 100644 --- a/drivers/iio/light/rpr0521.c +++ b/drivers/iio/light/rpr0521.c @@ -990,7 +990,6 @@ static int rpr0521_probe(struct i2c_client *client, ret = -ENOMEM; goto err_pm_disable; } - data->drdy_trigger0->dev.parent = indio_dev->dev.parent; data->drdy_trigger0->ops = &rpr0521_trigger_ops; indio_dev->available_scan_masks = rpr0521_available_scan_masks; iio_trigger_set_drvdata(data->drdy_trigger0, indio_dev); diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c index b304801c7916..9b5c99823943 100644 --- a/drivers/iio/light/si1145.c +++ b/drivers/iio/light/si1145.c @@ -1247,7 +1247,6 @@ static int si1145_probe_trigger(struct iio_dev *indio_dev) if (!trig) return -ENOMEM; - trig->dev.parent = &client->dev; trig->ops = &si1145_trigger_ops; iio_trigger_set_drvdata(trig, indio_dev); diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c index 1055594b2276..41a2ce5a2d53 100644 --- a/drivers/iio/light/st_uvis25_core.c +++ b/drivers/iio/light/st_uvis25_core.c @@ -210,7 +210,6 @@ static int st_uvis25_allocate_trigger(struct iio_dev *iio_dev) return -ENOMEM; iio_trigger_set_drvdata(hw->trig, iio_dev); - hw->trig->dev.parent = dev; return devm_iio_trigger_register(dev, hw->trig); } diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index fff4b36b8b58..2f7916f95689 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -1002,7 +1002,6 @@ static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) if (!trigger) return -ENOMEM; - trigger->dev.parent = &client->dev; trigger->ops = &vcnl4010_trigger_ops; iio_trigger_set_drvdata(trigger, indio_dev); diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c index 73a28e30dddc..ae87740d9cef 100644 --- a/drivers/iio/light/vcnl4035.c +++ b/drivers/iio/light/vcnl4035.c @@ -511,7 +511,6 @@ static int vcnl4035_probe_trigger(struct iio_dev *indio_dev) if (!data->drdy_trigger0) return -ENOMEM; - data->drdy_trigger0->dev.parent = indio_dev->dev.parent; data->drdy_trigger0->ops = &vcnl4035_trigger_ops; iio_trigger_set_drvdata(data->drdy_trigger0, indio_dev); ret = devm_iio_trigger_register(indio_dev->dev.parent, diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c index b2f3129e1b4f..00f9766bad5c 100644 --- a/drivers/iio/magnetometer/bmc150_magn.c +++ b/drivers/iio/magnetometer/bmc150_magn.c @@ -922,7 +922,6 @@ int bmc150_magn_probe(struct device *dev, struct regmap *regmap, goto err_poweroff; } - data->dready_trig->dev.parent = dev; data->dready_trig->ops = &bmc150_magn_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); ret = iio_trigger_register(data->dready_trig); diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c index 7242897a05e9..dd811da9cb6d 100644 --- a/drivers/iio/magnetometer/rm3100-core.c +++ b/drivers/iio/magnetometer/rm3100-core.c @@ -579,7 +579,6 @@ int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq) if (!data->drdy_trig) return -ENOMEM; - data->drdy_trig->dev.parent = dev; ret = devm_iio_trigger_register(dev, data->drdy_trig); if (ret < 0) return ret; diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c index 2cecbe0adb3f..70adff62cd20 100644 --- a/drivers/iio/pressure/zpa2326.c +++ b/drivers/iio/pressure/zpa2326.c @@ -1413,7 +1413,6 @@ static int zpa2326_init_managed_trigger(struct device *parent, return -ENOMEM; /* Basic setup. */ - trigger->dev.parent = parent; trigger->ops = &zpa2326_trigger_ops; private->trigger = trigger; diff --git a/drivers/iio/proximity/sx9310.c b/drivers/iio/proximity/sx9310.c index 37fd0b65a014..394c2afe0f23 100644 --- a/drivers/iio/proximity/sx9310.c +++ b/drivers/iio/proximity/sx9310.c @@ -1453,7 +1453,6 @@ static int sx9310_probe(struct i2c_client *client) if (!data->trig) return -ENOMEM; - data->trig->dev.parent = dev; data->trig->ops = &sx9310_trigger_ops; iio_trigger_set_drvdata(data->trig, indio_dev); diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 67353d33af94..a87f4a8e4327 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -950,7 +950,6 @@ static int sx9500_probe(struct i2c_client *client, if (!data->trig) return -ENOMEM; - data->trig->dev.parent = &client->dev; data->trig->ops = &sx9500_trigger_ops; iio_trigger_set_drvdata(data->trig, indio_dev); From 5c68f05305874dd2c755fa911e93a8819dde42e2 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Tue, 9 Mar 2021 11:36:15 -0800 Subject: [PATCH 086/119] iio: adis_trigger: Remove code to set trigger parent iio_trigger_set_drvdata() sets the trigger device parent to first argument of viio_trigger_alloc(), no need to do it again in the driver code. Remove adis_trigger_setup() to match other drivers where setting the trigger is usually done in the probe() routine. Signed-off-by: Gwendal Grignou Reviewed-by: Andy Shevchenko Reviewed-by: Nuno Sa Link: https://lore.kernel.org/r/20210309193620.2176163-4-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis_trigger.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c index 64e0ba51cb18..0f29e56200af 100644 --- a/drivers/iio/imu/adis_trigger.c +++ b/drivers/iio/imu/adis_trigger.c @@ -27,13 +27,6 @@ static const struct iio_trigger_ops adis_trigger_ops = { .set_trigger_state = &adis_data_rdy_trigger_set_state, }; -static void adis_trigger_setup(struct adis *adis) -{ - adis->trig->dev.parent = &adis->spi->dev; - adis->trig->ops = &adis_trigger_ops; - iio_trigger_set_drvdata(adis->trig, adis); -} - static int adis_validate_irq_flag(struct adis *adis) { /* @@ -72,7 +65,8 @@ int devm_adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev) if (!adis->trig) return -ENOMEM; - adis_trigger_setup(adis); + adis->trig->ops = &adis_trigger_ops; + iio_trigger_set_drvdata(adis->trig, adis); ret = adis_validate_irq_flag(adis); if (ret) From cd214139471a2b2135b4fbc2a7f9355a5df68f86 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Tue, 9 Mar 2021 11:36:16 -0800 Subject: [PATCH 087/119] iio: gp2ap020a00f: Remove code to set trigger parent iio_trigger_set_drvdata() sets the trigger device parent to first argument of viio_trigger_alloc(), no need to do it again in the driver code. Given data->client is client, and we call devm_iio_trigger_alloc() with &client->dev, we do not have to set data->trig->dev.parent to &data->client->dev anymore. Signed-off-by: Gwendal Grignou Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210309193620.2176163-5-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap020a00f.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index e2850c1a7353..d1d9f2d319e4 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -1551,7 +1551,6 @@ static int gp2ap020a00f_probe(struct i2c_client *client, } data->trig->ops = &gp2ap020a00f_trigger_ops; - data->trig->dev.parent = &data->client->dev; init_irq_work(&data->work, gp2ap020a00f_iio_trigger_work); From 4d031666560da23dfae3892335551b2405ee92e1 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Tue, 9 Mar 2021 11:36:17 -0800 Subject: [PATCH 088/119] iio: lmp91000: Remove code to set trigger parent iio_trigger_set_drvdata() sets the trigger device parent to first argument of viio_trigger_alloc(), no need to do it again in the driver code. Given data->dev is dev, and we call devm_iio_trigger_alloc with dev instead of data->dev, we do not have to set data->trig->dev.parent to dev anymore. Signed-off-by: Gwendal Grignou Reviewed-by: Andy Shevchenko Reviewed-by: Matt Ranostay Link: https://lore.kernel.org/r/20210309193620.2176163-6-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/potentiostat/lmp91000.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/potentiostat/lmp91000.c b/drivers/iio/potentiostat/lmp91000.c index f34ca769dc20..8a9c576616ee 100644 --- a/drivers/iio/potentiostat/lmp91000.c +++ b/drivers/iio/potentiostat/lmp91000.c @@ -322,7 +322,7 @@ static int lmp91000_probe(struct i2c_client *client, return PTR_ERR(data->regmap); } - data->trig = devm_iio_trigger_alloc(data->dev, "%s-mux%d", + data->trig = devm_iio_trigger_alloc(dev, "%s-mux%d", indio_dev->name, indio_dev->id); if (!data->trig) { dev_err(dev, "cannot allocate iio trigger.\n"); @@ -330,7 +330,6 @@ static int lmp91000_probe(struct i2c_client *client, } data->trig->ops = &lmp91000_trigger_ops; - data->trig->dev.parent = dev; init_completion(&data->completion); ret = lmp91000_read_config(data); From e3c9b034e7d6280eef20d753d4a9c33abb200cf6 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Tue, 9 Mar 2021 11:36:18 -0800 Subject: [PATCH 089/119] iio: chemical: atlas: Remove code to set trigger parent iio_trigger_set_drvdata() sets the trigger device parent to first argument of viio_trigger_alloc(), no need to do it again in the driver code. Given we call devm_iio_trigger_alloc() and devm_iio_device_alloc() with &client->dev as parent, we do not have to set data->trig->dev.parent to indio_dev->dev.parent anymore. Signed-off-by: Gwendal Grignou Reviewed-by: Andy Shevchenko Reviewed-by: Matt Ranostay Link: https://lore.kernel.org/r/20210309193620.2176163-7-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/atlas-sensor.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/chemical/atlas-sensor.c b/drivers/iio/chemical/atlas-sensor.c index cdab9d04dedd..56ba6c82b501 100644 --- a/drivers/iio/chemical/atlas-sensor.c +++ b/drivers/iio/chemical/atlas-sensor.c @@ -649,7 +649,6 @@ static int atlas_probe(struct i2c_client *client, data->client = client; data->trig = trig; data->chip = chip; - trig->dev.parent = indio_dev->dev.parent; trig->ops = &atlas_interrupt_trigger_ops; iio_trigger_set_drvdata(trig, indio_dev); From 86e52a25f38cc47f24000671a9581c7b7e94749e Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Tue, 9 Mar 2021 11:36:19 -0800 Subject: [PATCH 090/119] iio: as3935: Remove code to set trigger parent iio_trigger_set_drvdata() sets the trigger device parent to first argument of viio_trigger_alloc(), no need to do it again in the driver code. Given we call devm_iio_trigger_alloc() and devm_iio_device_alloc() with dev as parent, we do not have to set data->trig->dev.parent to indio_dev->dev.parent anymore. Signed-off-by: Gwendal Grignou Reviewed-by: Andy Shevchenko Reviewed-by: Matt Ranostay Link: https://lore.kernel.org/r/20210309193620.2176163-8-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/as3935.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index b79ada839e01..edc4a35ae66d 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -411,7 +411,6 @@ static int as3935_probe(struct spi_device *spi) st->trig = trig; st->noise_tripped = jiffies - HZ; - trig->dev.parent = indio_dev->dev.parent; iio_trigger_set_drvdata(trig, indio_dev); trig->ops = &iio_interrupt_trigger_ops; From 86073fa2b538847f4a623c040f66d1ec96e0643a Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Tue, 9 Mar 2021 11:36:20 -0800 Subject: [PATCH 091/119] iio: xilinx-xadc: Remove code to set trigger parent iio_trigger_set_drvdata() sets the trigger device parent to first argument of viio_trigger_alloc(), no need to do it again in the driver code. In xadc_alloc_trigger, given dev is indio_dev->dev.parent, and we call devm_iio_trigger_alloc wit dev as argument, we do not have to set data->trig->dev.parent to indio_dev->dev.parent anymore. Signed-off-by: Gwendal Grignou Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210309193620.2176163-9-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/xilinx-xadc-core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index 34800dccbf69..6914c1900ed0 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -747,7 +747,6 @@ static struct iio_trigger *xadc_alloc_trigger(struct iio_dev *indio_dev, if (trig == NULL) return ERR_PTR(-ENOMEM); - trig->dev.parent = indio_dev->dev.parent; trig->ops = &xadc_trigger_ops; iio_trigger_set_drvdata(trig, iio_priv(indio_dev)); From 635ef601b2387c3215252c9931786524d122c0e7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 8 Mar 2021 11:02:18 +0100 Subject: [PATCH 092/119] iio: Provide iio_read_channel_processed_scale() API Since the old iio_read_channel_processed() would lose precision if we fall back to reading raw and scaling, we introduce a new API that will pass in a scale factor when reading a processed channel: iio_read_channel_processed_scale(). Refactor iio_read_channel_processed() as a special case with scale factor 1. Cc: Peter Rosin Cc: Chris Lesiak Cc: Jonathan Cameron Cc: linux-iio@vger.kernel.org Link: https://lore.kernel.org/linux-iio/20201224011607.1059534-1-linus.walleij@linaro.org/ Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20210308100219.2732156-1-linus.walleij@linaro.org Signed-off-by: Jonathan Cameron --- drivers/iio/inkern.c | 16 ++++++++++++++-- include/linux/iio/consumer.h | 15 +++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index db77a2d4a56b..c61fc06f98b8 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -688,7 +688,8 @@ int iio_read_channel_offset(struct iio_channel *chan, int *val, int *val2) } EXPORT_SYMBOL_GPL(iio_read_channel_offset); -int iio_read_channel_processed(struct iio_channel *chan, int *val) +int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, + unsigned int scale) { int ret; @@ -701,11 +702,15 @@ int iio_read_channel_processed(struct iio_channel *chan, int *val) if (iio_channel_has_info(chan->channel, IIO_CHAN_INFO_PROCESSED)) { ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_PROCESSED); + if (ret) + goto err_unlock; + *val *= scale; } else { ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW); if (ret < 0) goto err_unlock; - ret = iio_convert_raw_to_processed_unlocked(chan, *val, val, 1); + ret = iio_convert_raw_to_processed_unlocked(chan, *val, val, + scale); } err_unlock: @@ -713,6 +718,13 @@ err_unlock: return ret; } +EXPORT_SYMBOL_GPL(iio_read_channel_processed_scale); + +int iio_read_channel_processed(struct iio_channel *chan, int *val) +{ + /* This is just a special case with scale factor 1 */ + return iio_read_channel_processed_scale(chan, val, 1); +} EXPORT_SYMBOL_GPL(iio_read_channel_processed); int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2) diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index 0a90ba8fa1bb..5fa5957586cf 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -241,6 +241,21 @@ int iio_read_channel_average_raw(struct iio_channel *chan, int *val); */ int iio_read_channel_processed(struct iio_channel *chan, int *val); +/** + * iio_read_channel_processed_scale() - read and scale a processed value + * @chan: The channel being queried. + * @val: Value read back. + * @scale: Scale factor to apply during the conversion + * + * Returns an error code or 0. + * + * This function will read a processed value from a channel. This will work + * like @iio_read_channel_processed() but also scale with an additional + * scale factor while attempting to minimize any precision loss. + */ +int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, + unsigned int scale); + /** * iio_write_channel_attribute() - Write values to the device attribute. * @chan: The channel being queried. From 4f2d9cced4c1ccda7ca0c888892954361c8a397e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 8 Mar 2021 11:02:19 +0100 Subject: [PATCH 093/119] hwmon: (ntc_thermistor): try reading processed Before trying the custom method of reading the sensor as raw and then converting, we want to use iio_read_channel_processed_scale() which first tries to see if the ADC can provide a processed value directly, else reads raw and applies scaling inside of IIO using the scale attributes of the ADC. We need to multiply the scaled value with 1000 to get to microvolts from millivolts which is what processed IIO channels returns. Keep the code that assumes 12bit ADC around as a fallback. This gives correct readings on the AB8500 thermistor inputs used in the Ux500 HREFP520 platform for reading battery and board temperature. Cc: Peter Rosin Cc: Chris Lesiak Cc: Jonathan Cameron Cc: linux-iio@vger.kernel.org Link: https://lore.kernel.org/linux-iio/20201224011607.1059534-1-linus.walleij@linaro.org/ Signed-off-by: Linus Walleij Tested-by: Chris Lesiak Acked-by: Guenter Roeck Link: https://lore.kernel.org/r/20210308100219.2732156-2-linus.walleij@linaro.org Signed-off-by: Jonathan Cameron --- drivers/hwmon/ntc_thermistor.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index 3aad62a0e661..8587189c7f15 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -326,18 +326,27 @@ struct ntc_data { static int ntc_adc_iio_read(struct ntc_thermistor_platform_data *pdata) { struct iio_channel *channel = pdata->chan; - int raw, uv, ret; + int uv, ret; - ret = iio_read_channel_raw(channel, &raw); + ret = iio_read_channel_processed_scale(channel, &uv, 1000); if (ret < 0) { - pr_err("read channel() error: %d\n", ret); - return ret; - } + int raw; - ret = iio_convert_raw_to_processed(channel, raw, &uv, 1000); - if (ret < 0) { - /* Assume 12 bit ADC with vref at pullup_uv */ - uv = (pdata->pullup_uv * (s64)raw) >> 12; + /* + * This fallback uses a raw read and then + * assumes the ADC is 12 bits, scaling with + * a factor 1000 to get to microvolts. + */ + ret = iio_read_channel_raw(channel, &raw); + if (ret < 0) { + pr_err("read channel() error: %d\n", ret); + return ret; + } + ret = iio_convert_raw_to_processed(channel, raw, &uv, 1000); + if (ret < 0) { + /* Assume 12 bit ADC with vref at pullup_uv */ + uv = (pdata->pullup_uv * (s64)raw) >> 12; + } } return uv; From 218bc53dc70022ac31381b43c82f4097a95b8605 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Sun, 7 Mar 2021 20:54:44 +0200 Subject: [PATCH 094/119] iio: buffer: fix use-after-free for attached_buffers array Thanks to Lars for finding this. The free of the 'attached_buffers' array should be done as late as possible. This change moves it to iio_buffers_put(), which looks like the best place for it, since it takes place right before the IIO device data is free'd. The free of this array will be handled by calling iio_device_free(). The iio_buffers_put() function is renamed to iio_device_detach_buffers() since the role of this function changes a bit. It looks like this issue was ocurring on the error path of iio_buffers_alloc_sysfs_and_mask() and in iio_buffers_free_sysfs_and_mask() Added a comment in the doc-header of iio_device_attach_buffer() to mention how this will be free'd in case anyone is reading the code and becoming confused about it. Fixes: ee708e6baacd ("iio: buffer: introduce support for attaching more IIO buffers") Reported-by: Lars-Peter Clausen Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210307185444.32924-1-ardeleanalex@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/iio_core.h | 4 ++-- drivers/iio/industrialio-buffer.c | 9 +++++---- drivers/iio/industrialio-core.c | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index 062fe16c6c49..8f4a9b264962 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -77,7 +77,7 @@ void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev); void iio_disable_all_buffers(struct iio_dev *indio_dev); void iio_buffer_wakeup_poll(struct iio_dev *indio_dev); -void iio_buffers_put(struct iio_dev *indio_dev); +void iio_device_detach_buffers(struct iio_dev *indio_dev); #else @@ -93,7 +93,7 @@ static inline void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev) {} static inline void iio_disable_all_buffers(struct iio_dev *indio_dev) {} static inline void iio_buffer_wakeup_poll(struct iio_dev *indio_dev) {} -static inline void iio_buffers_put(struct iio_dev *indio_dev) {} +static inline void iio_device_detach_buffers(struct iio_dev *indio_dev) {} #endif diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index a48e494a9fbb..ee5aab9d4a23 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -242,7 +242,7 @@ void iio_buffer_init(struct iio_buffer *buffer) } EXPORT_SYMBOL(iio_buffer_init); -void iio_buffers_put(struct iio_dev *indio_dev) +void iio_device_detach_buffers(struct iio_dev *indio_dev) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); struct iio_buffer *buffer; @@ -252,6 +252,8 @@ void iio_buffers_put(struct iio_dev *indio_dev) buffer = iio_dev_opaque->attached_buffers[i]; iio_buffer_put(buffer); } + + kfree(iio_dev_opaque->attached_buffers); } static ssize_t iio_show_scan_index(struct device *dev, @@ -1633,7 +1635,6 @@ error_unwind_sysfs_and_mask: buffer = iio_dev_opaque->attached_buffers[unwind_idx]; __iio_buffer_free_sysfs_and_mask(buffer); } - kfree(iio_dev_opaque->attached_buffers); return ret; } @@ -1655,8 +1656,6 @@ void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev) buffer = iio_dev_opaque->attached_buffers[i]; __iio_buffer_free_sysfs_and_mask(buffer); } - - kfree(iio_dev_opaque->attached_buffers); } /** @@ -1779,6 +1778,8 @@ EXPORT_SYMBOL_GPL(iio_buffer_put); * This function attaches a buffer to a IIO device. The buffer stays attached to * the device until the device is freed. For legacy reasons, the first attached * buffer will also be assigned to 'indio_dev->buffer'. + * The array allocated here, will be free'd via the iio_device_detach_buffers() + * call which is handled by the iio_device_free(). */ int iio_device_attach_buffer(struct iio_dev *indio_dev, struct iio_buffer *buffer) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index cb2735d2ae4b..b5750edf935c 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1586,7 +1586,7 @@ static void iio_dev_release(struct device *device) iio_device_unregister_eventset(indio_dev); iio_device_unregister_sysfs(indio_dev); - iio_buffers_put(indio_dev); + iio_device_detach_buffers(indio_dev); ida_simple_remove(&iio_ida, indio_dev->id); kfree(iio_dev_opaque); From 70da64153123460a10f3cb53fa9edca3a0ec2727 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Wed, 10 Mar 2021 11:38:00 +0200 Subject: [PATCH 095/119] iio: temperature: tmp007: use device-managed functions in probe This change converts the driver to use device-managed functions in the probe function. The power-down call is handled now via a devm_add_action_or_reset() hook, and then devm_iio_device_register() can be used to register the IIO device. The final aim here would be for IIO to export only the device-managed functions of it's API. That's a long way to go and this a small step in that direction. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210310093800.45822-1-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/tmp007.c | 36 +++++++++++++------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/drivers/iio/temperature/tmp007.c b/drivers/iio/temperature/tmp007.c index ad2b35c65548..b422371a4674 100644 --- a/drivers/iio/temperature/tmp007.c +++ b/drivers/iio/temperature/tmp007.c @@ -439,6 +439,13 @@ static bool tmp007_identify(struct i2c_client *client) return (manf_id == TMP007_MANUFACTURER_MAGIC && dev_id == TMP007_DEVICE_MAGIC); } +static void tmp007_powerdown_action_cb(void *priv) +{ + struct tmp007_data *data = priv; + + tmp007_powerdown(data); +} + static int tmp007_probe(struct i2c_client *client, const struct i2c_device_id *tmp007_id) { @@ -489,6 +496,10 @@ static int tmp007_probe(struct i2c_client *client, if (ret < 0) return ret; + ret = devm_add_action_or_reset(&client->dev, tmp007_powerdown_action_cb, data); + if (ret) + return ret; + /* * Only the following flags can activate ALERT pin. Data conversion/validity flags * flags can still be polled for getting temperature data @@ -502,7 +513,7 @@ static int tmp007_probe(struct i2c_client *client, ret = i2c_smbus_read_word_swapped(data->client, TMP007_STATUS_MASK); if (ret < 0) - goto error_powerdown; + return ret; data->status_mask = ret; data->status_mask |= (TMP007_STATUS_OHF | TMP007_STATUS_OLF @@ -510,7 +521,7 @@ static int tmp007_probe(struct i2c_client *client, ret = i2c_smbus_write_word_swapped(data->client, TMP007_STATUS_MASK, data->status_mask); if (ret < 0) - goto error_powerdown; + return ret; if (client->irq) { ret = devm_request_threaded_irq(&client->dev, client->irq, @@ -519,27 +530,11 @@ static int tmp007_probe(struct i2c_client *client, tmp007_id->name, indio_dev); if (ret) { dev_err(&client->dev, "irq request error %d\n", -ret); - goto error_powerdown; + return ret; } } - return iio_device_register(indio_dev); - -error_powerdown: - tmp007_powerdown(data); - - return ret; -} - -static int tmp007_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct tmp007_data *data = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - tmp007_powerdown(data); - - return 0; + return devm_iio_device_register(&client->dev, indio_dev); } #ifdef CONFIG_PM_SLEEP @@ -582,7 +577,6 @@ static struct i2c_driver tmp007_driver = { .pm = &tmp007_pm_ops, }, .probe = tmp007_probe, - .remove = tmp007_remove, .id_table = tmp007_id, }; module_i2c_driver(tmp007_driver); From b627e3b5f73b8decc3ebfe46d93b23ecf6f143ab Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Wed, 10 Mar 2021 11:51:31 +0200 Subject: [PATCH 096/119] staging: iio: ad9834: convert to device-managed functions in probe This change converts the driver to use device-managed functions in the probe function. For the clock and regulator disable, some devm_add_action_or_reset() calls are required, and then devm_iio_device_register() function can be used register the IIO device. The final aim here would be for IIO to export only the device-managed functions of it's API. That's a long way to go and this a small step in that direction. Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210310095131.47476-1-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/frequency/ad9834.c | 67 ++++++++++++-------------- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c index 262c3590e64e..60a3ae5587b9 100644 --- a/drivers/staging/iio/frequency/ad9834.c +++ b/drivers/staging/iio/frequency/ad9834.c @@ -56,7 +56,6 @@ /** * struct ad9834_state - driver instance specific data * @spi: spi_device - * @reg: supply regulator * @mclk: external master clock * @control: cached control word * @xfer: default spi transfer @@ -70,7 +69,6 @@ struct ad9834_state { struct spi_device *spi; - struct regulator *reg; struct clk *mclk; unsigned short control; unsigned short devid; @@ -390,6 +388,20 @@ static const struct iio_info ad9833_info = { .attrs = &ad9833_attribute_group, }; +static void ad9834_disable_reg(void *data) +{ + struct regulator *reg = data; + + regulator_disable(reg); +} + +static void ad9834_disable_clk(void *data) +{ + struct clk *clk = data; + + clk_disable_unprepare(clk); +} + static int ad9834_probe(struct spi_device *spi) { struct ad9834_state *st; @@ -407,29 +419,35 @@ static int ad9834_probe(struct spi_device *spi) return ret; } + ret = devm_add_action_or_reset(&spi->dev, ad9834_disable_reg, reg); + if (ret) + return ret; + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); if (!indio_dev) { ret = -ENOMEM; - goto error_disable_reg; + return ret; } - spi_set_drvdata(spi, indio_dev); st = iio_priv(indio_dev); mutex_init(&st->lock); st->mclk = devm_clk_get(&spi->dev, NULL); if (IS_ERR(st->mclk)) { ret = PTR_ERR(st->mclk); - goto error_disable_reg; + return ret; } ret = clk_prepare_enable(st->mclk); if (ret) { dev_err(&spi->dev, "Failed to enable master clock\n"); - goto error_disable_reg; + return ret; } + ret = devm_add_action_or_reset(&spi->dev, ad9834_disable_clk, st->mclk); + if (ret) + return ret; + st->spi = spi; st->devid = spi_get_device_id(spi)->driver_data; - st->reg = reg; indio_dev->name = spi_get_device_id(spi)->name; switch (st->devid) { case ID_AD9833: @@ -470,48 +488,26 @@ static int ad9834_probe(struct spi_device *spi) ret = spi_sync(st->spi, &st->msg); if (ret) { dev_err(&spi->dev, "device init failed\n"); - goto error_clock_unprepare; + return ret; } ret = ad9834_write_frequency(st, AD9834_REG_FREQ0, 1000000); if (ret) - goto error_clock_unprepare; + return ret; ret = ad9834_write_frequency(st, AD9834_REG_FREQ1, 5000000); if (ret) - goto error_clock_unprepare; + return ret; ret = ad9834_write_phase(st, AD9834_REG_PHASE0, 512); if (ret) - goto error_clock_unprepare; + return ret; ret = ad9834_write_phase(st, AD9834_REG_PHASE1, 1024); if (ret) - goto error_clock_unprepare; + return ret; - ret = iio_device_register(indio_dev); - if (ret) - goto error_clock_unprepare; - - return 0; -error_clock_unprepare: - clk_disable_unprepare(st->mclk); -error_disable_reg: - regulator_disable(reg); - - return ret; -} - -static int ad9834_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad9834_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - clk_disable_unprepare(st->mclk); - regulator_disable(st->reg); - - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id ad9834_id[] = { @@ -539,7 +535,6 @@ static struct spi_driver ad9834_driver = { .of_match_table = ad9834_of_match }, .probe = ad9834_probe, - .remove = ad9834_remove, .id_table = ad9834_id, }; module_spi_driver(ad9834_driver); From 0b8061c340b643e01da431dd60c75a41bb1d31ec Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Mon, 1 Mar 2021 09:04:00 +0100 Subject: [PATCH 097/119] dt-bindings: counter: add interrupt-counter binding Add binding for the interrupt counter node Signed-off-by: Oleksij Rempel Reviewed-by: Linus Walleij Acked-by: William Breathitt Gray Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20210301080401.22190-2-o.rempel@pengutronix.de Signed-off-by: Jonathan Cameron --- .../bindings/counter/interrupt-counter.yaml | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Documentation/devicetree/bindings/counter/interrupt-counter.yaml diff --git a/Documentation/devicetree/bindings/counter/interrupt-counter.yaml b/Documentation/devicetree/bindings/counter/interrupt-counter.yaml new file mode 100644 index 000000000000..fd075d104631 --- /dev/null +++ b/Documentation/devicetree/bindings/counter/interrupt-counter.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/counter/interrupt-counter.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Interrupt counter + +maintainers: + - Oleksij Rempel + +description: | + A generic interrupt counter to measure interrupt frequency. It was developed + and used for agricultural devices to measure rotation speed of wheels or + other tools. Since the direction of rotation is not important, only one + signal line is needed. + Interrupts or gpios are required. If both are defined, the interrupt will + take precedence for counting interrupts. + +properties: + compatible: + const: interrupt-counter + + interrupts: + maxItems: 1 + + gpios: + maxItems: 1 + +required: + - compatible + +anyOf: + - required: [ interrupts-extended ] + - required: [ interrupts ] + - required: [ gpios ] + +additionalProperties: false + +examples: + - | + + #include + #include + + counter-0 { + compatible = "interrupt-counter"; + interrupts-extended = <&gpio 0 IRQ_TYPE_EDGE_RISING>; + }; + + counter-1 { + compatible = "interrupt-counter"; + gpios = <&gpio 2 GPIO_ACTIVE_HIGH>; + }; + + counter-2 { + compatible = "interrupt-counter"; + interrupts-extended = <&gpio 2 IRQ_TYPE_EDGE_RISING>; + gpios = <&gpio 2 GPIO_ACTIVE_HIGH>; + }; + +... From a55ebd47f21f6f0472766fb52c973849e31d1466 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Mon, 1 Mar 2021 09:04:01 +0100 Subject: [PATCH 098/119] counter: add IRQ or GPIO based counter Add simple IRQ or GPIO base counter. This device is used to measure rotation speed of some agricultural devices, so no high frequency on the counter pin is expected. The maximal measurement frequency depends on the CPU and system load. On the idle iMX6S I was able to measure up to 20kHz without count drops. Signed-off-by: Oleksij Rempel Reviewed-by: Ahmad Fatoum Reviewed-by: William Breathitt Gray Link: https://lore.kernel.org/r/20210301080401.22190-3-o.rempel@pengutronix.de Signed-off-by: Jonathan Cameron --- MAINTAINERS | 7 + drivers/counter/Kconfig | 10 ++ drivers/counter/Makefile | 1 + drivers/counter/interrupt-cnt.c | 244 ++++++++++++++++++++++++++++++++ 4 files changed, 262 insertions(+) create mode 100644 drivers/counter/interrupt-cnt.c diff --git a/MAINTAINERS b/MAINTAINERS index 05279582917a..57638f0d7cd2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9288,6 +9288,13 @@ F: include/dt-bindings/interconnect/ F: include/linux/interconnect-provider.h F: include/linux/interconnect.h +INTERRUPT COUNTER DRIVER +M: Oleksij Rempel +R: Pengutronix Kernel Team +L: linux-iio@vger.kernel.org +F: Documentation/devicetree/bindings/counter/interrupt-counter.yaml +F: drivers/counter/interrupt-cnt.c + INVENSENSE ICM-426xx IMU DRIVER M: Jean-Baptiste Maneyrol L: linux-iio@vger.kernel.org diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index 1391e8ea64fe..5328705aa09c 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -29,6 +29,16 @@ config 104_QUAD_8 The base port addresses for the devices may be configured via the base array module parameter. +config INTERRUPT_CNT + tristate "Interrupt counter driver" + depends on GPIOLIB + help + Select this option to enable interrupt counter driver. Any interrupt + source can be used by this driver as the event source. + + To compile this driver as a module, choose M here: the + module will be called interrupt-cnt. + config STM32_TIMER_CNT tristate "STM32 Timer encoder counter driver" depends on MFD_STM32_TIMERS || COMPILE_TEST diff --git a/drivers/counter/Makefile b/drivers/counter/Makefile index 0a393f71e481..cb646ed2f039 100644 --- a/drivers/counter/Makefile +++ b/drivers/counter/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_COUNTER) += counter.o obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o +obj-$(CONFIG_INTERRUPT_CNT) += interrupt-cnt.o obj-$(CONFIG_STM32_TIMER_CNT) += stm32-timer-cnt.o obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o obj-$(CONFIG_TI_EQEP) += ti-eqep.o diff --git a/drivers/counter/interrupt-cnt.c b/drivers/counter/interrupt-cnt.c new file mode 100644 index 000000000000..a99ee7996977 --- /dev/null +++ b/drivers/counter/interrupt-cnt.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 Pengutronix, Oleksij Rempel + */ + +#include +#include +#include +#include +#include +#include +#include + +#define INTERRUPT_CNT_NAME "interrupt-cnt" + +struct interrupt_cnt_priv { + atomic_t count; + struct counter_device counter; + struct gpio_desc *gpio; + int irq; + bool enabled; + struct counter_signal signals; + struct counter_synapse synapses; + struct counter_count cnts; +}; + +static irqreturn_t interrupt_cnt_isr(int irq, void *dev_id) +{ + struct interrupt_cnt_priv *priv = dev_id; + + atomic_inc(&priv->count); + + return IRQ_HANDLED; +} + +static ssize_t interrupt_cnt_enable_read(struct counter_device *counter, + struct counter_count *count, + void *private, char *buf) +{ + struct interrupt_cnt_priv *priv = counter->priv; + + return sysfs_emit(buf, "%d\n", priv->enabled); +} + +static ssize_t interrupt_cnt_enable_write(struct counter_device *counter, + struct counter_count *count, + void *private, const char *buf, + size_t len) +{ + struct interrupt_cnt_priv *priv = counter->priv; + bool enable; + ssize_t ret; + + ret = kstrtobool(buf, &enable); + if (ret) + return ret; + + if (priv->enabled == enable) + return len; + + if (enable) { + priv->enabled = true; + enable_irq(priv->irq); + } else { + disable_irq(priv->irq); + priv->enabled = false; + } + + return len; +} + +static const struct counter_count_ext interrupt_cnt_ext[] = { + { + .name = "enable", + .read = interrupt_cnt_enable_read, + .write = interrupt_cnt_enable_write, + }, +}; + +static enum counter_synapse_action interrupt_cnt_synapse_actionss[] = { + COUNTER_SYNAPSE_ACTION_RISING_EDGE, +}; + +static int interrupt_cnt_action_get(struct counter_device *counter, + struct counter_count *count, + struct counter_synapse *synapse, + size_t *action) +{ + *action = 0; + + return 0; +} + +static int interrupt_cnt_read(struct counter_device *counter, + struct counter_count *count, unsigned long *val) +{ + struct interrupt_cnt_priv *priv = counter->priv; + + *val = atomic_read(&priv->count); + + return 0; +} + +static int interrupt_cnt_write(struct counter_device *counter, + struct counter_count *count, + const unsigned long val) +{ + struct interrupt_cnt_priv *priv = counter->priv; + + atomic_set(&priv->count, val); + + return 0; +} + +static enum counter_count_function interrupt_cnt_functions[] = { + COUNTER_COUNT_FUNCTION_INCREASE, +}; + +static int interrupt_cnt_function_get(struct counter_device *counter, + struct counter_count *count, + size_t *function) +{ + *function = 0; + + return 0; +} + +static int interrupt_cnt_signal_read(struct counter_device *counter, + struct counter_signal *signal, + enum counter_signal_value *val) +{ + struct interrupt_cnt_priv *priv = counter->priv; + int ret; + + if (!priv->gpio) + return -EINVAL; + + ret = gpiod_get_value(priv->gpio); + if (ret < 0) + return ret; + + *val = ret ? COUNTER_SIGNAL_HIGH : COUNTER_SIGNAL_LOW; + + return 0; +} + +static const struct counter_ops interrupt_cnt_ops = { + .action_get = interrupt_cnt_action_get, + .count_read = interrupt_cnt_read, + .count_write = interrupt_cnt_write, + .function_get = interrupt_cnt_function_get, + .signal_read = interrupt_cnt_signal_read, +}; + +static int interrupt_cnt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct interrupt_cnt_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->irq = platform_get_irq_optional(pdev, 0); + if (priv->irq == -ENXIO) + priv->irq = 0; + else if (priv->irq < 0) + return dev_err_probe(dev, priv->irq, "failed to get IRQ\n"); + + priv->gpio = devm_gpiod_get_optional(dev, NULL, GPIOD_IN); + if (IS_ERR(priv->gpio)) + return dev_err_probe(dev, PTR_ERR(priv->gpio), "failed to get GPIO\n"); + + if (!priv->irq && !priv->gpio) { + dev_err(dev, "IRQ and GPIO are not found. At least one source should be provided\n"); + return -ENODEV; + } + + if (!priv->irq) { + int irq = gpiod_to_irq(priv->gpio); + + if (irq < 0) + return dev_err_probe(dev, irq, "failed to get IRQ from GPIO\n"); + + priv->irq = irq; + } + + priv->signals.name = devm_kasprintf(dev, GFP_KERNEL, "IRQ %d", + priv->irq); + if (!priv->signals.name) + return -ENOMEM; + + priv->counter.signals = &priv->signals; + priv->counter.num_signals = 1; + + priv->synapses.actions_list = interrupt_cnt_synapse_actionss; + priv->synapses.num_actions = ARRAY_SIZE(interrupt_cnt_synapse_actionss); + priv->synapses.signal = &priv->signals; + + priv->cnts.name = "Channel 0 Count"; + priv->cnts.functions_list = interrupt_cnt_functions; + priv->cnts.num_functions = ARRAY_SIZE(interrupt_cnt_functions); + priv->cnts.synapses = &priv->synapses; + priv->cnts.num_synapses = 1; + priv->cnts.ext = interrupt_cnt_ext; + priv->cnts.num_ext = ARRAY_SIZE(interrupt_cnt_ext); + + priv->counter.priv = priv; + priv->counter.name = dev_name(dev); + priv->counter.parent = dev; + priv->counter.ops = &interrupt_cnt_ops; + priv->counter.counts = &priv->cnts; + priv->counter.num_counts = 1; + + irq_set_status_flags(priv->irq, IRQ_NOAUTOEN); + ret = devm_request_irq(dev, priv->irq, interrupt_cnt_isr, + IRQF_TRIGGER_RISING | IRQF_NO_THREAD, + dev_name(dev), priv); + if (ret) + return ret; + + return devm_counter_register(dev, &priv->counter); +} + +static const struct of_device_id interrupt_cnt_of_match[] = { + { .compatible = "interrupt-counter", }, + {} +}; +MODULE_DEVICE_TABLE(of, interrupt_cnt_of_match); + +static struct platform_driver interrupt_cnt_driver = { + .probe = interrupt_cnt_probe, + .driver = { + .name = INTERRUPT_CNT_NAME, + .of_match_table = interrupt_cnt_of_match, + }, +}; +module_platform_driver(interrupt_cnt_driver); + +MODULE_ALIAS("platform:interrupt-counter"); +MODULE_AUTHOR("Oleksij Rempel "); +MODULE_DESCRIPTION("Interrupt counter driver"); +MODULE_LICENSE("GPL v2"); From 44f14695bd936043de25313cfeb1c863a1060ad7 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 14 Mar 2021 16:46:48 +0000 Subject: [PATCH 099/119] iio: pressure: zpa2326: kernel-doc fixes Two comment blocks had wrong naming for function/structures that they referred to. Results in warnings when doing a W=1 build. Signed-off-by: Jonathan Cameron Reviewed-by: Alexandru Ardelean Reviewed-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20210314164655.408461-2-jic23@kernel.org --- drivers/iio/pressure/zpa2326.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c index 70adff62cd20..a93411216aee 100644 --- a/drivers/iio/pressure/zpa2326.c +++ b/drivers/iio/pressure/zpa2326.c @@ -103,7 +103,7 @@ static const struct zpa2326_frequency *zpa2326_highest_frequency(void) } /** - * struct zpa_private - Per-device internal private state + * struct zpa2326_private - Per-device internal private state * @timestamp: Buffered samples ready datum. * @regmap: Underlying I2C / SPI bus adapter used to abstract slave register * accesses. @@ -1382,7 +1382,7 @@ static const struct iio_trigger_ops zpa2326_trigger_ops = { }; /** - * zpa2326_init_trigger() - Create an interrupt driven / hardware trigger + * zpa2326_init_managed_trigger() - Create interrupt driven / hardware trigger * allowing to notify external devices a new sample is * ready. * @parent: Hardware sampling device @indio_dev is a child of. From b863ff94197f3118a18ceade7854bbaa53e0fc58 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 14 Mar 2021 16:46:49 +0000 Subject: [PATCH 100/119] iio: adc: ti-adc084s021: kernel-doc fixes, missing function names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documentation in this driver was nearly kernel-doc and was marked as such. Unfortunately the format was wrong and function names were missing. This patch puts them in with minor edits to keep the resulting line short. Signed-off-by: Jonathan Cameron Reviewed-by: Lars-Peter Clausen Cc: Gwendal Grignou Cc: Mårten Lindahl Link: https://lore.kernel.org/r/20210314164655.408461-3-jic23@kernel.org --- drivers/iio/adc/ti-adc084s021.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c index d6e1bf3de755..33aea961d850 100644 --- a/drivers/iio/adc/ti-adc084s021.c +++ b/drivers/iio/adc/ti-adc084s021.c @@ -65,7 +65,7 @@ static const struct iio_chan_spec adc084s021_channels[] = { }; /** - * Read an ADC channel and return its value. + * adc084s021_adc_conversion() - Read an ADC channel and return its value. * * @adc: The ADC SPI data. * @data: Buffer for converted data. @@ -136,7 +136,7 @@ static int adc084s021_read_raw(struct iio_dev *indio_dev, } /** - * Read enabled ADC channels and push data to the buffer. + * adc084s021_buffer_trigger_handler() - Read ADC channels and push to buffer. * * @irq: The interrupt number (not used). * @pollfunc: Pointer to the poll func. From e0549f34328f7f9b8058cfb6a9e779c5fcd3d6dc Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 14 Mar 2021 16:46:50 +0000 Subject: [PATCH 101/119] iio: dac: ad5770r: kernel-doc fix case of letter R wrong in structure name Nothing useful to add beyond this causing a warning with W=1 Signed-off-by: Jonathan Cameron Reviewed-by: Lars-Peter Clausen Acked-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210314164655.408461-4-jic23@kernel.org --- drivers/iio/dac/ad5770r.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/dac/ad5770r.c b/drivers/iio/dac/ad5770r.c index 84dcf149261f..4e7a8ed83cc1 100644 --- a/drivers/iio/dac/ad5770r.c +++ b/drivers/iio/dac/ad5770r.c @@ -118,7 +118,7 @@ struct ad5770r_out_range { }; /** - * struct ad5770R_state - driver instance specific data + * struct ad5770r_state - driver instance specific data * @spi: spi_device * @regmap: regmap * @vref_reg: fixed regulator for reference configuration From 92e212e597ac5118dd31108b7ca5871f04005b24 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 14 Mar 2021 16:46:51 +0000 Subject: [PATCH 102/119] iio: dac: ad5504: fix wrong part number in kernel-doc structure name. Probably a bit of cut and paste where someone forgot to change the part number. Signed-off-by: Jonathan Cameron Reviewed-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20210314164655.408461-5-jic23@kernel.org --- drivers/iio/dac/ad5504.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index e9297c25d4ef..f383bdcdc121 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -39,7 +39,7 @@ #define AD5504_DAC_PWRDN_3STATE 1 /** - * struct ad5446_state - driver instance specific data + * struct ad5504_state - driver instance specific data * @spi: spi_device * @reg: supply regulator * @vref_mv: actual reference voltage used From a80aeec0878edb6ac931609a1b67b958499f8313 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 14 Mar 2021 16:46:52 +0000 Subject: [PATCH 103/119] iio: adc: cpcap-adc: kernel-doc fix - that should be _ in structure name Fixes a W=1 warning. Signed-off-by: Jonathan Cameron Reviewed-by: Tony Lindgren Reviewed-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20210314164655.408461-6-jic23@kernel.org --- drivers/iio/adc/cpcap-adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/cpcap-adc.c b/drivers/iio/adc/cpcap-adc.c index f19c9aa93f17..40e59f4c95bc 100644 --- a/drivers/iio/adc/cpcap-adc.c +++ b/drivers/iio/adc/cpcap-adc.c @@ -100,7 +100,7 @@ struct cpcap_adc_ato { }; /** - * struct cpcap-adc - cpcap adc device driver data + * struct cpcap_adc - cpcap adc device driver data * @reg: cpcap regmap * @dev: struct device * @vendor: cpcap vendor From 63abed2a1ee8270c369db8111da2ad59f43bf9d4 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 14 Mar 2021 16:46:53 +0000 Subject: [PATCH 104/119] iio: adc: adi-axi-adc: Drop false marking for kernel-doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This comment block isn't in kernel-doc format so drop the /** marking. Signed-off-by: Jonathan Cameron Reviewed-by: Alexandru Ardelean Reviewed-by: Lars-Peter Clausen Cc: Nuno Sá Link: https://lore.kernel.org/r/20210314164655.408461-7-jic23@kernel.org --- drivers/iio/adc/adi-axi-adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index 2e84623f732e..d5f6ffc5b5bc 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -23,7 +23,7 @@ #include #include -/** +/* * Register definitions: * https://wiki.analog.com/resources/fpga/docs/axi_adc_ip#register_map */ From d7f1c0c313580d8243031978117f9b9fe0b3ddab Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 14 Mar 2021 16:46:54 +0000 Subject: [PATCH 105/119] iio: accel: sca3000: kernel-doc fixes. Missing - and wrong function names. All extremely obvious so nothing to add to patch title. Signed-off-by: Jonathan Cameron Reviewed-by: Alexandru Ardelean Reviewed-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20210314164655.408461-8-jic23@kernel.org --- drivers/iio/accel/sca3000.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index 467b5fcb81db..cb753a43533c 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -351,7 +351,7 @@ static int __sca3000_unlock_reg_lock(struct sca3000_state *st) } /** - * sca3000_write_ctrl_reg() write to a lock protect ctrl register + * sca3000_write_ctrl_reg() - write to a lock protect ctrl register * @st: Driver specific device instance data. * @sel: selects which registers we wish to write to * @val: the value to be written @@ -389,7 +389,7 @@ error_ret: } /** - * sca3000_read_ctrl_reg() read from lock protected control register. + * sca3000_read_ctrl_reg() - read from lock protected control register. * @st: Driver specific device instance data. * @ctrl_reg: Which ctrl register do we want to read. * @@ -421,7 +421,7 @@ error_ret: } /** - * sca3000_show_rev() - sysfs interface to read the chip revision number + * sca3000_print_rev() - sysfs interface to read the chip revision number * @indio_dev: Device instance specific generic IIO data. * Driver specific device instance data can be obtained via * via iio_priv(indio_dev) @@ -902,7 +902,7 @@ static int sca3000_read_event_value(struct iio_dev *indio_dev, } /** - * sca3000_write_value() - control of threshold and period + * sca3000_write_event_value() - control of threshold and period * @indio_dev: Device instance specific IIO information. * @chan: Description of the channel for which the event is being * configured. From 831aaea663acecddfc1b2d0851f6d0d36248c0f4 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 14 Mar 2021 16:46:55 +0000 Subject: [PATCH 106/119] iio: buffer: kfifo_buf: kernel-doc, typo in function name. Should have been _kfifo_ and was _fifo_ Signed-off-by: Jonathan Cameron Acked-by: Alexandru Ardelean Reviewed-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20210314164655.408461-9-jic23@kernel.org --- drivers/iio/buffer/kfifo_buf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c index 4ecfa0ec3016..d72cfb354be6 100644 --- a/drivers/iio/buffer/kfifo_buf.c +++ b/drivers/iio/buffer/kfifo_buf.c @@ -180,7 +180,7 @@ static void devm_iio_kfifo_release(struct device *dev, void *res) } /** - * devm_iio_fifo_allocate - Resource-managed iio_kfifo_allocate() + * devm_iio_kfifo_allocate - Resource-managed iio_kfifo_allocate() * @dev: Device to allocate kfifo buffer for * * RETURNS: From 85ece364065eb068439ca3fe4f11d096575d3074 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 13 Mar 2021 14:53:41 +0000 Subject: [PATCH 107/119] iio:cros_ec_sensors: Fix a wrong function name in kernel doc. cros_ec_sensors_read_data_unsafe() had wrong function name in kernel-doc This shows up with W=1 builds. No fixes tag because I don't want to waste time on this being backported. Signed-off-by: Jonathan Cameron Cc: Guenter Roeck Cc: Douglas Anderson Cc: Gwendal Grignou Cc: Enric Balletbo i Serra Acked-by: Enric Balletbo i Serra Link: https://lore.kernel.org/r/20210313145341.116088-1-jic23@kernel.org --- drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c index f8824afe595e..a266eedd9ec1 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c @@ -559,7 +559,7 @@ static int cros_ec_sensors_read_until_not_busy( } /** - * read_ec_sensors_data_unsafe() - read acceleration data from EC shared memory + * cros_ec_sensors_read_data_unsafe() - read acceleration data from EC shared memory * @indio_dev: pointer to IIO device * @scan_mask: bitmap of the sensor indices to scan * @data: location to store data From 2662e81745fc00cafcbbb532018013607cae84e0 Mon Sep 17 00:00:00 2001 From: Mugilraj Dhavachelvan Date: Sun, 14 Mar 2021 23:27:09 +0530 Subject: [PATCH 108/119] iio:dac:max517.c: Use devm_iio_device_register() Use devm_iio_device_register() to avoid remove function and drop explicit call to iio_device_unregister(). Signed-off-by: Mugilraj Dhavachelvan Reviewed-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20210314175709.34301-1-dmugil2000@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/max517.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/iio/dac/max517.c b/drivers/iio/dac/max517.c index daa60386bf0c..a6ef555153f4 100644 --- a/drivers/iio/dac/max517.c +++ b/drivers/iio/dac/max517.c @@ -153,7 +153,6 @@ static int max517_probe(struct i2c_client *client, if (!indio_dev) return -ENOMEM; data = iio_priv(indio_dev); - i2c_set_clientdata(client, indio_dev); data->client = client; switch (id->driver_data) { @@ -186,13 +185,7 @@ static int max517_probe(struct i2c_client *client, data->vref_mv[chan] = platform_data->vref_mv[chan]; } - return iio_device_register(indio_dev); -} - -static int max517_remove(struct i2c_client *client) -{ - iio_device_unregister(i2c_get_clientdata(client)); - return 0; + return devm_iio_device_register(&client->dev, indio_dev); } static const struct i2c_device_id max517_id[] = { @@ -211,7 +204,6 @@ static struct i2c_driver max517_driver = { .pm = &max517_pm_ops, }, .probe = max517_probe, - .remove = max517_remove, .id_table = max517_id, }; module_i2c_driver(max517_driver); From e5b64caaad768f78668d0d8563b4f98c0f4d4b6a Mon Sep 17 00:00:00 2001 From: Mugilraj Dhavachelvan Date: Mon, 15 Mar 2021 19:07:11 +0530 Subject: [PATCH 109/119] staging: iio: ad9832: kernel-doc fixes Fixes a W=1 warning. -Added ``:`` to lock parameter in 'ad9832_state' description. -It's a reference comment so removed /** Signed-off-by: Mugilraj Dhavachelvan Link: https://lore.kernel.org/r/20210315133711.26860-1-dmugil2000@gmail.com Signed-off-by: Jonathan Cameron --- drivers/staging/iio/frequency/ad9832.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c index 447937e04ebd..3f1981e287f5 100644 --- a/drivers/staging/iio/frequency/ad9832.c +++ b/drivers/staging/iio/frequency/ad9832.c @@ -86,7 +86,7 @@ * @freq_msg: tuning word spi message * @phase_xfer: tuning word spi transfer * @phase_msg: tuning word spi message - * @lock protect sensor state + * @lock: protect sensor state * @data: spi transmit buffer * @phase_data: tuning word spi transmit buffer * @freq_data: tuning word spi transmit buffer @@ -248,7 +248,7 @@ error_ret: return ret ? ret : len; } -/** +/* * see dds.h for further information */ From ac62f90c9636c5b7004f6696a9e8c0d109509898 Mon Sep 17 00:00:00 2001 From: LI Qingwu Date: Wed, 17 Mar 2021 06:39:01 +0000 Subject: [PATCH 110/119] dt-bindings: iio: st,st-sensors add IIS2MDC. Add support for ST magnetometer IIS2MDC, an I2C/SPI interface 3-axis magnetometer sensor. The patch was tested on the instrument with IIS2MDC via I2C interface. Acked-by: Rob Herring Signed-off-by: LI Qingwu Link: https://lore.kernel.org/r/20210317063902.19300-2-Qing-wu.Li@leica-geosystems.com.cn Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/st,st-sensors.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/iio/st,st-sensors.yaml b/Documentation/devicetree/bindings/iio/st,st-sensors.yaml index db291a9390b7..7e98f47987dc 100644 --- a/Documentation/devicetree/bindings/iio/st,st-sensors.yaml +++ b/Documentation/devicetree/bindings/iio/st,st-sensors.yaml @@ -66,6 +66,7 @@ properties: - st,lis3mdl-magn - st,lis2mdl - st,lsm9ds1-magn + - st,iis2mdc # Pressure sensors - st,lps001wp-press - st,lps25h-press From c71cfe5576d1f6e2d53b5fb9b74438eadf097b05 Mon Sep 17 00:00:00 2001 From: LI Qingwu Date: Wed, 17 Mar 2021 06:39:02 +0000 Subject: [PATCH 111/119] iio:magnetometer: Add Support for ST IIS2MDC Add support for ST magnetometer IIS2MDC, an I2C/SPI interface 3-axis magnetometer. The patch was tested on the instrument with IIS2MDC via I2C interface. Signed-off-by: LI Qingwu Link: https://lore.kernel.org/r/20210317063902.19300-3-Qing-wu.Li@leica-geosystems.com.cn Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/st_magn.h | 1 + drivers/iio/magnetometer/st_magn_core.c | 1 + drivers/iio/magnetometer/st_magn_i2c.c | 5 +++++ drivers/iio/magnetometer/st_magn_spi.c | 5 +++++ 4 files changed, 12 insertions(+) diff --git a/drivers/iio/magnetometer/st_magn.h b/drivers/iio/magnetometer/st_magn.h index 204b285725c8..7ba6a6ba5c58 100644 --- a/drivers/iio/magnetometer/st_magn.h +++ b/drivers/iio/magnetometer/st_magn.h @@ -21,6 +21,7 @@ #define LSM303AGR_MAGN_DEV_NAME "lsm303agr_magn" #define LIS2MDL_MAGN_DEV_NAME "lis2mdl" #define LSM9DS1_MAGN_DEV_NAME "lsm9ds1_magn" +#define IIS2MDC_MAGN_DEV_NAME "iis2mdc" const struct st_sensor_settings *st_magn_get_settings(const char *name); int st_magn_common_probe(struct iio_dev *indio_dev); diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 79de721e6015..71faebd07feb 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -337,6 +337,7 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = { .sensors_supported = { [0] = LSM303AGR_MAGN_DEV_NAME, [1] = LIS2MDL_MAGN_DEV_NAME, + [2] = IIS2MDC_MAGN_DEV_NAME, }, .ch = (struct iio_chan_spec *)st_magn_3_16bit_channels, .odr = { diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c index c6bb4ce77594..36f4e7b53b24 100644 --- a/drivers/iio/magnetometer/st_magn_i2c.c +++ b/drivers/iio/magnetometer/st_magn_i2c.c @@ -46,6 +46,10 @@ static const struct of_device_id st_magn_of_match[] = { .compatible = "st,lsm9ds1-magn", .data = LSM9DS1_MAGN_DEV_NAME, }, + { + .compatible = "st,iis2mdc", + .data = IIS2MDC_MAGN_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_magn_of_match); @@ -101,6 +105,7 @@ static const struct i2c_device_id st_magn_id_table[] = { { LSM303AGR_MAGN_DEV_NAME }, { LIS2MDL_MAGN_DEV_NAME }, { LSM9DS1_MAGN_DEV_NAME }, + { IIS2MDC_MAGN_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(i2c, st_magn_id_table); diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c index 3d08d74c367d..0e2323dfc687 100644 --- a/drivers/iio/magnetometer/st_magn_spi.c +++ b/drivers/iio/magnetometer/st_magn_spi.c @@ -41,6 +41,10 @@ static const struct of_device_id st_magn_of_match[] = { .compatible = "st,lsm9ds1-magn", .data = LSM9DS1_MAGN_DEV_NAME, }, + { + .compatible = "st,iis2mdc", + .data = IIS2MDC_MAGN_DEV_NAME, + }, {} }; MODULE_DEVICE_TABLE(of, st_magn_of_match); @@ -92,6 +96,7 @@ static const struct spi_device_id st_magn_id_table[] = { { LSM303AGR_MAGN_DEV_NAME }, { LIS2MDL_MAGN_DEV_NAME }, { LSM9DS1_MAGN_DEV_NAME }, + { IIS2MDC_MAGN_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(spi, st_magn_id_table); From 346e19ec04e07789d1fc9982d6adc016582620cd Mon Sep 17 00:00:00 2001 From: Seiya Wang Date: Fri, 19 Mar 2021 10:34:24 +0800 Subject: [PATCH 112/119] dt-bindings: iio: adc: Add compatible for Mediatek MT8195 This commit adds dt-binding documentation of auxadc for Mediatek MT8195 SoC Platform. Signed-off-by: Seiya Wang Link: https://lore.kernel.org/r/20210319023427.16711-7-seiya.wang@mediatek.com Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/mediatek,mt2701-auxadc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/iio/adc/mediatek,mt2701-auxadc.yaml b/Documentation/devicetree/bindings/iio/adc/mediatek,mt2701-auxadc.yaml index 5b21a9fba5dd..b939f9652e3a 100644 --- a/Documentation/devicetree/bindings/iio/adc/mediatek,mt2701-auxadc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/mediatek,mt2701-auxadc.yaml @@ -34,6 +34,7 @@ properties: - items: - enum: - mediatek,mt8183-auxadc + - mediatek,mt8195-auxadc - mediatek,mt8516-auxadc - const: mediatek,mt8173-auxadc From e1d392dc8875556d8a742d4be38ab452516428eb Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 19 Mar 2021 14:53:01 +0100 Subject: [PATCH 113/119] iio: event_monitor: Enable events before monitoring After some painful sessions with a driver that register an enable/disable sysfs knob (gp2ap002) and manually going in and enabling the event before monitoring it: cd /sys/bus/iio/devices/iio\:device2/events # ls in_proximity_thresh_either_en # echo 1 > in_proximity_thresh_either_en I realized that it's better if the iio_event_monitor is smart enough to enable all events by itself and disable them after use, if passed the -a flag familiar from the iio_generic_buffer tool. Auto-enabling events depend on the hardware being able to handle all events at the same time which isn't necessarily the case, so a command line option is required for this. Cc: Bastien Nocera Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20210319135301.542911-1-linus.walleij@linaro.org Signed-off-by: Jonathan Cameron --- tools/iio/iio_event_monitor.c | 67 ++++++++++++++++++++++++++++++++--- tools/iio/iio_utils.h | 1 + 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index bb03859db89d..0076437f6e3f 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -280,22 +281,69 @@ static void print_event(struct iio_event_data *event) printf("\n"); } +/* Enable or disable events in sysfs if the knob is available */ +static void enable_events(char *dev_dir, int enable) +{ + const struct dirent *ent; + char evdir[256]; + int ret; + DIR *dp; + + snprintf(evdir, sizeof(evdir), FORMAT_EVENTS_DIR, dev_dir); + evdir[sizeof(evdir)-1] = '\0'; + + dp = opendir(evdir); + if (!dp) { + fprintf(stderr, "Enabling/disabling events: can't open %s\n", + evdir); + return; + } + + while (ent = readdir(dp), ent) { + if (iioutils_check_suffix(ent->d_name, "_en")) { + printf("%sabling: %s\n", + enable ? "En" : "Dis", + ent->d_name); + ret = write_sysfs_int(ent->d_name, evdir, + enable); + if (ret < 0) + fprintf(stderr, "Failed to enable/disable %s\n", + ent->d_name); + } + } + + if (closedir(dp) == -1) { + perror("Enabling/disabling channels: " + "Failed to close directory"); + return; + } +} + int main(int argc, char **argv) { struct iio_event_data event; const char *device_name; + char *dev_dir_name = NULL; char *chrdev_name; int ret; int dev_num; int fd, event_fd; + bool all_events = false; - if (argc <= 1) { - fprintf(stderr, "Usage: %s \n", argv[0]); + if (argc == 2) { + device_name = argv[1]; + } else if (argc == 3) { + device_name = argv[2]; + if (!strcmp(argv[1], "-a")) + all_events = true; + } else { + fprintf(stderr, + "Usage: iio_event_monitor [options] \n" + "Listen and display events from IIO devices\n" + " -a Auto-activate all available events\n"); return -1; } - device_name = argv[1]; - dev_num = find_type_by_name(device_name, "iio:device"); if (dev_num >= 0) { printf("Found IIO device with name %s with device number %d\n", @@ -303,6 +351,10 @@ int main(int argc, char **argv) ret = asprintf(&chrdev_name, "/dev/iio:device%d", dev_num); if (ret < 0) return -ENOMEM; + /* Look up sysfs dir as well if we can */ + ret = asprintf(&dev_dir_name, "%siio:device%d", iio_dir, dev_num); + if (ret < 0) + return -ENOMEM; } else { /* * If we can't find an IIO device by name assume device_name is @@ -313,6 +365,9 @@ int main(int argc, char **argv) return -ENOMEM; } + if (all_events && dev_dir_name) + enable_events(dev_dir_name, 1); + fd = open(chrdev_name, 0); if (fd == -1) { ret = -errno; @@ -365,6 +420,10 @@ int main(int argc, char **argv) perror("Failed to close event file"); error_free_chrdev_name: + /* Disable events after use */ + if (all_events && dev_dir_name) + enable_events(dev_dir_name, 0); + free(chrdev_name); return ret; diff --git a/tools/iio/iio_utils.h b/tools/iio/iio_utils.h index 336752cade4f..663c94a6c705 100644 --- a/tools/iio/iio_utils.h +++ b/tools/iio/iio_utils.h @@ -13,6 +13,7 @@ #define IIO_MAX_NAME_LENGTH 64 #define FORMAT_SCAN_ELEMENTS_DIR "%s/buffer%d" +#define FORMAT_EVENTS_DIR "%s/events" #define FORMAT_TYPE_FILE "%s_type" #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) From 0a21526bc1d41456f1b320cce35c9c66238fb1c9 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Thu, 11 Mar 2021 11:10:42 +0200 Subject: [PATCH 114/119] iio: kfifo: add devm_iio_triggered_buffer_setup_ext variant This is similar to the {devm_}iio_triggered_buffer_setup_ext variants added via commit 5164c7889857 ("iio: triggered-buffer: add {devm_}iio_triggered_buffer_setup_ext variants"). These can be used to pass extra buffer attributes to the buffer object. This is a bit of temporary mechanism (hopefully) so that drivers that want to allocate a kfifo buffer with extra buffer attributes, don't need to include 'buffer_impl.h' directly. This can also become an API function (in it's own right, unfortunately), but it may be a little less bad vs drivers having to include 'buffer_impl.h'. So, far the drivers that want to pass buffer attributes, all have to do with some HW FIFO attributes, so there may be a chance of unifying them into IIO core somehow (as some standard API). But, until that happens, we just need to let them register their HW FIFO attributes directly (without having to let them include 'buffer_impl.h' directly). Signed-off-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20210311091042.22417-1-aardelean@deviqon.com Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/kfifo_buf.c | 16 ++++++++++------ include/linux/iio/kfifo_buf.h | 12 ++++++++---- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c index d72cfb354be6..516eb3465de1 100644 --- a/drivers/iio/buffer/kfifo_buf.c +++ b/drivers/iio/buffer/kfifo_buf.c @@ -206,22 +206,24 @@ static struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev) } /** - * devm_iio_kfifo_buffer_setup - Allocate a kfifo buffer & attach it to an IIO device + * devm_iio_kfifo_buffer_setup_ext - Allocate a kfifo buffer & attach it to an IIO device * @dev: Device object to which to attach the life-time of this kfifo buffer * @indio_dev: The device the buffer should be attached to * @mode_flags: The mode flags for this buffer (INDIO_BUFFER_SOFTWARE and/or * INDIO_BUFFER_TRIGGERED). * @setup_ops: The setup_ops required to configure the HW part of the buffer (optional) + * @buffer_attrs: Extra sysfs buffer attributes for this IIO buffer * * This function allocates a kfifo buffer via devm_iio_kfifo_allocate() and * attaches it to the IIO device via iio_device_attach_buffer(). * This is meant to be a bit of a short-hand/helper function as there are a few * drivers that seem to do this. */ -int devm_iio_kfifo_buffer_setup(struct device *dev, - struct iio_dev *indio_dev, - int mode_flags, - const struct iio_buffer_setup_ops *setup_ops) +int devm_iio_kfifo_buffer_setup_ext(struct device *dev, + struct iio_dev *indio_dev, + int mode_flags, + const struct iio_buffer_setup_ops *setup_ops, + const struct attribute **buffer_attrs) { struct iio_buffer *buffer; @@ -237,8 +239,10 @@ int devm_iio_kfifo_buffer_setup(struct device *dev, indio_dev->modes |= mode_flags; indio_dev->setup_ops = setup_ops; + buffer->attrs = buffer_attrs; + return iio_device_attach_buffer(indio_dev, buffer); } -EXPORT_SYMBOL_GPL(devm_iio_kfifo_buffer_setup); +EXPORT_SYMBOL_GPL(devm_iio_kfifo_buffer_setup_ext); MODULE_LICENSE("GPL"); diff --git a/include/linux/iio/kfifo_buf.h b/include/linux/iio/kfifo_buf.h index 1522896e1daf..ccd2ceae7b25 100644 --- a/include/linux/iio/kfifo_buf.h +++ b/include/linux/iio/kfifo_buf.h @@ -10,9 +10,13 @@ struct device; struct iio_buffer *iio_kfifo_allocate(void); void iio_kfifo_free(struct iio_buffer *r); -int devm_iio_kfifo_buffer_setup(struct device *dev, - struct iio_dev *indio_dev, - int mode_flags, - const struct iio_buffer_setup_ops *setup_ops); +int devm_iio_kfifo_buffer_setup_ext(struct device *dev, + struct iio_dev *indio_dev, + int mode_flags, + const struct iio_buffer_setup_ops *setup_ops, + const struct attribute **buffer_attrs); + +#define devm_iio_kfifo_buffer_setup(dev, indio_dev, mode_flags, setup_ops) \ + devm_iio_kfifo_buffer_setup_ext((dev), (indio_dev), (mode_flags), (setup_ops), NULL) #endif From 80346b2b55fcbb042acd0b90120004da8738101f Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 18 Mar 2021 11:48:57 -0700 Subject: [PATCH 115/119] iio: cros: unify hw fifo attributes without API changes Commit 2e2366c2d141 ("iio: cros_ec: unify hw fifo attributes into the core file") should be reverted as it set buffer extended attributes at the wrong place. However, to revert it will requires to revert more commits: commit 165aea80e2e2 ("iio: cros_ec: use devm_iio_triggered_buffer_setup_ext()") commit 21232b4456ba ("iio: buffer: remove iio_buffer_set_attrs() helper")). and we would still have conflict with more recent development. commit ee708e6baacd ("iio: buffer: introduce support for attaching more IIO buffers") Instead, this commit reverts the first 2 commits without re-adding iio_buffer_set_attrs() and set the buffer extended attributes at the right place: 1. Instead of adding has_fw_fifo, deduct it from the configuration: - EC must support FIFO (EC_FEATURE_MOTION_SENSE_FIFO) set. - sensors send data a regular interval (accelerometer, gyro, magnetomer, barometer, light sensor). - "Legacy accelerometer" is only present on EC without FIFO, so we don't need to set buffer attributes. 2. devm_iio_triggered_buffer_setup_ext() does not need to be called when EC does not support FIFO, as there is no FIFO to manage. 3. Use devm_iio_triggered_buffer_setup_ext() when EC has a FIFO to specify the buffer extended attributes. Fixes: 2e2366c2d141 ("iio: cros_ec: unify hw fifo attributes into the core file") Fixes: 165aea80e2e2 ("iio: cros_ec: use devm_iio_triggered_buffer_setup_ext()") Signed-off-by: Gwendal Grignou Reviewed-by: Alexandru Ardelean Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210318184857.2679181-1-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/accel/cros_ec_accel_legacy.c | 2 +- .../cros_ec_sensors/cros_ec_lid_angle.c | 3 +-- .../common/cros_ec_sensors/cros_ec_sensors.c | 3 +-- .../cros_ec_sensors/cros_ec_sensors_core.c | 24 +++++++------------ drivers/iio/light/cros_ec_light_prox.c | 3 +-- drivers/iio/pressure/cros_ec_baro.c | 3 +-- .../linux/iio/common/cros_ec_sensors_core.h | 3 +-- 7 files changed, 14 insertions(+), 27 deletions(-) diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c index 8f1232c38e0d..b6f3471b62dc 100644 --- a/drivers/iio/accel/cros_ec_accel_legacy.c +++ b/drivers/iio/accel/cros_ec_accel_legacy.c @@ -215,7 +215,7 @@ static int cros_ec_accel_legacy_probe(struct platform_device *pdev) return -ENOMEM; ret = cros_ec_sensors_core_init(pdev, indio_dev, true, - cros_ec_sensors_capture, NULL, false); + cros_ec_sensors_capture, NULL); if (ret) return ret; diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c b/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c index 752f59037715..af801e203623 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c @@ -97,8 +97,7 @@ static int cros_ec_lid_angle_probe(struct platform_device *pdev) if (!indio_dev) return -ENOMEM; - ret = cros_ec_sensors_core_init(pdev, indio_dev, false, NULL, - NULL, false); + ret = cros_ec_sensors_core_init(pdev, indio_dev, false, NULL, NULL); if (ret) return ret; diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c index dee1191de752..376a5b30010a 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c @@ -236,8 +236,7 @@ static int cros_ec_sensors_probe(struct platform_device *pdev) ret = cros_ec_sensors_core_init(pdev, indio_dev, true, cros_ec_sensors_capture, - cros_ec_sensors_push_data, - true); + cros_ec_sensors_push_data); if (ret) return ret; diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c index a266eedd9ec1..28bde13003b7 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -240,7 +241,6 @@ static void cros_ec_sensors_core_clean(void *arg) * for backward compatibility. * @push_data: function to call when cros_ec_sensorhub receives * a sample for that sensor. - * @has_hw_fifo: Set true if this device has/uses a HW FIFO * * Return: 0 on success, -errno on failure. */ @@ -248,8 +248,7 @@ int cros_ec_sensors_core_init(struct platform_device *pdev, struct iio_dev *indio_dev, bool physical_device, cros_ec_sensors_capture_t trigger_capture, - cros_ec_sensorhub_push_data_cb_t push_data, - bool has_hw_fifo) + cros_ec_sensorhub_push_data_cb_t push_data) { struct device *dev = &pdev->dev; struct cros_ec_sensors_core_state *state = iio_priv(indio_dev); @@ -334,9 +333,9 @@ int cros_ec_sensors_core_init(struct platform_device *pdev, * We can not use trigger here, as events are generated * as soon as sample_frequency is set. */ - ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, - INDIO_BUFFER_SOFTWARE, - NULL); + ret = devm_iio_kfifo_buffer_setup_ext(dev, indio_dev, + INDIO_BUFFER_SOFTWARE, NULL, + cros_ec_sensor_fifo_attributes); if (ret) return ret; @@ -355,21 +354,14 @@ int cros_ec_sensors_core_init(struct platform_device *pdev, ret = iio_device_set_clock(indio_dev, CLOCK_BOOTTIME); if (ret) return ret; + } else { - const struct attribute **fifo_attrs; - - if (has_hw_fifo) - fifo_attrs = cros_ec_sensor_fifo_attributes; - else - fifo_attrs = NULL; - /* * The only way to get samples in buffer is to set a * software trigger (systrig, hrtimer). */ - ret = devm_iio_triggered_buffer_setup_ext( - dev, indio_dev, NULL, trigger_capture, - NULL, fifo_attrs); + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + NULL, trigger_capture, NULL); if (ret) return ret; } diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c index 75d6b5fcf2cc..de472f23d1cb 100644 --- a/drivers/iio/light/cros_ec_light_prox.c +++ b/drivers/iio/light/cros_ec_light_prox.c @@ -182,8 +182,7 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev) ret = cros_ec_sensors_core_init(pdev, indio_dev, true, cros_ec_sensors_capture, - cros_ec_sensors_push_data, - true); + cros_ec_sensors_push_data); if (ret) return ret; diff --git a/drivers/iio/pressure/cros_ec_baro.c b/drivers/iio/pressure/cros_ec_baro.c index aa043cb9ac42..2f882e109423 100644 --- a/drivers/iio/pressure/cros_ec_baro.c +++ b/drivers/iio/pressure/cros_ec_baro.c @@ -139,8 +139,7 @@ static int cros_ec_baro_probe(struct platform_device *pdev) ret = cros_ec_sensors_core_init(pdev, indio_dev, true, cros_ec_sensors_capture, - cros_ec_sensors_push_data, - true); + cros_ec_sensors_push_data); if (ret) return ret; diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h index c9b80be82440..7ce8a8adad58 100644 --- a/include/linux/iio/common/cros_ec_sensors_core.h +++ b/include/linux/iio/common/cros_ec_sensors_core.h @@ -96,8 +96,7 @@ struct platform_device; int cros_ec_sensors_core_init(struct platform_device *pdev, struct iio_dev *indio_dev, bool physical_device, cros_ec_sensors_capture_t trigger_capture, - cros_ec_sensorhub_push_data_cb_t push_data, - bool has_hw_fifo); + cros_ec_sensorhub_push_data_cb_t push_data); irqreturn_t cros_ec_sensors_capture(int irq, void *p); int cros_ec_sensors_push_data(struct iio_dev *indio_dev, From 707182b4ff3e644393f785aba36b6edfcc316b16 Mon Sep 17 00:00:00 2001 From: Guoqing Chi Date: Fri, 19 Mar 2021 14:27:06 +0800 Subject: [PATCH 116/119] iio: adc: ad7292: Modify the bool initialization assignment A bool initializer is best assigned to false rather than 0. Signed-off-by: Guoqing Chi Reviewed-by: Marcelo Schmitt Link: https://lore.kernel.org/r/20210319062706.5135-1-chi962464zy@163.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7292.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad7292.c b/drivers/iio/adc/ad7292.c index 70e33dd1c9f7..3271a31afde1 100644 --- a/drivers/iio/adc/ad7292.c +++ b/drivers/iio/adc/ad7292.c @@ -260,7 +260,7 @@ static int ad7292_probe(struct spi_device *spi) struct ad7292_state *st; struct iio_dev *indio_dev; struct device_node *child; - bool diff_channels = 0; + bool diff_channels = false; int ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); From dbd7e992a55dd5c9b3e0771b2c417187c5de059e Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Wed, 17 Mar 2021 00:40:10 -0700 Subject: [PATCH 117/119] iio: acpi_als: Add timestamp channel Add timestamp channel in list of channel, to allow retrieving timestamps when events are produced. Signed-off-by: Gwendal Grignou Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210317074012.2336454-2-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/light/acpi-als.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c index f8e547fd35e7..784868aa8fbf 100644 --- a/drivers/iio/light/acpi-als.c +++ b/drivers/iio/light/acpi-als.c @@ -45,24 +45,22 @@ static const struct iio_chan_spec acpi_als_channels[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), }, + IIO_CHAN_SOFT_TIMESTAMP(1), }; /* * The event buffer contains timestamp and all the data from * the ACPI0008 block. There are multiple, but so far we only - * support _ALI (illuminance). Once someone adds new channels - * to acpi_als_channels[], the evt_buffer below will grow - * automatically. + * support _ALI (illuminance): One channel, padding and timestamp. */ -#define ACPI_ALS_EVT_NR_SOURCES ARRAY_SIZE(acpi_als_channels) #define ACPI_ALS_EVT_BUFFER_SIZE \ - (sizeof(s64) + (ACPI_ALS_EVT_NR_SOURCES * sizeof(s32))) + (sizeof(s32) + sizeof(s32) + sizeof(s64)) struct acpi_als { struct acpi_device *device; struct mutex lock; - s32 evt_buffer[ACPI_ALS_EVT_BUFFER_SIZE]; + s32 evt_buffer[ACPI_ALS_EVT_BUFFER_SIZE / sizeof(s32)] __aligned(8); }; /* From ddaf14dab78c915b52f6c359b3f00ef3b97bccf6 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Wed, 17 Mar 2021 00:40:11 -0700 Subject: [PATCH 118/119] iio: acpi_als: Add local variable dev in probe Use dev = &device->dev in probe routine for clarity. Signed-off-by: Gwendal Grignou Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210317074012.2336454-3-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/light/acpi-als.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c index 784868aa8fbf..a2107945cff9 100644 --- a/drivers/iio/light/acpi-als.c +++ b/drivers/iio/light/acpi-als.c @@ -161,11 +161,12 @@ static const struct iio_info acpi_als_info = { static int acpi_als_add(struct acpi_device *device) { - struct acpi_als *als; + struct device *dev = &device->dev; struct iio_dev *indio_dev; + struct acpi_als *als; int ret; - indio_dev = devm_iio_device_alloc(&device->dev, sizeof(*als)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*als)); if (!indio_dev) return -ENOMEM; @@ -180,12 +181,12 @@ static int acpi_als_add(struct acpi_device *device) indio_dev->channels = acpi_als_channels; indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels); - ret = devm_iio_kfifo_buffer_setup(&device->dev, indio_dev, + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, INDIO_BUFFER_SOFTWARE, NULL); if (ret) return ret; - return devm_iio_device_register(&device->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct acpi_device_id acpi_als_device_ids[] = { From 24b84444eb6f8d6a5090f6fdf172cacfa43f89e9 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Wed, 17 Mar 2021 00:40:12 -0700 Subject: [PATCH 119/119] iio: acpi_als: Add trigger support As some firmware does not notify on illuminance changes, add a trigger to be able to query light via software (sysfs-trigger or hrtrigger). Add a hardware trigger set as the default trigger to maintain backward compatibility. Check iio_info reports the sensor as buffer capable: iio:device0: acpi-als (buffer capable) To test, check we can get data on demand on an Intel based chromebook: IIO_DEV="iio:device0" echo 1 > iio_sysfs_trigger/add_trigger cat trigger2/name > ${IIO_DEV}/trigger/current_trigger for i in ${IIO_DEV}/scan_elements/*_en ${IIO_DEV}/buffer/enable ; do echo 1 > $i done od -x /dev/${IIO_DEV} & echo 1 > trigger2/trigger_now Signed-off-by: Gwendal Grignou Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210317074012.2336454-4-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/light/acpi-als.c | 96 +++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 28 deletions(-) diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c index a2107945cff9..30393f08e082 100644 --- a/drivers/iio/light/acpi-als.c +++ b/drivers/iio/light/acpi-als.c @@ -16,11 +16,14 @@ #include #include #include +#include #include #include #include -#include +#include +#include +#include #define ACPI_ALS_CLASS "als" #define ACPI_ALS_DEVICE_NAME "acpi-als" @@ -59,6 +62,7 @@ static const struct iio_chan_spec acpi_als_channels[] = { struct acpi_als { struct acpi_device *device; struct mutex lock; + struct iio_trigger *trig; s32 evt_buffer[ACPI_ALS_EVT_BUFFER_SIZE / sizeof(s32)] __aligned(8); }; @@ -102,33 +106,19 @@ static void acpi_als_notify(struct acpi_device *device, u32 event) { struct iio_dev *indio_dev = acpi_driver_data(device); struct acpi_als *als = iio_priv(indio_dev); - s32 *buffer = als->evt_buffer; - s64 time_ns = iio_get_time_ns(indio_dev); - s32 val; - int ret; - mutex_lock(&als->lock); - - memset(buffer, 0, ACPI_ALS_EVT_BUFFER_SIZE); - - switch (event) { - case ACPI_ALS_NOTIFY_ILLUMINANCE: - ret = acpi_als_read_value(als, ACPI_ALS_ILLUMINANCE, &val); - if (ret < 0) - goto out; - *buffer++ = val; - break; - default: - /* Unhandled event */ - dev_dbg(&device->dev, "Unhandled ACPI ALS event (%08x)!\n", - event); - goto out; + if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev)) { + switch (event) { + case ACPI_ALS_NOTIFY_ILLUMINANCE: + iio_trigger_poll_chained(als->trig); + break; + default: + /* Unhandled event */ + dev_dbg(&device->dev, + "Unhandled ACPI ALS event (%08x)!\n", + event); + } } - - iio_push_to_buffers_with_timestamp(indio_dev, als->evt_buffer, time_ns); - -out: - mutex_unlock(&als->lock); } static int acpi_als_read_raw(struct iio_dev *indio_dev, @@ -159,6 +149,41 @@ static const struct iio_info acpi_als_info = { .read_raw = acpi_als_read_raw, }; +static irqreturn_t acpi_als_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct acpi_als *als = iio_priv(indio_dev); + s32 *buffer = als->evt_buffer; + s32 val; + int ret; + + mutex_lock(&als->lock); + + ret = acpi_als_read_value(als, ACPI_ALS_ILLUMINANCE, &val); + if (ret < 0) + goto out; + *buffer = val; + + /* + * When coming from own trigger via polls, set polling function + * timestamp here. Given ACPI notifier is already in a thread and call + * function directly, there is no need to set the timestamp in the + * notify function. + * + * If the timestamp was actually 0, the timestamp is set one more time. + */ + if (!pf->timestamp) + pf->timestamp = iio_get_time_ns(indio_dev); + + iio_push_to_buffers_with_timestamp(indio_dev, buffer, pf->timestamp); +out: + mutex_unlock(&als->lock); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + static int acpi_als_add(struct acpi_device *device) { struct device *dev = &device->dev; @@ -181,8 +206,23 @@ static int acpi_als_add(struct acpi_device *device) indio_dev->channels = acpi_als_channels; indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels); - ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, - INDIO_BUFFER_SOFTWARE, NULL); + als->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, indio_dev->id); + if (!als->trig) + return -ENOMEM; + + ret = devm_iio_trigger_register(dev, als->trig); + if (ret) + return ret; + /* + * Set hardware trigger by default to let events flow when + * BIOS support notification. + */ + indio_dev->trig = iio_trigger_get(als->trig); + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + acpi_als_trigger_handler, + NULL); if (ret) return ret;