diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-09-01 09:53:54 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-09-01 09:53:54 -0700 |
commit | 1c9f8dff62d85ce00b0e99f774a84bd783af7cac (patch) | |
tree | cd1fcbc26856dfd1981ef1f81396eb67dde993bd /drivers/iio | |
parent | 28a4f91f5f251689c69155bc6a0b1afc9916c874 (diff) | |
parent | 704e2c6107f1a5353a1038bac137dda0df2a6dd0 (diff) |
Merge tag 'char-misc-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc driver updates from Greg KH:
"Here is the big set of char/misc and other small driver subsystem
changes for 6.6-rc1.
Stuff all over the place here, lots of driver updates and changes and
new additions. Short summary is:
- new IIO drivers and updates
- Interconnect driver updates
- fpga driver updates and additions
- fsi driver updates
- mei driver updates
- coresight driver updates
- nvmem driver updates
- counter driver updates
- lots of smaller misc and char driver updates and additions
All of these have been in linux-next for a long time with no reported
problems"
* tag 'char-misc-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (267 commits)
nvmem: core: Notify when a new layout is registered
nvmem: core: Do not open-code existing functions
nvmem: core: Return NULL when no nvmem layout is found
nvmem: core: Create all cells before adding the nvmem device
nvmem: u-boot-env:: Replace zero-length array with DECLARE_FLEX_ARRAY() helper
nvmem: sec-qfprom: Add Qualcomm secure QFPROM support
dt-bindings: nvmem: sec-qfprom: Add bindings for secure qfprom
dt-bindings: nvmem: Add compatible for QCM2290
nvmem: Kconfig: Fix typo "drive" -> "driver"
nvmem: Explicitly include correct DT includes
nvmem: add new NXP QorIQ eFuse driver
dt-bindings: nvmem: Add t1023-sfp efuse support
dt-bindings: nvmem: qfprom: Add compatible for MSM8226
nvmem: uniphier: Use devm_platform_get_and_ioremap_resource()
nvmem: qfprom: do some cleanup
nvmem: stm32-romem: Use devm_platform_get_and_ioremap_resource()
nvmem: rockchip-efuse: Use devm_platform_get_and_ioremap_resource()
nvmem: meson-mx-efuse: Convert to devm_platform_ioremap_resource()
nvmem: lpc18xx_otp: Convert to devm_platform_ioremap_resource()
nvmem: brcm_nvram: Use devm_platform_get_and_ioremap_resource()
...
Diffstat (limited to 'drivers/iio')
79 files changed, 3911 insertions, 784 deletions
diff --git a/drivers/iio/accel/adxl313_i2c.c b/drivers/iio/accel/adxl313_i2c.c index 524327ea3663..a4cf0cf2c5aa 100644 --- a/drivers/iio/accel/adxl313_i2c.c +++ b/drivers/iio/accel/adxl313_i2c.c @@ -40,8 +40,8 @@ static const struct regmap_config adxl31x_i2c_regmap_config[] = { static const struct i2c_device_id adxl313_i2c_id[] = { { .name = "adxl312", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] }, - { .name = "adxl313", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] }, - { .name = "adxl314", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] }, + { .name = "adxl313", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL313] }, + { .name = "adxl314", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL314] }, { } }; @@ -65,9 +65,7 @@ static int adxl313_i2c_probe(struct i2c_client *client) * Retrieves device specific data as a pointer to a * adxl313_chip_info structure */ - chip_data = device_get_match_data(&client->dev); - if (!chip_data) - chip_data = (const struct adxl313_chip_info *)i2c_match_id(adxl313_i2c_id, client)->driver_data; + chip_data = i2c_get_match_data(client); regmap = devm_regmap_init_i2c(client, &adxl31x_i2c_regmap_config[chip_data->type]); diff --git a/drivers/iio/accel/adxl355_i2c.c b/drivers/iio/accel/adxl355_i2c.c index d5beea61479d..32398cde9608 100644 --- a/drivers/iio/accel/adxl355_i2c.c +++ b/drivers/iio/accel/adxl355_i2c.c @@ -24,19 +24,10 @@ static int adxl355_i2c_probe(struct i2c_client *client) { struct regmap *regmap; const struct adxl355_chip_info *chip_data; - const struct i2c_device_id *adxl355; - chip_data = device_get_match_data(&client->dev); - if (!chip_data) { - adxl355 = to_i2c_driver(client->dev.driver)->id_table; - if (!adxl355) - return -EINVAL; - - chip_data = (void *)i2c_match_id(adxl355, client)->driver_data; - - if (!chip_data) - return -EINVAL; - } + chip_data = i2c_get_match_data(client); + if (!chip_data) + return -ENODEV; regmap = devm_regmap_init_i2c(client, &adxl355_i2c_regmap_config); if (IS_ERR(regmap)) { diff --git a/drivers/iio/accel/adxl372_spi.c b/drivers/iio/accel/adxl372_spi.c index 2bd267a22f29..75a88f16c6c9 100644 --- a/drivers/iio/accel/adxl372_spi.c +++ b/drivers/iio/accel/adxl372_spi.c @@ -8,7 +8,6 @@ #include <linux/module.h> #include <linux/regmap.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/spi/spi.h> #include "adxl372.h" diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index e8ab0d249351..13439f52d26d 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -16,7 +16,6 @@ #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/delay.h> -#include <linux/of_device.h> #include <linux/of.h> #include <linux/bitops.h> #include <linux/regulator/consumer.h> diff --git a/drivers/iio/accel/da280.c b/drivers/iio/accel/da280.c index 2f27a5ded94c..572bfe9694b0 100644 --- a/drivers/iio/accel/da280.c +++ b/drivers/iio/accel/da280.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * IIO driver for the MiraMEMS DA280 3-axis accelerometer and + * IIO driver for the MiraMEMS DA217 and DA280 3-axis accelerometer and * IIO driver for the MiraMEMS DA226 2-axis accelerometer * * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com> @@ -23,7 +23,7 @@ #define DA280_MODE_ENABLE 0x1e #define DA280_MODE_DISABLE 0x9e -enum da280_chipset { da226, da280 }; +enum da280_chipset { da217, da226, da280 }; /* * a value of + or -4096 corresponds to + or - 1G @@ -134,7 +134,10 @@ static int da280_probe(struct i2c_client *client) chip = id->driver_data; } - if (chip == da226) { + if (chip == da217) { + indio_dev->name = "da217"; + indio_dev->num_channels = 3; + } else if (chip == da226) { indio_dev->name = "da226"; indio_dev->num_channels = 2; } else { @@ -166,12 +169,14 @@ static int da280_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume); static const struct acpi_device_id da280_acpi_match[] = { + {"NSA2513", da217}, {"MIRAACC", da280}, {}, }; MODULE_DEVICE_TABLE(acpi, da280_acpi_match); static const struct i2c_device_id da280_i2c_id[] = { + { "da217", da217 }, { "da226", da226 }, { "da280", da280 }, {} diff --git a/drivers/iio/accel/kxsd9-spi.c b/drivers/iio/accel/kxsd9-spi.c index 07f14a9f22c7..1719a9f1d90a 100644 --- a/drivers/iio/accel/kxsd9-spi.c +++ b/drivers/iio/accel/kxsd9-spi.c @@ -2,7 +2,6 @@ #include <linux/device.h> #include <linux/kernel.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/spi/spi.h> #include <linux/module.h> #include <linux/slab.h> diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 6e7399e72221..f42a88711486 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -28,7 +28,7 @@ #include <linux/iio/triggered_buffer.h> #include <linux/iio/events.h> #include <linux/delay.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/of_irq.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index dc14bde31ac1..517b3db114b8 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1116,7 +1116,7 @@ config STMPE_ADC built-in ADC block (stmpe811). config SUN4I_GPADC - tristate "Support for the Allwinner SoCs GPADC" + tristate "Allwinner A10/A13/A31 and similar GPADCs driver" depends on IIO depends on MFD_SUN4I_GPADC || MACH_SUN8I depends on THERMAL || !THERMAL_OF @@ -1134,6 +1134,16 @@ config SUN4I_GPADC To compile this driver as a module, choose M here: the module will be called sun4i-gpadc-iio. +config SUN20I_GPADC + tristate "Allwinner D1/T113s/T507/R329 and similar GPADCs driver" + depends on ARCH_SUNXI || COMPILE_TEST + help + Say yes here to build support for Allwinner (D1, T113, T507 and R329) + SoCs GPADC. This ADC provides up to 16 channels. + + To compile this driver as a module, choose M here: the module will be + called sun20i-gpadc-iio. + config TI_ADC081C tristate "Texas Instruments ADC081C/ADC101C/ADC121C family" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index eb6e891790fb..2facf979327d 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -96,6 +96,7 @@ obj-$(CONFIG_RZG2L_ADC) += rzg2l_adc.o obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o obj-$(CONFIG_SPEAR_ADC) += spear_adc.o obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o +obj-$(CONFIG_SUN20I_GPADC) += sun20i-gpadc-iio.o obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o obj-$(CONFIG_STM32_ADC) += stm32-adc.o obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o diff --git a/drivers/iio/adc/ab8500-gpadc.c b/drivers/iio/adc/ab8500-gpadc.c index 4fa2126a354b..3b1bdd0b531d 100644 --- a/drivers/iio/adc/ab8500-gpadc.c +++ b/drivers/iio/adc/ab8500-gpadc.c @@ -1099,14 +1099,12 @@ static int ab8500_gpadc_probe(struct platform_device *pdev) gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END"); if (gpadc->irq_sw < 0) - return dev_err_probe(dev, gpadc->irq_sw, - "failed to get platform sw_conv_end irq\n"); + return gpadc->irq_sw; if (is_ab8500(gpadc->ab8500)) { gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END"); if (gpadc->irq_hw < 0) - return dev_err_probe(dev, gpadc->irq_hw, - "failed to get platform hw_conv_end irq\n"); + return gpadc->irq_hw; } else { gpadc->irq_hw = 0; } diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index 050a2fbf5c49..b9b206fcd748 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -14,7 +14,7 @@ #include <linux/kernel.h> #include <linux/kfifo.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/regulator/consumer.h> #include <linux/spi/spi.h> diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index 7bc3ebfe8081..69d1103b9508 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -16,7 +16,7 @@ #include <linux/err.h> #include <linux/sched.h> #include <linux/delay.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -402,8 +402,8 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np) id &= AD7192_ID_MASK; if (id != st->chip_info->chip_id) - dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X)\n", - id); + dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X != 0x%X)\n", + id, st->chip_info->chip_id); st->mode = AD7192_MODE_SEL(AD7192_MODE_IDLE) | AD7192_MODE_CLKSRC(st->clock_sel) | @@ -561,9 +561,8 @@ static ssize_t ad7192_show_filter_avail(struct device *dev, ad7192_get_available_filter_freq(st, freq_avail); for (i = 0; i < ARRAY_SIZE(freq_avail); i++) - len += scnprintf(buf + len, PAGE_SIZE - len, - "%d.%d ", freq_avail[i] / 1000, - freq_avail[i] % 1000); + len += sysfs_emit_at(buf, len, "%d.%03d ", freq_avail[i] / 1000, + freq_avail[i] % 1000); buf[len - 1] = '\n'; @@ -972,11 +971,6 @@ static void ad7192_reg_disable(void *reg) regulator_disable(reg); } -static void ad7192_clk_disable(void *clk) -{ - clk_disable_unprepare(clk); -} - static int ad7192_probe(struct spi_device *spi) { struct ad7192_state *st; @@ -1036,7 +1030,9 @@ static int ad7192_probe(struct spi_device *spi) else indio_dev->info = &ad7192_info; - ad_sd_init(&st->sd, indio_dev, spi, &ad7192_sigma_delta_info); + ret = ad_sd_init(&st->sd, indio_dev, spi, &ad7192_sigma_delta_info); + if (ret) + return ret; ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev); if (ret) @@ -1044,7 +1040,7 @@ static int ad7192_probe(struct spi_device *spi) st->fclk = AD7192_INT_FREQ_MHZ; - st->mclk = devm_clk_get_optional(&spi->dev, "mclk"); + st->mclk = devm_clk_get_optional_enabled(&spi->dev, "mclk"); if (IS_ERR(st->mclk)) return PTR_ERR(st->mclk); @@ -1052,15 +1048,6 @@ static int ad7192_probe(struct spi_device *spi) if (st->clock_sel == AD7192_CLK_EXT_MCLK1_2 || st->clock_sel == AD7192_CLK_EXT_MCLK2) { - ret = clk_prepare_enable(st->mclk); - if (ret < 0) - return ret; - - ret = devm_add_action_or_reset(&spi->dev, ad7192_clk_disable, - st->mclk); - if (ret) - return ret; - st->fclk = clk_get_rate(st->mclk); if (!ad7192_valid_external_frequency(st->fclk)) { dev_err(&spi->dev, diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c index 0621cf59d614..39eccc28debe 100644 --- a/drivers/iio/adc/ad9467.c +++ b/drivers/iio/adc/ad9467.c @@ -13,7 +13,7 @@ #include <linux/err.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/iio/iio.h> diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index e8a8ea4140f1..aff0532a974a 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -11,8 +11,9 @@ #include <linux/io.h> #include <linux/delay.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/slab.h> #include <linux/iio/iio.h> diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 366e252ebeb0..de6650f9c4b1 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -16,7 +16,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/sched.h> #include <linux/slab.h> diff --git a/drivers/iio/adc/bcm_iproc_adc.c b/drivers/iio/adc/bcm_iproc_adc.c index 44e1e53ada72..0d6885413a7e 100644 --- a/drivers/iio/adc/bcm_iproc_adc.c +++ b/drivers/iio/adc/bcm_iproc_adc.c @@ -540,8 +540,8 @@ static int iproc_adc_probe(struct platform_device *pdev) } adc_priv->irqno = platform_get_irq(pdev, 0); - if (adc_priv->irqno <= 0) - return -ENODEV; + if (adc_priv->irqno < 0) + return adc_priv->irqno; ret = regmap_update_bits(adc_priv->regmap, IPROC_REGCTL2, IPROC_ADC_AUXIN_SCAN_ENA, 0); diff --git a/drivers/iio/adc/cc10001_adc.c b/drivers/iio/adc/cc10001_adc.c index 2cde4b44fc6e..a432342348ab 100644 --- a/drivers/iio/adc/cc10001_adc.c +++ b/drivers/iio/adc/cc10001_adc.c @@ -9,7 +9,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> diff --git a/drivers/iio/adc/imx7d_adc.c b/drivers/iio/adc/imx7d_adc.c index 22da81bac97f..828d3fea6d43 100644 --- a/drivers/iio/adc/imx7d_adc.c +++ b/drivers/iio/adc/imx7d_adc.c @@ -496,7 +496,7 @@ static int imx7d_adc_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) - return dev_err_probe(dev, irq, "Failed getting irq\n"); + return irq; info->clk = devm_clk_get(dev, "adc"); if (IS_ERR(info->clk)) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index aea83f369437..9e52207352fb 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -28,7 +28,7 @@ #include <linux/iio/sysfs.h> #include <linux/kthread.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/regmap.h> #include <linux/sched/task.h> #include <linux/util_macros.h> diff --git a/drivers/iio/adc/lpc32xx_adc.c b/drivers/iio/adc/lpc32xx_adc.c index 732c924a976d..e34ed7dacd89 100644 --- a/drivers/iio/adc/lpc32xx_adc.c +++ b/drivers/iio/adc/lpc32xx_adc.c @@ -176,8 +176,8 @@ static int lpc32xx_adc_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return -ENXIO; + if (irq < 0) + return irq; retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0, LPC32XXAD_NAME, st); diff --git a/drivers/iio/adc/men_z188_adc.c b/drivers/iio/adc/men_z188_adc.c index adc5ceaef8c9..198c7e68e0cf 100644 --- a/drivers/iio/adc/men_z188_adc.c +++ b/drivers/iio/adc/men_z188_adc.c @@ -161,7 +161,6 @@ MODULE_DEVICE_TABLE(mcb, men_z188_ids); static struct mcb_driver men_z188_driver = { .driver = { .name = "z188-adc", - .owner = THIS_MODULE, }, .probe = men_z188_probe, .remove = men_z188_remove, diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index eb78a6f17fd0..320e3e7e3d4d 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -17,7 +17,6 @@ #include <linux/interrupt.h> #include <linux/of.h> #include <linux/of_irq.h> -#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> @@ -156,6 +155,10 @@ */ #define MESON_SAR_ADC_REG11 0x2c #define MESON_SAR_ADC_REG11_BANDGAP_EN BIT(13) + #define MESON_SAR_ADC_REG11_CMV_SEL BIT(6) + #define MESON_SAR_ADC_REG11_VREF_VOLTAGE BIT(5) + #define MESON_SAR_ADC_REG11_EOC BIT(1) + #define MESON_SAR_ADC_REG11_VREF_SEL BIT(0) #define MESON_SAR_ADC_REG13 0x34 #define MESON_SAR_ADC_REG13_12BIT_CALIBRATION_MASK GENMASK(13, 8) @@ -163,6 +166,7 @@ #define MESON_SAR_ADC_MAX_FIFO_SIZE 32 #define MESON_SAR_ADC_TIMEOUT 100 /* ms */ #define MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL 6 +#define MESON_SAR_ADC_VOLTAGE_AND_MUX_CHANNEL 7 #define MESON_SAR_ADC_TEMP_OFFSET 27 /* temperature sensor calibration information in eFuse */ @@ -202,29 +206,22 @@ .datasheet_name = "TEMP_SENSOR", \ } -static const struct iio_chan_spec meson_sar_adc_iio_channels[] = { - MESON_SAR_ADC_CHAN(0), - MESON_SAR_ADC_CHAN(1), - MESON_SAR_ADC_CHAN(2), - MESON_SAR_ADC_CHAN(3), - MESON_SAR_ADC_CHAN(4), - MESON_SAR_ADC_CHAN(5), - MESON_SAR_ADC_CHAN(6), - MESON_SAR_ADC_CHAN(7), - IIO_CHAN_SOFT_TIMESTAMP(8), -}; +#define MESON_SAR_ADC_MUX(_chan, _sel) { \ + .type = IIO_VOLTAGE, \ + .channel = _chan, \ + .indexed = 1, \ + .address = MESON_SAR_ADC_VOLTAGE_AND_MUX_CHANNEL, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_AVERAGE_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + BIT(IIO_CHAN_INFO_CALIBSCALE), \ + .datasheet_name = "SAR_ADC_MUX_"#_sel, \ +} -static const struct iio_chan_spec meson_sar_adc_and_temp_iio_channels[] = { - MESON_SAR_ADC_CHAN(0), - MESON_SAR_ADC_CHAN(1), - MESON_SAR_ADC_CHAN(2), - MESON_SAR_ADC_CHAN(3), - MESON_SAR_ADC_CHAN(4), - MESON_SAR_ADC_CHAN(5), - MESON_SAR_ADC_CHAN(6), - MESON_SAR_ADC_CHAN(7), - MESON_SAR_ADC_TEMP_CHAN(8), - IIO_CHAN_SOFT_TIMESTAMP(9), +enum meson_sar_adc_vref_sel { + VREF_CALIBATION_VOLTAGE = 0, + VREF_VDDA = 1, }; enum meson_sar_adc_avg_mode { @@ -249,6 +246,72 @@ enum meson_sar_adc_chan7_mux_sel { CHAN7_MUX_CH7_INPUT = 0x7, }; +enum meson_sar_adc_channel_index { + NUM_CHAN_0, + NUM_CHAN_1, + NUM_CHAN_2, + NUM_CHAN_3, + NUM_CHAN_4, + NUM_CHAN_5, + NUM_CHAN_6, + NUM_CHAN_7, + NUM_CHAN_TEMP, + NUM_MUX_0_VSS, + NUM_MUX_1_VDD_DIV4, + NUM_MUX_2_VDD_DIV2, + NUM_MUX_3_VDD_MUL3_DIV4, + NUM_MUX_4_VDD, +}; + +static enum meson_sar_adc_chan7_mux_sel chan7_mux_values[] = { + CHAN7_MUX_VSS, + CHAN7_MUX_VDD_DIV4, + CHAN7_MUX_VDD_DIV2, + CHAN7_MUX_VDD_MUL3_DIV4, + CHAN7_MUX_VDD, +}; + +static const char * const chan7_mux_names[] = { + [CHAN7_MUX_VSS] = "gnd", + [CHAN7_MUX_VDD_DIV4] = "0.25vdd", + [CHAN7_MUX_VDD_DIV2] = "0.5vdd", + [CHAN7_MUX_VDD_MUL3_DIV4] = "0.75vdd", + [CHAN7_MUX_VDD] = "vdd", +}; + +static const struct iio_chan_spec meson_sar_adc_iio_channels[] = { + MESON_SAR_ADC_CHAN(NUM_CHAN_0), + MESON_SAR_ADC_CHAN(NUM_CHAN_1), + MESON_SAR_ADC_CHAN(NUM_CHAN_2), + MESON_SAR_ADC_CHAN(NUM_CHAN_3), + MESON_SAR_ADC_CHAN(NUM_CHAN_4), + MESON_SAR_ADC_CHAN(NUM_CHAN_5), + MESON_SAR_ADC_CHAN(NUM_CHAN_6), + MESON_SAR_ADC_CHAN(NUM_CHAN_7), + MESON_SAR_ADC_MUX(NUM_MUX_0_VSS, 0), + MESON_SAR_ADC_MUX(NUM_MUX_1_VDD_DIV4, 1), + MESON_SAR_ADC_MUX(NUM_MUX_2_VDD_DIV2, 2), + MESON_SAR_ADC_MUX(NUM_MUX_3_VDD_MUL3_DIV4, 3), + MESON_SAR_ADC_MUX(NUM_MUX_4_VDD, 4), +}; + +static const struct iio_chan_spec meson_sar_adc_and_temp_iio_channels[] = { + MESON_SAR_ADC_CHAN(NUM_CHAN_0), + MESON_SAR_ADC_CHAN(NUM_CHAN_1), + MESON_SAR_ADC_CHAN(NUM_CHAN_2), + MESON_SAR_ADC_CHAN(NUM_CHAN_3), + MESON_SAR_ADC_CHAN(NUM_CHAN_4), + MESON_SAR_ADC_CHAN(NUM_CHAN_5), + MESON_SAR_ADC_CHAN(NUM_CHAN_6), + MESON_SAR_ADC_CHAN(NUM_CHAN_7), + MESON_SAR_ADC_TEMP_CHAN(NUM_CHAN_TEMP), + MESON_SAR_ADC_MUX(NUM_MUX_0_VSS, 0), + MESON_SAR_ADC_MUX(NUM_MUX_1_VDD_DIV4, 1), + MESON_SAR_ADC_MUX(NUM_MUX_2_VDD_DIV2, 2), + MESON_SAR_ADC_MUX(NUM_MUX_3_VDD_MUL3_DIV4, 3), + MESON_SAR_ADC_MUX(NUM_MUX_4_VDD, 4), +}; + struct meson_sar_adc_param { bool has_bl30_integration; unsigned long clock_rate; @@ -258,6 +321,13 @@ struct meson_sar_adc_param { u8 temperature_trimming_bits; unsigned int temperature_multiplier; unsigned int temperature_divider; + u8 disable_ring_counter; + bool has_reg11; + bool has_vref_select; + u8 vref_select; + u8 cmv_select; + u8 adc_eoc; + enum meson_sar_adc_vref_sel vref_volatge; }; struct meson_sar_adc_data { @@ -285,6 +355,7 @@ struct meson_sar_adc_priv { bool temperature_sensor_calibrated; u8 temperature_sensor_coefficient; u16 temperature_sensor_adc_val; + enum meson_sar_adc_chan7_mux_sel chan7_mux_sel; }; static const struct regmap_config meson_sar_adc_regmap_config_gxbb = { @@ -301,6 +372,17 @@ static const struct regmap_config meson_sar_adc_regmap_config_meson8 = { .max_register = MESON_SAR_ADC_DELTA_10, }; +static const struct iio_chan_spec * +find_channel_by_num(struct iio_dev *indio_dev, int num) +{ + int i; + + for (i = 0; i < indio_dev->num_channels; i++) + if (indio_dev->channels[i].channel == num) + return &indio_dev->channels[i]; + return NULL; +} + static unsigned int meson_sar_adc_get_fifo_count(struct iio_dev *indio_dev) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); @@ -338,6 +420,21 @@ static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev) 1, 10000); } +static void meson_sar_adc_set_chan7_mux(struct iio_dev *indio_dev, + enum meson_sar_adc_chan7_mux_sel sel) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + u32 regval; + + regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, sel); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, regval); + + usleep_range(10, 20); + + priv->chan7_mux_sel = sel; +} + static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, int *val) @@ -431,20 +528,16 @@ static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev, regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, MESON_SAR_ADC_DELTA_10_TEMP_SEL, regval); - } -} - -static void meson_sar_adc_set_chan7_mux(struct iio_dev *indio_dev, - enum meson_sar_adc_chan7_mux_sel sel) -{ - struct meson_sar_adc_priv *priv = iio_priv(indio_dev); - u32 regval; - - regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, sel); - regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, - MESON_SAR_ADC_REG3_CTRL_CHAN7_MUX_SEL_MASK, regval); + } else if (chan->address == MESON_SAR_ADC_VOLTAGE_AND_MUX_CHANNEL) { + enum meson_sar_adc_chan7_mux_sel sel; - usleep_range(10, 20); + if (chan->channel == NUM_CHAN_7) + sel = CHAN7_MUX_CH7_INPUT; + else + sel = chan7_mux_values[chan->channel - NUM_MUX_0_VSS]; + if (sel != priv->chan7_mux_sel) + meson_sar_adc_set_chan7_mux(indio_dev, sel); + } } static void meson_sar_adc_start_sample_engine(struct iio_dev *indio_dev) @@ -821,6 +914,22 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) MESON_SAR_ADC_CHAN_10_SW_CHAN1_MUX_SEL_MASK, regval); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW, + MESON_SAR_ADC_CHAN_10_SW_CHAN0_XP_DRIVE_SW, + MESON_SAR_ADC_CHAN_10_SW_CHAN0_XP_DRIVE_SW); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW, + MESON_SAR_ADC_CHAN_10_SW_CHAN0_YP_DRIVE_SW, + MESON_SAR_ADC_CHAN_10_SW_CHAN0_YP_DRIVE_SW); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW, + MESON_SAR_ADC_CHAN_10_SW_CHAN1_XP_DRIVE_SW, + MESON_SAR_ADC_CHAN_10_SW_CHAN1_XP_DRIVE_SW); + + regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_10_SW, + MESON_SAR_ADC_CHAN_10_SW_CHAN1_YP_DRIVE_SW, + MESON_SAR_ADC_CHAN_10_SW_CHAN1_YP_DRIVE_SW); + /* * set up the input channel muxes in MESON_SAR_ADC_AUX_SW * (2 = SAR_ADC_CH2, 3 = SAR_ADC_CH3, ...) and enable @@ -873,6 +982,35 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) MESON_SAR_ADC_DELTA_10_TS_REVE0, 0); } + regval = FIELD_PREP(MESON_SAR_ADC_REG3_CTRL_CONT_RING_COUNTER_EN, + priv->param->disable_ring_counter); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG3, + MESON_SAR_ADC_REG3_CTRL_CONT_RING_COUNTER_EN, + regval); + + if (priv->param->has_reg11) { + regval = FIELD_PREP(MESON_SAR_ADC_REG11_EOC, priv->param->adc_eoc); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, + MESON_SAR_ADC_REG11_EOC, regval); + + if (priv->param->has_vref_select) { + regval = FIELD_PREP(MESON_SAR_ADC_REG11_VREF_SEL, + priv->param->vref_select); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, + MESON_SAR_ADC_REG11_VREF_SEL, regval); + } + + regval = FIELD_PREP(MESON_SAR_ADC_REG11_VREF_VOLTAGE, + priv->param->vref_volatge); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, + MESON_SAR_ADC_REG11_VREF_VOLTAGE, regval); + + regval = FIELD_PREP(MESON_SAR_ADC_REG11_CMV_SEL, + priv->param->cmv_select); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11, + MESON_SAR_ADC_REG11_CMV_SEL, regval); + } + ret = clk_set_parent(priv->adc_sel_clk, priv->clkin); if (ret) return dev_err_probe(dev, ret, "failed to set adc parent to clkin\n"); @@ -1006,7 +1144,8 @@ static int meson_sar_adc_calib(struct iio_dev *indio_dev) meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_DIV4); usleep_range(10, 20); ret = meson_sar_adc_get_sample(indio_dev, - &indio_dev->channels[7], + find_channel_by_num(indio_dev, + NUM_MUX_1_VDD_DIV4), MEAN_AVERAGING, EIGHT_SAMPLES, &value0); if (ret < 0) goto out; @@ -1014,7 +1153,8 @@ static int meson_sar_adc_calib(struct iio_dev *indio_dev) meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_MUL3_DIV4); usleep_range(10, 20); ret = meson_sar_adc_get_sample(indio_dev, - &indio_dev->channels[7], + find_channel_by_num(indio_dev, + NUM_MUX_3_VDD_MUL3_DIV4), MEAN_AVERAGING, EIGHT_SAMPLES, &value1); if (ret < 0) goto out; @@ -1035,8 +1175,23 @@ out: return ret; } +static int read_label(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + char *label) +{ + if (chan->type == IIO_TEMP) + return sprintf(label, "temp-sensor\n"); + if (chan->type == IIO_VOLTAGE && chan->channel >= NUM_MUX_0_VSS) + return sprintf(label, "%s\n", + chan7_mux_names[chan->channel - NUM_MUX_0_VSS]); + if (chan->type == IIO_VOLTAGE) + return sprintf(label, "channel-%d\n", chan->channel); + return 0; +} + static const struct iio_info meson_sar_adc_iio_info = { .read_raw = meson_sar_adc_iio_info_read_raw, + .read_label = read_label, }; static const struct meson_sar_adc_param meson_sar_adc_meson8_param = { @@ -1067,6 +1222,9 @@ static const struct meson_sar_adc_param meson_sar_adc_gxbb_param = { .bandgap_reg = MESON_SAR_ADC_REG11, .regmap_config = &meson_sar_adc_regmap_config_gxbb, .resolution = 10, + .has_reg11 = true, + .vref_volatge = 1, + .cmv_select = 1, }; static const struct meson_sar_adc_param meson_sar_adc_gxl_param = { @@ -1075,6 +1233,10 @@ static const struct meson_sar_adc_param meson_sar_adc_gxl_param = { .bandgap_reg = MESON_SAR_ADC_REG11, .regmap_config = &meson_sar_adc_regmap_config_gxbb, .resolution = 12, + .disable_ring_counter = 1, + .has_reg11 = true, + .vref_volatge = 1, + .cmv_select = 1, }; static const struct meson_sar_adc_param meson_sar_adc_g12a_param = { @@ -1083,6 +1245,11 @@ static const struct meson_sar_adc_param meson_sar_adc_g12a_param = { .bandgap_reg = MESON_SAR_ADC_REG11, .regmap_config = &meson_sar_adc_regmap_config_gxbb, .resolution = 12, + .disable_ring_counter = 1, + .has_reg11 = true, + .adc_eoc = 1, + .has_vref_select = true, + .vref_select = VREF_VDDA, }; static const struct meson_sar_adc_data meson_sar_adc_meson8_data = { diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c index ba4cd8f49f66..3d9207c160eb 100644 --- a/drivers/iio/adc/npcm_adc.c +++ b/drivers/iio/adc/npcm_adc.c @@ -244,8 +244,8 @@ static int npcm_adc_probe(struct platform_device *pdev) info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2); irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - ret = -EINVAL; + if (irq < 0) { + ret = irq; goto err_disable_clk; } diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index 27b2632c1037..e202ea18af10 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -18,7 +18,6 @@ #include <linux/mfd/palmas.h> #include <linux/completion.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/iio/events.h> #include <linux/iio/iio.h> #include <linux/iio/machine.h> @@ -916,8 +915,7 @@ static int palmas_gpadc_probe(struct platform_device *pdev) adc->irq_auto_0 = platform_get_irq(pdev, 1); if (adc->irq_auto_0 < 0) - return dev_err_probe(adc->dev, adc->irq_auto_0, - "get auto0 irq failed\n"); + return adc->irq_auto_0; ret = devm_request_threaded_irq(&pdev->dev, adc->irq_auto_0, NULL, palmas_gpadc_irq_auto, IRQF_ONESHOT, @@ -929,8 +927,7 @@ static int palmas_gpadc_probe(struct platform_device *pdev) adc->irq_auto_1 = platform_get_irq(pdev, 2); if (adc->irq_auto_1 < 0) - return dev_err_probe(adc->dev, adc->irq_auto_1, - "get auto1 irq failed\n"); + return adc->irq_auto_1; ret = devm_request_threaded_irq(&pdev->dev, adc->irq_auto_1, NULL, palmas_gpadc_irq_auto, IRQF_ONESHOT, diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c index 0a4fd3a46113..b6b612d733ff 100644 --- a/drivers/iio/adc/qcom-spmi-adc5.c +++ b/drivers/iio/adc/qcom-spmi-adc5.c @@ -555,6 +555,8 @@ static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = { SCALE_HW_CALIB_PM5_SMB_TEMP) [ADC5_GPIO1_100K_PU] = ADC5_CHAN_TEMP("gpio1_100k_pu", 0, SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_GPIO2_100K_PU] = ADC5_CHAN_TEMP("gpio2_100k_pu", 0, + SCALE_HW_CALIB_THERM_100K_PULLUP) [ADC5_GPIO3_100K_PU] = ADC5_CHAN_TEMP("gpio3_100k_pu", 0, SCALE_HW_CALIB_THERM_100K_PULLUP) [ADC5_GPIO4_100K_PU] = ADC5_CHAN_TEMP("gpio4_100k_pu", 0, diff --git a/drivers/iio/adc/qcom-spmi-iadc.c b/drivers/iio/adc/qcom-spmi-iadc.c index acbda6636dc5..7fb8b2499a1d 100644 --- a/drivers/iio/adc/qcom-spmi-iadc.c +++ b/drivers/iio/adc/qcom-spmi-iadc.c @@ -13,7 +13,6 @@ #include <linux/mutex.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/slab.h> diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c index 4b011f7eddec..dd94667a623b 100644 --- a/drivers/iio/adc/rockchip_saradc.c +++ b/drivers/iio/adc/rockchip_saradc.c @@ -11,7 +11,6 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/clk.h> #include <linux/completion.h> #include <linux/delay.h> @@ -467,7 +466,7 @@ static int rockchip_saradc_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) - return dev_err_probe(&pdev->dev, irq, "failed to get irq\n"); + return irq; ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr, 0, dev_name(&pdev->dev), info); diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c index ff1fc329bb9b..b4a2e057d80f 100644 --- a/drivers/iio/adc/sc27xx_adc.c +++ b/drivers/iio/adc/sc27xx_adc.c @@ -7,7 +7,6 @@ #include <linux/mutex.h> #include <linux/nvmem-consumer.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c index d93e580b3dc5..ad54ef798109 100644 --- a/drivers/iio/adc/spear_adc.c +++ b/drivers/iio/adc/spear_adc.c @@ -310,8 +310,8 @@ static int spear_adc_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq <= 0) { - ret = -EINVAL; + if (irq < 0) { + ret = irq; goto errout2; } diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index 48f02dcc81c1..2f082006550f 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -18,6 +18,8 @@ #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> @@ -723,8 +725,7 @@ static int stm32_adc_probe(struct platform_device *pdev) priv->nb_adc_max = priv->cfg->num_adcs; spin_lock_init(&priv->common.lock); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->common.base = devm_ioremap_resource(&pdev->dev, res); + priv->common.base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(priv->common.base)) return PTR_ERR(priv->common.base); priv->common.phys_base = res->start; diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index a428bdb567d5..b5cc43d12b6f 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -19,7 +19,8 @@ #include <linux/iio/triggered_buffer.h> #include <linux/interrupt.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/slab.h> diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c index 0362df285a57..0f6ebb3061a0 100644 --- a/drivers/iio/adc/stm32-dfsdm-core.c +++ b/drivers/iio/adc/stm32-dfsdm-core.c @@ -12,8 +12,10 @@ #include <linux/iio/sysfs.h> #include <linux/interrupt.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/of_platform.h> #include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> diff --git a/drivers/iio/adc/stmpe-adc.c b/drivers/iio/adc/stmpe-adc.c index 67518e460e05..8e56def1c9e5 100644 --- a/drivers/iio/adc/stmpe-adc.c +++ b/drivers/iio/adc/stmpe-adc.c @@ -14,7 +14,7 @@ #include <linux/kernel.h> #include <linux/mfd/stmpe.h> #include <linux/module.h> -#include <linux/of_platform.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/device.h> diff --git a/drivers/iio/adc/sun20i-gpadc-iio.c b/drivers/iio/adc/sun20i-gpadc-iio.c new file mode 100644 index 000000000000..6a893d484cf7 --- /dev/null +++ b/drivers/iio/adc/sun20i-gpadc-iio.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * GPADC driver for sunxi platforms (D1, T113-S3 and R329) + * Copyright (c) 2023 Maksim Kiselev <bigunclemax@gmail.com> + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/reset.h> + +#include <linux/iio/iio.h> + +#define SUN20I_GPADC_DRIVER_NAME "sun20i-gpadc" + +/* Register map definition */ +#define SUN20I_GPADC_SR 0x00 +#define SUN20I_GPADC_CTRL 0x04 +#define SUN20I_GPADC_CS_EN 0x08 +#define SUN20I_GPADC_FIFO_INTC 0x0c +#define SUN20I_GPADC_FIFO_INTS 0x10 +#define SUN20I_GPADC_FIFO_DATA 0X14 +#define SUN20I_GPADC_CB_DATA 0X18 +#define SUN20I_GPADC_DATAL_INTC 0x20 +#define SUN20I_GPADC_DATAH_INTC 0x24 +#define SUN20I_GPADC_DATA_INTC 0x28 +#define SUN20I_GPADC_DATAL_INTS 0x30 +#define SUN20I_GPADC_DATAH_INTS 0x34 +#define SUN20I_GPADC_DATA_INTS 0x38 +#define SUN20I_GPADC_CH_CMP_DATA(x) (0x40 + (x) * 4) +#define SUN20I_GPADC_CH_DATA(x) (0x80 + (x) * 4) + +#define SUN20I_GPADC_CTRL_ADC_AUTOCALI_EN_MASK BIT(23) +#define SUN20I_GPADC_CTRL_WORK_MODE_MASK GENMASK(19, 18) +#define SUN20I_GPADC_CTRL_ADC_EN_MASK BIT(16) +#define SUN20I_GPADC_CS_EN_ADC_CH(x) BIT(x) +#define SUN20I_GPADC_DATA_INTC_CH_DATA_IRQ_EN(x) BIT(x) + +#define SUN20I_GPADC_WORK_MODE_SINGLE 0 + +struct sun20i_gpadc_iio { + void __iomem *regs; + struct completion completion; + int last_channel; + /* + * 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; +}; + +static int sun20i_gpadc_adc_read(struct sun20i_gpadc_iio *info, + struct iio_chan_spec const *chan, int *val) +{ + u32 ctrl; + int ret = IIO_VAL_INT; + + mutex_lock(&info->lock); + + reinit_completion(&info->completion); + + if (info->last_channel != chan->channel) { + info->last_channel = chan->channel; + + /* enable the analog input channel */ + writel(SUN20I_GPADC_CS_EN_ADC_CH(chan->channel), + info->regs + SUN20I_GPADC_CS_EN); + + /* enable the data irq for input channel */ + writel(SUN20I_GPADC_DATA_INTC_CH_DATA_IRQ_EN(chan->channel), + info->regs + SUN20I_GPADC_DATA_INTC); + } + + /* enable the ADC function */ + ctrl = readl(info->regs + SUN20I_GPADC_CTRL); + ctrl |= FIELD_PREP(SUN20I_GPADC_CTRL_ADC_EN_MASK, 1); + writel(ctrl, info->regs + SUN20I_GPADC_CTRL); + + /* + * According to the datasheet maximum acquire time(TACQ) can be + * (65535+1)/24Mhz and conversion time(CONV_TIME) is always constant + * and equal to 14/24Mhz, so (TACQ+CONV_TIME) <= 2.73125ms. + * A 10ms delay should be enough to make sure an interrupt occurs in + * normal conditions. If it doesn't occur, then there is a timeout. + */ + if (!wait_for_completion_timeout(&info->completion, msecs_to_jiffies(10))) { + ret = -ETIMEDOUT; + goto err_unlock; + } + + /* read the ADC data */ + *val = readl(info->regs + SUN20I_GPADC_CH_DATA(chan->channel)); + +err_unlock: + mutex_unlock(&info->lock); + + return ret; +} + +static int sun20i_gpadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct sun20i_gpadc_iio *info = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return sun20i_gpadc_adc_read(info, chan, val); + case IIO_CHAN_INFO_SCALE: + /* value in mv = 1800mV / 4096 raw */ + *val = 1800; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static irqreturn_t sun20i_gpadc_irq_handler(int irq, void *data) +{ + struct sun20i_gpadc_iio *info = data; + + /* clear data interrupt status register */ + writel(GENMASK(31, 0), info->regs + SUN20I_GPADC_DATA_INTS); + + complete(&info->completion); + + return IRQ_HANDLED; +} + +static const struct iio_info sun20i_gpadc_iio_info = { + .read_raw = sun20i_gpadc_read_raw, +}; + +static void sun20i_gpadc_reset_assert(void *data) +{ + struct reset_control *rst = data; + + reset_control_assert(rst); +} + +static int sun20i_gpadc_alloc_channels(struct iio_dev *indio_dev, + struct device *dev) +{ + unsigned int channel; + int num_channels, i, ret; + struct iio_chan_spec *channels; + struct fwnode_handle *node; + + num_channels = device_get_child_node_count(dev); + if (num_channels == 0) + return dev_err_probe(dev, -ENODEV, "no channel children\n"); + + channels = devm_kcalloc(dev, num_channels, sizeof(*channels), + GFP_KERNEL); + if (!channels) + return -ENOMEM; + + i = 0; + device_for_each_child_node(dev, node) { + ret = fwnode_property_read_u32(node, "reg", &channel); + if (ret) { + fwnode_handle_put(node); + return dev_err_probe(dev, ret, "invalid channel number\n"); + } + + channels[i].type = IIO_VOLTAGE; + channels[i].indexed = 1; + channels[i].channel = channel; + channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + channels[i].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); + + i++; + } + + indio_dev->channels = channels; + indio_dev->num_channels = num_channels; + + return 0; +} + +static int sun20i_gpadc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct sun20i_gpadc_iio *info; + struct reset_control *rst; + struct clk *clk; + int irq; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + info->last_channel = -1; + + mutex_init(&info->lock); + init_completion(&info->completion); + + ret = sun20i_gpadc_alloc_channels(indio_dev, dev); + if (ret) + return ret; + + indio_dev->info = &sun20i_gpadc_iio_info; + indio_dev->name = SUN20I_GPADC_DRIVER_NAME; + + info->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(info->regs)) + return PTR_ERR(info->regs); + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "failed to enable bus clock\n"); + + rst = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(rst)) + return dev_err_probe(dev, PTR_ERR(rst), "failed to get reset control\n"); + + ret = reset_control_deassert(rst); + if (ret) + return dev_err_probe(dev, ret, "failed to deassert reset\n"); + + ret = devm_add_action_or_reset(dev, sun20i_gpadc_reset_assert, rst); + if (ret) + return ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(dev, irq, sun20i_gpadc_irq_handler, 0, + dev_name(dev), info); + if (ret) + return dev_err_probe(dev, ret, "failed requesting irq %d\n", irq); + + writel(FIELD_PREP(SUN20I_GPADC_CTRL_ADC_AUTOCALI_EN_MASK, 1) | + FIELD_PREP(SUN20I_GPADC_CTRL_WORK_MODE_MASK, SUN20I_GPADC_WORK_MODE_SINGLE), + info->regs + SUN20I_GPADC_CTRL); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "could not register the device\n"); + + return 0; +} + +static const struct of_device_id sun20i_gpadc_of_id[] = { + { .compatible = "allwinner,sun20i-d1-gpadc" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sun20i_gpadc_of_id); + +static struct platform_driver sun20i_gpadc_driver = { + .driver = { + .name = SUN20I_GPADC_DRIVER_NAME, + .of_match_table = sun20i_gpadc_of_id, + }, + .probe = sun20i_gpadc_probe, +}; +module_platform_driver(sun20i_gpadc_driver); + +MODULE_DESCRIPTION("ADC driver for sunxi platforms"); +MODULE_AUTHOR("Maksim Kiselev <bigunclemax@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c index a5322550c422..25bba96367a8 100644 --- a/drivers/iio/adc/sun4i-gpadc-iio.c +++ b/drivers/iio/adc/sun4i-gpadc-iio.c @@ -24,7 +24,6 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> diff --git a/drivers/iio/adc/ti-lmp92064.c b/drivers/iio/adc/ti-lmp92064.c index c30ed824924f..84ba5c4a0eea 100644 --- a/drivers/iio/adc/ti-lmp92064.c +++ b/drivers/iio/adc/ti-lmp92064.c @@ -16,7 +16,10 @@ #include <linux/spi/spi.h> #include <linux/iio/iio.h> +#include <linux/iio/buffer.h> #include <linux/iio/driver.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> #define TI_LMP92064_REG_CONFIG_A 0x0000 #define TI_LMP92064_REG_CONFIG_B 0x0001 @@ -91,6 +94,12 @@ static const struct iio_chan_spec lmp92064_adc_channels[] = { .address = TI_LMP92064_CHAN_INC, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = TI_LMP92064_CHAN_INC, + .scan_type = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + }, .datasheet_name = "INC", }, { @@ -98,8 +107,20 @@ static const struct iio_chan_spec lmp92064_adc_channels[] = { .address = TI_LMP92064_CHAN_INV, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = TI_LMP92064_CHAN_INV, + .scan_type = { + .sign = 'u', + .realbits = 12, + .storagebits = 16, + }, .datasheet_name = "INV", }, + IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +static const unsigned long lmp92064_scan_masks[] = { + BIT(TI_LMP92064_CHAN_INC) | BIT(TI_LMP92064_CHAN_INV), + 0 }; static int lmp92064_read_meas(struct lmp92064_adc_priv *priv, u16 *res) @@ -171,6 +192,32 @@ static int lmp92064_read_raw(struct iio_dev *indio_dev, } } +static irqreturn_t lmp92064_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct lmp92064_adc_priv *priv = iio_priv(indio_dev); + struct { + u16 values[2]; + int64_t timestamp __aligned(8); + } data; + int ret; + + memset(&data, 0, sizeof(data)); + + ret = lmp92064_read_meas(priv, data.values); + if (ret) + goto err; + + iio_push_to_buffers_with_timestamp(indio_dev, &data, + iio_get_time_ns(indio_dev)); + +err: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + static int lmp92064_reset(struct lmp92064_adc_priv *priv, struct gpio_desc *gpio_reset) { @@ -301,6 +348,12 @@ static int lmp92064_adc_probe(struct spi_device *spi) indio_dev->channels = lmp92064_adc_channels; indio_dev->num_channels = ARRAY_SIZE(lmp92064_adc_channels); indio_dev->info = &lmp92064_adc_info; + indio_dev->available_scan_masks = lmp92064_scan_masks; + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + lmp92064_trigger_handler, NULL); + if (ret) + return dev_err_probe(dev, ret, "Failed to setup buffered read\n"); return devm_iio_device_register(dev, indio_dev); } diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 642c5c4895e3..8db7a01cb5fb 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -14,7 +14,6 @@ #include <linux/io.h> #include <linux/iio/iio.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/iio/machine.h> #include <linux/iio/driver.h> #include <linux/iopoll.h> diff --git a/drivers/iio/amplifiers/Kconfig b/drivers/iio/amplifiers/Kconfig index f217a2a1e958..b54fe01734b0 100644 --- a/drivers/iio/amplifiers/Kconfig +++ b/drivers/iio/amplifiers/Kconfig @@ -18,6 +18,7 @@ config AD8366 AD8366 Dual-Digital Variable Gain Amplifier (VGA) ADA4961 BiCMOS RF Digital Gain Amplifier (DGA) ADL5240 Digitally controlled variable gain amplifier (VGA) + HMC792A 0.25 dB LSB GaAs MMIC 6-Bit Digital Attenuator HMC1119 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator To compile this driver as a module, choose M here: the diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index 8d8c8ea94258..31564afb13a2 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -5,6 +5,7 @@ * AD8366 Dual-Digital Variable Gain Amplifier (VGA) * ADA4961 BiCMOS RF Digital Gain Amplifier (DGA) * ADL5240 Digitally controlled variable gain amplifier (VGA) + * HMC792A 0.25 dB LSB GaAs MMIC 6-Bit Digital Attenuator * HMC1119 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator * * Copyright 2012-2019 Analog Devices Inc. @@ -28,6 +29,7 @@ enum ad8366_type { ID_AD8366, ID_ADA4961, ID_ADL5240, + ID_HMC792, ID_HMC1119, }; @@ -64,6 +66,10 @@ static struct ad8366_info ad8366_infos[] = { .gain_min = -11500, .gain_max = 20000, }, + [ID_HMC792] = { + .gain_min = -15750, + .gain_max = 0, + }, [ID_HMC1119] = { .gain_min = -31750, .gain_max = 0, @@ -90,6 +96,7 @@ static int ad8366_write(struct iio_dev *indio_dev, case ID_ADL5240: st->data[0] = (ch_a & 0x3F); break; + case ID_HMC792: case ID_HMC1119: st->data[0] = ch_a; break; @@ -127,6 +134,9 @@ static int ad8366_read_raw(struct iio_dev *indio_dev, case ID_ADL5240: gain = 20000 - 31500 + code * 500; break; + case ID_HMC792: + gain = -1 * code * 500; + break; case ID_HMC1119: gain = -1 * code * 250; break; @@ -176,6 +186,9 @@ static int ad8366_write_raw(struct iio_dev *indio_dev, case ID_ADL5240: code = ((gain - 500 - 20000) / 500) & 0x3F; break; + case ID_HMC792: + code = (abs(gain) / 500) & 0x3F; + break; case ID_HMC1119: code = (abs(gain) / 250) & 0x7F; break; @@ -261,6 +274,7 @@ static int ad8366_probe(struct spi_device *spi) break; case ID_ADA4961: case ID_ADL5240: + case ID_HMC792: case ID_HMC1119: st->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(st->reset_gpio)) { @@ -314,6 +328,7 @@ static const struct spi_device_id ad8366_id[] = { {"ad8366", ID_AD8366}, {"ada4961", ID_ADA4961}, {"adl5240", ID_ADL5240}, + {"hmc792a", ID_HMC792}, {"hmc1119", ID_HMC1119}, {} }; diff --git a/drivers/iio/cdc/ad7150.c b/drivers/iio/cdc/ad7150.c index d656d2f12755..4c03b9e834b8 100644 --- a/drivers/iio/cdc/ad7150.c +++ b/drivers/iio/cdc/ad7150.c @@ -541,6 +541,7 @@ static int ad7150_probe(struct i2c_client *client) const struct i2c_device_id *id = i2c_client_get_device_id(client); struct ad7150_chip_info *chip; struct iio_dev *indio_dev; + bool use_irq = true; int ret; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); @@ -561,14 +562,13 @@ static int ad7150_probe(struct i2c_client *client) chip->interrupts[0] = fwnode_irq_get(dev_fwnode(&client->dev), 0); if (chip->interrupts[0] < 0) - return chip->interrupts[0]; - if (id->driver_data == AD7150) { + use_irq = false; + else if (id->driver_data == AD7150) { chip->interrupts[1] = fwnode_irq_get(dev_fwnode(&client->dev), 1); if (chip->interrupts[1] < 0) - return chip->interrupts[1]; + use_irq = false; } - if (chip->interrupts[0] && - (id->driver_data == AD7151 || chip->interrupts[1])) { + if (use_irq) { irq_set_status_flags(chip->interrupts[0], IRQ_NOAUTOEN); ret = devm_request_threaded_irq(&client->dev, chip->interrupts[0], diff --git a/drivers/iio/chemical/scd4x.c b/drivers/iio/chemical/scd4x.c index a4f22d926400..ca6b20270711 100644 --- a/drivers/iio/chemical/scd4x.c +++ b/drivers/iio/chemical/scd4x.c @@ -36,6 +36,8 @@ #define SCD4X_WRITE_BUF_SIZE 5 #define SCD4X_FRC_MIN_PPM 0 #define SCD4X_FRC_MAX_PPM 2000 +#define SCD4X_PRESSURE_COMP_MIN_MBAR 700 +#define SCD4X_PRESSURE_COMP_MAX_MBAR 1200 #define SCD4X_READY_MASK 0x01 /*Commands SCD4X*/ @@ -45,6 +47,8 @@ enum scd4x_cmd { CMD_STOP_MEAS = 0x3f86, CMD_SET_TEMP_OFFSET = 0x241d, CMD_GET_TEMP_OFFSET = 0x2318, + CMD_SET_AMB_PRESSURE = 0xe000, + CMD_GET_AMB_PRESSURE = 0xe000, CMD_FRC = 0x362f, CMD_SET_ASC = 0x2416, CMD_GET_ASC = 0x2313, @@ -137,7 +141,8 @@ static int scd4x_read(struct scd4x_state *state, enum scd4x_cmd cmd, * Measurement needs to be stopped before sending commands. * Except for reading measurement and data ready command. */ - if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS)) { + if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS) && + (cmd != CMD_GET_AMB_PRESSURE)) { ret = scd4x_send_command(state, CMD_STOP_MEAS); if (ret) return ret; @@ -166,7 +171,8 @@ static int scd4x_read(struct scd4x_state *state, enum scd4x_cmd cmd, } /* start measurement */ - if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS)) { + if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS) && + (cmd != CMD_GET_AMB_PRESSURE)) { ret = scd4x_send_command(state, CMD_START_MEAS); if (ret) return ret; @@ -188,9 +194,11 @@ static int scd4x_write(struct scd4x_state *state, enum scd4x_cmd cmd, uint16_t a buf[4] = crc; /* measurement needs to be stopped before sending commands */ - ret = scd4x_send_command(state, CMD_STOP_MEAS); - if (ret) - return ret; + if (cmd != CMD_SET_AMB_PRESSURE) { + ret = scd4x_send_command(state, CMD_STOP_MEAS); + if (ret) + return ret; + } /* execution time */ msleep_interruptible(500); @@ -200,7 +208,7 @@ static int scd4x_write(struct scd4x_state *state, enum scd4x_cmd cmd, uint16_t a return ret; /* start measurement, except for forced calibration command */ - if (cmd != CMD_FRC) { + if ((cmd != CMD_FRC) && (cmd != CMD_SET_AMB_PRESSURE)) { ret = scd4x_send_command(state, CMD_START_MEAS); if (ret) return ret; @@ -338,6 +346,18 @@ static int scd4x_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: + if (chan->output) { + mutex_lock(&state->lock); + ret = scd4x_read(state, CMD_GET_AMB_PRESSURE, &tmp, sizeof(tmp)); + mutex_unlock(&state->lock); + + if (ret) + return ret; + + *val = be16_to_cpu(tmp); + return IIO_VAL_INT; + } + ret = iio_device_claim_direct_mode(indio_dev); if (ret) return ret; @@ -386,6 +406,25 @@ static int scd4x_read_raw(struct iio_dev *indio_dev, } } +static const int scd4x_pressure_calibbias_available[] = { + SCD4X_PRESSURE_COMP_MIN_MBAR, 1, SCD4X_PRESSURE_COMP_MAX_MBAR, +}; + +static int scd4x_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_RAW: + *vals = scd4x_pressure_calibbias_available; + *type = IIO_VAL_INT; + + return IIO_AVAIL_RANGE; + } + + return -EINVAL; +} + + static int scd4x_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { @@ -399,6 +438,21 @@ static int scd4x_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const mutex_unlock(&state->lock); return ret; + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_PRESSURE: + if (val < SCD4X_PRESSURE_COMP_MIN_MBAR || + val > SCD4X_PRESSURE_COMP_MAX_MBAR) + return -EINVAL; + + mutex_lock(&state->lock); + ret = scd4x_write(state, CMD_SET_AMB_PRESSURE, val); + mutex_unlock(&state->lock); + + return ret; + default: + return -EINVAL; + } default: return -EINVAL; } @@ -503,10 +557,23 @@ static const struct iio_info scd4x_info = { .attrs = &scd4x_attr_group, .read_raw = scd4x_read_raw, .write_raw = scd4x_write_raw, + .read_avail = scd4x_read_avail, }; static const struct iio_chan_spec scd4x_channels[] = { { + /* + * this channel is special in a sense we are pretending that + * sensor is able to change measurement chamber pressure but in + * fact we're just setting pressure compensation value + */ + .type = IIO_PRESSURE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), + .output = 1, + .scan_index = -1, + }, + { .type = IIO_CONCENTRATION, .channel2 = IIO_MOD_CO2, .modified = 1, diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig index 0334b4954773..1ccb5ccf3706 100644 --- a/drivers/iio/common/Kconfig +++ b/drivers/iio/common/Kconfig @@ -5,6 +5,7 @@ source "drivers/iio/common/cros_ec_sensors/Kconfig" source "drivers/iio/common/hid-sensors/Kconfig" +source "drivers/iio/common/inv_sensors/Kconfig" source "drivers/iio/common/ms_sensors/Kconfig" source "drivers/iio/common/scmi_sensors/Kconfig" source "drivers/iio/common/ssp_sensors/Kconfig" diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile index fad40e1e1718..d3e952239a62 100644 --- a/drivers/iio/common/Makefile +++ b/drivers/iio/common/Makefile @@ -10,6 +10,7 @@ # When adding new entries keep the list in alphabetical order obj-y += cros_ec_sensors/ obj-y += hid-sensors/ +obj-y += inv_sensors/ obj-y += ms_sensors/ obj-y += scmi_sensors/ obj-y += ssp_sensors/ diff --git a/drivers/iio/common/inv_sensors/Kconfig b/drivers/iio/common/inv_sensors/Kconfig new file mode 100644 index 000000000000..28815fb43157 --- /dev/null +++ b/drivers/iio/common/inv_sensors/Kconfig @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# TDK-InvenSense sensors common library +# + +config IIO_INV_SENSORS_TIMESTAMP + tristate diff --git a/drivers/iio/common/inv_sensors/Makefile b/drivers/iio/common/inv_sensors/Makefile new file mode 100644 index 000000000000..dcf39f249112 --- /dev/null +++ b/drivers/iio/common/inv_sensors/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for TDK-InvenSense sensors module. +# + +obj-$(CONFIG_IIO_INV_SENSORS_TIMESTAMP) += inv_sensors_timestamp.o diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.c b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c index 37cbf08acb3a..03823ee57f59 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.c +++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c @@ -3,25 +3,23 @@ * Copyright (C) 2020 Invensense, Inc. */ +#include <linux/errno.h> #include <linux/kernel.h> -#include <linux/regmap.h> #include <linux/math64.h> +#include <linux/module.h> -#include "inv_icm42600.h" -#include "inv_icm42600_timestamp.h" +#include <linux/iio/common/inv_sensors_timestamp.h> -/* internal chip period is 32kHz, 31250ns */ -#define INV_ICM42600_TIMESTAMP_PERIOD 31250 -/* allow a jitter of +/- 2% */ -#define INV_ICM42600_TIMESTAMP_JITTER 2 -/* compute min and max periods accepted */ -#define INV_ICM42600_TIMESTAMP_MIN_PERIOD(_p) \ - (((_p) * (100 - INV_ICM42600_TIMESTAMP_JITTER)) / 100) -#define INV_ICM42600_TIMESTAMP_MAX_PERIOD(_p) \ - (((_p) * (100 + INV_ICM42600_TIMESTAMP_JITTER)) / 100) +/* compute jitter, min and max following jitter in per mille */ +#define INV_SENSORS_TIMESTAMP_JITTER(_val, _jitter) \ + (div_s64((_val) * (_jitter), 1000)) +#define INV_SENSORS_TIMESTAMP_MIN(_val, _jitter) \ + (((_val) * (1000 - (_jitter))) / 1000) +#define INV_SENSORS_TIMESTAMP_MAX(_val, _jitter) \ + (((_val) * (1000 + (_jitter))) / 1000) /* Add a new value inside an accumulator and update the estimate value */ -static void inv_update_acc(struct inv_icm42600_timestamp_acc *acc, uint32_t val) +static void inv_update_acc(struct inv_sensors_timestamp_acc *acc, uint32_t val) { uint64_t sum = 0; size_t i; @@ -40,65 +38,57 @@ static void inv_update_acc(struct inv_icm42600_timestamp_acc *acc, uint32_t val) acc->val = div_u64(sum, i); } -void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts, - uint32_t period) +void inv_sensors_timestamp_init(struct inv_sensors_timestamp *ts, + const struct inv_sensors_timestamp_chip *chip) { - /* initial odr for sensor after reset is 1kHz */ - const uint32_t default_period = 1000000; + memset(ts, 0, sizeof(*ts)); + + /* save chip parameters and compute min and max clock period */ + ts->chip = *chip; + ts->min_period = INV_SENSORS_TIMESTAMP_MIN(chip->clock_period, chip->jitter); + ts->max_period = INV_SENSORS_TIMESTAMP_MAX(chip->clock_period, chip->jitter); /* current multiplier and period values after reset */ - ts->mult = default_period / INV_ICM42600_TIMESTAMP_PERIOD; - ts->period = default_period; - /* new set multiplier is the one from chip initialization */ - ts->new_mult = period / INV_ICM42600_TIMESTAMP_PERIOD; + ts->mult = chip->init_period / chip->clock_period; + ts->period = chip->init_period; /* use theoretical value for chip period */ - inv_update_acc(&ts->chip_period, INV_ICM42600_TIMESTAMP_PERIOD); + inv_update_acc(&ts->chip_period, chip->clock_period); } +EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_init, IIO_INV_SENSORS_TIMESTAMP); -int inv_icm42600_timestamp_setup(struct inv_icm42600_state *st) -{ - unsigned int val; - - /* enable timestamp register */ - val = INV_ICM42600_TMST_CONFIG_TMST_TO_REGS_EN | - INV_ICM42600_TMST_CONFIG_TMST_EN; - return regmap_update_bits(st->map, INV_ICM42600_REG_TMST_CONFIG, - INV_ICM42600_TMST_CONFIG_MASK, val); -} - -int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts, - uint32_t period, bool fifo) +int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts, + uint32_t period, bool fifo) { /* when FIFO is on, prevent odr change if one is already pending */ if (fifo && ts->new_mult != 0) return -EAGAIN; - ts->new_mult = period / INV_ICM42600_TIMESTAMP_PERIOD; + ts->new_mult = period / ts->chip.clock_period; return 0; } +EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_update_odr, IIO_INV_SENSORS_TIMESTAMP); -static bool inv_validate_period(uint32_t period, uint32_t mult) +static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t period, uint32_t mult) { - const uint32_t chip_period = INV_ICM42600_TIMESTAMP_PERIOD; uint32_t period_min, period_max; /* check that period is acceptable */ - period_min = INV_ICM42600_TIMESTAMP_MIN_PERIOD(chip_period) * mult; - period_max = INV_ICM42600_TIMESTAMP_MAX_PERIOD(chip_period) * mult; + period_min = ts->min_period * mult; + period_max = ts->max_period * mult; if (period > period_min && period < period_max) return true; else return false; } -static bool inv_update_chip_period(struct inv_icm42600_timestamp *ts, - uint32_t mult, uint32_t period) +static bool inv_update_chip_period(struct inv_sensors_timestamp *ts, + uint32_t mult, uint32_t period) { uint32_t new_chip_period; - if (!inv_validate_period(period, mult)) + if (!inv_validate_period(ts, period, mult)) return false; /* update chip internal period estimation */ @@ -109,7 +99,7 @@ static bool inv_update_chip_period(struct inv_icm42600_timestamp *ts, return true; } -static void inv_align_timestamp_it(struct inv_icm42600_timestamp *ts) +static void inv_align_timestamp_it(struct inv_sensors_timestamp *ts) { int64_t delta, jitter; int64_t adjust; @@ -118,7 +108,7 @@ static void inv_align_timestamp_it(struct inv_icm42600_timestamp *ts) delta = ts->it.lo - ts->timestamp; /* adjust timestamp while respecting jitter */ - jitter = div_s64((int64_t)ts->period * INV_ICM42600_TIMESTAMP_JITTER, 100); + jitter = INV_SENSORS_TIMESTAMP_JITTER((int64_t)ts->period, ts->chip.jitter); if (delta > jitter) adjust = jitter; else if (delta < -jitter) @@ -129,13 +119,13 @@ static void inv_align_timestamp_it(struct inv_icm42600_timestamp *ts) ts->timestamp += adjust; } -void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts, +void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, uint32_t fifo_period, size_t fifo_nb, size_t sensor_nb, int64_t timestamp) { - struct inv_icm42600_timestamp_interval *it; + struct inv_sensors_timestamp_interval *it; int64_t delta, interval; - const uint32_t fifo_mult = fifo_period / INV_ICM42600_TIMESTAMP_PERIOD; + const uint32_t fifo_mult = fifo_period / ts->chip.clock_period; uint32_t period = ts->period; bool valid = false; @@ -165,10 +155,11 @@ void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts, if (valid) inv_align_timestamp_it(ts); } +EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_interrupt, IIO_INV_SENSORS_TIMESTAMP); -void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts, - uint32_t fifo_period, size_t fifo_nb, - unsigned int fifo_no) +void inv_sensors_timestamp_apply_odr(struct inv_sensors_timestamp *ts, + uint32_t fifo_period, size_t fifo_nb, + unsigned int fifo_no) { int64_t interval; uint32_t fifo_mult; @@ -189,10 +180,15 @@ void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts, */ if (ts->timestamp != 0) { /* compute measured fifo period */ - fifo_mult = fifo_period / INV_ICM42600_TIMESTAMP_PERIOD; + fifo_mult = fifo_period / ts->chip.clock_period; fifo_period = fifo_mult * ts->chip_period.val; /* computes time interval between interrupt and this sample */ interval = (int64_t)(fifo_nb - fifo_no) * (int64_t)fifo_period; ts->timestamp = ts->it.up - interval; } } +EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_apply_odr, IIO_INV_SENSORS_TIMESTAMP); + +MODULE_AUTHOR("InvenSense, Inc."); +MODULE_DESCRIPTION("InvenSense sensors timestamp module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 3acd9c3f388e..93b8be183de6 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -389,6 +389,17 @@ config MCP4725 To compile this driver as a module, choose M here: the module will be called mcp4725. +config MCP4728 + tristate "MCP4728 DAC driver" + depends on I2C + help + Say Y here if you want to build a driver for the Microchip + MCP4728 quad channel, 12-bit digital-to-analog converter (DAC) + with I2C interface. + + To compile this driver as a module, choose M here: the module + will be called mcp4728. + config MCP4922 tristate "MCP4902, MCP4912, MCP4922 DAC driver" depends on SPI diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index addd97a78838..5b2bac900d5a 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_MAX517) += max517.o obj-$(CONFIG_MAX5522) += max5522.o obj-$(CONFIG_MAX5821) += max5821.o obj-$(CONFIG_MCP4725) += mcp4725.o +obj-$(CONFIG_MCP4728) += mcp4728.o obj-$(CONFIG_MCP4922) += mcp4922.o obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o obj-$(CONFIG_STM32_DAC) += stm32-dac.o diff --git a/drivers/iio/dac/mcp4728.c b/drivers/iio/dac/mcp4728.c new file mode 100644 index 000000000000..5113f67ddc31 --- /dev/null +++ b/drivers/iio/dac/mcp4728.c @@ -0,0 +1,618 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Support for Microchip MCP4728 + * + * Copyright (C) 2023 Andrea Collamati <andrea.collamati@gmail.com> + * + * Based on mcp4725 by Peter Meerwald <pmeerw@pmeerw.net> + * + * Driver for the Microchip I2C 12-bit digital-to-analog quad channels + * converter (DAC). + * + * (7-bit I2C slave address 0x60, the three LSBs can be configured in + * hardware) + */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> + +#define MCP4728_RESOLUTION 12 +#define MCP4728_N_CHANNELS 4 + +#define MCP4728_CMD_MASK GENMASK(7, 3) +#define MCP4728_CHSEL_MASK GENMASK(2, 1) +#define MCP4728_UDAC_MASK BIT(0) + +#define MCP4728_VREF_MASK BIT(7) +#define MCP4728_PDMODE_MASK GENMASK(6, 5) +#define MCP4728_GAIN_MASK BIT(4) + +#define MCP4728_DAC_H_MASK GENMASK(3, 0) +#define MCP4728_DAC_L_MASK GENMASK(7, 0) + +#define MCP4728_RDY_MASK BIT(7) + +#define MCP4728_MW_CMD 0x08 /* Multiwrite Command */ +#define MCP4728_SW_CMD 0x0A /* Sequential Write Command with EEPROM */ + +#define MCP4728_READ_RESPONSE_LEN (MCP4728_N_CHANNELS * 3 * 2) +#define MCP4728_WRITE_EEPROM_LEN (1 + MCP4728_N_CHANNELS * 2) + +enum vref_mode { + MCP4728_VREF_EXTERNAL_VDD = 0, + MCP4728_VREF_INTERNAL_2048mV = 1, +}; + +enum gain_mode { + MCP4728_GAIN_X1 = 0, + MCP4728_GAIN_X2 = 1, +}; + +enum iio_powerdown_mode { + MCP4728_IIO_1K, + MCP4728_IIO_100K, + MCP4728_IIO_500K, +}; + +struct mcp4728_channel_data { + enum vref_mode ref_mode; + enum iio_powerdown_mode pd_mode; + enum gain_mode g_mode; + u16 dac_value; +}; + +/* MCP4728 Full Scale Ranges + * the device available ranges are + * - VREF = VDD FSR = from 0.0V to VDD + * - VREF = Internal Gain = 1 FSR = from 0.0V to VREF + * - VREF = Internal Gain = 2 FSR = from 0.0V to 2*VREF + */ +enum mcp4728_scale { + MCP4728_SCALE_VDD, + MCP4728_SCALE_VINT_NO_GAIN, + MCP4728_SCALE_VINT_GAIN_X2, + MCP4728_N_SCALES +}; + +struct mcp4728_data { + struct i2c_client *client; + struct regulator *vdd_reg; + bool powerdown; + int scales_avail[MCP4728_N_SCALES * 2]; + struct mcp4728_channel_data chdata[MCP4728_N_CHANNELS]; +}; + +#define MCP4728_CHAN(chan) { \ + .type = IIO_VOLTAGE, \ + .output = 1, \ + .indexed = 1, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \ + .ext_info = mcp4728_ext_info, \ +} + +static int mcp4728_suspend(struct device *dev); +static int mcp4728_resume(struct device *dev); + +static ssize_t mcp4728_store_eeprom(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mcp4728_data *data = iio_priv(indio_dev); + u8 outbuf[MCP4728_WRITE_EEPROM_LEN]; + int tries = 20; + u8 inbuf[3]; + bool state; + int ret; + unsigned int i; + + ret = kstrtobool(buf, &state); + if (ret < 0) + return ret; + + if (!state) + return 0; + + outbuf[0] = FIELD_PREP(MCP4728_CMD_MASK, MCP4728_SW_CMD); + + for (i = 0; i < MCP4728_N_CHANNELS; i++) { + struct mcp4728_channel_data *ch = &data->chdata[i]; + int offset = 1 + i * 2; + + outbuf[offset] = FIELD_PREP(MCP4728_VREF_MASK, ch->ref_mode); + + if (data->powerdown) { + u8 mcp4728_pd_mode = ch->pd_mode + 1; + + outbuf[offset] |= FIELD_PREP(MCP4728_PDMODE_MASK, + mcp4728_pd_mode); + } + + outbuf[offset] |= FIELD_PREP(MCP4728_GAIN_MASK, ch->g_mode); + outbuf[offset] |= + FIELD_PREP(MCP4728_DAC_H_MASK, ch->dac_value >> 8); + outbuf[offset + 1] = + FIELD_PREP(MCP4728_DAC_L_MASK, ch->dac_value); + } + + ret = i2c_master_send(data->client, outbuf, MCP4728_WRITE_EEPROM_LEN); + if (ret < 0) + return ret; + else if (ret != MCP4728_WRITE_EEPROM_LEN) + return -EIO; + + /* wait RDY signal for write complete, takes up to 50ms */ + while (tries--) { + msleep(20); + ret = i2c_master_recv(data->client, inbuf, 3); + if (ret < 0) + return ret; + else if (ret != 3) + return -EIO; + + if (FIELD_GET(MCP4728_RDY_MASK, inbuf[0])) + break; + } + + if (tries < 0) { + dev_err(&data->client->dev, "%s failed, incomplete\n", + __func__); + return -EIO; + } + return len; +} + +static IIO_DEVICE_ATTR(store_eeprom, 0200, NULL, mcp4728_store_eeprom, 0); + +static struct attribute *mcp4728_attributes[] = { + &iio_dev_attr_store_eeprom.dev_attr.attr, + NULL, +}; + +static const struct attribute_group mcp4728_attribute_group = { + .attrs = mcp4728_attributes, +}; + +static int mcp4728_program_channel_cfg(int channel, struct iio_dev *indio_dev) +{ + struct mcp4728_data *data = iio_priv(indio_dev); + struct mcp4728_channel_data *ch = &data->chdata[channel]; + u8 outbuf[3]; + int ret; + + outbuf[0] = FIELD_PREP(MCP4728_CMD_MASK, MCP4728_MW_CMD); + outbuf[0] |= FIELD_PREP(MCP4728_CHSEL_MASK, channel); + outbuf[0] |= FIELD_PREP(MCP4728_UDAC_MASK, 0); + + outbuf[1] = FIELD_PREP(MCP4728_VREF_MASK, ch->ref_mode); + + if (data->powerdown) + outbuf[1] |= FIELD_PREP(MCP4728_PDMODE_MASK, ch->pd_mode + 1); + + outbuf[1] |= FIELD_PREP(MCP4728_GAIN_MASK, ch->g_mode); + outbuf[1] |= FIELD_PREP(MCP4728_DAC_H_MASK, ch->dac_value >> 8); + outbuf[2] = FIELD_PREP(MCP4728_DAC_L_MASK, ch->dac_value); + + ret = i2c_master_send(data->client, outbuf, 3); + if (ret < 0) + return ret; + else if (ret != 3) + return -EIO; + + return 0; +} + +static const char *const mcp4728_powerdown_modes[] = { "1kohm_to_gnd", + "100kohm_to_gnd", + "500kohm_to_gnd" }; + +static int mcp4728_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct mcp4728_data *data = iio_priv(indio_dev); + + return data->chdata[chan->channel].pd_mode; +} + +static int mcp4728_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct mcp4728_data *data = iio_priv(indio_dev); + + data->chdata[chan->channel].pd_mode = mode; + + return 0; +} + +static ssize_t mcp4728_read_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct mcp4728_data *data = iio_priv(indio_dev); + + return sysfs_emit(buf, "%d\n", data->powerdown); +} + +static ssize_t mcp4728_write_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct mcp4728_data *data = iio_priv(indio_dev); + bool state; + int ret; + + ret = kstrtobool(buf, &state); + if (ret) + return ret; + + if (state) + ret = mcp4728_suspend(&data->client->dev); + else + ret = mcp4728_resume(&data->client->dev); + + if (ret < 0) + return ret; + + return len; +} + +static const struct iio_enum mcp4728_powerdown_mode_enum = { + .items = mcp4728_powerdown_modes, + .num_items = ARRAY_SIZE(mcp4728_powerdown_modes), + .get = mcp4728_get_powerdown_mode, + .set = mcp4728_set_powerdown_mode, +}; + +static const struct iio_chan_spec_ext_info mcp4728_ext_info[] = { + { + .name = "powerdown", + .read = mcp4728_read_powerdown, + .write = mcp4728_write_powerdown, + .shared = IIO_SEPARATE, + }, + IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp4728_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, + &mcp4728_powerdown_mode_enum), + {}, +}; + +static const struct iio_chan_spec mcp4728_channels[MCP4728_N_CHANNELS] = { + MCP4728_CHAN(0), + MCP4728_CHAN(1), + MCP4728_CHAN(2), + MCP4728_CHAN(3), +}; + +static void mcp4728_get_scale_avail(enum mcp4728_scale scale, + struct mcp4728_data *data, int *val, + int *val2) +{ + *val = data->scales_avail[scale * 2]; + *val2 = data->scales_avail[scale * 2 + 1]; +} + +static void mcp4728_get_scale(int channel, struct mcp4728_data *data, int *val, + int *val2) +{ + int ref_mode = data->chdata[channel].ref_mode; + int g_mode = data->chdata[channel].g_mode; + + if (ref_mode == MCP4728_VREF_EXTERNAL_VDD) { + mcp4728_get_scale_avail(MCP4728_SCALE_VDD, data, val, val2); + } else { + if (g_mode == MCP4728_GAIN_X1) { + mcp4728_get_scale_avail(MCP4728_SCALE_VINT_NO_GAIN, + data, val, val2); + } else { + mcp4728_get_scale_avail(MCP4728_SCALE_VINT_GAIN_X2, + data, val, val2); + } + } +} + +static int mcp4728_find_matching_scale(struct mcp4728_data *data, int val, + int val2) +{ + for (int i = 0; i < MCP4728_N_SCALES; i++) { + if (data->scales_avail[i * 2] == val && + data->scales_avail[i * 2 + 1] == val2) + return i; + } + return -EINVAL; +} + +static int mcp4728_set_scale(int channel, struct mcp4728_data *data, int val, + int val2) +{ + int scale = mcp4728_find_matching_scale(data, val, val2); + + if (scale < 0) + return scale; + + switch (scale) { + case MCP4728_SCALE_VDD: + data->chdata[channel].ref_mode = MCP4728_VREF_EXTERNAL_VDD; + return 0; + case MCP4728_SCALE_VINT_NO_GAIN: + data->chdata[channel].ref_mode = MCP4728_VREF_INTERNAL_2048mV; + data->chdata[channel].g_mode = MCP4728_GAIN_X1; + return 0; + case MCP4728_SCALE_VINT_GAIN_X2: + data->chdata[channel].ref_mode = MCP4728_VREF_INTERNAL_2048mV; + data->chdata[channel].g_mode = MCP4728_GAIN_X2; + return 0; + default: + return -EINVAL; + } +} + +static int mcp4728_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct mcp4728_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *val = data->chdata[chan->channel].dac_value; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + mcp4728_get_scale(chan->channel, data, val, val2); + return IIO_VAL_INT_PLUS_MICRO; + } + return -EINVAL; +} + +static int mcp4728_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct mcp4728_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val < 0 || val > GENMASK(MCP4728_RESOLUTION - 1, 0)) + return -EINVAL; + data->chdata[chan->channel].dac_value = val; + return mcp4728_program_channel_cfg(chan->channel, indio_dev); + case IIO_CHAN_INFO_SCALE: + ret = mcp4728_set_scale(chan->channel, data, val, val2); + if (ret) + return ret; + + return mcp4728_program_channel_cfg(chan->channel, indio_dev); + default: + return -EINVAL; + } +} + +static void mcp4728_init_scale_avail(enum mcp4728_scale scale, int vref_mv, + struct mcp4728_data *data) +{ + s64 tmp; + int value_micro; + int value_int; + + tmp = (s64)vref_mv * 1000000LL >> MCP4728_RESOLUTION; + value_int = div_s64_rem(tmp, 1000000LL, &value_micro); + + data->scales_avail[scale * 2] = value_int; + data->scales_avail[scale * 2 + 1] = value_micro; +} + +static int mcp4728_init_scales_avail(struct mcp4728_data *data) +{ + int ret; + + ret = regulator_get_voltage(data->vdd_reg); + if (ret < 0) + return ret; + + mcp4728_init_scale_avail(MCP4728_SCALE_VDD, ret / 1000, data); + mcp4728_init_scale_avail(MCP4728_SCALE_VINT_NO_GAIN, 2048, data); + mcp4728_init_scale_avail(MCP4728_SCALE_VINT_GAIN_X2, 4096, data); + + return 0; +} + +static int mcp4728_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + struct mcp4728_data *data = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SCALE: + *type = IIO_VAL_INT_PLUS_MICRO; + + switch (chan->type) { + case IIO_VOLTAGE: + *vals = data->scales_avail; + *length = MCP4728_N_SCALES * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const struct iio_info mcp4728_info = { + .read_raw = mcp4728_read_raw, + .write_raw = mcp4728_write_raw, + .read_avail = &mcp4728_read_avail, + .attrs = &mcp4728_attribute_group, +}; + +static int mcp4728_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct mcp4728_data *data = iio_priv(indio_dev); + unsigned int i; + + data->powerdown = true; + + for (i = 0; i < MCP4728_N_CHANNELS; i++) { + int err = mcp4728_program_channel_cfg(i, indio_dev); + + if (err) + return err; + } + return 0; +} + +static int mcp4728_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct mcp4728_data *data = iio_priv(indio_dev); + int err = 0; + unsigned int i; + + data->powerdown = false; + + for (i = 0; i < MCP4728_N_CHANNELS; i++) { + int ret = mcp4728_program_channel_cfg(i, indio_dev); + + if (ret) + err = ret; + } + return err; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(mcp4728_pm_ops, mcp4728_suspend, + mcp4728_resume); + +static int mcp4728_init_channels_data(struct mcp4728_data *data) +{ + u8 inbuf[MCP4728_READ_RESPONSE_LEN]; + int ret; + unsigned int i; + + ret = i2c_master_recv(data->client, inbuf, MCP4728_READ_RESPONSE_LEN); + if (ret < 0) { + return dev_err_probe(&data->client->dev, ret, + "failed to read mcp4728 conf.\n"); + } else if (ret != MCP4728_READ_RESPONSE_LEN) { + return dev_err_probe(&data->client->dev, -EIO, + "failed to read mcp4728 conf. Wrong Response Len ret=%d\n", + ret); + } + + for (i = 0; i < MCP4728_N_CHANNELS; i++) { + struct mcp4728_channel_data *ch = &data->chdata[i]; + u8 r2 = inbuf[i * 6 + 1]; + u8 r3 = inbuf[i * 6 + 2]; + + ch->dac_value = FIELD_GET(MCP4728_DAC_H_MASK, r2) << 8 | + FIELD_GET(MCP4728_DAC_L_MASK, r3); + ch->ref_mode = FIELD_GET(MCP4728_VREF_MASK, r2); + ch->pd_mode = FIELD_GET(MCP4728_PDMODE_MASK, r2); + ch->g_mode = FIELD_GET(MCP4728_GAIN_MASK, r2); + } + + return 0; +} + +static void mcp4728_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +static int mcp4728_probe(struct i2c_client *client) +{ + const struct i2c_device_id *id = i2c_client_get_device_id(client); + struct mcp4728_data *data; + struct iio_dev *indio_dev; + int err; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + data->vdd_reg = devm_regulator_get(&client->dev, "vdd"); + if (IS_ERR(data->vdd_reg)) + return PTR_ERR(data->vdd_reg); + + err = regulator_enable(data->vdd_reg); + if (err) + return err; + + err = devm_add_action_or_reset(&client->dev, mcp4728_reg_disable, + data->vdd_reg); + if (err) + return err; + + /* + * MCP4728 has internal EEPROM that save each channel boot + * configuration. It means that device configuration is unknown to the + * driver at kernel boot. mcp4728_init_channels_data() reads back DAC + * settings and stores them in data structure. + */ + err = mcp4728_init_channels_data(data); + if (err) { + return dev_err_probe(&client->dev, err, + "failed to read mcp4728 current configuration\n"); + } + + err = mcp4728_init_scales_avail(data); + if (err) { + return dev_err_probe(&client->dev, err, + "failed to init scales\n"); + } + + indio_dev->name = id->name; + indio_dev->info = &mcp4728_info; + indio_dev->channels = mcp4728_channels; + indio_dev->num_channels = MCP4728_N_CHANNELS; + indio_dev->modes = INDIO_DIRECT_MODE; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id mcp4728_id[] = { + { "mcp4728", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, mcp4728_id); + +static const struct of_device_id mcp4728_of_match[] = { + { .compatible = "microchip,mcp4728" }, + {} +}; +MODULE_DEVICE_TABLE(of, mcp4728_of_match); + +static struct i2c_driver mcp4728_driver = { + .driver = { + .name = "mcp4728", + .of_match_table = mcp4728_of_match, + .pm = pm_sleep_ptr(&mcp4728_pm_ops), + }, + .probe = mcp4728_probe, + .id_table = mcp4728_id, +}; +module_i2c_driver(mcp4728_driver); + +MODULE_AUTHOR("Andrea Collamati <andrea.collamati@gmail.com>"); +MODULE_DESCRIPTION("MCP4728 12-bit DAC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/filter/admv8818.c b/drivers/iio/filter/admv8818.c index fe8d46cb7f1d..848baa6e3bbf 100644 --- a/drivers/iio/filter/admv8818.c +++ b/drivers/iio/filter/admv8818.c @@ -78,6 +78,7 @@ enum { enum { ADMV8818_AUTO_MODE, ADMV8818_MANUAL_MODE, + ADMV8818_BYPASS_MODE, }; struct admv8818_state { @@ -114,7 +115,8 @@ static const struct regmap_config admv8818_regmap_config = { static const char * const admv8818_modes[] = { [0] = "auto", - [1] = "manual" + [1] = "manual", + [2] = "bypass" }; static int __admv8818_hpf_select(struct admv8818_state *st, u64 freq) @@ -394,6 +396,36 @@ static int admv8818_reg_access(struct iio_dev *indio_dev, return regmap_write(st->regmap, reg, write_val); } +static int admv8818_filter_bypass(struct admv8818_state *st) +{ + int ret; + + mutex_lock(&st->lock); + + ret = regmap_update_bits(st->regmap, ADMV8818_REG_WR0_SW, + ADMV8818_SW_IN_SET_WR0_MSK | + ADMV8818_SW_IN_WR0_MSK | + ADMV8818_SW_OUT_SET_WR0_MSK | + ADMV8818_SW_OUT_WR0_MSK, + FIELD_PREP(ADMV8818_SW_IN_SET_WR0_MSK, 1) | + FIELD_PREP(ADMV8818_SW_IN_WR0_MSK, 0) | + FIELD_PREP(ADMV8818_SW_OUT_SET_WR0_MSK, 1) | + FIELD_PREP(ADMV8818_SW_OUT_WR0_MSK, 0)); + if (ret) + goto exit; + + ret = regmap_update_bits(st->regmap, ADMV8818_REG_WR0_FILTER, + ADMV8818_HPF_WR0_MSK | + ADMV8818_LPF_WR0_MSK, + FIELD_PREP(ADMV8818_HPF_WR0_MSK, 0) | + FIELD_PREP(ADMV8818_LPF_WR0_MSK, 0)); + +exit: + mutex_unlock(&st->lock); + + return ret; +} + static int admv8818_get_mode(struct iio_dev *indio_dev, const struct iio_chan_spec *chan) { @@ -411,14 +443,22 @@ static int admv8818_set_mode(struct iio_dev *indio_dev, if (!st->clkin) { if (mode == ADMV8818_MANUAL_MODE) - return 0; + goto set_mode; + + if (mode == ADMV8818_BYPASS_MODE) { + ret = admv8818_filter_bypass(st); + if (ret) + return ret; + + goto set_mode; + } return -EINVAL; } switch (mode) { case ADMV8818_AUTO_MODE: - if (!st->filter_mode) + if (st->filter_mode == ADMV8818_AUTO_MODE) return 0; ret = clk_prepare_enable(st->clkin); @@ -434,20 +474,27 @@ static int admv8818_set_mode(struct iio_dev *indio_dev, break; case ADMV8818_MANUAL_MODE: - if (st->filter_mode) - return 0; + case ADMV8818_BYPASS_MODE: + if (st->filter_mode == ADMV8818_AUTO_MODE) { + clk_disable_unprepare(st->clkin); - clk_disable_unprepare(st->clkin); + ret = clk_notifier_unregister(st->clkin, &st->nb); + if (ret) + return ret; + } - ret = clk_notifier_unregister(st->clkin, &st->nb); - if (ret) - return ret; + if (mode == ADMV8818_BYPASS_MODE) { + ret = admv8818_filter_bypass(st); + if (ret) + return ret; + } break; default: return -EINVAL; } +set_mode: st->filter_mode = mode; return ret; diff --git a/drivers/iio/frequency/admv1013.c b/drivers/iio/frequency/admv1013.c index 8c8e0bbfc99f..6355c1f28423 100644 --- a/drivers/iio/frequency/admv1013.c +++ b/drivers/iio/frequency/admv1013.c @@ -382,6 +382,11 @@ static const struct iio_info admv1013_info = { .debugfs_reg_access = &admv1013_reg_access, }; +static const char * const admv1013_vcc_regs[] = { + "vcc-drv", "vcc2-drv", "vcc-vva", "vcc-amp1", "vcc-amp2", + "vcc-env", "vcc-bg", "vcc-bg2", "vcc-mixer", "vcc-quad" +}; + static int admv1013_freq_change(struct notifier_block *nb, unsigned long action, void *data) { struct admv1013_state *st = container_of(nb, struct admv1013_state, nb); @@ -557,6 +562,15 @@ static int admv1013_properties_parse(struct admv1013_state *st) return dev_err_probe(&spi->dev, PTR_ERR(st->reg), "failed to get the common-mode voltage\n"); + ret = devm_regulator_bulk_get_enable(&st->spi->dev, + ARRAY_SIZE(admv1013_vcc_regs), + admv1013_vcc_regs); + if (ret) { + dev_err_probe(&spi->dev, ret, + "Failed to request VCC regulators\n"); + return ret; + } + return 0; } diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index 3abffb01ba31..17275a53ca2c 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -115,8 +115,6 @@ enum { ADIS16475_SCAN_ACCEL_Y, ADIS16475_SCAN_ACCEL_Z, ADIS16475_SCAN_TEMP, - ADIS16475_SCAN_DIAG_S_FLAGS, - ADIS16475_SCAN_CRC_FAILURE, }; static bool low_rate_allow; @@ -728,6 +726,7 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .max_dec = 1999, .sync = adis16475_sync_mode, .num_sync = ARRAY_SIZE(adis16475_sync_mode), + .has_burst32 = true, .adis_data = ADIS16475_DATA(16477, &adis16475_timeouts), }, [ADIS16477_2] = { @@ -743,6 +742,7 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .max_dec = 1999, .sync = adis16475_sync_mode, .num_sync = ARRAY_SIZE(adis16475_sync_mode), + .has_burst32 = true, .adis_data = ADIS16475_DATA(16477, &adis16475_timeouts), }, [ADIS16477_3] = { @@ -758,6 +758,7 @@ static const struct adis16475_chip_info adis16475_chip_info[] = { .max_dec = 1999, .sync = adis16475_sync_mode, .num_sync = ARRAY_SIZE(adis16475_sync_mode), + .has_burst32 = true, .adis_data = ADIS16475_DATA(16477, &adis16475_timeouts), }, [ADIS16465_1] = { diff --git a/drivers/iio/imu/inv_icm42600/Kconfig b/drivers/iio/imu/inv_icm42600/Kconfig index 50cbcfcb6cf1..f56b0816cc4d 100644 --- a/drivers/iio/imu/inv_icm42600/Kconfig +++ b/drivers/iio/imu/inv_icm42600/Kconfig @@ -3,6 +3,7 @@ config INV_ICM42600 tristate select IIO_BUFFER + select IIO_INV_SENSORS_TIMESTAMP config INV_ICM42600_I2C tristate "InvenSense ICM-426xx I2C driver" diff --git a/drivers/iio/imu/inv_icm42600/Makefile b/drivers/iio/imu/inv_icm42600/Makefile index 291714d9aa54..0f49f6df3647 100644 --- a/drivers/iio/imu/inv_icm42600/Makefile +++ b/drivers/iio/imu/inv_icm42600/Makefile @@ -6,7 +6,6 @@ inv-icm42600-y += inv_icm42600_gyro.o inv-icm42600-y += inv_icm42600_accel.o inv-icm42600-y += inv_icm42600_temp.o inv-icm42600-y += inv_icm42600_buffer.o -inv-icm42600-y += inv_icm42600_timestamp.o obj-$(CONFIG_INV_ICM42600_I2C) += inv-icm42600-i2c.o inv-icm42600-i2c-y += inv_icm42600_i2c.o diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index c3f433ad3af6..b1e4fde27d25 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -10,14 +10,15 @@ #include <linux/regmap.h> #include <linux/delay.h> #include <linux/math64.h> -#include <linux/iio/iio.h> + #include <linux/iio/buffer.h> +#include <linux/iio/common/inv_sensors_timestamp.h> +#include <linux/iio/iio.h> #include <linux/iio/kfifo_buf.h> #include "inv_icm42600.h" #include "inv_icm42600_temp.h" #include "inv_icm42600_buffer.h" -#include "inv_icm42600_timestamp.h" #define INV_ICM42600_ACCEL_CHAN(_modifier, _index, _ext_info) \ { \ @@ -98,7 +99,7 @@ static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *scan_mask) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); - struct inv_icm42600_timestamp *ts = iio_priv(indio_dev); + struct inv_sensors_timestamp *ts = iio_priv(indio_dev); struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; unsigned int fifo_en = 0; unsigned int sleep_temp = 0; @@ -126,7 +127,7 @@ static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev, } /* update data FIFO write */ - inv_icm42600_timestamp_apply_odr(ts, 0, 0, 0); + inv_sensors_timestamp_apply_odr(ts, 0, 0, 0); ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en); if (ret) goto out_unlock; @@ -311,7 +312,7 @@ static int inv_icm42600_accel_write_odr(struct iio_dev *indio_dev, int val, int val2) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); - struct inv_icm42600_timestamp *ts = iio_priv(indio_dev); + struct inv_sensors_timestamp *ts = iio_priv(indio_dev); struct device *dev = regmap_get_device(st->map); unsigned int idx; struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; @@ -330,8 +331,8 @@ static int inv_icm42600_accel_write_odr(struct iio_dev *indio_dev, pm_runtime_get_sync(dev); mutex_lock(&st->lock); - ret = inv_icm42600_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr), - iio_buffer_enabled(indio_dev)); + ret = inv_sensors_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr), + iio_buffer_enabled(indio_dev)); if (ret) goto out_unlock; @@ -707,7 +708,8 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) { struct device *dev = regmap_get_device(st->map); const char *name; - struct inv_icm42600_timestamp *ts; + struct inv_sensors_timestamp_chip ts_chip; + struct inv_sensors_timestamp *ts; struct iio_dev *indio_dev; int ret; @@ -719,8 +721,15 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) if (!indio_dev) return ERR_PTR(-ENOMEM); + /* + * clock period is 32kHz (31250ns) + * jitter is +/- 2% (20 per mille) + */ + ts_chip.clock_period = 31250; + ts_chip.jitter = 20; + ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr); ts = iio_priv(indio_dev); - inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.accel.odr)); + inv_sensors_timestamp_init(ts, &ts_chip); iio_device_set_drvdata(indio_dev, st); indio_dev->name = name; @@ -745,7 +754,7 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); - struct inv_icm42600_timestamp *ts = iio_priv(indio_dev); + struct inv_sensors_timestamp *ts = iio_priv(indio_dev); ssize_t i, size; unsigned int no; const void *accel, *gyro, *timestamp; @@ -768,15 +777,15 @@ int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev) /* update odr */ if (odr & INV_ICM42600_SENSOR_ACCEL) - inv_icm42600_timestamp_apply_odr(ts, st->fifo.period, - st->fifo.nb.total, no); + inv_sensors_timestamp_apply_odr(ts, st->fifo.period, + st->fifo.nb.total, no); /* buffer is copied to userspace, zeroing it to avoid any data leak */ memset(&buffer, 0, sizeof(buffer)); memcpy(&buffer.accel, accel, sizeof(buffer.accel)); /* convert 8 bits FIFO temperature in high resolution format */ buffer.temp = temp ? (*temp * 64) : 0; - ts_val = inv_icm42600_timestamp_pop(ts); + ts_val = inv_sensors_timestamp_pop(ts); iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val); } diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c index 32d7f8364230..6ef1df9d60b7 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c @@ -9,11 +9,12 @@ #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/delay.h> -#include <linux/iio/iio.h> + #include <linux/iio/buffer.h> +#include <linux/iio/common/inv_sensors_timestamp.h> +#include <linux/iio/iio.h> #include "inv_icm42600.h" -#include "inv_icm42600_timestamp.h" #include "inv_icm42600_buffer.h" /* FIFO header: 1 byte */ @@ -275,12 +276,12 @@ static int inv_icm42600_buffer_preenable(struct iio_dev *indio_dev) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); struct device *dev = regmap_get_device(st->map); - struct inv_icm42600_timestamp *ts = iio_priv(indio_dev); + struct inv_sensors_timestamp *ts = iio_priv(indio_dev); pm_runtime_get_sync(dev); mutex_lock(&st->lock); - inv_icm42600_timestamp_reset(ts); + inv_sensors_timestamp_reset(ts); mutex_unlock(&st->lock); return 0; @@ -504,7 +505,7 @@ int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st, int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st) { - struct inv_icm42600_timestamp *ts; + struct inv_sensors_timestamp *ts; int ret; if (st->fifo.nb.total == 0) @@ -512,8 +513,8 @@ int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st) /* handle gyroscope timestamp and FIFO data parsing */ ts = iio_priv(st->indio_gyro); - inv_icm42600_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total, - st->fifo.nb.gyro, st->timestamp.gyro); + inv_sensors_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total, + st->fifo.nb.gyro, st->timestamp.gyro); if (st->fifo.nb.gyro > 0) { ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro); if (ret) @@ -522,8 +523,8 @@ int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st) /* handle accelerometer timestamp and FIFO data parsing */ ts = iio_priv(st->indio_accel); - inv_icm42600_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total, - st->fifo.nb.accel, st->timestamp.accel); + inv_sensors_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total, + st->fifo.nb.accel, st->timestamp.accel); if (st->fifo.nb.accel > 0) { ret = inv_icm42600_accel_parse_fifo(st->indio_accel); if (ret) @@ -536,7 +537,7 @@ int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st) int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st, unsigned int count) { - struct inv_icm42600_timestamp *ts; + struct inv_sensors_timestamp *ts; int64_t gyro_ts, accel_ts; int ret; @@ -552,9 +553,9 @@ int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st, if (st->fifo.nb.gyro > 0) { ts = iio_priv(st->indio_gyro); - inv_icm42600_timestamp_interrupt(ts, st->fifo.period, - st->fifo.nb.total, st->fifo.nb.gyro, - gyro_ts); + inv_sensors_timestamp_interrupt(ts, st->fifo.period, + st->fifo.nb.total, st->fifo.nb.gyro, + gyro_ts); ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro); if (ret) return ret; @@ -562,9 +563,9 @@ int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st, if (st->fifo.nb.accel > 0) { ts = iio_priv(st->indio_accel); - inv_icm42600_timestamp_interrupt(ts, st->fifo.period, - st->fifo.nb.total, st->fifo.nb.accel, - accel_ts); + inv_sensors_timestamp_interrupt(ts, st->fifo.period, + st->fifo.nb.total, st->fifo.nb.accel, + accel_ts); ret = inv_icm42600_accel_parse_fifo(st->indio_accel); if (ret) return ret; diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c index 7b3a2a0dc2cb..a5e81906e37e 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c @@ -15,11 +15,11 @@ #include <linux/pm_runtime.h> #include <linux/property.h> #include <linux/regmap.h> + #include <linux/iio/iio.h> #include "inv_icm42600.h" #include "inv_icm42600_buffer.h" -#include "inv_icm42600_timestamp.h" static const struct regmap_range_cfg inv_icm42600_regmap_ranges[] = { { @@ -516,6 +516,17 @@ static int inv_icm42600_irq_init(struct inv_icm42600_state *st, int irq, "inv_icm42600", st); } +static int inv_icm42600_timestamp_setup(struct inv_icm42600_state *st) +{ + unsigned int val; + + /* enable timestamp register */ + val = INV_ICM42600_TMST_CONFIG_TMST_TO_REGS_EN | + INV_ICM42600_TMST_CONFIG_TMST_EN; + return regmap_update_bits(st->map, INV_ICM42600_REG_TMST_CONFIG, + INV_ICM42600_TMST_CONFIG_MASK, val); +} + static int inv_icm42600_enable_regulator_vddio(struct inv_icm42600_state *st) { int ret; @@ -788,3 +799,4 @@ EXPORT_NS_GPL_DEV_PM_OPS(inv_icm42600_pm_ops, IIO_ICM42600) = { MODULE_AUTHOR("InvenSense, Inc."); MODULE_DESCRIPTION("InvenSense ICM-426xx device driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_INV_SENSORS_TIMESTAMP); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c index 9d94a8518e3c..3bf946e56e1d 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c @@ -10,14 +10,15 @@ #include <linux/regmap.h> #include <linux/delay.h> #include <linux/math64.h> -#include <linux/iio/iio.h> + #include <linux/iio/buffer.h> +#include <linux/iio/common/inv_sensors_timestamp.h> +#include <linux/iio/iio.h> #include <linux/iio/kfifo_buf.h> #include "inv_icm42600.h" #include "inv_icm42600_temp.h" #include "inv_icm42600_buffer.h" -#include "inv_icm42600_timestamp.h" #define INV_ICM42600_GYRO_CHAN(_modifier, _index, _ext_info) \ { \ @@ -98,7 +99,7 @@ static int inv_icm42600_gyro_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *scan_mask) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); - struct inv_icm42600_timestamp *ts = iio_priv(indio_dev); + struct inv_sensors_timestamp *ts = iio_priv(indio_dev); struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; unsigned int fifo_en = 0; unsigned int sleep_gyro = 0; @@ -126,7 +127,7 @@ static int inv_icm42600_gyro_update_scan_mode(struct iio_dev *indio_dev, } /* update data FIFO write */ - inv_icm42600_timestamp_apply_odr(ts, 0, 0, 0); + inv_sensors_timestamp_apply_odr(ts, 0, 0, 0); ret = inv_icm42600_buffer_set_fifo_en(st, fifo_en | st->fifo.en); if (ret) goto out_unlock; @@ -323,7 +324,7 @@ static int inv_icm42600_gyro_write_odr(struct iio_dev *indio_dev, int val, int val2) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); - struct inv_icm42600_timestamp *ts = iio_priv(indio_dev); + struct inv_sensors_timestamp *ts = iio_priv(indio_dev); struct device *dev = regmap_get_device(st->map); unsigned int idx; struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; @@ -342,8 +343,8 @@ static int inv_icm42600_gyro_write_odr(struct iio_dev *indio_dev, pm_runtime_get_sync(dev); mutex_lock(&st->lock); - ret = inv_icm42600_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr), - iio_buffer_enabled(indio_dev)); + ret = inv_sensors_timestamp_update_odr(ts, inv_icm42600_odr_to_period(conf.odr), + iio_buffer_enabled(indio_dev)); if (ret) goto out_unlock; @@ -718,7 +719,8 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st) { struct device *dev = regmap_get_device(st->map); const char *name; - struct inv_icm42600_timestamp *ts; + struct inv_sensors_timestamp_chip ts_chip; + struct inv_sensors_timestamp *ts; struct iio_dev *indio_dev; int ret; @@ -730,8 +732,15 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st) if (!indio_dev) return ERR_PTR(-ENOMEM); + /* + * clock period is 32kHz (31250ns) + * jitter is +/- 2% (20 per mille) + */ + ts_chip.clock_period = 31250; + ts_chip.jitter = 20; + ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr); ts = iio_priv(indio_dev); - inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.gyro.odr)); + inv_sensors_timestamp_init(ts, &ts_chip); iio_device_set_drvdata(indio_dev, st); indio_dev->name = name; @@ -757,7 +766,7 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st) int inv_icm42600_gyro_parse_fifo(struct iio_dev *indio_dev) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); - struct inv_icm42600_timestamp *ts = iio_priv(indio_dev); + struct inv_sensors_timestamp *ts = iio_priv(indio_dev); ssize_t i, size; unsigned int no; const void *accel, *gyro, *timestamp; @@ -780,15 +789,15 @@ int inv_icm42600_gyro_parse_fifo(struct iio_dev *indio_dev) /* update odr */ if (odr & INV_ICM42600_SENSOR_GYRO) - inv_icm42600_timestamp_apply_odr(ts, st->fifo.period, - st->fifo.nb.total, no); + inv_sensors_timestamp_apply_odr(ts, st->fifo.period, + st->fifo.nb.total, no); /* buffer is copied to userspace, zeroing it to avoid any data leak */ memset(&buffer, 0, sizeof(buffer)); memcpy(&buffer.gyro, gyro, sizeof(buffer.gyro)); /* convert 8 bits FIFO temperature in high resolution format */ buffer.temp = temp ? (*temp * 64) : 0; - ts_val = inv_icm42600_timestamp_pop(ts); + ts_val = inv_sensors_timestamp_pop(ts); iio_push_to_buffers_with_timestamp(indio_dev, &buffer, ts_val); } diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.h b/drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.h deleted file mode 100644 index 4e4f331d4fe4..000000000000 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_timestamp.h +++ /dev/null @@ -1,85 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (C) 2020 Invensense, Inc. - */ - -#ifndef INV_ICM42600_TIMESTAMP_H_ -#define INV_ICM42600_TIMESTAMP_H_ - -#include <linux/kernel.h> - -struct inv_icm42600_state; - -/** - * struct inv_icm42600_timestamp_interval - timestamps interval - * @lo: interval lower bound - * @up: interval upper bound - */ -struct inv_icm42600_timestamp_interval { - int64_t lo; - int64_t up; -}; - -/** - * struct inv_icm42600_timestamp_acc - accumulator for computing an estimation - * @val: current estimation of the value, the mean of all values - * @idx: current index of the next free place in values table - * @values: table of all measured values, use for computing the mean - */ -struct inv_icm42600_timestamp_acc { - uint32_t val; - size_t idx; - uint32_t values[32]; -}; - -/** - * struct inv_icm42600_timestamp - timestamp management states - * @it: interrupts interval timestamps - * @timestamp: store last timestamp for computing next data timestamp - * @mult: current internal period multiplier - * @new_mult: new set internal period multiplier (not yet effective) - * @period: measured current period of the sensor - * @chip_period: accumulator for computing internal chip period - */ -struct inv_icm42600_timestamp { - struct inv_icm42600_timestamp_interval it; - int64_t timestamp; - uint32_t mult; - uint32_t new_mult; - uint32_t period; - struct inv_icm42600_timestamp_acc chip_period; -}; - -void inv_icm42600_timestamp_init(struct inv_icm42600_timestamp *ts, - uint32_t period); - -int inv_icm42600_timestamp_setup(struct inv_icm42600_state *st); - -int inv_icm42600_timestamp_update_odr(struct inv_icm42600_timestamp *ts, - uint32_t period, bool fifo); - -void inv_icm42600_timestamp_interrupt(struct inv_icm42600_timestamp *ts, - uint32_t fifo_period, size_t fifo_nb, - size_t sensor_nb, int64_t timestamp); - -static inline int64_t -inv_icm42600_timestamp_pop(struct inv_icm42600_timestamp *ts) -{ - ts->timestamp += ts->period; - return ts->timestamp; -} - -void inv_icm42600_timestamp_apply_odr(struct inv_icm42600_timestamp *ts, - uint32_t fifo_period, size_t fifo_nb, - unsigned int fifo_no); - -static inline void -inv_icm42600_timestamp_reset(struct inv_icm42600_timestamp *ts) -{ - const struct inv_icm42600_timestamp_interval interval_init = {0LL, 0LL}; - - ts->it = interval_init; - ts->timestamp = 0; -} - -#endif diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig index 64dd73dcc4ba..5f62e4fd475d 100644 --- a/drivers/iio/imu/inv_mpu6050/Kconfig +++ b/drivers/iio/imu/inv_mpu6050/Kconfig @@ -7,6 +7,7 @@ config INV_MPU6050_IIO tristate select IIO_BUFFER select IIO_TRIGGERED_BUFFER + select IIO_INV_SENSORS_TIMESTAMP config INV_MPU6050_I2C tristate "Invensense MPU6050 devices (I2C)" diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 592a6e60b413..29f906c884bd 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -12,12 +12,15 @@ #include <linux/jiffies.h> #include <linux/irq.h> #include <linux/interrupt.h> -#include <linux/iio/iio.h> #include <linux/acpi.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include <linux/pm.h> #include <linux/pm_runtime.h> + +#include <linux/iio/common/inv_sensors_timestamp.h> +#include <linux/iio/iio.h> + #include "inv_mpu_iio.h" #include "inv_mpu_magn.h" @@ -521,6 +524,7 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev) int result; u8 d; struct inv_mpu6050_state *st = iio_priv(indio_dev); + struct inv_sensors_timestamp_chip timestamp; result = inv_mpu6050_set_gyro_fsr(st, st->chip_config.fsr); if (result) @@ -544,12 +548,12 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev) if (result) return result; - /* - * Internal chip period is 1ms (1kHz). - * Let's use at the beginning the theorical value before measuring - * with interrupt timestamps. - */ - st->chip_period = NSEC_PER_MSEC; + /* clock jitter is +/- 2% */ + timestamp.clock_period = NSEC_PER_SEC / INV_MPU6050_INTERNAL_FREQ_HZ; + timestamp.jitter = 20; + timestamp.init_period = + NSEC_PER_SEC / INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider); + inv_sensors_timestamp_init(&st->timestamp, ×tamp); /* magn chip init, noop if not present in the chip */ result = inv_mpu_magn_probe(st); @@ -936,6 +940,8 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int fifo_rate; + u32 fifo_period; + bool fifo_on; u8 d; int result; struct iio_dev *indio_dev = dev_to_iio_dev(dev); @@ -952,12 +958,21 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr, d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(fifo_rate); /* compute back the fifo rate to handle truncation cases */ fifo_rate = INV_MPU6050_DIVIDER_TO_FIFO_RATE(d); + fifo_period = NSEC_PER_SEC / fifo_rate; mutex_lock(&st->lock); if (d == st->chip_config.divider) { result = 0; goto fifo_rate_fail_unlock; } + + fifo_on = st->chip_config.accl_fifo_enable || + st->chip_config.gyro_fifo_enable || + st->chip_config.magn_fifo_enable; + result = inv_sensors_timestamp_update_odr(&st->timestamp, fifo_period, fifo_on); + if (result) + goto fifo_rate_fail_unlock; + result = pm_runtime_resume_and_get(pdev); if (result) goto fifo_rate_fail_unlock; @@ -1330,6 +1345,9 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st) st->reg = hw_info[st->chip_type].reg; memcpy(&st->chip_config, hw_info[st->chip_type].config, sizeof(st->chip_config)); + st->data = devm_kzalloc(regmap_get_device(st->map), st->hw->fifo_size, GFP_KERNEL); + if (st->data == NULL) + return -ENOMEM; /* check chip self-identification */ result = regmap_read(st->map, INV_MPU6050_REG_WHOAMI, ®val); @@ -1785,3 +1803,4 @@ EXPORT_NS_GPL_DEV_PM_OPS(inv_mpu_pmops, IIO_MPU6050) = { MODULE_AUTHOR("Invensense Corporation"); MODULE_DESCRIPTION("Invensense device MPU6050 driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_INV_SENSORS_TIMESTAMP); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index b4ab2c397d0f..ed5a96e78df0 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -9,15 +9,17 @@ #include <linux/i2c.h> #include <linux/i2c-mux.h> #include <linux/mutex.h> -#include <linux/iio/iio.h> -#include <linux/iio/buffer.h> +#include <linux/platform_data/invensense_mpu6050.h> #include <linux/regmap.h> -#include <linux/iio/sysfs.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/common/inv_sensors_timestamp.h> +#include <linux/iio/iio.h> #include <linux/iio/kfifo_buf.h> #include <linux/iio/trigger.h> #include <linux/iio/triggered_buffer.h> #include <linux/iio/trigger_consumer.h> -#include <linux/platform_data/invensense_mpu6050.h> +#include <linux/iio/sysfs.h> /** * struct inv_mpu6050_reg_map - Notable registers. @@ -170,16 +172,14 @@ struct inv_mpu6050_hw { * @map regmap pointer. * @irq interrupt number. * @irq_mask the int_pin_cfg mask to configure interrupt type. - * @chip_period: chip internal period estimation (~1kHz). - * @it_timestamp: timestamp from previous interrupt. - * @data_timestamp: timestamp for next data sample. + * @timestamp: timestamping module * @vdd_supply: VDD voltage regulator for the chip. * @vddio_supply I/O voltage regulator for the chip. * @magn_disabled: magnetometer disabled for backward compatibility reason. * @magn_raw_to_gauss: coefficient to convert mag raw value to Gauss. * @magn_orient: magnetometer sensor chip orientation if available. * @suspended_sensors: sensors mask of sensors turned off for suspend - * @data: dma safe buffer used for bulk reads. + * @data: read buffer used for bulk reads. */ struct inv_mpu6050_state { struct mutex lock; @@ -196,16 +196,14 @@ struct inv_mpu6050_state { int irq; u8 irq_mask; unsigned skip_samples; - s64 chip_period; - s64 it_timestamp; - s64 data_timestamp; + struct inv_sensors_timestamp timestamp; struct regulator *vdd_supply; struct regulator *vddio_supply; bool magn_disabled; s32 magn_raw_to_gauss[3]; struct iio_mount_matrix magn_orient; unsigned int suspended_sensors; - u8 data[INV_MPU6050_OUTPUT_DATA_SIZE] __aligned(IIO_DMA_MINALIGN); + u8 *data; }; /*register and associated bit definition*/ diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c index 45c37525c2f1..66d4ba088e70 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c @@ -13,81 +13,10 @@ #include <linux/interrupt.h> #include <linux/poll.h> #include <linux/math64.h> -#include "inv_mpu_iio.h" - -/** - * inv_mpu6050_update_period() - Update chip internal period estimation - * - * @st: driver state - * @timestamp: the interrupt timestamp - * @nb: number of data set in the fifo - * - * This function uses interrupt timestamps to estimate the chip period and - * to choose the data timestamp to come. - */ -static void inv_mpu6050_update_period(struct inv_mpu6050_state *st, - s64 timestamp, size_t nb) -{ - /* Period boundaries for accepting timestamp */ - const s64 period_min = - (NSEC_PER_MSEC * (100 - INV_MPU6050_TS_PERIOD_JITTER)) / 100; - const s64 period_max = - (NSEC_PER_MSEC * (100 + INV_MPU6050_TS_PERIOD_JITTER)) / 100; - const s32 divider = INV_MPU6050_FREQ_DIVIDER(st); - s64 delta, interval; - bool use_it_timestamp = false; - - if (st->it_timestamp == 0) { - /* not initialized, forced to use it_timestamp */ - use_it_timestamp = true; - } else if (nb == 1) { - /* - * Validate the use of it timestamp by checking if interrupt - * has been delayed. - * nb > 1 means interrupt was delayed for more than 1 sample, - * so it's obviously not good. - * Compute the chip period between 2 interrupts for validating. - */ - delta = div_s64(timestamp - st->it_timestamp, divider); - if (delta > period_min && delta < period_max) { - /* update chip period and use it timestamp */ - st->chip_period = (st->chip_period + delta) / 2; - use_it_timestamp = true; - } - } - if (use_it_timestamp) { - /* - * Manage case of multiple samples in the fifo (nb > 1): - * compute timestamp corresponding to the first sample using - * estimated chip period. - */ - interval = (nb - 1) * st->chip_period * divider; - st->data_timestamp = timestamp - interval; - } +#include <linux/iio/common/inv_sensors_timestamp.h> - /* save it timestamp */ - st->it_timestamp = timestamp; -} - -/** - * inv_mpu6050_get_timestamp() - Return the current data timestamp - * - * @st: driver state - * @return: current data timestamp - * - * This function returns the current data timestamp and prepares for next one. - */ -static s64 inv_mpu6050_get_timestamp(struct inv_mpu6050_state *st) -{ - s64 ts; - - /* return current data timestamp and increment */ - ts = st->data_timestamp; - st->data_timestamp += st->chip_period * INV_MPU6050_FREQ_DIVIDER(st); - - return ts; -} +#include "inv_mpu_iio.h" static int inv_reset_fifo(struct iio_dev *indio_dev) { @@ -121,7 +50,9 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) size_t bytes_per_datum; int result; u16 fifo_count; + u32 fifo_period; s64 timestamp; + u8 data[INV_MPU6050_OUTPUT_DATA_SIZE]; int int_status; size_t i, nb; @@ -175,21 +106,30 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) goto flush_fifo; } - /* compute and process all complete datum */ + /* compute and process only all complete datum */ nb = fifo_count / bytes_per_datum; - inv_mpu6050_update_period(st, pf->timestamp, nb); + fifo_count = nb * bytes_per_datum; + /* Each FIFO data contains all sensors, so same number for FIFO and sensor data */ + fifo_period = NSEC_PER_SEC / INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider); + inv_sensors_timestamp_interrupt(&st->timestamp, fifo_period, nb, nb, pf->timestamp); + inv_sensors_timestamp_apply_odr(&st->timestamp, fifo_period, nb, 0); + + /* clear internal data buffer for avoiding kernel data leak */ + memset(data, 0, sizeof(data)); + + /* read all data once and process every samples */ + result = regmap_noinc_read(st->map, st->reg->fifo_r_w, st->data, fifo_count); + if (result) + goto flush_fifo; for (i = 0; i < nb; ++i) { - result = regmap_noinc_read(st->map, st->reg->fifo_r_w, - st->data, bytes_per_datum); - if (result) - goto flush_fifo; /* skip first samples if needed */ if (st->skip_samples) { st->skip_samples--; continue; } - timestamp = inv_mpu6050_get_timestamp(st); - iio_push_to_buffers_with_timestamp(indio_dev, st->data, timestamp); + memcpy(data, &st->data[i * bytes_per_datum], bytes_per_datum); + timestamp = inv_sensors_timestamp_pop(&st->timestamp); + iio_push_to_buffers_with_timestamp(indio_dev, data, timestamp); } end_session: diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c index 882546897255..676704f9151f 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c @@ -4,6 +4,9 @@ */ #include <linux/pm_runtime.h> + +#include <linux/iio/common/inv_sensors_timestamp.h> + #include "inv_mpu_iio.h" static unsigned int inv_scan_query_mpu6050(struct iio_dev *indio_dev) @@ -106,7 +109,8 @@ int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable) int ret; if (enable) { - st->it_timestamp = 0; + /* reset timestamping */ + inv_sensors_timestamp_reset(&st->timestamp); /* reset FIFO */ d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_RST; ret = regmap_write(st->map, st->reg->user_ctrl, d); diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index adcba832e6fa..d752e9c0499b 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only -/* The industrial I/O core +/* + * The industrial I/O core * * Copyright (c) 2008 Jonathan Cameron * @@ -183,7 +184,9 @@ static const char * const iio_chan_info_postfix[] = { * @indio_dev: Device structure whose ID is being queried * * The IIO device ID is a unique index used for example for the naming - * of the character device /dev/iio\:device[ID] + * of the character device /dev/iio\:device[ID]. + * + * Returns: Unique ID for the device. */ int iio_device_id(struct iio_dev *indio_dev) { @@ -196,14 +199,16 @@ EXPORT_SYMBOL_GPL(iio_device_id); /** * iio_buffer_enabled() - helper function to test if the buffer is enabled * @indio_dev: IIO device structure for device + * + * Returns: True, if the buffer is enabled. */ bool iio_buffer_enabled(struct iio_dev *indio_dev) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); - return iio_dev_opaque->currentmode - & (INDIO_BUFFER_TRIGGERED | INDIO_BUFFER_HARDWARE | - INDIO_BUFFER_SOFTWARE); + return iio_dev_opaque->currentmode & + (INDIO_BUFFER_HARDWARE | INDIO_BUFFER_SOFTWARE | + INDIO_BUFFER_TRIGGERED); } EXPORT_SYMBOL_GPL(iio_buffer_enabled); @@ -225,6 +230,9 @@ EXPORT_SYMBOL_GPL(iio_get_debugfs_dentry); * iio_find_channel_from_si() - get channel from its scan index * @indio_dev: device * @si: scan index to match + * + * Returns: + * Constant pointer to iio_chan_spec, if scan index matches, NULL on failure. */ const struct iio_chan_spec *iio_find_channel_from_si(struct iio_dev *indio_dev, int si) @@ -249,7 +257,9 @@ EXPORT_SYMBOL(iio_read_const_attr); /** * iio_device_set_clock() - Set current timestamping clock for the device * @indio_dev: IIO device structure containing the device - * @clock_id: timestamping clock posix identifier to set. + * @clock_id: timestamping clock POSIX identifier to set. + * + * Returns: 0 on success, or a negative error code. */ int iio_device_set_clock(struct iio_dev *indio_dev, clockid_t clock_id) { @@ -275,6 +285,8 @@ EXPORT_SYMBOL(iio_device_set_clock); /** * iio_device_get_clock() - Retrieve current timestamping clock for the device * @indio_dev: IIO device structure containing the device + * + * Returns: Clock ID of the current timestamping clock for the device. */ clockid_t iio_device_get_clock(const struct iio_dev *indio_dev) { @@ -287,6 +299,8 @@ EXPORT_SYMBOL(iio_device_get_clock); /** * iio_get_time_ns() - utility function to get a time stamp for events etc * @indio_dev: device + * + * Returns: Timestamp of the event in nanoseconds. */ s64 iio_get_time_ns(const struct iio_dev *indio_dev) { @@ -372,8 +386,8 @@ static ssize_t iio_debugfs_read_reg(struct file *file, char __user *userbuf, } iio_dev_opaque->read_buf_len = snprintf(iio_dev_opaque->read_buf, - sizeof(iio_dev_opaque->read_buf), - "0x%X\n", val); + sizeof(iio_dev_opaque->read_buf), + "0x%X\n", val); return simple_read_from_buffer(userbuf, count, ppos, iio_dev_opaque->read_buf, @@ -389,7 +403,7 @@ static ssize_t iio_debugfs_write_reg(struct file *file, char buf[80]; int ret; - count = min_t(size_t, count, (sizeof(buf)-1)); + count = min(count, sizeof(buf) - 1); if (copy_from_user(buf, userbuf, count)) return -EFAULT; @@ -476,8 +490,7 @@ static ssize_t iio_read_channel_ext_info(struct device *dev, static ssize_t iio_write_channel_ext_info(struct device *dev, struct device_attribute *attr, - const char *buf, - size_t len) + const char *buf, size_t len) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); @@ -524,7 +537,7 @@ ssize_t iio_enum_read(struct iio_dev *indio_dev, i = e->get(indio_dev, chan); if (i < 0) return i; - else if (i >= e->num_items || !e->items[i]) + if (i >= e->num_items || !e->items[i]) return -EINVAL; return sysfs_emit(buf, "%s\n", e->items[i]); @@ -569,9 +582,9 @@ static int iio_setup_mount_idmatrix(const struct device *dev, ssize_t iio_show_mount_matrix(struct iio_dev *indio_dev, uintptr_t priv, const struct iio_chan_spec *chan, char *buf) { - const struct iio_mount_matrix *mtx = ((iio_get_mount_matrix_t *) - priv)(indio_dev, chan); + const struct iio_mount_matrix *mtx; + mtx = ((iio_get_mount_matrix_t *)priv)(indio_dev, chan); if (IS_ERR(mtx)) return PTR_ERR(mtx); @@ -594,7 +607,7 @@ EXPORT_SYMBOL_GPL(iio_show_mount_matrix); * If device is assigned no mounting matrix property, a default 3x3 identity * matrix will be filled in. * - * Return: 0 if success, or a negative error code on failure. + * Returns: 0 if success, or a negative error code on failure. */ int iio_read_mount_matrix(struct device *dev, struct iio_mount_matrix *matrix) { @@ -692,9 +705,9 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type, * @vals: Pointer to the values, exact meaning depends on the * type parameter. * - * Return: 0 by default, a negative number on failure or the - * total number of characters written for a type that belongs - * to the IIO_VAL_* constant. + * Returns: + * 0 by default, a negative number on failure or the total number of characters + * written for a type that belongs to the IIO_VAL_* constant. */ ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals) { @@ -847,8 +860,8 @@ static ssize_t iio_read_channel_info_avail(struct device *dev, * @fract: The fractional part of the number * @scale_db: True if this should parse as dB * - * Returns 0 on success, or a negative error code if the string could not be - * parsed. + * Returns: + * 0 on success, or a negative error code if the string could not be parsed. */ static int __iio_str_to_fixpoint(const char *str, int fract_mult, int *integer, int *fract, bool scale_db) @@ -917,8 +930,8 @@ static int __iio_str_to_fixpoint(const char *str, int fract_mult, * @integer: The integer part of the number * @fract: The fractional part of the number * - * Returns 0 on success, or a negative error code if the string could not be - * parsed. + * Returns: + * 0 on success, or a negative error code if the string could not be parsed. */ int iio_str_to_fixpoint(const char *str, int fract_mult, int *integer, int *fract) @@ -1009,14 +1022,12 @@ int __iio_device_attr_init(struct device_attribute *dev_attr, if (chan->modified && (shared_by == IIO_SEPARATE)) { if (chan->extend_name) full_postfix = kasprintf(GFP_KERNEL, "%s_%s_%s", - iio_modifier_names[chan - ->channel2], + iio_modifier_names[chan->channel2], chan->extend_name, postfix); else full_postfix = kasprintf(GFP_KERNEL, "%s_%s", - iio_modifier_names[chan - ->channel2], + iio_modifier_names[chan->channel2], postfix); } else { if (chan->extend_name == NULL || shared_by != IIO_SEPARATE) @@ -1217,7 +1228,7 @@ static int iio_device_add_info_mask_type(struct iio_dev *indio_dev, &iio_dev_opaque->channel_attr_list); if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE)) continue; - else if (ret < 0) + if (ret < 0) return ret; attrcount++; } @@ -1255,7 +1266,7 @@ static int iio_device_add_info_mask_type_avail(struct iio_dev *indio_dev, kfree(avail_postfix); if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE)) continue; - else if (ret < 0) + if (ret < 0) return ret; attrcount++; } @@ -1400,50 +1411,42 @@ static ssize_t label_show(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR_RO(label); +static const char * const clock_names[] = { + [CLOCK_REALTIME] = "realtime", + [CLOCK_MONOTONIC] = "monotonic", + [CLOCK_PROCESS_CPUTIME_ID] = "process_cputime_id", + [CLOCK_THREAD_CPUTIME_ID] = "thread_cputime_id", + [CLOCK_MONOTONIC_RAW] = "monotonic_raw", + [CLOCK_REALTIME_COARSE] = "realtime_coarse", + [CLOCK_MONOTONIC_COARSE] = "monotonic_coarse", + [CLOCK_BOOTTIME] = "boottime", + [CLOCK_REALTIME_ALARM] = "realtime_alarm", + [CLOCK_BOOTTIME_ALARM] = "boottime_alarm", + [CLOCK_SGI_CYCLE] = "sgi_cycle", + [CLOCK_TAI] = "tai", +}; + static ssize_t current_timestamp_clock_show(struct device *dev, struct device_attribute *attr, char *buf) { const struct iio_dev *indio_dev = dev_to_iio_dev(dev); const clockid_t clk = iio_device_get_clock(indio_dev); - const char *name; - ssize_t sz; switch (clk) { case CLOCK_REALTIME: - name = "realtime\n"; - sz = sizeof("realtime\n"); - break; case CLOCK_MONOTONIC: - name = "monotonic\n"; - sz = sizeof("monotonic\n"); - break; case CLOCK_MONOTONIC_RAW: - name = "monotonic_raw\n"; - sz = sizeof("monotonic_raw\n"); - break; case CLOCK_REALTIME_COARSE: - name = "realtime_coarse\n"; - sz = sizeof("realtime_coarse\n"); - break; case CLOCK_MONOTONIC_COARSE: - name = "monotonic_coarse\n"; - sz = sizeof("monotonic_coarse\n"); - break; case CLOCK_BOOTTIME: - name = "boottime\n"; - sz = sizeof("boottime\n"); - break; case CLOCK_TAI: - name = "tai\n"; - sz = sizeof("tai\n"); break; default: BUG(); } - memcpy(buf, name, sz); - return sz; + return sysfs_emit(buf, "%s\n", clock_names[clk]); } static ssize_t current_timestamp_clock_store(struct device *dev, @@ -1453,22 +1456,23 @@ static ssize_t current_timestamp_clock_store(struct device *dev, clockid_t clk; int ret; - if (sysfs_streq(buf, "realtime")) - clk = CLOCK_REALTIME; - else if (sysfs_streq(buf, "monotonic")) - clk = CLOCK_MONOTONIC; - else if (sysfs_streq(buf, "monotonic_raw")) - clk = CLOCK_MONOTONIC_RAW; - else if (sysfs_streq(buf, "realtime_coarse")) - clk = CLOCK_REALTIME_COARSE; - else if (sysfs_streq(buf, "monotonic_coarse")) - clk = CLOCK_MONOTONIC_COARSE; - else if (sysfs_streq(buf, "boottime")) - clk = CLOCK_BOOTTIME; - else if (sysfs_streq(buf, "tai")) - clk = CLOCK_TAI; - else + ret = sysfs_match_string(clock_names, buf); + if (ret < 0) + return ret; + clk = ret; + + switch (clk) { + case CLOCK_REALTIME: + case CLOCK_MONOTONIC: + case CLOCK_MONOTONIC_RAW: + case CLOCK_REALTIME_COARSE: + case CLOCK_MONOTONIC_COARSE: + case CLOCK_BOOTTIME: + case CLOCK_TAI: + break; + default: return -EINVAL; + } ret = iio_device_set_clock(dev_to_iio_dev(dev), clk); if (ret) @@ -1484,7 +1488,7 @@ int iio_device_register_sysfs_group(struct iio_dev *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); + new = krealloc_array(old, cnt + 2, sizeof(*new), GFP_KERNEL); if (!new) return -ENOMEM; @@ -1621,7 +1625,10 @@ const struct device_type iio_device_type = { * iio_device_alloc() - allocate an iio_dev from a driver * @parent: Parent device. * @sizeof_priv: Space to allocate for private structure. - **/ + * + * Returns: + * Pointer to allocated iio_dev on success, NULL on failure. + */ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv) { struct iio_dev_opaque *iio_dev_opaque; @@ -1677,7 +1684,7 @@ EXPORT_SYMBOL(iio_device_alloc); /** * iio_device_free() - free an iio_dev from a driver * @dev: the iio_dev associated with the device - **/ + */ void iio_device_free(struct iio_dev *dev) { if (dev) @@ -1698,7 +1705,7 @@ static void devm_iio_device_release(void *iio_dev) * Managed iio_device_alloc. iio_dev allocated with this function is * automatically freed on driver detach. * - * RETURNS: + * Returns: * Pointer to allocated iio_dev on success, NULL on failure. */ struct iio_dev *devm_iio_device_alloc(struct device *parent, int sizeof_priv) @@ -1725,8 +1732,8 @@ EXPORT_SYMBOL_GPL(devm_iio_device_alloc); * @filp: File structure for iio device used to keep and later access * private data * - * Return: 0 on success or -EBUSY if the device is already opened - **/ + * Returns: 0 on success or -EBUSY if the device is already opened + */ static int iio_chrdev_open(struct inode *inode, struct file *filp) { struct iio_dev_opaque *iio_dev_opaque = @@ -1759,7 +1766,7 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp) * @inode: Inode structure pointer for the char device * @filp: File structure pointer for the char device * - * Return: 0 for successful release + * Returns: 0 for successful release. */ static int iio_chrdev_release(struct inode *inode, struct file *filp) { @@ -1798,7 +1805,7 @@ static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) mutex_lock(&iio_dev_opaque->info_exist_lock); - /** + /* * The NULL check here is required to prevent crashing when a device * is being removed while userspace would still have open file handles * to try to access this device. @@ -1976,7 +1983,7 @@ EXPORT_SYMBOL(__iio_device_register); /** * iio_device_unregister() - unregister a device from the IIO subsystem * @indio_dev: Device structure representing the device. - **/ + */ void iio_device_unregister(struct iio_dev *indio_dev) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); @@ -2027,7 +2034,7 @@ EXPORT_SYMBOL_GPL(__devm_iio_device_register); * * Use with iio_device_release_direct_mode() * - * Returns: 0 on success, -EBUSY on failure + * Returns: 0 on success, -EBUSY on failure. */ int iio_device_claim_direct_mode(struct iio_dev *indio_dev) { diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index f77ce49d4c36..19f7a91157ee 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -252,6 +252,8 @@ static const char * const iio_ev_info_text[] = { [IIO_EV_INFO_TIMEOUT] = "timeout", [IIO_EV_INFO_RESET_TIMEOUT] = "reset_timeout", [IIO_EV_INFO_TAP2_MIN_DELAY] = "tap2_min_delay", + [IIO_EV_INFO_RUNNING_PERIOD] = "runningperiod", + [IIO_EV_INFO_RUNNING_COUNT] = "runningcount", }; static enum iio_event_direction iio_ev_attr_dir(struct iio_dev_attr *attr) diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index f207e36b12cc..18f83158f637 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -313,7 +313,7 @@ int iio_trigger_attach_poll_func(struct iio_trigger *trig, /* Enable trigger in driver */ if (trig->ops && trig->ops->set_trigger_state && notinuse) { ret = trig->ops->set_trigger_state(trig, true); - if (ret < 0) + if (ret) goto out_free_irq; } diff --git a/drivers/iio/light/cm3605.c b/drivers/iio/light/cm3605.c index 0b30db77f78b..e7f0b81b7f5a 100644 --- a/drivers/iio/light/cm3605.c +++ b/drivers/iio/light/cm3605.c @@ -227,7 +227,7 @@ static int cm3605_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - ret = dev_err_probe(dev, irq, "failed to get irq\n"); + ret = irq; goto out_disable_aset; } diff --git a/drivers/iio/light/rohm-bu27008.c b/drivers/iio/light/rohm-bu27008.c index b50bf8973d9a..6a6d77805091 100644 --- a/drivers/iio/light/rohm-bu27008.c +++ b/drivers/iio/light/rohm-bu27008.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * BU27008 ROHM Colour Sensor + * ROHM Colour Sensor driver for + * - BU27008 RGBC sensor + * - BU27010 RGBC + Flickering sensor * * Copyright (c) 2023, ROHM Semiconductor. */ @@ -22,6 +24,25 @@ #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> +/* + * A word about register address and mask definitions. + * + * At a quick glance to the data-sheet register tables, the BU27010 has all the + * registers that the BU27008 has. On top of that the BU27010 adds couple of new + * ones. + * + * So, all definitions BU27008_REG_* are there also for BU27010 but none of the + * BU27010_REG_* are present on BU27008. This makes sense as BU27010 just adds + * some features (Flicker FIFO, more power control) on top of the BU27008. + * + * Unfortunately, some of the wheel has been re-invented. Even though the names + * of the registers have stayed the same, pretty much all of the functionality + * provided by the registers has changed place. Contents of all MODE_CONTROL + * registers on BU27008 and BU27010 are different. + * + * Chip-specific mapping from register addresses/bits to functionality is done + * in bu27_chip_data structures. + */ #define BU27008_REG_SYSTEM_CONTROL 0x40 #define BU27008_MASK_SW_RESET BIT(7) #define BU27008_MASK_PART_ID GENMASK(5, 0) @@ -52,6 +73,56 @@ #define BU27008_REG_MANUFACTURER_ID 0x92 #define BU27008_REG_MAX BU27008_REG_MANUFACTURER_ID +/* BU27010 specific definitions */ + +#define BU27010_MASK_SW_RESET BIT(7) +#define BU27010_ID 0x1b +#define BU27010_REG_POWER 0x3e +#define BU27010_MASK_POWER BIT(0) + +#define BU27010_REG_RESET 0x3f +#define BU27010_MASK_RESET BIT(0) +#define BU27010_RESET_RELEASE BU27010_MASK_RESET + +#define BU27010_MASK_MEAS_EN BIT(1) + +#define BU27010_MASK_CHAN_SEL GENMASK(7, 6) +#define BU27010_MASK_MEAS_MODE GENMASK(5, 4) +#define BU27010_MASK_RGBC_GAIN GENMASK(3, 0) + +#define BU27010_MASK_DATA3_GAIN GENMASK(7, 6) +#define BU27010_MASK_DATA2_GAIN GENMASK(5, 4) +#define BU27010_MASK_DATA1_GAIN GENMASK(3, 2) +#define BU27010_MASK_DATA0_GAIN GENMASK(1, 0) + +#define BU27010_MASK_FLC_MODE BIT(7) +#define BU27010_MASK_FLC_GAIN GENMASK(4, 0) + +#define BU27010_REG_MODE_CONTROL4 0x44 +/* If flicker is ever to be supported the IRQ must be handled as a field */ +#define BU27010_IRQ_DIS_ALL GENMASK(1, 0) +#define BU27010_DRDY_EN BIT(0) +#define BU27010_MASK_INT_SEL GENMASK(1, 0) + +#define BU27010_REG_MODE_CONTROL5 0x45 +#define BU27010_MASK_RGB_VALID BIT(7) +#define BU27010_MASK_FLC_VALID BIT(6) +#define BU27010_MASK_WAIT_EN BIT(3) +#define BU27010_MASK_FIFO_EN BIT(2) +#define BU27010_MASK_RGB_EN BIT(1) +#define BU27010_MASK_FLC_EN BIT(0) + +#define BU27010_REG_DATA_FLICKER_LO 0x56 +#define BU27010_MASK_DATA_FLICKER_HI GENMASK(2, 0) +#define BU27010_REG_FLICKER_COUNT 0x5a +#define BU27010_REG_FIFO_LEVEL_LO 0x5b +#define BU27010_MASK_FIFO_LEVEL_HI BIT(0) +#define BU27010_REG_FIFO_DATA_LO 0x5d +#define BU27010_REG_FIFO_DATA_HI 0x5e +#define BU27010_MASK_FIFO_DATA_HI GENMASK(2, 0) +#define BU27010_REG_MANUFACTURER_ID 0x92 +#define BU27010_REG_MAX BU27010_REG_MANUFACTURER_ID + /** * enum bu27008_chan_type - BU27008 channel types * @BU27008_RED: Red channel. Always via data0. @@ -117,6 +188,17 @@ static const unsigned long bu27008_scan_masks[] = { */ #define BU27008_SCALE_1X 16 +/* + * On BU27010 available scales with gain 1x - 4096x, + * timings 55, 100, 200, 400 mS. Time impacts to gain: 1x, 2x, 4x, 8x. + * + * => Max total gain is HWGAIN * gain by integration time (8 * 4096) + * + * Using NANO precision for scale we must use scale 64x corresponding gain 1x + * to avoid precision loss. + */ +#define BU27010_SCALE_1X 64 + /* See the data sheet for the "Gain Setting" table */ #define BU27008_GSEL_1X 0x00 #define BU27008_GSEL_4X 0x08 @@ -152,10 +234,44 @@ static const struct iio_gain_sel_pair bu27008_gains_ir[] = { GAIN_SCALE_GAIN(1024, BU27008_GSEL_1024X), }; +#define BU27010_GSEL_1X 0x00 /* 000000 */ +#define BU27010_GSEL_4X 0x08 /* 001000 */ +#define BU27010_GSEL_16X 0x09 /* 001001 */ +#define BU27010_GSEL_64X 0x0e /* 001110 */ +#define BU27010_GSEL_256X 0x1e /* 011110 */ +#define BU27010_GSEL_1024X 0x2e /* 101110 */ +#define BU27010_GSEL_4096X 0x3f /* 111111 */ + +static const struct iio_gain_sel_pair bu27010_gains[] = { + GAIN_SCALE_GAIN(1, BU27010_GSEL_1X), + GAIN_SCALE_GAIN(4, BU27010_GSEL_4X), + GAIN_SCALE_GAIN(16, BU27010_GSEL_16X), + GAIN_SCALE_GAIN(64, BU27010_GSEL_64X), + GAIN_SCALE_GAIN(256, BU27010_GSEL_256X), + GAIN_SCALE_GAIN(1024, BU27010_GSEL_1024X), + GAIN_SCALE_GAIN(4096, BU27010_GSEL_4096X), +}; + +static const struct iio_gain_sel_pair bu27010_gains_ir[] = { + GAIN_SCALE_GAIN(2, BU27010_GSEL_1X), + GAIN_SCALE_GAIN(4, BU27010_GSEL_4X), + GAIN_SCALE_GAIN(16, BU27010_GSEL_16X), + GAIN_SCALE_GAIN(64, BU27010_GSEL_64X), + GAIN_SCALE_GAIN(256, BU27010_GSEL_256X), + GAIN_SCALE_GAIN(1024, BU27010_GSEL_1024X), + GAIN_SCALE_GAIN(4096, BU27010_GSEL_4096X), +}; + #define BU27008_MEAS_MODE_100MS 0x00 #define BU27008_MEAS_MODE_55MS 0x01 #define BU27008_MEAS_MODE_200MS 0x02 #define BU27008_MEAS_MODE_400MS 0x04 + +#define BU27010_MEAS_MODE_100MS 0x00 +#define BU27010_MEAS_MODE_55MS 0x03 +#define BU27010_MEAS_MODE_200MS 0x01 +#define BU27010_MEAS_MODE_400MS 0x02 + #define BU27008_MEAS_TIME_MAX_MS 400 static const struct iio_itime_sel_mul bu27008_itimes[] = { @@ -165,6 +281,13 @@ static const struct iio_itime_sel_mul bu27008_itimes[] = { GAIN_SCALE_ITIME_US(55000, BU27008_MEAS_MODE_55MS, 1), }; +static const struct iio_itime_sel_mul bu27010_itimes[] = { + GAIN_SCALE_ITIME_US(400000, BU27010_MEAS_MODE_400MS, 8), + GAIN_SCALE_ITIME_US(200000, BU27010_MEAS_MODE_200MS, 4), + GAIN_SCALE_ITIME_US(100000, BU27010_MEAS_MODE_100MS, 2), + GAIN_SCALE_ITIME_US(55000, BU27010_MEAS_MODE_55MS, 1), +}; + /* * All the RGBC channels share the same gain. * IR gain can be fine-tuned from the gain set for the RGBC by 2 bit, but this @@ -211,7 +334,35 @@ static const struct iio_chan_spec bu27008_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(BU27008_NUM_CHANS), }; +struct bu27008_data; + +struct bu27_chip_data { + const char *name; + int (*chip_init)(struct bu27008_data *data); + int (*get_gain_sel)(struct bu27008_data *data, int *sel); + int (*write_gain_sel)(struct bu27008_data *data, int sel); + const struct regmap_config *regmap_cfg; + const struct iio_gain_sel_pair *gains; + const struct iio_gain_sel_pair *gains_ir; + const struct iio_itime_sel_mul *itimes; + int num_gains; + int num_gains_ir; + int num_itimes; + int scale1x; + + int drdy_en_reg; + int drdy_en_mask; + int meas_en_reg; + int meas_en_mask; + int valid_reg; + int chan_sel_reg; + int chan_sel_mask; + int int_time_mask; + u8 part_id; +}; + struct bu27008_data { + const struct bu27_chip_data *cd; struct regmap *regmap; struct iio_trigger *trig; struct device *dev; @@ -240,11 +391,29 @@ static const struct regmap_range bu27008_volatile_ranges[] = { }, }; +static const struct regmap_range bu27010_volatile_ranges[] = { + { + .range_min = BU27010_REG_RESET, /* RSTB */ + .range_max = BU27008_REG_SYSTEM_CONTROL, /* RESET */ + }, { + .range_min = BU27010_REG_MODE_CONTROL5, /* VALID bits */ + .range_max = BU27010_REG_MODE_CONTROL5, + }, { + .range_min = BU27008_REG_DATA0_LO, + .range_max = BU27010_REG_FIFO_DATA_HI, + }, +}; + static const struct regmap_access_table bu27008_volatile_regs = { .yes_ranges = &bu27008_volatile_ranges[0], .n_yes_ranges = ARRAY_SIZE(bu27008_volatile_ranges), }; +static const struct regmap_access_table bu27010_volatile_regs = { + .yes_ranges = &bu27010_volatile_ranges[0], + .n_yes_ranges = ARRAY_SIZE(bu27010_volatile_ranges), +}; + static const struct regmap_range bu27008_read_only_ranges[] = { { .range_min = BU27008_REG_DATA0_LO, @@ -255,11 +424,26 @@ static const struct regmap_range bu27008_read_only_ranges[] = { }, }; +static const struct regmap_range bu27010_read_only_ranges[] = { + { + .range_min = BU27008_REG_DATA0_LO, + .range_max = BU27010_REG_FIFO_DATA_HI, + }, { + .range_min = BU27010_REG_MANUFACTURER_ID, + .range_max = BU27010_REG_MANUFACTURER_ID, + } +}; + static const struct regmap_access_table bu27008_ro_regs = { .no_ranges = &bu27008_read_only_ranges[0], .n_no_ranges = ARRAY_SIZE(bu27008_read_only_ranges), }; +static const struct regmap_access_table bu27010_ro_regs = { + .no_ranges = &bu27010_read_only_ranges[0], + .n_no_ranges = ARRAY_SIZE(bu27010_read_only_ranges), +}; + static const struct regmap_config bu27008_regmap = { .reg_bits = 8, .val_bits = 8, @@ -282,50 +466,16 @@ static const struct regmap_config bu27008_regmap = { .disable_locking = true, }; -#define BU27008_MAX_VALID_RESULT_WAIT_US 50000 -#define BU27008_VALID_RESULT_WAIT_QUANTA_US 1000 - -static int bu27008_chan_read_data(struct bu27008_data *data, int reg, int *val) -{ - int ret, valid; - __le16 tmp; - - ret = regmap_read_poll_timeout(data->regmap, BU27008_REG_MODE_CONTROL3, - valid, (valid & BU27008_MASK_VALID), - BU27008_VALID_RESULT_WAIT_QUANTA_US, - BU27008_MAX_VALID_RESULT_WAIT_US); - if (ret) - return ret; - - ret = regmap_bulk_read(data->regmap, reg, &tmp, sizeof(tmp)); - if (ret) - dev_err(data->dev, "Reading channel data failed\n"); - - *val = le16_to_cpu(tmp); - - return ret; -} - -static int bu27008_get_gain(struct bu27008_data *data, struct iio_gts *gts, int *gain) -{ - int ret, sel; - - ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, &sel); - if (ret) - return ret; - - sel = FIELD_GET(BU27008_MASK_RGBC_GAIN, sel); +static const struct regmap_config bu27010_regmap = { + .reg_bits = 8, + .val_bits = 8, - ret = iio_gts_find_gain_by_sel(gts, sel); - if (ret < 0) { - dev_err(data->dev, "unknown gain value 0x%x\n", sel); - return ret; - } - - *gain = ret; - - return 0; -} + .max_register = BU27010_REG_MAX, + .cache_type = REGCACHE_RBTREE, + .volatile_table = &bu27010_volatile_regs, + .wr_table = &bu27010_ro_regs, + .disable_locking = true, +}; static int bu27008_write_gain_sel(struct bu27008_data *data, int sel) { @@ -368,6 +518,264 @@ static int bu27008_write_gain_sel(struct bu27008_data *data, int sel) BU27008_MASK_RGBC_GAIN, regval); } +static int bu27010_write_gain_sel(struct bu27008_data *data, int sel) +{ + unsigned int regval; + int ret, chan_selector; + + /* + * Gain 'selector' is composed of two registers. Selector is 6bit value, + * 4 high bits being the RGBC gain fieild in MODE_CONTROL1 register and + * two low bits being the channel specific gain in MODE_CONTROL2. + * + * Let's take the 4 high bits of whole 6 bit selector, and prepare + * the MODE_CONTROL1 value (RGBC gain part). + */ + regval = FIELD_PREP(BU27010_MASK_RGBC_GAIN, (sel >> 2)); + + ret = regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1, + BU27010_MASK_RGBC_GAIN, regval); + if (ret) + return ret; + + /* + * Two low two bits of the selector must be written for all 4 + * channels in the MODE_CONTROL2 register. Copy these two bits for + * all channels. + */ + chan_selector = sel & GENMASK(1, 0); + + regval = FIELD_PREP(BU27010_MASK_DATA0_GAIN, chan_selector); + regval |= FIELD_PREP(BU27010_MASK_DATA1_GAIN, chan_selector); + regval |= FIELD_PREP(BU27010_MASK_DATA2_GAIN, chan_selector); + regval |= FIELD_PREP(BU27010_MASK_DATA3_GAIN, chan_selector); + + return regmap_write(data->regmap, BU27008_REG_MODE_CONTROL2, regval); +} + +static int bu27008_get_gain_sel(struct bu27008_data *data, int *sel) +{ + int ret; + + /* + * If we always "lock" the gain selectors for all channels to prevent + * unsupported configs, then it does not matter which channel is used + * we can just return selector from any of them. + * + * This, however is not true if we decide to support only 4X and 16X + * and then individual gains for channels. Currently this is not the + * case. + * + * If we some day decide to support individual gains, then we need to + * have channel information here. + */ + + ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, sel); + if (ret) + return ret; + + *sel = FIELD_GET(BU27008_MASK_RGBC_GAIN, *sel); + + return 0; +} + +static int bu27010_get_gain_sel(struct bu27008_data *data, int *sel) +{ + int ret, tmp; + + /* + * We always "lock" the gain selectors for all channels to prevent + * unsupported configs. It does not matter which channel is used + * we can just return selector from any of them. + * + * Read the channel0 gain. + */ + ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL2, sel); + if (ret) + return ret; + + *sel = FIELD_GET(BU27010_MASK_DATA0_GAIN, *sel); + + /* Read the shared gain */ + ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL1, &tmp); + if (ret) + return ret; + + /* + * The gain selector is made as a combination of common RGBC gain and + * the channel specific gain. The channel specific gain forms the low + * bits of selector and RGBC gain is appended right after it. + * + * Compose the selector from channel0 gain and shared RGBC gain. + */ + *sel |= FIELD_GET(BU27010_MASK_RGBC_GAIN, tmp) << fls(BU27010_MASK_DATA0_GAIN); + + return ret; +} + +static int bu27008_chip_init(struct bu27008_data *data) +{ + int ret; + + ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL, + BU27008_MASK_SW_RESET, BU27008_MASK_SW_RESET); + if (ret) + return dev_err_probe(data->dev, ret, "Sensor reset failed\n"); + + /* + * The data-sheet does not tell how long performing the IC reset takes. + * However, the data-sheet says the minimum time it takes the IC to be + * able to take inputs after power is applied, is 100 uS. I'd assume + * > 1 mS is enough. + */ + msleep(1); + + ret = regmap_reinit_cache(data->regmap, data->cd->regmap_cfg); + if (ret) + dev_err(data->dev, "Failed to reinit reg cache\n"); + + return ret; +} + +static int bu27010_chip_init(struct bu27008_data *data) +{ + int ret; + + ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL, + BU27010_MASK_SW_RESET, BU27010_MASK_SW_RESET); + if (ret) + return dev_err_probe(data->dev, ret, "Sensor reset failed\n"); + + msleep(1); + + /* Power ON*/ + ret = regmap_write_bits(data->regmap, BU27010_REG_POWER, + BU27010_MASK_POWER, BU27010_MASK_POWER); + if (ret) + return dev_err_probe(data->dev, ret, "Sensor power-on failed\n"); + + msleep(1); + + /* Release blocks from reset */ + ret = regmap_write_bits(data->regmap, BU27010_REG_RESET, + BU27010_MASK_RESET, BU27010_RESET_RELEASE); + if (ret) + return dev_err_probe(data->dev, ret, "Sensor powering failed\n"); + + msleep(1); + + /* + * The IRQ enabling on BU27010 is done in a peculiar way. The IRQ + * enabling is not a bit mask where individual IRQs could be enabled but + * a field which values are: + * 00 => IRQs disabled + * 01 => Data-ready (RGBC/IR) + * 10 => Data-ready (flicker) + * 11 => Flicker FIFO + * + * So, only one IRQ can be enabled at a time and enabling for example + * flicker FIFO would automagically disable data-ready IRQ. + * + * Currently the driver does not support the flicker. Hence, we can + * just treat the RGBC data-ready as single bit which can be enabled / + * disabled. This works for as long as the second bit in the field + * stays zero. Here we ensure it gets zeroed. + */ + return regmap_clear_bits(data->regmap, BU27010_REG_MODE_CONTROL4, + BU27010_IRQ_DIS_ALL); +} + +static const struct bu27_chip_data bu27010_chip = { + .name = "bu27010", + .chip_init = bu27010_chip_init, + .get_gain_sel = bu27010_get_gain_sel, + .write_gain_sel = bu27010_write_gain_sel, + .regmap_cfg = &bu27010_regmap, + .gains = &bu27010_gains[0], + .gains_ir = &bu27010_gains_ir[0], + .itimes = &bu27010_itimes[0], + .num_gains = ARRAY_SIZE(bu27010_gains), + .num_gains_ir = ARRAY_SIZE(bu27010_gains_ir), + .num_itimes = ARRAY_SIZE(bu27010_itimes), + .scale1x = BU27010_SCALE_1X, + .drdy_en_reg = BU27010_REG_MODE_CONTROL4, + .drdy_en_mask = BU27010_DRDY_EN, + .meas_en_reg = BU27010_REG_MODE_CONTROL5, + .meas_en_mask = BU27010_MASK_MEAS_EN, + .valid_reg = BU27010_REG_MODE_CONTROL5, + .chan_sel_reg = BU27008_REG_MODE_CONTROL1, + .chan_sel_mask = BU27010_MASK_CHAN_SEL, + .int_time_mask = BU27010_MASK_MEAS_MODE, + .part_id = BU27010_ID, +}; + +static const struct bu27_chip_data bu27008_chip = { + .name = "bu27008", + .chip_init = bu27008_chip_init, + .get_gain_sel = bu27008_get_gain_sel, + .write_gain_sel = bu27008_write_gain_sel, + .regmap_cfg = &bu27008_regmap, + .gains = &bu27008_gains[0], + .gains_ir = &bu27008_gains_ir[0], + .itimes = &bu27008_itimes[0], + .num_gains = ARRAY_SIZE(bu27008_gains), + .num_gains_ir = ARRAY_SIZE(bu27008_gains_ir), + .num_itimes = ARRAY_SIZE(bu27008_itimes), + .scale1x = BU27008_SCALE_1X, + .drdy_en_reg = BU27008_REG_MODE_CONTROL3, + .drdy_en_mask = BU27008_MASK_INT_EN, + .valid_reg = BU27008_REG_MODE_CONTROL3, + .meas_en_reg = BU27008_REG_MODE_CONTROL3, + .meas_en_mask = BU27008_MASK_MEAS_EN, + .chan_sel_reg = BU27008_REG_MODE_CONTROL3, + .chan_sel_mask = BU27008_MASK_CHAN_SEL, + .int_time_mask = BU27008_MASK_MEAS_MODE, + .part_id = BU27008_ID, +}; + +#define BU27008_MAX_VALID_RESULT_WAIT_US 50000 +#define BU27008_VALID_RESULT_WAIT_QUANTA_US 1000 + +static int bu27008_chan_read_data(struct bu27008_data *data, int reg, int *val) +{ + int ret, valid; + __le16 tmp; + + ret = regmap_read_poll_timeout(data->regmap, data->cd->valid_reg, + valid, (valid & BU27008_MASK_VALID), + BU27008_VALID_RESULT_WAIT_QUANTA_US, + BU27008_MAX_VALID_RESULT_WAIT_US); + if (ret) + return ret; + + ret = regmap_bulk_read(data->regmap, reg, &tmp, sizeof(tmp)); + if (ret) + dev_err(data->dev, "Reading channel data failed\n"); + + *val = le16_to_cpu(tmp); + + return ret; +} + +static int bu27008_get_gain(struct bu27008_data *data, struct iio_gts *gts, int *gain) +{ + int ret, sel; + + ret = data->cd->get_gain_sel(data, &sel); + if (ret) + return ret; + + ret = iio_gts_find_gain_by_sel(gts, sel); + if (ret < 0) { + dev_err(data->dev, "unknown gain value 0x%x\n", sel); + return ret; + } + + *gain = ret; + + return 0; +} + static int bu27008_set_gain(struct bu27008_data *data, int gain) { int ret; @@ -376,7 +784,7 @@ static int bu27008_set_gain(struct bu27008_data *data, int gain) if (ret < 0) return ret; - return bu27008_write_gain_sel(data, ret); + return data->cd->write_gain_sel(data, ret); } static int bu27008_get_int_time_sel(struct bu27008_data *data, int *sel) @@ -384,15 +792,23 @@ static int bu27008_get_int_time_sel(struct bu27008_data *data, int *sel) int ret, val; ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL1, &val); - *sel = FIELD_GET(BU27008_MASK_MEAS_MODE, val); + if (ret) + return ret; - return ret; + val &= data->cd->int_time_mask; + val >>= ffs(data->cd->int_time_mask) - 1; + + *sel = val; + + return 0; } static int bu27008_set_int_time_sel(struct bu27008_data *data, int sel) { + sel <<= ffs(data->cd->int_time_mask) - 1; + return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1, - BU27008_MASK_MEAS_MODE, sel); + data->cd->int_time_mask, sel); } static int bu27008_get_int_time_us(struct bu27008_data *data) @@ -448,8 +864,7 @@ static int bu27008_set_int_time(struct bu27008_data *data, int time) if (ret < 0) return ret; - return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL1, - BU27008_MASK_MEAS_MODE, ret); + return bu27008_set_int_time_sel(data, ret); } /* Try to change the time so that the scale is maintained */ @@ -527,10 +942,13 @@ unlock_out: return ret; } -static int bu27008_meas_set(struct bu27008_data *data, int state) +static int bu27008_meas_set(struct bu27008_data *data, bool enable) { - return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL3, - BU27008_MASK_MEAS_EN, state); + if (enable) + return regmap_set_bits(data->regmap, data->cd->meas_en_reg, + data->cd->meas_en_mask); + return regmap_clear_bits(data->regmap, data->cd->meas_en_reg, + data->cd->meas_en_mask); } static int bu27008_chan_cfg(struct bu27008_data *data, @@ -543,9 +961,15 @@ static int bu27008_chan_cfg(struct bu27008_data *data, else chan_sel = BU27008_CLEAR2_IR3; - chan_sel = FIELD_PREP(BU27008_MASK_CHAN_SEL, chan_sel); + /* + * prepare bitfield for channel sel. The FIELD_PREP works only when + * mask is constant. In our case the mask is assigned based on the + * chip type. Hence the open-coded FIELD_PREP here. We don't bother + * zeroing the irrelevant bits though - update_bits takes care of that. + */ + chan_sel <<= ffs(data->cd->chan_sel_mask) - 1; - return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL3, + return regmap_update_bits(data->regmap, data->cd->chan_sel_reg, BU27008_MASK_CHAN_SEL, chan_sel); } @@ -558,7 +982,7 @@ static int bu27008_read_one(struct bu27008_data *data, struct iio_dev *idev, if (ret) return ret; - ret = bu27008_meas_set(data, BU27008_MEAS_EN); + ret = bu27008_meas_set(data, true); if (ret) return ret; @@ -574,7 +998,7 @@ static int bu27008_read_one(struct bu27008_data *data, struct iio_dev *idev, if (!ret) ret = IIO_VAL_INT; - if (bu27008_meas_set(data, BU27008_MEAS_DIS)) + if (bu27008_meas_set(data, false)) dev_warn(data->dev, "measurement disabling failed\n"); return ret; @@ -669,7 +1093,7 @@ static int bu27008_set_scale(struct bu27008_data *data, goto unlock_out; } - ret = bu27008_write_gain_sel(data, gain_sel); + ret = data->cd->write_gain_sel(data, gain_sel); unlock_out: mutex_unlock(&data->mutex); @@ -762,10 +1186,10 @@ static int bu27008_update_scan_mode(struct iio_dev *idev, chan_sel = BU27008_CLEAR2_IR3; } - chan_sel = FIELD_PREP(BU27008_MASK_CHAN_SEL, chan_sel); + chan_sel <<= ffs(data->cd->chan_sel_mask) - 1; - return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL3, - BU27008_MASK_CHAN_SEL, chan_sel); + return regmap_update_bits(data->regmap, data->cd->chan_sel_reg, + data->cd->chan_sel_mask, chan_sel); } static const struct iio_info bu27008_info = { @@ -777,46 +1201,18 @@ static const struct iio_info bu27008_info = { .validate_trigger = iio_validate_own_trigger, }; -static int bu27008_chip_init(struct bu27008_data *data) -{ - int ret; - - ret = regmap_write_bits(data->regmap, BU27008_REG_SYSTEM_CONTROL, - BU27008_MASK_SW_RESET, BU27008_MASK_SW_RESET); - if (ret) - return dev_err_probe(data->dev, ret, "Sensor reset failed\n"); - - /* - * The data-sheet does not tell how long performing the IC reset takes. - * However, the data-sheet says the minimum time it takes the IC to be - * able to take inputs after power is applied, is 100 uS. I'd assume - * > 1 mS is enough. - */ - msleep(1); - - ret = regmap_reinit_cache(data->regmap, &bu27008_regmap); - if (ret) - dev_err(data->dev, "Failed to reinit reg cache\n"); - - return ret; -} - -static int bu27008_set_drdy_irq(struct bu27008_data *data, int state) -{ - return regmap_update_bits(data->regmap, BU27008_REG_MODE_CONTROL3, - BU27008_MASK_INT_EN, state); -} - -static int bu27008_trigger_set_state(struct iio_trigger *trig, - bool state) +static int bu27008_trigger_set_state(struct iio_trigger *trig, bool state) { struct bu27008_data *data = iio_trigger_get_drvdata(trig); int ret; + if (state) - ret = bu27008_set_drdy_irq(data, BU27008_INT_EN); + ret = regmap_set_bits(data->regmap, data->cd->drdy_en_reg, + data->cd->drdy_en_mask); else - ret = bu27008_set_drdy_irq(data, BU27008_INT_DIS); + ret = regmap_clear_bits(data->regmap, data->cd->drdy_en_reg, + data->cd->drdy_en_mask); if (ret) dev_err(data->dev, "Failed to set trigger state\n"); @@ -852,7 +1248,7 @@ static irqreturn_t bu27008_trigger_handler(int irq, void *p) * After some measurements, it seems reading the * BU27008_REG_MODE_CONTROL3 debounces the IRQ line */ - ret = regmap_read(data->regmap, BU27008_REG_MODE_CONTROL3, &dummy); + ret = regmap_read(data->regmap, data->cd->valid_reg, &dummy); if (ret < 0) goto err_read; @@ -872,14 +1268,14 @@ static int bu27008_buffer_preenable(struct iio_dev *idev) { struct bu27008_data *data = iio_priv(idev); - return bu27008_meas_set(data, BU27008_MEAS_EN); + return bu27008_meas_set(data, true); } static int bu27008_buffer_postdisable(struct iio_dev *idev) { struct bu27008_data *data = iio_priv(idev); - return bu27008_meas_set(data, BU27008_MEAS_DIS); + return bu27008_meas_set(data, false); } static const struct iio_buffer_setup_ops bu27008_buffer_ops = { @@ -952,11 +1348,6 @@ static int bu27008_probe(struct i2c_client *i2c) struct iio_dev *idev; int ret; - regmap = devm_regmap_init_i2c(i2c, &bu27008_regmap); - if (IS_ERR(regmap)) - return dev_err_probe(dev, PTR_ERR(regmap), - "Failed to initialize Regmap\n"); - idev = devm_iio_device_alloc(dev, sizeof(*data)); if (!idev) return -ENOMEM; @@ -967,24 +1358,34 @@ static int bu27008_probe(struct i2c_client *i2c) data = iio_priv(idev); + data->cd = device_get_match_data(&i2c->dev); + if (!data->cd) + return -ENODEV; + + regmap = devm_regmap_init_i2c(i2c, data->cd->regmap_cfg); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to initialize Regmap\n"); + + ret = regmap_read(regmap, BU27008_REG_SYSTEM_CONTROL, ®); if (ret) return dev_err_probe(dev, ret, "Failed to access sensor\n"); part_id = FIELD_GET(BU27008_MASK_PART_ID, reg); - if (part_id != BU27008_ID) + if (part_id != data->cd->part_id) dev_warn(dev, "unknown device 0x%x\n", part_id); - ret = devm_iio_init_iio_gts(dev, BU27008_SCALE_1X, 0, bu27008_gains, - ARRAY_SIZE(bu27008_gains), bu27008_itimes, - ARRAY_SIZE(bu27008_itimes), &data->gts); + ret = devm_iio_init_iio_gts(dev, data->cd->scale1x, 0, data->cd->gains, + data->cd->num_gains, data->cd->itimes, + data->cd->num_itimes, &data->gts); if (ret) return ret; - ret = devm_iio_init_iio_gts(dev, BU27008_SCALE_1X, 0, bu27008_gains_ir, - ARRAY_SIZE(bu27008_gains_ir), bu27008_itimes, - ARRAY_SIZE(bu27008_itimes), &data->gts_ir); + ret = devm_iio_init_iio_gts(dev, data->cd->scale1x, 0, data->cd->gains_ir, + data->cd->num_gains_ir, data->cd->itimes, + data->cd->num_itimes, &data->gts_ir); if (ret) return ret; @@ -995,12 +1396,12 @@ static int bu27008_probe(struct i2c_client *i2c) idev->channels = bu27008_channels; idev->num_channels = ARRAY_SIZE(bu27008_channels); - idev->name = "bu27008"; + idev->name = data->cd->name; idev->info = &bu27008_info; idev->modes = INDIO_DIRECT_MODE; idev->available_scan_masks = bu27008_scan_masks; - ret = bu27008_chip_init(data); + ret = data->cd->chip_init(data); if (ret) return ret; @@ -1021,7 +1422,8 @@ static int bu27008_probe(struct i2c_client *i2c) } static const struct of_device_id bu27008_of_match[] = { - { .compatible = "rohm,bu27008" }, + { .compatible = "rohm,bu27008", .data = &bu27008_chip }, + { .compatible = "rohm,bu27010", .data = &bu27010_chip }, { } }; MODULE_DEVICE_TABLE(of, bu27008_of_match); @@ -1036,7 +1438,7 @@ static struct i2c_driver bu27008_i2c_driver = { }; module_i2c_driver(bu27008_i2c_driver); -MODULE_DESCRIPTION("ROHM BU27008 colour sensor driver"); +MODULE_DESCRIPTION("ROHM BU27008 and BU27010 colour sensor driver"); MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>"); MODULE_LICENSE("GPL"); MODULE_IMPORT_NS(IIO_GTS_HELPER); diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 7c7362e28821..3a52b09c2823 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -24,6 +24,7 @@ #include <linux/delay.h> #include <linux/pm_runtime.h> #include <linux/interrupt.h> +#include <linux/units.h> #include <linux/iio/buffer.h> #include <linux/iio/events.h> @@ -60,11 +61,15 @@ #define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */ #define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */ +#define VCNL4200_PS_CONF3 0x04 /* Proximity configuration */ #define VCNL4040_PS_THDL_LM 0x06 /* Proximity threshold low */ #define VCNL4040_PS_THDH_LM 0x07 /* Proximity threshold high */ +#define VCNL4040_ALS_THDL_LM 0x02 /* Ambient light threshold low */ +#define VCNL4040_ALS_THDH_LM 0x01 /* Ambient light threshold high */ #define VCNL4200_PS_DATA 0x08 /* Proximity data */ #define VCNL4200_AL_DATA 0x09 /* Ambient light data */ #define VCNL4040_INT_FLAGS 0x0b /* Interrupt register */ +#define VCNL4200_INT_FLAGS 0x0d /* Interrupt register */ #define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */ #define VCNL4040_DEV_ID 0x0c /* Device ID and version */ @@ -79,11 +84,19 @@ #define VCNL4000_SELF_TIMED_EN BIT(0) /* start self-timed measurement */ #define VCNL4040_ALS_CONF_ALS_SHUTDOWN BIT(0) +#define VCNL4040_ALS_CONF_IT GENMASK(7, 6) /* Ambient integration time */ +#define VCNL4040_ALS_CONF_INT_EN BIT(1) /* Ambient light Interrupt enable */ +#define VCNL4040_ALS_CONF_PERS GENMASK(3, 2) /* Ambient interrupt persistence setting */ #define VCNL4040_PS_CONF1_PS_SHUTDOWN BIT(0) #define VCNL4040_PS_CONF2_PS_IT GENMASK(3, 1) /* Proximity integration time */ +#define VCNL4040_CONF1_PS_PERS GENMASK(5, 4) /* Proximity interrupt persistence setting */ #define VCNL4040_PS_CONF2_PS_INT GENMASK(9, 8) /* Proximity interrupt mode */ +#define VCNL4040_PS_CONF3_MPS GENMASK(6, 5) /* Proximity multi pulse number */ +#define VCNL4040_PS_MS_LED_I GENMASK(10, 8) /* Proximity current */ #define VCNL4040_PS_IF_AWAY BIT(8) /* Proximity event cross low threshold */ #define VCNL4040_PS_IF_CLOSE BIT(9) /* Proximity event cross high threshold */ +#define VCNL4040_ALS_RISING BIT(12) /* Ambient Light cross high threshold */ +#define VCNL4040_ALS_FALLING BIT(13) /* Ambient Light cross low threshold */ /* Bit masks for interrupt registers. */ #define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */ @@ -123,6 +136,44 @@ static const int vcnl4040_ps_it_times[][2] = { {0, 800}, }; +static const int vcnl4200_ps_it_times[][2] = { + {0, 96}, + {0, 144}, + {0, 192}, + {0, 384}, + {0, 768}, + {0, 864}, +}; + +static const int vcnl4040_als_it_times[][2] = { + {0, 80000}, + {0, 160000}, + {0, 320000}, + {0, 640000}, +}; + +static const int vcnl4200_als_it_times[][2] = { + {0, 50000}, + {0, 100000}, + {0, 200000}, + {0, 400000}, +}; + +static const int vcnl4040_ps_calibbias_ua[][2] = { + {0, 50000}, + {0, 75000}, + {0, 100000}, + {0, 120000}, + {0, 140000}, + {0, 160000}, + {0, 180000}, + {0, 200000}, +}; + +static const int vcnl4040_als_persistence[] = {1, 2, 4, 8}; +static const int vcnl4040_ps_persistence[] = {1, 2, 3, 4}; +static const int vcnl4040_ps_oversampling_ratio[] = {1, 2, 4, 8}; + #define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ enum vcnl4000_device_ids { @@ -145,6 +196,7 @@ struct vcnl4000_data { int rev; int al_scale; u8 ps_int; /* proximity interrupt mode */ + u8 als_int; /* ambient light interrupt mode*/ const struct vcnl4000_chip_spec *chip_spec; struct mutex vcnl4000_lock; struct vcnl4200_channel vcnl4200_al; @@ -164,6 +216,13 @@ struct vcnl4000_chip_spec { int (*set_power_state)(struct vcnl4000_data *data, bool on); irqreturn_t (*irq_thread)(int irq, void *priv); irqreturn_t (*trig_buffer_func)(int irq, void *priv); + + u8 int_reg; + const int(*ps_it_times)[][2]; + const int num_ps_it_times; + const int(*als_it_times)[][2]; + const int num_als_it_times; + const unsigned int ulux_step; }; static const struct i2c_device_id vcnl4000_id[] = { @@ -263,7 +322,7 @@ static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) int ret; /* Do not power down if interrupts are enabled */ - if (!on && data->ps_int) + if (!on && (data->ps_int || data->als_int)) return 0; ret = vcnl4000_write_als_enable(data, on); @@ -308,6 +367,7 @@ static int vcnl4200_init(struct vcnl4000_data *data) data->rev = (ret >> 8) & 0xf; data->ps_int = 0; + data->als_int = 0; data->vcnl4200_al.reg = VCNL4200_AL_DATA; data->vcnl4200_ps.reg = VCNL4200_PS_DATA; @@ -317,16 +377,15 @@ static int vcnl4200_init(struct vcnl4000_data *data) data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000); /* Default wait time is 4.8ms, add 20% tolerance. */ data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000); - data->al_scale = 24000; break; case VCNL4040_PROD_ID: /* Default wait time is 80ms, add 20% tolerance. */ data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000); /* Default wait time is 5ms, add 20% tolerance. */ data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000); - data->al_scale = 120000; break; } + data->al_scale = data->chip_spec->ulux_step; mutex_init(&data->vcnl4200_al.lock); mutex_init(&data->vcnl4200_ps.lock); @@ -496,6 +555,60 @@ static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) return ret; } +static int vcnl4040_read_als_it(struct vcnl4000_data *data, int *val, int *val2) +{ + int ret; + + ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); + if (ret < 0) + return ret; + + ret = FIELD_GET(VCNL4040_ALS_CONF_IT, ret); + if (ret >= data->chip_spec->num_als_it_times) + return -EINVAL; + + *val = (*data->chip_spec->als_it_times)[ret][0]; + *val2 = (*data->chip_spec->als_it_times)[ret][1]; + + return 0; +} + +static ssize_t vcnl4040_write_als_it(struct vcnl4000_data *data, int val) +{ + unsigned int i; + int ret; + u16 regval; + + for (i = 0; i < data->chip_spec->num_als_it_times; i++) { + if (val == (*data->chip_spec->als_it_times)[i][1]) + break; + } + + if (i == data->chip_spec->num_als_it_times) + return -EINVAL; + + data->vcnl4200_al.sampling_rate = ktime_set(0, val * 1200); + data->al_scale = div_u64(mul_u32_u32(data->chip_spec->ulux_step, + (*data->chip_spec->als_it_times)[0][1]), + val); + + mutex_lock(&data->vcnl4000_lock); + + ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); + if (ret < 0) + goto out_unlock; + + regval = FIELD_PREP(VCNL4040_ALS_CONF_IT, i); + regval |= (ret & ~VCNL4040_ALS_CONF_IT); + ret = i2c_smbus_write_word_data(data->client, + VCNL4200_AL_CONF, + regval); + +out_unlock: + mutex_unlock(&data->vcnl4000_lock); + return ret; +} + static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2) { int ret; @@ -506,11 +619,11 @@ static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2) ret = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret); - if (ret >= ARRAY_SIZE(vcnl4040_ps_it_times)) + if (ret >= data->chip_spec->num_ps_it_times) return -EINVAL; - *val = vcnl4040_ps_it_times[ret][0]; - *val2 = vcnl4040_ps_it_times[ret][1]; + *val = (*data->chip_spec->ps_it_times)[ret][0]; + *val2 = (*data->chip_spec->ps_it_times)[ret][1]; return 0; } @@ -521,8 +634,8 @@ static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val) int ret, index = -1; u16 regval; - for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_it_times); i++) { - if (val == vcnl4040_ps_it_times[i][1]) { + for (i = 0; i < data->chip_spec->num_ps_it_times; i++) { + if (val == (*data->chip_spec->ps_it_times)[i][1]) { index = i; break; } @@ -531,6 +644,8 @@ static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val) if (index < 0) return -EINVAL; + data->vcnl4200_ps.sampling_rate = ktime_set(0, val * 60 * NSEC_PER_USEC); + mutex_lock(&data->vcnl4000_lock); ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); @@ -547,6 +662,224 @@ out: return ret; } +static ssize_t vcnl4040_read_als_period(struct vcnl4000_data *data, int *val, int *val2) +{ + int ret, ret_pers, it; + int64_t val_c; + + ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); + if (ret < 0) + return ret; + + ret_pers = FIELD_GET(VCNL4040_ALS_CONF_PERS, ret); + if (ret_pers >= ARRAY_SIZE(vcnl4040_als_persistence)) + return -EINVAL; + + it = FIELD_GET(VCNL4040_ALS_CONF_IT, ret); + if (it >= data->chip_spec->num_als_it_times) + return -EINVAL; + + val_c = mul_u32_u32((*data->chip_spec->als_it_times)[it][1], + vcnl4040_als_persistence[ret_pers]); + *val = div_u64_rem(val_c, MICRO, val2); + + return IIO_VAL_INT_PLUS_MICRO; +} + +static ssize_t vcnl4040_write_als_period(struct vcnl4000_data *data, int val, int val2) +{ + unsigned int i; + int ret, it; + u16 regval; + u64 val_n = mul_u32_u32(val, MICRO) + val2; + + ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); + if (ret < 0) + return ret; + + it = FIELD_GET(VCNL4040_ALS_CONF_IT, ret); + if (it >= data->chip_spec->num_als_it_times) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(vcnl4040_als_persistence) - 1; i++) { + if (val_n < mul_u32_u32(vcnl4040_als_persistence[i], + (*data->chip_spec->als_it_times)[it][1])) + break; + } + + mutex_lock(&data->vcnl4000_lock); + + ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); + if (ret < 0) + goto out_unlock; + + regval = FIELD_PREP(VCNL4040_ALS_CONF_PERS, i); + regval |= (ret & ~VCNL4040_ALS_CONF_PERS); + ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, + regval); + +out_unlock: + mutex_unlock(&data->vcnl4000_lock); + return ret; +} + +static ssize_t vcnl4040_read_ps_period(struct vcnl4000_data *data, int *val, int *val2) +{ + int ret, ret_pers, it; + + ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); + if (ret < 0) + return ret; + + ret_pers = FIELD_GET(VCNL4040_CONF1_PS_PERS, ret); + if (ret_pers >= ARRAY_SIZE(vcnl4040_ps_persistence)) + return -EINVAL; + + it = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret); + if (it >= data->chip_spec->num_ps_it_times) + return -EINVAL; + + *val = (*data->chip_spec->ps_it_times)[it][0]; + *val2 = (*data->chip_spec->ps_it_times)[it][1] * + vcnl4040_ps_persistence[ret_pers]; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static ssize_t vcnl4040_write_ps_period(struct vcnl4000_data *data, int val, int val2) +{ + int ret, it, i; + u16 regval; + + ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); + if (ret < 0) + return ret; + + it = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret); + if (it >= data->chip_spec->num_ps_it_times) + return -EINVAL; + + if (val > 0) + i = ARRAY_SIZE(vcnl4040_ps_persistence) - 1; + else { + for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_persistence) - 1; i++) { + if (val2 <= vcnl4040_ps_persistence[i] * + (*data->chip_spec->ps_it_times)[it][1]) + break; + } + } + + mutex_lock(&data->vcnl4000_lock); + + ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); + if (ret < 0) + goto out_unlock; + + regval = FIELD_PREP(VCNL4040_CONF1_PS_PERS, i); + regval |= (ret & ~VCNL4040_CONF1_PS_PERS); + ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, + regval); + +out_unlock: + mutex_unlock(&data->vcnl4000_lock); + return ret; +} + +static ssize_t vcnl4040_read_ps_oversampling_ratio(struct vcnl4000_data *data, int *val) +{ + int ret; + + ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3); + if (ret < 0) + return ret; + + ret = FIELD_GET(VCNL4040_PS_CONF3_MPS, ret); + if (ret >= ARRAY_SIZE(vcnl4040_ps_oversampling_ratio)) + return -EINVAL; + + *val = vcnl4040_ps_oversampling_ratio[ret]; + + return ret; +} + +static ssize_t vcnl4040_write_ps_oversampling_ratio(struct vcnl4000_data *data, int val) +{ + unsigned int i; + int ret; + u16 regval; + + for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_oversampling_ratio); i++) { + if (val == vcnl4040_ps_oversampling_ratio[i]) + break; + } + + if (i >= ARRAY_SIZE(vcnl4040_ps_oversampling_ratio)) + return -EINVAL; + + mutex_lock(&data->vcnl4000_lock); + + ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3); + if (ret < 0) + goto out_unlock; + + regval = FIELD_PREP(VCNL4040_PS_CONF3_MPS, i); + regval |= (ret & ~VCNL4040_PS_CONF3_MPS); + ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3, + regval); + +out_unlock: + mutex_unlock(&data->vcnl4000_lock); + return ret; +} + +static ssize_t vcnl4040_read_ps_calibbias(struct vcnl4000_data *data, int *val, int *val2) +{ + int ret; + + ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3); + if (ret < 0) + return ret; + + ret = FIELD_GET(VCNL4040_PS_MS_LED_I, ret); + if (ret >= ARRAY_SIZE(vcnl4040_ps_calibbias_ua)) + return -EINVAL; + + *val = vcnl4040_ps_calibbias_ua[ret][0]; + *val2 = vcnl4040_ps_calibbias_ua[ret][1]; + + return ret; +} + +static ssize_t vcnl4040_write_ps_calibbias(struct vcnl4000_data *data, int val) +{ + unsigned int i; + int ret; + u16 regval; + + for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_calibbias_ua); i++) { + if (val == vcnl4040_ps_calibbias_ua[i][1]) + break; + } + + if (i >= ARRAY_SIZE(vcnl4040_ps_calibbias_ua)) + return -EINVAL; + + mutex_lock(&data->vcnl4000_lock); + + ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3); + if (ret < 0) + goto out_unlock; + + regval = (ret & ~VCNL4040_PS_MS_LED_I); + regval |= FIELD_PREP(VCNL4040_PS_MS_LED_I, i); + ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3, + regval); + +out_unlock: + mutex_unlock(&data->vcnl4000_lock); + return ret; +} + static int vcnl4000_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -584,12 +917,39 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev, *val2 = data->al_scale; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_INT_TIME: - if (chan->type != IIO_PROXIMITY) + switch (chan->type) { + case IIO_LIGHT: + ret = vcnl4040_read_als_it(data, val, val2); + break; + case IIO_PROXIMITY: + ret = vcnl4040_read_ps_it(data, val, val2); + break; + default: return -EINVAL; - ret = vcnl4040_read_ps_it(data, val, val2); + } if (ret < 0) return ret; return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + switch (chan->type) { + case IIO_PROXIMITY: + ret = vcnl4040_read_ps_oversampling_ratio(data, val); + if (ret < 0) + return ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_PROXIMITY: + ret = vcnl4040_read_ps_calibbias(data, val, val2); + if (ret < 0) + return ret; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } default: return -EINVAL; } @@ -605,9 +965,28 @@ static int vcnl4040_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_INT_TIME: if (val != 0) return -EINVAL; - if (chan->type != IIO_PROXIMITY) + switch (chan->type) { + case IIO_LIGHT: + return vcnl4040_write_als_it(data, val2); + case IIO_PROXIMITY: + return vcnl4040_write_ps_it(data, val2); + default: return -EINVAL; - return vcnl4040_write_ps_it(data, val2); + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + switch (chan->type) { + case IIO_PROXIMITY: + return vcnl4040_write_ps_oversampling_ratio(data, val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_PROXIMITY: + return vcnl4040_write_ps_calibbias(data, val2); + default: + return -EINVAL; + } default: return -EINVAL; } @@ -618,12 +997,44 @@ static int vcnl4040_read_avail(struct iio_dev *indio_dev, const int **vals, int *type, int *length, long mask) { + struct vcnl4000_data *data = iio_priv(indio_dev); + switch (mask) { case IIO_CHAN_INFO_INT_TIME: - *vals = (int *)vcnl4040_ps_it_times; + switch (chan->type) { + case IIO_LIGHT: + *vals = (int *)(*data->chip_spec->als_it_times); + *length = 2 * data->chip_spec->num_als_it_times; + break; + case IIO_PROXIMITY: + *vals = (int *)(*data->chip_spec->ps_it_times); + *length = 2 * data->chip_spec->num_ps_it_times; + break; + default: + return -EINVAL; + } *type = IIO_VAL_INT_PLUS_MICRO; - *length = 2 * ARRAY_SIZE(vcnl4040_ps_it_times); return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + switch (chan->type) { + case IIO_PROXIMITY: + *vals = (int *)vcnl4040_ps_oversampling_ratio; + *length = ARRAY_SIZE(vcnl4040_ps_oversampling_ratio); + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_PROXIMITY: + *vals = (int *)vcnl4040_ps_calibbias_ua; + *length = 2 * ARRAY_SIZE(vcnl4040_ps_calibbias_ua); + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } default: return -EINVAL; } @@ -818,24 +1229,58 @@ static int vcnl4040_read_event(struct iio_dev *indio_dev, int ret; struct vcnl4000_data *data = iio_priv(indio_dev); - switch (dir) { - case IIO_EV_DIR_RISING: - ret = i2c_smbus_read_word_data(data->client, - VCNL4040_PS_THDH_LM); - if (ret < 0) - return ret; - *val = ret; - return IIO_VAL_INT; - case IIO_EV_DIR_FALLING: - ret = i2c_smbus_read_word_data(data->client, - VCNL4040_PS_THDL_LM); - if (ret < 0) - return ret; - *val = ret; - return IIO_VAL_INT; + switch (chan->type) { + case IIO_LIGHT: + switch (info) { + case IIO_EV_INFO_PERIOD: + return vcnl4040_read_als_period(data, val, val2); + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + ret = i2c_smbus_read_word_data(data->client, + VCNL4040_ALS_THDH_LM); + break; + case IIO_EV_DIR_FALLING: + ret = i2c_smbus_read_word_data(data->client, + VCNL4040_ALS_THDL_LM); + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + break; + case IIO_PROXIMITY: + switch (info) { + case IIO_EV_INFO_PERIOD: + return vcnl4040_read_ps_period(data, val, val2); + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + ret = i2c_smbus_read_word_data(data->client, + VCNL4040_PS_THDH_LM); + break; + case IIO_EV_DIR_FALLING: + ret = i2c_smbus_read_word_data(data->client, + VCNL4040_PS_THDL_LM); + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + break; default: return -EINVAL; } + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; } static int vcnl4040_write_event(struct iio_dev *indio_dev, @@ -848,22 +1293,61 @@ static int vcnl4040_write_event(struct iio_dev *indio_dev, int ret; struct vcnl4000_data *data = iio_priv(indio_dev); - switch (dir) { - case IIO_EV_DIR_RISING: - ret = i2c_smbus_write_word_data(data->client, - VCNL4040_PS_THDH_LM, val); - if (ret < 0) - return ret; - return IIO_VAL_INT; - case IIO_EV_DIR_FALLING: - ret = i2c_smbus_write_word_data(data->client, - VCNL4040_PS_THDL_LM, val); - if (ret < 0) - return ret; - return IIO_VAL_INT; + switch (chan->type) { + case IIO_LIGHT: + switch (info) { + case IIO_EV_INFO_PERIOD: + return vcnl4040_write_als_period(data, val, val2); + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + ret = i2c_smbus_write_word_data(data->client, + VCNL4040_ALS_THDH_LM, + val); + break; + case IIO_EV_DIR_FALLING: + ret = i2c_smbus_write_word_data(data->client, + VCNL4040_ALS_THDL_LM, + val); + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + break; + case IIO_PROXIMITY: + switch (info) { + case IIO_EV_INFO_PERIOD: + return vcnl4040_write_ps_period(data, val, val2); + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + ret = i2c_smbus_write_word_data(data->client, + VCNL4040_PS_THDH_LM, + val); + break; + case IIO_EV_DIR_FALLING: + ret = i2c_smbus_write_word_data(data->client, + VCNL4040_PS_THDL_LM, + val); + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + break; default: return -EINVAL; } + if (ret < 0) + return ret; + return IIO_VAL_INT; } static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data) @@ -956,15 +1440,28 @@ static int vcnl4040_read_event_config(struct iio_dev *indio_dev, int ret; struct vcnl4000_data *data = iio_priv(indio_dev); - ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); - if (ret < 0) - return ret; + switch (chan->type) { + case IIO_LIGHT: + ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); + if (ret < 0) + return ret; + + data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, ret); - data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret); + return data->als_int; + case IIO_PROXIMITY: + ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); + if (ret < 0) + return ret; - return (dir == IIO_EV_DIR_RISING) ? - FIELD_GET(VCNL4040_PS_IF_AWAY, ret) : - FIELD_GET(VCNL4040_PS_IF_CLOSE, ret); + data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret); + + return (dir == IIO_EV_DIR_RISING) ? + FIELD_GET(VCNL4040_PS_IF_AWAY, ret) : + FIELD_GET(VCNL4040_PS_IF_CLOSE, ret); + default: + return -EINVAL; + } } static int vcnl4040_write_event_config(struct iio_dev *indio_dev, @@ -972,29 +1469,51 @@ static int vcnl4040_write_event_config(struct iio_dev *indio_dev, enum iio_event_type type, enum iio_event_direction dir, int state) { - int ret; + int ret = -EINVAL; u16 val, mask; struct vcnl4000_data *data = iio_priv(indio_dev); mutex_lock(&data->vcnl4000_lock); - ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); - if (ret < 0) - goto out; + switch (chan->type) { + case IIO_LIGHT: + ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); + if (ret < 0) + goto out; - if (dir == IIO_EV_DIR_RISING) - mask = VCNL4040_PS_IF_AWAY; - else - mask = VCNL4040_PS_IF_CLOSE; + mask = VCNL4040_ALS_CONF_INT_EN; + if (state) + val = (ret | mask); + else + val = (ret & ~mask); - val = state ? (ret | mask) : (ret & ~mask); + data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, val); + ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, + val); + break; + case IIO_PROXIMITY: + ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); + if (ret < 0) + goto out; - data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val); - ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val); + if (dir == IIO_EV_DIR_RISING) + mask = VCNL4040_PS_IF_AWAY; + else + mask = VCNL4040_PS_IF_CLOSE; + + val = state ? (ret | mask) : (ret & ~mask); + + data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val); + ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, + val); + break; + default: + break; + } out: mutex_unlock(&data->vcnl4000_lock); - data->chip_spec->set_power_state(data, data->ps_int != 0); + data->chip_spec->set_power_state(data, data->ps_int || data->als_int); return ret; } @@ -1005,7 +1524,7 @@ static irqreturn_t vcnl4040_irq_thread(int irq, void *p) struct vcnl4000_data *data = iio_priv(indio_dev); int ret; - ret = i2c_smbus_read_word_data(data->client, VCNL4040_INT_FLAGS); + ret = i2c_smbus_read_word_data(data->client, data->chip_spec->int_reg); if (ret < 0) return IRQ_HANDLED; @@ -1025,6 +1544,22 @@ static irqreturn_t vcnl4040_irq_thread(int irq, void *p) iio_get_time_ns(indio_dev)); } + if (ret & VCNL4040_ALS_FALLING) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns(indio_dev)); + } + + if (ret & VCNL4040_ALS_RISING) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns(indio_dev)); + } + return IRQ_HANDLED; } @@ -1191,6 +1726,22 @@ static const struct iio_event_spec vcnl4000_event_spec[] = { } }; +static const struct iio_event_spec vcnl4040_als_event_spec[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_PERIOD), + }, +}; + static const struct iio_event_spec vcnl4040_event_spec[] = { { .type = IIO_EV_TYPE_THRESH, @@ -1200,6 +1751,10 @@ static const struct iio_event_spec vcnl4040_event_spec[] = { .type = IIO_EV_TYPE_THRESH, .dir = IIO_EV_DIR_FALLING, .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_PERIOD), }, }; @@ -1244,12 +1799,20 @@ static const struct iio_chan_spec vcnl4040_channels[] = { { .type = IIO_LIGHT, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_SCALE), + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_INT_TIME), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME), + .event_spec = vcnl4040_als_event_spec, + .num_event_specs = ARRAY_SIZE(vcnl4040_als_event_spec), }, { .type = IIO_PROXIMITY, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_INT_TIME), - .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME), + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_CALIBBIAS), .ext_info = vcnl4000_ext_info, .event_spec = vcnl4040_event_spec, .num_event_specs = ARRAY_SIZE(vcnl4040_event_spec), @@ -1314,6 +1877,12 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { .num_channels = ARRAY_SIZE(vcnl4040_channels), .info = &vcnl4040_info, .irq_thread = vcnl4040_irq_thread, + .int_reg = VCNL4040_INT_FLAGS, + .ps_it_times = &vcnl4040_ps_it_times, + .num_ps_it_times = ARRAY_SIZE(vcnl4040_ps_it_times), + .als_it_times = &vcnl4040_als_it_times, + .num_als_it_times = ARRAY_SIZE(vcnl4040_als_it_times), + .ulux_step = 100000, }, [VCNL4200] = { .prod = "VCNL4200", @@ -1321,9 +1890,16 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { .measure_light = vcnl4200_measure_light, .measure_proximity = vcnl4200_measure_proximity, .set_power_state = vcnl4200_set_power_state, - .channels = vcnl4000_channels, + .channels = vcnl4040_channels, .num_channels = ARRAY_SIZE(vcnl4000_channels), - .info = &vcnl4000_info, + .info = &vcnl4040_info, + .irq_thread = vcnl4040_irq_thread, + .int_reg = VCNL4200_INT_FLAGS, + .ps_it_times = &vcnl4200_ps_it_times, + .num_ps_it_times = ARRAY_SIZE(vcnl4200_ps_it_times), + .als_it_times = &vcnl4200_als_it_times, + .num_als_it_times = ARRAY_SIZE(vcnl4200_als_it_times), + .ulux_step = 24000, }, }; diff --git a/drivers/iio/potentiometer/mcp4018.c b/drivers/iio/potentiometer/mcp4018.c index 89daecc90305..44678d372126 100644 --- a/drivers/iio/potentiometer/mcp4018.c +++ b/drivers/iio/potentiometer/mcp4018.c @@ -99,20 +99,25 @@ static const struct iio_info mcp4018_info = { .write_raw = mcp4018_write_raw, }; +#define MCP4018_ID_TABLE(_name, cfg) { \ + .name = _name, \ + .driver_data = (kernel_ulong_t)&mcp4018_cfg[cfg], \ +} + static const struct i2c_device_id mcp4018_id[] = { - { "mcp4017-502", MCP4018_502 }, - { "mcp4017-103", MCP4018_103 }, - { "mcp4017-503", MCP4018_503 }, - { "mcp4017-104", MCP4018_104 }, - { "mcp4018-502", MCP4018_502 }, - { "mcp4018-103", MCP4018_103 }, - { "mcp4018-503", MCP4018_503 }, - { "mcp4018-104", MCP4018_104 }, - { "mcp4019-502", MCP4018_502 }, - { "mcp4019-103", MCP4018_103 }, - { "mcp4019-503", MCP4018_503 }, - { "mcp4019-104", MCP4018_104 }, - {} + MCP4018_ID_TABLE("mcp4017-502", MCP4018_502), + MCP4018_ID_TABLE("mcp4017-103", MCP4018_103), + MCP4018_ID_TABLE("mcp4017-503", MCP4018_503), + MCP4018_ID_TABLE("mcp4017-104", MCP4018_104), + MCP4018_ID_TABLE("mcp4018-502", MCP4018_502), + MCP4018_ID_TABLE("mcp4018-103", MCP4018_103), + MCP4018_ID_TABLE("mcp4018-503", MCP4018_503), + MCP4018_ID_TABLE("mcp4018-104", MCP4018_104), + MCP4018_ID_TABLE("mcp4019-502", MCP4018_502), + MCP4018_ID_TABLE("mcp4019-103", MCP4018_103), + MCP4018_ID_TABLE("mcp4019-503", MCP4018_503), + MCP4018_ID_TABLE("mcp4019-104", MCP4018_104), + { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, mcp4018_id); @@ -157,9 +162,7 @@ static int mcp4018_probe(struct i2c_client *client) i2c_set_clientdata(client, indio_dev); data->client = client; - data->cfg = device_get_match_data(dev); - if (!data->cfg) - data->cfg = &mcp4018_cfg[i2c_match_id(mcp4018_id, client)->driver_data]; + data->cfg = i2c_get_match_data(client); indio_dev->info = &mcp4018_info; indio_dev->channels = &mcp4018_channel; diff --git a/drivers/iio/potentiometer/mcp4531.c b/drivers/iio/potentiometer/mcp4531.c index c513c00c8243..f28880ebd758 100644 --- a/drivers/iio/potentiometer/mcp4531.c +++ b/drivers/iio/potentiometer/mcp4531.c @@ -206,72 +206,77 @@ static const struct iio_info mcp4531_info = { .write_raw = mcp4531_write_raw, }; +#define MCP4531_ID_TABLE(_name, cfg) { \ + .name = _name, \ + .driver_data = (kernel_ulong_t)&mcp4531_cfg[cfg], \ +} + static const struct i2c_device_id mcp4531_id[] = { - { "mcp4531-502", MCP453x_502 }, - { "mcp4531-103", MCP453x_103 }, - { "mcp4531-503", MCP453x_503 }, - { "mcp4531-104", MCP453x_104 }, - { "mcp4532-502", MCP453x_502 }, - { "mcp4532-103", MCP453x_103 }, - { "mcp4532-503", MCP453x_503 }, - { "mcp4532-104", MCP453x_104 }, - { "mcp4541-502", MCP454x_502 }, - { "mcp4541-103", MCP454x_103 }, - { "mcp4541-503", MCP454x_503 }, - { "mcp4541-104", MCP454x_104 }, - { "mcp4542-502", MCP454x_502 }, - { "mcp4542-103", MCP454x_103 }, - { "mcp4542-503", MCP454x_503 }, - { "mcp4542-104", MCP454x_104 }, - { "mcp4551-502", MCP455x_502 }, - { "mcp4551-103", MCP455x_103 }, - { "mcp4551-503", MCP455x_503 }, - { "mcp4551-104", MCP455x_104 }, - { "mcp4552-502", MCP455x_502 }, - { "mcp4552-103", MCP455x_103 }, - { "mcp4552-503", MCP455x_503 }, - { "mcp4552-104", MCP455x_104 }, - { "mcp4561-502", MCP456x_502 }, - { "mcp4561-103", MCP456x_103 }, - { "mcp4561-503", MCP456x_503 }, - { "mcp4561-104", MCP456x_104 }, - { "mcp4562-502", MCP456x_502 }, - { "mcp4562-103", MCP456x_103 }, - { "mcp4562-503", MCP456x_503 }, - { "mcp4562-104", MCP456x_104 }, - { "mcp4631-502", MCP463x_502 }, - { "mcp4631-103", MCP463x_103 }, - { "mcp4631-503", MCP463x_503 }, - { "mcp4631-104", MCP463x_104 }, - { "mcp4632-502", MCP463x_502 }, - { "mcp4632-103", MCP463x_103 }, - { "mcp4632-503", MCP463x_503 }, - { "mcp4632-104", MCP463x_104 }, - { "mcp4641-502", MCP464x_502 }, - { "mcp4641-103", MCP464x_103 }, - { "mcp4641-503", MCP464x_503 }, - { "mcp4641-104", MCP464x_104 }, - { "mcp4642-502", MCP464x_502 }, - { "mcp4642-103", MCP464x_103 }, - { "mcp4642-503", MCP464x_503 }, - { "mcp4642-104", MCP464x_104 }, - { "mcp4651-502", MCP465x_502 }, - { "mcp4651-103", MCP465x_103 }, - { "mcp4651-503", MCP465x_503 }, - { "mcp4651-104", MCP465x_104 }, - { "mcp4652-502", MCP465x_502 }, - { "mcp4652-103", MCP465x_103 }, - { "mcp4652-503", MCP465x_503 }, - { "mcp4652-104", MCP465x_104 }, - { "mcp4661-502", MCP466x_502 }, - { "mcp4661-103", MCP466x_103 }, - { "mcp4661-503", MCP466x_503 }, - { "mcp4661-104", MCP466x_104 }, - { "mcp4662-502", MCP466x_502 }, - { "mcp4662-103", MCP466x_103 }, - { "mcp4662-503", MCP466x_503 }, - { "mcp4662-104", MCP466x_104 }, - {} + MCP4531_ID_TABLE("mcp4531-502", MCP453x_502), + MCP4531_ID_TABLE("mcp4531-103", MCP453x_103), + MCP4531_ID_TABLE("mcp4531-503", MCP453x_503), + MCP4531_ID_TABLE("mcp4531-104", MCP453x_104), + MCP4531_ID_TABLE("mcp4532-502", MCP453x_502), + MCP4531_ID_TABLE("mcp4532-103", MCP453x_103), + MCP4531_ID_TABLE("mcp4532-503", MCP453x_503), + MCP4531_ID_TABLE("mcp4532-104", MCP453x_104), + MCP4531_ID_TABLE("mcp4541-502", MCP454x_502), + MCP4531_ID_TABLE("mcp4541-103", MCP454x_103), + MCP4531_ID_TABLE("mcp4541-503", MCP454x_503), + MCP4531_ID_TABLE("mcp4541-104", MCP454x_104), + MCP4531_ID_TABLE("mcp4542-502", MCP454x_502), + MCP4531_ID_TABLE("mcp4542-103", MCP454x_103), + MCP4531_ID_TABLE("mcp4542-503", MCP454x_503), + MCP4531_ID_TABLE("mcp4542-104", MCP454x_104), + MCP4531_ID_TABLE("mcp4551-502", MCP455x_502), + MCP4531_ID_TABLE("mcp4551-103", MCP455x_103), + MCP4531_ID_TABLE("mcp4551-503", MCP455x_503), + MCP4531_ID_TABLE("mcp4551-104", MCP455x_104), + MCP4531_ID_TABLE("mcp4552-502", MCP455x_502), + MCP4531_ID_TABLE("mcp4552-103", MCP455x_103), + MCP4531_ID_TABLE("mcp4552-503", MCP455x_503), + MCP4531_ID_TABLE("mcp4552-104", MCP455x_104), + MCP4531_ID_TABLE("mcp4561-502", MCP456x_502), + MCP4531_ID_TABLE("mcp4561-103", MCP456x_103), + MCP4531_ID_TABLE("mcp4561-503", MCP456x_503), + MCP4531_ID_TABLE("mcp4561-104", MCP456x_104), + MCP4531_ID_TABLE("mcp4562-502", MCP456x_502), + MCP4531_ID_TABLE("mcp4562-103", MCP456x_103), + MCP4531_ID_TABLE("mcp4562-503", MCP456x_503), + MCP4531_ID_TABLE("mcp4562-104", MCP456x_104), + MCP4531_ID_TABLE("mcp4631-502", MCP463x_502), + MCP4531_ID_TABLE("mcp4631-103", MCP463x_103), + MCP4531_ID_TABLE("mcp4631-503", MCP463x_503), + MCP4531_ID_TABLE("mcp4631-104", MCP463x_104), + MCP4531_ID_TABLE("mcp4632-502", MCP463x_502), + MCP4531_ID_TABLE("mcp4632-103", MCP463x_103), + MCP4531_ID_TABLE("mcp4632-503", MCP463x_503), + MCP4531_ID_TABLE("mcp4632-104", MCP463x_104), + MCP4531_ID_TABLE("mcp4641-502", MCP464x_502), + MCP4531_ID_TABLE("mcp4641-103", MCP464x_103), + MCP4531_ID_TABLE("mcp4641-503", MCP464x_503), + MCP4531_ID_TABLE("mcp4641-104", MCP464x_104), + MCP4531_ID_TABLE("mcp4642-502", MCP464x_502), + MCP4531_ID_TABLE("mcp4642-103", MCP464x_103), + MCP4531_ID_TABLE("mcp4642-503", MCP464x_503), + MCP4531_ID_TABLE("mcp4642-104", MCP464x_104), + MCP4531_ID_TABLE("mcp4651-502", MCP465x_502), + MCP4531_ID_TABLE("mcp4651-103", MCP465x_103), + MCP4531_ID_TABLE("mcp4651-503", MCP465x_503), + MCP4531_ID_TABLE("mcp4651-104", MCP465x_104), + MCP4531_ID_TABLE("mcp4652-502", MCP465x_502), + MCP4531_ID_TABLE("mcp4652-103", MCP465x_103), + MCP4531_ID_TABLE("mcp4652-503", MCP465x_503), + MCP4531_ID_TABLE("mcp4652-104", MCP465x_104), + MCP4531_ID_TABLE("mcp4661-502", MCP466x_502), + MCP4531_ID_TABLE("mcp4661-103", MCP466x_103), + MCP4531_ID_TABLE("mcp4661-503", MCP466x_503), + MCP4531_ID_TABLE("mcp4661-104", MCP466x_104), + MCP4531_ID_TABLE("mcp4662-502", MCP466x_502), + MCP4531_ID_TABLE("mcp4662-103", MCP466x_103), + MCP4531_ID_TABLE("mcp4662-503", MCP466x_503), + MCP4531_ID_TABLE("mcp4662-104", MCP466x_104), + { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, mcp4531_id); @@ -368,9 +373,7 @@ static int mcp4531_probe(struct i2c_client *client) i2c_set_clientdata(client, indio_dev); data->client = client; - data->cfg = device_get_match_data(dev); - if (!data->cfg) - data->cfg = &mcp4531_cfg[i2c_match_id(mcp4531_id, client)->driver_data]; + data->cfg = i2c_get_match_data(client); indio_dev->info = &mcp4531_info; indio_dev->channels = mcp4531_channels; diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig index 0e5c17530b8b..2ca3b0bc5eba 100644 --- a/drivers/iio/proximity/Kconfig +++ b/drivers/iio/proximity/Kconfig @@ -32,6 +32,18 @@ config CROS_EC_MKBP_PROXIMITY To compile this driver as a module, choose M here: the module will be called cros_ec_mkbp_proximity. +config IRSD200 + tristate "Murata IRS-D200 PIR sensor" + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select REGMAP_I2C + depends on I2C + help + Say Y here to build a driver for the Murata IRS-D200 PIR sensor. + + To compile this driver as a module, choose M here: the module will be + called irsd200. + 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 cc838bb5408a..f36598380446 100644 --- a/drivers/iio/proximity/Makefile +++ b/drivers/iio/proximity/Makefile @@ -6,6 +6,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_IRSD200) += irsd200.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/irsd200.c b/drivers/iio/proximity/irsd200.c new file mode 100644 index 000000000000..5bd791b46d98 --- /dev/null +++ b/drivers/iio/proximity/irsd200.c @@ -0,0 +1,958 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Murata IRS-D200 PIR sensor. + * + * Copyright (C) 2023 Axis Communications AB + */ + +#include <asm/unaligned.h> +#include <linux/bitfield.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/types.h> + +#define IRS_DRV_NAME "irsd200" + +/* Registers. */ +#define IRS_REG_OP 0x00 /* Operation mode. */ +#define IRS_REG_DATA_LO 0x02 /* Sensor data LSB. */ +#define IRS_REG_DATA_HI 0x03 /* Sensor data MSB. */ +#define IRS_REG_STATUS 0x04 /* Interrupt status. */ +#define IRS_REG_COUNT 0x05 /* Count of exceeding threshold. */ +#define IRS_REG_DATA_RATE 0x06 /* Output data rate. */ +#define IRS_REG_FILTER 0x07 /* High-pass and low-pass filter. */ +#define IRS_REG_INTR 0x09 /* Interrupt mode. */ +#define IRS_REG_NR_COUNT 0x0a /* Number of counts before interrupt. */ +#define IRS_REG_THR_HI 0x0b /* Upper threshold. */ +#define IRS_REG_THR_LO 0x0c /* Lower threshold. */ +#define IRS_REG_TIMER_LO 0x0d /* Timer setting LSB. */ +#define IRS_REG_TIMER_HI 0x0e /* Timer setting MSB. */ + +/* Interrupt status bits. */ +#define IRS_INTR_DATA 0 /* Data update. */ +#define IRS_INTR_TIMER 1 /* Timer expiration. */ +#define IRS_INTR_COUNT_THR_AND 2 /* Count "AND" threshold. */ +#define IRS_INTR_COUNT_THR_OR 3 /* Count "OR" threshold. */ + +/* Operation states. */ +#define IRS_OP_ACTIVE 0x00 +#define IRS_OP_SLEEP 0x01 + +/* + * Quantization scale value for threshold. Used for conversion from/to register + * value. + */ +#define IRS_THR_QUANT_SCALE 128 + +#define IRS_UPPER_COUNT(count) FIELD_GET(GENMASK(7, 4), count) +#define IRS_LOWER_COUNT(count) FIELD_GET(GENMASK(3, 0), count) + +/* Index corresponds to the value of IRS_REG_DATA_RATE register. */ +static const int irsd200_data_rates[] = { + 50, + 100, +}; + +/* Index corresponds to the (field) value of IRS_REG_FILTER register. */ +static const unsigned int irsd200_lp_filter_freq[] = { + 10, + 7, +}; + +/* + * Index corresponds to the (field) value of IRS_REG_FILTER register. Note that + * this represents a fractional value (e.g the first value corresponds to 3 / 10 + * = 0.3 Hz). + */ +static const unsigned int irsd200_hp_filter_freq[][2] = { + { 3, 10 }, + { 5, 10 }, +}; + +/* Register fields. */ +enum irsd200_regfield { + /* Data interrupt. */ + IRS_REGF_INTR_DATA, + /* Timer interrupt. */ + IRS_REGF_INTR_TIMER, + /* AND count threshold interrupt. */ + IRS_REGF_INTR_COUNT_THR_AND, + /* OR count threshold interrupt. */ + IRS_REGF_INTR_COUNT_THR_OR, + + /* Low-pass filter frequency. */ + IRS_REGF_LP_FILTER, + /* High-pass filter frequency. */ + IRS_REGF_HP_FILTER, + + /* Sentinel value. */ + IRS_REGF_MAX +}; + +static const struct reg_field irsd200_regfields[] = { + [IRS_REGF_INTR_DATA] = + REG_FIELD(IRS_REG_INTR, IRS_INTR_DATA, IRS_INTR_DATA), + [IRS_REGF_INTR_TIMER] = + REG_FIELD(IRS_REG_INTR, IRS_INTR_TIMER, IRS_INTR_TIMER), + [IRS_REGF_INTR_COUNT_THR_AND] = REG_FIELD( + IRS_REG_INTR, IRS_INTR_COUNT_THR_AND, IRS_INTR_COUNT_THR_AND), + [IRS_REGF_INTR_COUNT_THR_OR] = REG_FIELD( + IRS_REG_INTR, IRS_INTR_COUNT_THR_OR, IRS_INTR_COUNT_THR_OR), + + [IRS_REGF_LP_FILTER] = REG_FIELD(IRS_REG_FILTER, 1, 1), + [IRS_REGF_HP_FILTER] = REG_FIELD(IRS_REG_FILTER, 0, 0), +}; + +static const struct regmap_config irsd200_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = IRS_REG_TIMER_HI, +}; + +struct irsd200_data { + struct regmap *regmap; + struct regmap_field *regfields[IRS_REGF_MAX]; + struct device *dev; +}; + +static int irsd200_setup(struct irsd200_data *data) +{ + unsigned int val; + int ret; + + /* Disable all interrupt sources. */ + ret = regmap_write(data->regmap, IRS_REG_INTR, 0); + if (ret) { + dev_err(data->dev, "Could not set interrupt sources (%d)\n", + ret); + return ret; + } + + /* Set operation to active. */ + ret = regmap_write(data->regmap, IRS_REG_OP, IRS_OP_ACTIVE); + if (ret) { + dev_err(data->dev, "Could not set operation mode (%d)\n", ret); + return ret; + } + + /* Clear threshold count. */ + ret = regmap_read(data->regmap, IRS_REG_COUNT, &val); + if (ret) { + dev_err(data->dev, "Could not clear threshold count (%d)\n", + ret); + return ret; + } + + /* Clear status. */ + ret = regmap_write(data->regmap, IRS_REG_STATUS, 0x0f); + if (ret) { + dev_err(data->dev, "Could not clear status (%d)\n", ret); + return ret; + } + + return 0; +} + +static int irsd200_read_threshold(struct irsd200_data *data, + enum iio_event_direction dir, int *val) +{ + unsigned int regval; + unsigned int reg; + int scale; + int ret; + + /* Set quantization scale. */ + if (dir == IIO_EV_DIR_RISING) { + scale = IRS_THR_QUANT_SCALE; + reg = IRS_REG_THR_HI; + } else if (dir == IIO_EV_DIR_FALLING) { + scale = -IRS_THR_QUANT_SCALE; + reg = IRS_REG_THR_LO; + } else { + return -EINVAL; + } + + ret = regmap_read(data->regmap, reg, ®val); + if (ret) { + dev_err(data->dev, "Could not read threshold (%d)\n", ret); + return ret; + } + + *val = ((int)regval) * scale; + + return 0; +} + +static int irsd200_write_threshold(struct irsd200_data *data, + enum iio_event_direction dir, int val) +{ + unsigned int regval; + unsigned int reg; + int scale; + int ret; + + /* Set quantization scale. */ + if (dir == IIO_EV_DIR_RISING) { + if (val < 0) + return -ERANGE; + + scale = IRS_THR_QUANT_SCALE; + reg = IRS_REG_THR_HI; + } else if (dir == IIO_EV_DIR_FALLING) { + if (val > 0) + return -ERANGE; + + scale = -IRS_THR_QUANT_SCALE; + reg = IRS_REG_THR_LO; + } else { + return -EINVAL; + } + + regval = val / scale; + + if (regval >= BIT(8)) + return -ERANGE; + + ret = regmap_write(data->regmap, reg, regval); + if (ret) { + dev_err(data->dev, "Could not write threshold (%d)\n", ret); + return ret; + } + + return 0; +} + +static int irsd200_read_data(struct irsd200_data *data, s16 *val) +{ + __le16 buf; + int ret; + + ret = regmap_bulk_read(data->regmap, IRS_REG_DATA_LO, &buf, + sizeof(buf)); + if (ret) { + dev_err(data->dev, "Could not bulk read data (%d)\n", ret); + return ret; + } + + *val = le16_to_cpu(buf); + + return 0; +} + +static int irsd200_read_data_rate(struct irsd200_data *data, int *val) +{ + unsigned int regval; + int ret; + + ret = regmap_read(data->regmap, IRS_REG_DATA_RATE, ®val); + if (ret) { + dev_err(data->dev, "Could not read data rate (%d)\n", ret); + return ret; + } + + if (regval >= ARRAY_SIZE(irsd200_data_rates)) + return -ERANGE; + + *val = irsd200_data_rates[regval]; + + return 0; +} + +static int irsd200_write_data_rate(struct irsd200_data *data, int val) +{ + size_t idx; + int ret; + + for (idx = 0; idx < ARRAY_SIZE(irsd200_data_rates); ++idx) { + if (irsd200_data_rates[idx] == val) + break; + } + + if (idx == ARRAY_SIZE(irsd200_data_rates)) + return -ERANGE; + + ret = regmap_write(data->regmap, IRS_REG_DATA_RATE, idx); + if (ret) { + dev_err(data->dev, "Could not write data rate (%d)\n", ret); + return ret; + } + + /* + * Data sheet says the device needs 3 seconds of settling time. The + * device operates normally during this period though. This is more of a + * "guarantee" than trying to prevent other user space reads/writes. + */ + ssleep(3); + + return 0; +} + +static int irsd200_read_timer(struct irsd200_data *data, int *val, int *val2) +{ + __le16 buf; + int ret; + + ret = regmap_bulk_read(data->regmap, IRS_REG_TIMER_LO, &buf, + sizeof(buf)); + if (ret) { + dev_err(data->dev, "Could not bulk read timer (%d)\n", ret); + return ret; + } + + ret = irsd200_read_data_rate(data, val2); + if (ret) + return ret; + + *val = le16_to_cpu(buf); + + return 0; +} + +static int irsd200_write_timer(struct irsd200_data *data, int val, int val2) +{ + unsigned int regval; + int data_rate; + __le16 buf; + int ret; + + if (val < 0 || val2 < 0) + return -ERANGE; + + ret = irsd200_read_data_rate(data, &data_rate); + if (ret) + return ret; + + /* Quantize from seconds. */ + regval = val * data_rate + (val2 * data_rate) / 1000000; + + /* Value is 10 bits. */ + if (regval >= BIT(10)) + return -ERANGE; + + buf = cpu_to_le16((u16)regval); + + ret = regmap_bulk_write(data->regmap, IRS_REG_TIMER_LO, &buf, + sizeof(buf)); + if (ret) { + dev_err(data->dev, "Could not bulk write timer (%d)\n", ret); + return ret; + } + + return 0; +} + +static int irsd200_read_nr_count(struct irsd200_data *data, int *val) +{ + unsigned int regval; + int ret; + + ret = regmap_read(data->regmap, IRS_REG_NR_COUNT, ®val); + if (ret) { + dev_err(data->dev, "Could not read nr count (%d)\n", ret); + return ret; + } + + *val = regval; + + return 0; +} + +static int irsd200_write_nr_count(struct irsd200_data *data, int val) +{ + unsigned int regval; + int ret; + + /* A value of zero means that IRS_REG_STATUS is never set. */ + if (val <= 0 || val >= 8) + return -ERANGE; + + regval = val; + + if (regval >= 2) { + /* + * According to the data sheet, timer must be also set in this + * case (i.e. be non-zero). Check and enforce that. + */ + ret = irsd200_read_timer(data, &val, &val); + if (ret) + return ret; + + if (val == 0) { + dev_err(data->dev, + "Timer must be non-zero when nr count is %u\n", + regval); + return -EPERM; + } + } + + ret = regmap_write(data->regmap, IRS_REG_NR_COUNT, regval); + if (ret) { + dev_err(data->dev, "Could not write nr count (%d)\n", ret); + return ret; + } + + return 0; +} + +static int irsd200_read_lp_filter(struct irsd200_data *data, int *val) +{ + unsigned int regval; + int ret; + + ret = regmap_field_read(data->regfields[IRS_REGF_LP_FILTER], ®val); + if (ret) { + dev_err(data->dev, "Could not read lp filter frequency (%d)\n", + ret); + return ret; + } + + *val = irsd200_lp_filter_freq[regval]; + + return 0; +} + +static int irsd200_write_lp_filter(struct irsd200_data *data, int val) +{ + size_t idx; + int ret; + + for (idx = 0; idx < ARRAY_SIZE(irsd200_lp_filter_freq); ++idx) { + if (irsd200_lp_filter_freq[idx] == val) + break; + } + + if (idx == ARRAY_SIZE(irsd200_lp_filter_freq)) + return -ERANGE; + + ret = regmap_field_write(data->regfields[IRS_REGF_LP_FILTER], idx); + if (ret) { + dev_err(data->dev, "Could not write lp filter frequency (%d)\n", + ret); + return ret; + } + + return 0; +} + +static int irsd200_read_hp_filter(struct irsd200_data *data, int *val, + int *val2) +{ + unsigned int regval; + int ret; + + ret = regmap_field_read(data->regfields[IRS_REGF_HP_FILTER], ®val); + if (ret) { + dev_err(data->dev, "Could not read hp filter frequency (%d)\n", + ret); + return ret; + } + + *val = irsd200_hp_filter_freq[regval][0]; + *val2 = irsd200_hp_filter_freq[regval][1]; + + return 0; +} + +static int irsd200_write_hp_filter(struct irsd200_data *data, int val, int val2) +{ + size_t idx; + int ret; + + /* Truncate fractional part to one digit. */ + val2 /= 100000; + + for (idx = 0; idx < ARRAY_SIZE(irsd200_hp_filter_freq); ++idx) { + if (irsd200_hp_filter_freq[idx][0] == val2) + break; + } + + if (idx == ARRAY_SIZE(irsd200_hp_filter_freq) || val != 0) + return -ERANGE; + + ret = regmap_field_write(data->regfields[IRS_REGF_HP_FILTER], idx); + if (ret) { + dev_err(data->dev, "Could not write hp filter frequency (%d)\n", + ret); + return ret; + } + + return 0; +} + +static int irsd200_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct irsd200_data *data = iio_priv(indio_dev); + int ret; + s16 buf; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = irsd200_read_data(data, &buf); + if (ret) + return ret; + + *val = buf; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = irsd200_read_data_rate(data, val); + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + ret = irsd200_read_lp_filter(data, val); + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + ret = irsd200_read_hp_filter(data, val, val2); + if (ret) + return ret; + + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } +} + +static int irsd200_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 = irsd200_data_rates; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(irsd200_data_rates); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + *vals = irsd200_lp_filter_freq; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(irsd200_lp_filter_freq); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + *vals = (int *)irsd200_hp_filter_freq; + *type = IIO_VAL_FRACTIONAL; + *length = 2 * ARRAY_SIZE(irsd200_hp_filter_freq); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int irsd200_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct irsd200_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return irsd200_write_data_rate(data, val); + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + return irsd200_write_lp_filter(data, val); + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + return irsd200_write_hp_filter(data, val, val2); + default: + return -EINVAL; + } +} + +static int irsd200_read_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, int *val2) +{ + struct irsd200_data *data = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_EV_INFO_VALUE: + ret = irsd200_read_threshold(data, dir, val); + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_EV_INFO_RUNNING_PERIOD: + ret = irsd200_read_timer(data, val, val2); + if (ret) + return ret; + + return IIO_VAL_FRACTIONAL; + case IIO_EV_INFO_RUNNING_COUNT: + ret = irsd200_read_nr_count(data, val); + if (ret) + return ret; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int irsd200_write_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val, int val2) +{ + struct irsd200_data *data = iio_priv(indio_dev); + + switch (info) { + case IIO_EV_INFO_VALUE: + return irsd200_write_threshold(data, dir, val); + case IIO_EV_INFO_RUNNING_PERIOD: + return irsd200_write_timer(data, val, val2); + case IIO_EV_INFO_RUNNING_COUNT: + return irsd200_write_nr_count(data, val); + default: + return -EINVAL; + } +} + +static int irsd200_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 irsd200_data *data = iio_priv(indio_dev); + unsigned int val; + int ret; + + switch (type) { + case IIO_EV_TYPE_THRESH: + ret = regmap_field_read( + data->regfields[IRS_REGF_INTR_COUNT_THR_OR], &val); + if (ret) + return ret; + + return val; + default: + return -EINVAL; + } +} + +static int irsd200_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 irsd200_data *data = iio_priv(indio_dev); + unsigned int tmp; + int ret; + + switch (type) { + case IIO_EV_TYPE_THRESH: + /* Clear the count register (by reading from it). */ + ret = regmap_read(data->regmap, IRS_REG_COUNT, &tmp); + if (ret) + return ret; + + return regmap_field_write( + data->regfields[IRS_REGF_INTR_COUNT_THR_OR], !!state); + default: + return -EINVAL; + } +} + +static irqreturn_t irsd200_irq_thread(int irq, void *dev_id) +{ + struct iio_dev *indio_dev = dev_id; + struct irsd200_data *data = iio_priv(indio_dev); + enum iio_event_direction dir; + unsigned int lower_count; + unsigned int upper_count; + unsigned int status = 0; + unsigned int source = 0; + unsigned int clear = 0; + unsigned int count = 0; + int ret; + + ret = regmap_read(data->regmap, IRS_REG_INTR, &source); + if (ret) { + dev_err(data->dev, "Could not read interrupt source (%d)\n", + ret); + return IRQ_HANDLED; + } + + ret = regmap_read(data->regmap, IRS_REG_STATUS, &status); + if (ret) { + dev_err(data->dev, "Could not acknowledge interrupt (%d)\n", + ret); + return IRQ_HANDLED; + } + + if (status & BIT(IRS_INTR_DATA) && iio_buffer_enabled(indio_dev)) { + iio_trigger_poll_nested(indio_dev->trig); + clear |= BIT(IRS_INTR_DATA); + } + + if (status & BIT(IRS_INTR_COUNT_THR_OR) && + source & BIT(IRS_INTR_COUNT_THR_OR)) { + /* + * The register value resets to zero after reading. We therefore + * need to read once and manually extract the lower and upper + * count register fields. + */ + ret = regmap_read(data->regmap, IRS_REG_COUNT, &count); + if (ret) + dev_err(data->dev, "Could not read count (%d)\n", ret); + + upper_count = IRS_UPPER_COUNT(count); + lower_count = IRS_LOWER_COUNT(count); + + /* + * We only check the OR mode to be able to push events for + * rising and falling thresholds. AND mode is covered when both + * upper and lower count is non-zero, and is signaled with + * IIO_EV_DIR_EITHER. + */ + if (upper_count && !lower_count) + dir = IIO_EV_DIR_RISING; + else if (!upper_count && lower_count) + dir = IIO_EV_DIR_FALLING; + else + dir = IIO_EV_DIR_EITHER; + + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, + IIO_EV_TYPE_THRESH, dir), + iio_get_time_ns(indio_dev)); + + /* + * The OR mode will always trigger when the AND mode does, but + * not vice versa. However, it seems like the AND bit needs to + * be cleared if data capture _and_ threshold count interrupts + * are desirable, even though it hasn't explicitly been selected + * (with IRS_REG_INTR). Either way, it doesn't hurt... + */ + clear |= BIT(IRS_INTR_COUNT_THR_OR) | + BIT(IRS_INTR_COUNT_THR_AND); + } + + if (!clear) + return IRQ_NONE; + + ret = regmap_write(data->regmap, IRS_REG_STATUS, clear); + if (ret) + dev_err(data->dev, + "Could not clear interrupt status (%d)\n", ret); + + return IRQ_HANDLED; +} + +static irqreturn_t irsd200_trigger_handler(int irq, void *pollf) +{ + struct iio_dev *indio_dev = ((struct iio_poll_func *)pollf)->indio_dev; + struct irsd200_data *data = iio_priv(indio_dev); + s16 buf = 0; + int ret; + + ret = irsd200_read_data(data, &buf); + if (ret) + goto end; + + iio_push_to_buffers_with_timestamp(indio_dev, &buf, + iio_get_time_ns(indio_dev)); + +end: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int irsd200_set_trigger_state(struct iio_trigger *trig, bool state) +{ + struct irsd200_data *data = iio_trigger_get_drvdata(trig); + int ret; + + ret = regmap_field_write(data->regfields[IRS_REGF_INTR_DATA], state); + if (ret) { + dev_err(data->dev, "Could not %s data interrupt source (%d)\n", + state ? "enable" : "disable", ret); + } + + return ret; +} + +static const struct iio_info irsd200_info = { + .read_raw = irsd200_read_raw, + .read_avail = irsd200_read_avail, + .write_raw = irsd200_write_raw, + .read_event_value = irsd200_read_event, + .write_event_value = irsd200_write_event, + .read_event_config = irsd200_read_event_config, + .write_event_config = irsd200_write_event_config, +}; + +static const struct iio_trigger_ops irsd200_trigger_ops = { + .set_trigger_state = irsd200_set_trigger_state, + .validate_device = iio_trigger_validate_own_device, +}; + +static const struct iio_event_spec irsd200_event_spec[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = + BIT(IIO_EV_INFO_RUNNING_PERIOD) | + BIT(IIO_EV_INFO_RUNNING_COUNT) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_chan_spec irsd200_channels[] = { + { + .type = IIO_PROXIMITY, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), + .event_spec = irsd200_event_spec, + .num_event_specs = ARRAY_SIZE(irsd200_event_spec), + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, +}; + +static int irsd200_probe(struct i2c_client *client) +{ + struct iio_trigger *trigger; + struct irsd200_data *data; + struct iio_dev *indio_dev; + size_t i; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return dev_err_probe(&client->dev, -ENOMEM, + "Could not allocate iio device\n"); + + data = iio_priv(indio_dev); + data->dev = &client->dev; + + data->regmap = devm_regmap_init_i2c(client, &irsd200_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(data->dev, PTR_ERR(data->regmap), + "Could not initialize regmap\n"); + + for (i = 0; i < IRS_REGF_MAX; ++i) { + data->regfields[i] = devm_regmap_field_alloc( + data->dev, data->regmap, irsd200_regfields[i]); + if (IS_ERR(data->regfields[i])) + return dev_err_probe( + data->dev, PTR_ERR(data->regfields[i]), + "Could not allocate register field %zu\n", i); + } + + ret = devm_regulator_get_enable(data->dev, "vdd"); + if (ret) + return dev_err_probe( + data->dev, ret, + "Could not get and enable regulator (%d)\n", ret); + + ret = irsd200_setup(data); + if (ret) + return ret; + + indio_dev->info = &irsd200_info; + indio_dev->name = IRS_DRV_NAME; + indio_dev->channels = irsd200_channels; + indio_dev->num_channels = ARRAY_SIZE(irsd200_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + if (!client->irq) + return dev_err_probe(data->dev, -ENXIO, "No irq available\n"); + + ret = devm_iio_triggered_buffer_setup(data->dev, indio_dev, NULL, + irsd200_trigger_handler, NULL); + if (ret) + return dev_err_probe( + data->dev, ret, + "Could not setup iio triggered buffer (%d)\n", ret); + + ret = devm_request_threaded_irq(data->dev, client->irq, NULL, + irsd200_irq_thread, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + NULL, indio_dev); + if (ret) + return dev_err_probe(data->dev, ret, + "Could not request irq (%d)\n", ret); + + trigger = devm_iio_trigger_alloc(data->dev, "%s-dev%d", indio_dev->name, + iio_device_id(indio_dev)); + if (!trigger) + return dev_err_probe(data->dev, -ENOMEM, + "Could not allocate iio trigger\n"); + + trigger->ops = &irsd200_trigger_ops; + iio_trigger_set_drvdata(trigger, data); + + ret = devm_iio_trigger_register(data->dev, trigger); + if (ret) + return dev_err_probe(data->dev, ret, + "Could not register iio trigger (%d)\n", + ret); + + ret = devm_iio_device_register(data->dev, indio_dev); + if (ret) + return dev_err_probe(data->dev, ret, + "Could not register iio device (%d)\n", + ret); + + return 0; +} + +static const struct of_device_id irsd200_of_match[] = { + { + .compatible = "murata,irsd200", + }, + {} +}; +MODULE_DEVICE_TABLE(of, irsd200_of_match); + +static struct i2c_driver irsd200_driver = { + .driver = { + .name = IRS_DRV_NAME, + .of_match_table = irsd200_of_match, + }, + .probe = irsd200_probe, +}; +module_i2c_driver(irsd200_driver); + +MODULE_AUTHOR("Waqar Hameed <waqar.hameed@axis.com>"); +MODULE_DESCRIPTION("Murata IRS-D200 PIR sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/proximity/isl29501.c b/drivers/iio/proximity/isl29501.c index fe45ca35a124..bcebacaf3dab 100644 --- a/drivers/iio/proximity/isl29501.c +++ b/drivers/iio/proximity/isl29501.c @@ -12,7 +12,7 @@ #include <linux/module.h> #include <linux/i2c.h> #include <linux/err.h> -#include <linux/of_device.h> +#include <linux/mod_devicetable.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> diff --git a/drivers/iio/proximity/mb1232.c b/drivers/iio/proximity/mb1232.c index fb1073c8d9f7..614e65cb9d42 100644 --- a/drivers/iio/proximity/mb1232.c +++ b/drivers/iio/proximity/mb1232.c @@ -76,7 +76,7 @@ static s16 mb1232_read_distance(struct mb1232_data *data) goto error_unlock; } - if (data->irqnr >= 0) { + if (data->irqnr > 0) { /* it cannot take more than 100 ms */ ret = wait_for_completion_killable_timeout(&data->ranging, HZ/10); @@ -212,10 +212,7 @@ static int mb1232_probe(struct i2c_client *client) init_completion(&data->ranging); data->irqnr = fwnode_irq_get(dev_fwnode(&client->dev), 0); - if (data->irqnr <= 0) { - /* usage of interrupt is optional */ - data->irqnr = -1; - } else { + if (data->irqnr > 0) { ret = devm_request_irq(dev, data->irqnr, mb1232_handle_irq, IRQF_TRIGGER_FALLING, id->name, indio_dev); if (ret < 0) { diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c index 676dc8701924..07bb5df24ab3 100644 --- a/drivers/iio/temperature/mlx90614.c +++ b/drivers/iio/temperature/mlx90614.c @@ -27,8 +27,8 @@ #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/jiffies.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> -#include <linux/of_device.h> #include <linux/pm_runtime.h> #include <linux/iio/iio.h> diff --git a/drivers/iio/trigger/stm32-lptimer-trigger.c b/drivers/iio/trigger/stm32-lptimer-trigger.c index 2e447a3f047d..f1e18913236a 100644 --- a/drivers/iio/trigger/stm32-lptimer-trigger.c +++ b/drivers/iio/trigger/stm32-lptimer-trigger.c @@ -73,7 +73,6 @@ static int stm32_lptim_trigger_probe(struct platform_device *pdev) { struct stm32_lptim_trigger *priv; u32 index; - int ret; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -88,13 +87,7 @@ static int stm32_lptim_trigger_probe(struct platform_device *pdev) priv->dev = &pdev->dev; priv->trg = stm32_lptim_triggers[index]; - ret = stm32_lptim_setup_trig(priv); - if (ret) - return ret; - - platform_set_drvdata(pdev, priv); - - return 0; + return stm32_lptim_setup_trig(priv); } static const struct of_device_id stm32_lptim_trig_of_match[] = { |