diff options
31 files changed, 908 insertions, 130 deletions
diff --git a/Documentation/devicetree/bindings/iio/accel/bma180.txt b/Documentation/devicetree/bindings/iio/accel/bma180.txt index f53237270b32..33da4a6fdb39 100644 --- a/Documentation/devicetree/bindings/iio/accel/bma180.txt +++ b/Documentation/devicetree/bindings/iio/accel/bma180.txt @@ -1,15 +1,21 @@ -* Bosch BMA180 / BMA25x triaxial acceleration sensor +* Bosch BMA023 / BMA150/ BMA180 / BMA25x / SMB380 triaxial acceleration sensor +https://media.digikey.com/pdf/Data%20Sheets/Bosch/BMA150.pdf http://omapworld.com/BMA180_111_1002839.pdf http://ae-bst.resource.bosch.com/media/products/dokumente/bma250/bst-bma250-ds002-05.pdf Required properties: - compatible : should be one of: + "bosch,bma023" + "bosch,bma150" "bosch,bma180" "bosch,bma250" "bosch,bma254" + "bosch,smb380" - reg : the I2C address of the sensor + - vdd-supply : regulator phandle connected to the VDD pin + - vddio-supply : regulator phandle connected to the VDDIO pin Optional properties: diff --git a/Documentation/devicetree/bindings/iio/chemical/atlas,sensor.yaml b/Documentation/devicetree/bindings/iio/chemical/atlas,sensor.yaml index 0e510a52920d..69e8931e0ae8 100644 --- a/Documentation/devicetree/bindings/iio/chemical/atlas,sensor.yaml +++ b/Documentation/devicetree/bindings/iio/chemical/atlas,sensor.yaml @@ -4,13 +4,13 @@ $id: http://devicetree.org/schemas/iio/chemical/atlas,sensor.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Atlas Scientific OEM sensors +title: Atlas Scientific OEM + EZO sensors maintainers: - Matt Ranostay <matt.ranostay@konsulko.com> description: | - Atlas Scientific OEM sensors connected via I2C + Atlas Scientific OEM + EZO sensors connected via I2C Datasheets: http://www.atlas-scientific.com/_files/_datasheets/_oem/DO_oem_datasheet.pdf @@ -18,6 +18,7 @@ description: | http://www.atlas-scientific.com/_files/_datasheets/_oem/ORP_oem_datasheet.pdf http://www.atlas-scientific.com/_files/_datasheets/_oem/pH_oem_datasheet.pdf http://www.atlas-scientific.com/_files/_datasheets/_oem/RTD_oem_datasheet.pdf + http://www.atlas-scientific.com/_files/_datasheets/_probe/EZO_CO2_Datasheet.pdf properties: compatible: @@ -27,6 +28,7 @@ properties: - atlas,orp-sm - atlas,ph-sm - atlas,rtd-sm + - atlas,co2-ezo reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/proximity/vishay,vcnl3020.yaml b/Documentation/devicetree/bindings/iio/proximity/vishay,vcnl3020.yaml new file mode 100644 index 000000000000..4190253336ec --- /dev/null +++ b/Documentation/devicetree/bindings/iio/proximity/vishay,vcnl3020.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/proximity/vishay,vcnl3020.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Integrated Proximity Sensor With Infrared Emitter + +maintainers: + - Ivan Mikhaylov <i.mikhaylov@yadro.com> + +description: | + The VCNL3020 is a fully integrated proximity sensor. Fully integrated means + that the infrared emitter is included in the package. It has 16-bit + resolution. It includes a signal processing IC and features standard I2C + communication interface. It features an interrupt function. + + Specifications about the devices can be found at: + https://www.vishay.com/docs/84150/vcnl3020.pdf + +properties: + compatible: + enum: + - vishay,vcnl3020 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vdd-supply: + description: Regulator that provides power to the sensor + + vddio-supply: + description: Regulator that provides power to the bus + + vishay,led-current-microamp: + description: + The driver current for the LED used in proximity sensing. + enum: [0, 10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000, 90000, + 100000, 110000, 120000, 130000, 140000, 150000, 160000, 170000, + 180000, 190000, 200000] + default: 20000 + +required: + - compatible + - reg + +examples: + - | + i2c { + + #address-cells = <1>; + #size-cells = <0>; + + proximity@13 { + compatible = "vishay,vcnl3020"; + reg = <0x13>; + vishay,led-current-microamp = <200000>; + }; + }; diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 24ebe9e76915..1080637ca40e 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -89,13 +89,13 @@ config ADXL372_I2C module will be called adxl372_i2c. config BMA180 - tristate "Bosch BMA180/BMA25x 3-Axis Accelerometer Driver" - depends on I2C + tristate "Bosch BMA023/BMA1x0/BMA25x 3-Axis Accelerometer Driver" + depends on I2C && INPUT_BMA150=n select IIO_BUFFER select IIO_TRIGGERED_BUFFER help - Say Y here if you want to build a driver for the Bosch BMA180 or - BMA25x triaxial acceleration sensor. + Say Y here if you want to build a driver for the Bosch BMA023, BMA150 + BMA180, SMB380, or BMA25x triaxial acceleration sensor. To compile this driver as a module, choose M here: the module will be called bma180. diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index fcd91d5f05fd..265722e4b13f 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -7,6 +7,7 @@ * Support for BMA250 (c) Peter Meerwald <pmeerw@pmeerw.net> * * SPI is not supported by driver + * BMA023/BMA150/SMB380: 7-bit I2C slave address 0x38 * BMA180: 7-bit I2C slave address 0x40 or 0x41 * BMA250: 7-bit I2C slave address 0x18 or 0x19 * BMA254: 7-bit I2C slave address 0x18 or 0x19 @@ -33,6 +34,8 @@ #define BMA180_IRQ_NAME "bma180_event" enum chip_ids { + BMA023, + BMA150, BMA180, BMA250, BMA254, @@ -48,7 +51,7 @@ struct bma180_part_info { unsigned int num_scales; const int *bw_table; unsigned int num_bw; - int center_temp; + int temp_offset; u8 int_reset_reg, int_reset_mask; u8 sleep_reg, sleep_mask; @@ -57,13 +60,25 @@ struct bma180_part_info { u8 power_reg, power_mask, lowpower_val; u8 int_enable_reg, int_enable_mask; u8 int_map_reg, int_enable_dataready_int1_mask; - u8 softreset_reg; + u8 softreset_reg, softreset_val; int (*chip_config)(struct bma180_data *data); void (*chip_disable)(struct bma180_data *data); }; /* Register set */ +#define BMA023_CTRL_REG0 0x0a +#define BMA023_CTRL_REG1 0x0b +#define BMA023_CTRL_REG2 0x14 +#define BMA023_CTRL_REG3 0x15 + +#define BMA023_RANGE_MASK GENMASK(4, 3) /* Range of accel values */ +#define BMA023_BW_MASK GENMASK(2, 0) /* Accel bandwidth */ +#define BMA023_SLEEP BIT(0) +#define BMA023_INT_RESET_MASK BIT(6) +#define BMA023_NEW_DATA_INT BIT(5) /* Intr every new accel data is ready */ +#define BMA023_RESET_VAL BIT(1) + #define BMA180_CHIP_ID 0x00 /* Need to distinguish BMA180 from other */ #define BMA180_ACC_X_LSB 0x02 /* First of 6 registers of accel data */ #define BMA180_TEMP 0x08 @@ -94,6 +109,7 @@ struct bma180_part_info { /* We have to write this value in reset register to do soft reset */ #define BMA180_RESET_VAL 0xb6 +#define BMA023_ID_REG_VAL 0x02 #define BMA180_ID_REG_VAL 0x03 #define BMA250_ID_REG_VAL 0x03 #define BMA254_ID_REG_VAL 0xfa /* 250 decimal */ @@ -156,6 +172,9 @@ enum bma180_chan { TEMP }; +static int bma023_bw_table[] = { 25, 50, 100, 190, 375, 750, 1500 }; /* Hz */ +static int bma023_scale_table[] = { 2452, 4903, 9709, }; + static int bma180_bw_table[] = { 10, 20, 40, 75, 150, 300 }; /* Hz */ static int bma180_scale_table[] = { 1275, 1863, 2452, 3727, 4903, 9709, 19417 }; @@ -319,7 +338,8 @@ static int bma180_set_pmode(struct bma180_data *data, bool mode) static int bma180_soft_reset(struct bma180_data *data) { int ret = i2c_smbus_write_byte_data(data->client, - data->part_info->softreset_reg, BMA180_RESET_VAL); + data->part_info->softreset_reg, + data->part_info->softreset_val); if (ret) dev_err(&data->client->dev, "failed to reset the chip\n"); @@ -349,11 +369,28 @@ static int bma180_chip_init(struct bma180_data *data) */ msleep(20); - ret = bma180_set_new_data_intr_state(data, false); + return bma180_set_new_data_intr_state(data, false); +} + +static int bma023_chip_config(struct bma180_data *data) +{ + int ret = bma180_chip_init(data); + if (ret) - return ret; + goto err; + + ret = bma180_set_bw(data, 50); /* 50 Hz */ + if (ret) + goto err; + ret = bma180_set_scale(data, 2452); /* 2 G */ + if (ret) + goto err; - return bma180_set_pmode(data, false); + return 0; + +err: + dev_err(&data->client->dev, "failed to config the chip\n"); + return ret; } static int bma180_chip_config(struct bma180_data *data) @@ -362,6 +399,9 @@ static int bma180_chip_config(struct bma180_data *data) if (ret) goto err; + ret = bma180_set_pmode(data, false); + if (ret) + goto err; ret = bma180_set_bits(data, BMA180_CTRL_REG0, BMA180_DIS_WAKE_UP, 1); if (ret) goto err; @@ -391,6 +431,9 @@ static int bma25x_chip_config(struct bma180_data *data) if (ret) goto err; + ret = bma180_set_pmode(data, false); + if (ret) + goto err; ret = bma180_set_bw(data, 16); /* 16 Hz */ if (ret) goto err; @@ -413,6 +456,17 @@ err: return ret; } +static void bma023_chip_disable(struct bma180_data *data) +{ + if (bma180_set_sleep_state(data, true)) + goto err; + + return; + +err: + dev_err(&data->client->dev, "failed to disable the chip\n"); +} + static void bma180_chip_disable(struct bma180_data *data) { if (bma180_set_new_data_intr_state(data, false)) @@ -512,8 +566,12 @@ static int bma180_read_raw(struct iio_dev *indio_dev, iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; - *val = sign_extend32(ret >> chan->scan_type.shift, - chan->scan_type.realbits - 1); + if (chan->scan_type.sign == 's') { + *val = sign_extend32(ret >> chan->scan_type.shift, + chan->scan_type.realbits - 1); + } else { + *val = ret; + } return IIO_VAL_INT; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: *val = data->bw; @@ -531,7 +589,7 @@ static int bma180_read_raw(struct iio_dev *indio_dev, return -EINVAL; } case IIO_CHAN_INFO_OFFSET: - *val = data->part_info->center_temp; + *val = data->part_info->temp_offset; return IIO_VAL_INT; default: return -EINVAL; @@ -609,6 +667,11 @@ static const struct iio_enum bma180_power_mode_enum = { .set = bma180_set_power_mode, }; +static const struct iio_chan_spec_ext_info bma023_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, bma180_accel_get_mount_matrix), + { } +}; + static const struct iio_chan_spec_ext_info bma180_ext_info[] = { IIO_ENUM("power_mode", true, &bma180_power_mode_enum), IIO_ENUM_AVAILABLE("power_mode", &bma180_power_mode_enum), @@ -616,6 +679,35 @@ static const struct iio_chan_spec_ext_info bma180_ext_info[] = { { } }; +#define BMA023_ACC_CHANNEL(_axis, _bits) { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ + .scan_index = AXIS_##_axis, \ + .scan_type = { \ + .sign = 's', \ + .realbits = _bits, \ + .storagebits = 16, \ + .shift = 16 - _bits, \ + }, \ + .ext_info = bma023_ext_info, \ +} + +#define BMA150_TEMP_CHANNEL { \ + .type = IIO_TEMP, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), \ + .scan_index = TEMP, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 8, \ + .storagebits = 16, \ + }, \ +} + #define BMA180_ACC_CHANNEL(_axis, _bits) { \ .type = IIO_ACCEL, \ .modified = 1, \ @@ -645,6 +737,21 @@ static const struct iio_chan_spec_ext_info bma180_ext_info[] = { }, \ } +static const struct iio_chan_spec bma023_channels[] = { + BMA023_ACC_CHANNEL(X, 10), + BMA023_ACC_CHANNEL(Y, 10), + BMA023_ACC_CHANNEL(Z, 10), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static const struct iio_chan_spec bma150_channels[] = { + BMA023_ACC_CHANNEL(X, 10), + BMA023_ACC_CHANNEL(Y, 10), + BMA023_ACC_CHANNEL(Z, 10), + BMA150_TEMP_CHANNEL, + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + static const struct iio_chan_spec bma180_channels[] = { BMA180_ACC_CHANNEL(X, 14), BMA180_ACC_CHANNEL(Y, 14), @@ -670,6 +777,63 @@ static const struct iio_chan_spec bma254_channels[] = { }; static const struct bma180_part_info bma180_part_info[] = { + [BMA023] = { + .chip_id = BMA023_ID_REG_VAL, + .channels = bma023_channels, + .num_channels = ARRAY_SIZE(bma023_channels), + .scale_table = bma023_scale_table, + .num_scales = ARRAY_SIZE(bma023_scale_table), + .bw_table = bma023_bw_table, + .num_bw = ARRAY_SIZE(bma023_bw_table), + /* No temperature channel */ + .temp_offset = 0, + .int_reset_reg = BMA023_CTRL_REG0, + .int_reset_mask = BMA023_INT_RESET_MASK, + .sleep_reg = BMA023_CTRL_REG0, + .sleep_mask = BMA023_SLEEP, + .bw_reg = BMA023_CTRL_REG2, + .bw_mask = BMA023_BW_MASK, + .scale_reg = BMA023_CTRL_REG2, + .scale_mask = BMA023_RANGE_MASK, + /* No power mode on bma023 */ + .power_reg = 0, + .power_mask = 0, + .lowpower_val = 0, + .int_enable_reg = BMA023_CTRL_REG3, + .int_enable_mask = BMA023_NEW_DATA_INT, + .softreset_reg = BMA023_CTRL_REG0, + .softreset_val = BMA023_RESET_VAL, + .chip_config = bma023_chip_config, + .chip_disable = bma023_chip_disable, + }, + [BMA150] = { + .chip_id = BMA023_ID_REG_VAL, + .channels = bma150_channels, + .num_channels = ARRAY_SIZE(bma150_channels), + .scale_table = bma023_scale_table, + .num_scales = ARRAY_SIZE(bma023_scale_table), + .bw_table = bma023_bw_table, + .num_bw = ARRAY_SIZE(bma023_bw_table), + .temp_offset = -60, /* 0 LSB @ -30 degree C */ + .int_reset_reg = BMA023_CTRL_REG0, + .int_reset_mask = BMA023_INT_RESET_MASK, + .sleep_reg = BMA023_CTRL_REG0, + .sleep_mask = BMA023_SLEEP, + .bw_reg = BMA023_CTRL_REG2, + .bw_mask = BMA023_BW_MASK, + .scale_reg = BMA023_CTRL_REG2, + .scale_mask = BMA023_RANGE_MASK, + /* No power mode on bma150 */ + .power_reg = 0, + .power_mask = 0, + .lowpower_val = 0, + .int_enable_reg = BMA023_CTRL_REG3, + .int_enable_mask = BMA023_NEW_DATA_INT, + .softreset_reg = BMA023_CTRL_REG0, + .softreset_val = BMA023_RESET_VAL, + .chip_config = bma023_chip_config, + .chip_disable = bma023_chip_disable, + }, [BMA180] = { .chip_id = BMA180_ID_REG_VAL, .channels = bma180_channels, @@ -678,7 +842,7 @@ static const struct bma180_part_info bma180_part_info[] = { .num_scales = ARRAY_SIZE(bma180_scale_table), .bw_table = bma180_bw_table, .num_bw = ARRAY_SIZE(bma180_bw_table), - .center_temp = 48, /* 0 LSB @ 24 degree C */ + .temp_offset = 48, /* 0 LSB @ 24 degree C */ .int_reset_reg = BMA180_CTRL_REG0, .int_reset_mask = BMA180_RESET_INT, .sleep_reg = BMA180_CTRL_REG0, @@ -693,6 +857,7 @@ static const struct bma180_part_info bma180_part_info[] = { .int_enable_reg = BMA180_CTRL_REG3, .int_enable_mask = BMA180_NEW_DATA_INT, .softreset_reg = BMA180_RESET, + .softreset_val = BMA180_RESET_VAL, .chip_config = bma180_chip_config, .chip_disable = bma180_chip_disable, }, @@ -704,7 +869,7 @@ static const struct bma180_part_info bma180_part_info[] = { .num_scales = ARRAY_SIZE(bma25x_scale_table), .bw_table = bma25x_bw_table, .num_bw = ARRAY_SIZE(bma25x_bw_table), - .center_temp = 48, /* 0 LSB @ 24 degree C */ + .temp_offset = 48, /* 0 LSB @ 24 degree C */ .int_reset_reg = BMA250_INT_RESET_REG, .int_reset_mask = BMA250_INT_RESET_MASK, .sleep_reg = BMA250_POWER_REG, @@ -721,6 +886,7 @@ static const struct bma180_part_info bma180_part_info[] = { .int_map_reg = BMA250_INT_MAP_REG, .int_enable_dataready_int1_mask = BMA250_INT1_DATA_MASK, .softreset_reg = BMA250_RESET_REG, + .softreset_val = BMA180_RESET_VAL, .chip_config = bma25x_chip_config, .chip_disable = bma25x_chip_disable, }, @@ -732,7 +898,7 @@ static const struct bma180_part_info bma180_part_info[] = { .num_scales = ARRAY_SIZE(bma25x_scale_table), .bw_table = bma25x_bw_table, .num_bw = ARRAY_SIZE(bma25x_bw_table), - .center_temp = 46, /* 0 LSB @ 23 degree C */ + .temp_offset = 46, /* 0 LSB @ 23 degree C */ .int_reset_reg = BMA254_INT_RESET_REG, .int_reset_mask = BMA254_INT_RESET_MASK, .sleep_reg = BMA254_POWER_REG, @@ -749,6 +915,7 @@ static const struct bma180_part_info bma180_part_info[] = { .int_map_reg = BMA254_INT_MAP_REG, .int_enable_dataready_int1_mask = BMA254_INT1_DATA_MASK, .softreset_reg = BMA254_RESET_REG, + .softreset_val = BMA180_RESET_VAL, .chip_config = bma25x_chip_config, .chip_disable = bma25x_chip_disable, }, @@ -990,9 +1157,12 @@ static SIMPLE_DEV_PM_OPS(bma180_pm_ops, bma180_suspend, bma180_resume); #endif static const struct i2c_device_id bma180_ids[] = { + { "bma023", BMA023 }, + { "bma150", BMA150 }, { "bma180", BMA180 }, { "bma250", BMA250 }, { "bma254", BMA254 }, + { "smb380", BMA150 }, { } }; @@ -1000,6 +1170,14 @@ MODULE_DEVICE_TABLE(i2c, bma180_ids); static const struct of_device_id bma180_of_match[] = { { + .compatible = "bosch,bma023", + .data = (void *)BMA023 + }, + { + .compatible = "bosch,bma150", + .data = (void *)BMA150 + }, + { .compatible = "bosch,bma180", .data = (void *)BMA180 }, @@ -1011,6 +1189,10 @@ static const struct of_device_id bma180_of_match[] = { .compatible = "bosch,bma254", .data = (void *)BMA254 }, + { + .compatible = "bosch,smb380", + .data = (void *)BMA150 + }, { } }; MODULE_DEVICE_TABLE(of, bma180_of_match); @@ -1030,5 +1212,5 @@ module_i2c_driver(bma180_driver); MODULE_AUTHOR("Kravchenko Oleksandr <x0199363@ti.com>"); MODULE_AUTHOR("Texas Instruments, Inc."); -MODULE_DESCRIPTION("Bosch BMA180/BMA25x triaxial acceleration sensor"); +MODULE_DESCRIPTION("Bosch BMA023/BMA1x0/BMA25x triaxial acceleration sensor"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index 22131a677445..6bda4f4d89fe 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c @@ -449,9 +449,6 @@ static void exynos_adc_exynos7_init_hw(struct exynos_adc *info) { u32 con1, con2; - if (info->data->needs_adc_phy) - regmap_write(info->pmu_map, info->data->phy_offset, 1); - con1 = ADC_V2_CON1_SOFT_RESET; writel(con1, ADC_V2_CON1(info->regs)); @@ -531,8 +528,19 @@ static int exynos_read_raw(struct iio_dev *indio_dev, unsigned long timeout; int ret; - if (mask != IIO_CHAN_INFO_RAW) + if (mask == IIO_CHAN_INFO_SCALE) { + ret = regulator_get_voltage(info->vdd); + if (ret < 0) + return ret; + + /* Regulator voltage is in uV, but need mV */ + *val = ret / 1000; + *val2 = info->data->mask; + + return IIO_VAL_FRACTIONAL; + } else if (mask != IIO_CHAN_INFO_RAW) { return -EINVAL; + } mutex_lock(&indio_dev->mlock); reinit_completion(&info->completion); @@ -683,6 +691,7 @@ static const struct iio_info exynos_adc_iio_info = { .channel = _index, \ .address = _index, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE), \ .datasheet_name = _id, \ } diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index 2df88d2b880a..0e2068ec068b 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -65,12 +65,14 @@ struct stm32_adc_priv; * @clk_sel: clock selection routine * @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet) * @has_syscfg: SYSCFG capability flags + * @num_irqs: number of interrupt lines */ struct stm32_adc_priv_cfg { const struct stm32_adc_common_regs *regs; int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *); u32 max_clk_rate_hz; unsigned int has_syscfg; + unsigned int num_irqs; }; /** @@ -375,21 +377,15 @@ static int stm32_adc_irq_probe(struct platform_device *pdev, struct device_node *np = pdev->dev.of_node; unsigned int i; - for (i = 0; i < STM32_ADC_MAX_ADCS; i++) { + /* + * Interrupt(s) must be provided, depending on the compatible: + * - stm32f4/h7 shares a common interrupt line. + * - stm32mp1, has one line per ADC + */ + for (i = 0; i < priv->cfg->num_irqs; i++) { priv->irq[i] = platform_get_irq(pdev, i); - if (priv->irq[i] < 0) { - /* - * At least one interrupt must be provided, make others - * optional: - * - stm32f4/h7 shares a common interrupt. - * - stm32mp1, has one line per ADC (either for ADC1, - * ADC2 or both). - */ - if (i && priv->irq[i] == -ENXIO) - continue; - + if (priv->irq[i] < 0) return priv->irq[i]; - } } priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0, @@ -400,9 +396,7 @@ static int stm32_adc_irq_probe(struct platform_device *pdev, return -ENOMEM; } - for (i = 0; i < STM32_ADC_MAX_ADCS; i++) { - if (priv->irq[i] < 0) - continue; + for (i = 0; i < priv->cfg->num_irqs; i++) { irq_set_chained_handler(priv->irq[i], stm32_adc_irq_handler); irq_set_handler_data(priv->irq[i], priv); } @@ -420,11 +414,8 @@ static void stm32_adc_irq_remove(struct platform_device *pdev, irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq)); irq_domain_remove(priv->domain); - for (i = 0; i < STM32_ADC_MAX_ADCS; i++) { - if (priv->irq[i] < 0) - continue; + for (i = 0; i < priv->cfg->num_irqs; i++) irq_set_chained_handler(priv->irq[i], NULL); - } } static int stm32_adc_core_switches_supply_en(struct stm32_adc_priv *priv, @@ -817,6 +808,7 @@ static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = { .regs = &stm32f4_adc_common_regs, .clk_sel = stm32f4_adc_clk_sel, .max_clk_rate_hz = 36000000, + .num_irqs = 1, }; static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = { @@ -824,6 +816,7 @@ static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = { .clk_sel = stm32h7_adc_clk_sel, .max_clk_rate_hz = 36000000, .has_syscfg = HAS_VBOOSTER, + .num_irqs = 1, }; static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = { @@ -831,6 +824,7 @@ static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = { .clk_sel = stm32h7_adc_clk_sel, .max_clk_rate_hz = 40000000, .has_syscfg = HAS_VBOOSTER | HAS_ANASWVDD, + .num_irqs = 2, }; static const struct of_device_id stm32_adc_of_match[] = { diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig index a7e65a59bf42..7f21afd73b1c 100644 --- a/drivers/iio/chemical/Kconfig +++ b/drivers/iio/chemical/Kconfig @@ -22,6 +22,17 @@ config ATLAS_PH_SENSOR To compile this driver as module, choose M here: the module will be called atlas-ph-sensor. +config ATLAS_EZO_SENSOR + tristate "Atlas Scientific EZO sensors" + depends on I2C + help + Say Y here to build I2C interface support for the following + Atlas Scientific EZO sensors + * CO2 EZO Sensor + + To compile this driver as module, choose M here: the + module will be called atlas-ezo-sensor. + config BME680 tristate "Bosch Sensortec BME680 sensor driver" depends on (I2C || SPI) diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile index 33d3a595dda9..aba4167db745 100644 --- a/drivers/iio/chemical/Makefile +++ b/drivers/iio/chemical/Makefile @@ -5,6 +5,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_ATLAS_PH_SENSOR) += atlas-sensor.o +obj-$(CONFIG_ATLAS_EZO_SENSOR) += atlas-ezo-sensor.o obj-$(CONFIG_BME680) += bme680_core.o obj-$(CONFIG_BME680_I2C) += bme680_i2c.o obj-$(CONFIG_BME680_SPI) += bme680_spi.o diff --git a/drivers/iio/chemical/atlas-ezo-sensor.c b/drivers/iio/chemical/atlas-ezo-sensor.c new file mode 100644 index 000000000000..f5a6d8ec6d4d --- /dev/null +++ b/drivers/iio/chemical/atlas-ezo-sensor.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * atlas-ezo-sensor.c - Support for Atlas Scientific EZO sensors + * + * Copyright (C) 2020 Konsulko Group + * Author: Matt Ranostay <matt.ranostay@konsulko.com> + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/of_device.h> +#include <linux/iio/iio.h> + +#define ATLAS_EZO_DRV_NAME "atlas-ezo-sensor" +#define ATLAS_CO2_INT_TIME_IN_MS 950 + +enum { + ATLAS_CO2_EZO, +}; + +struct atlas_ezo_device { + const struct iio_chan_spec *channels; + int num_channels; + int delay; +}; + +struct atlas_ezo_data { + struct i2c_client *client; + struct atlas_ezo_device *chip; + + /* lock to avoid multiple concurrent read calls */ + struct mutex lock; + + u8 buffer[8]; +}; + +static const struct iio_chan_spec atlas_co2_ezo_channels[] = { + { + .type = IIO_CONCENTRATION, + .modified = 1, + .channel2 = IIO_MOD_CO2, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_CPU, + }, + }, +}; + +static struct atlas_ezo_device atlas_ezo_devices[] = { + [ATLAS_CO2_EZO] = { + .channels = atlas_co2_ezo_channels, + .num_channels = 1, + .delay = ATLAS_CO2_INT_TIME_IN_MS, + }, +}; + +static int atlas_ezo_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct atlas_ezo_data *data = iio_priv(indio_dev); + struct i2c_client *client = data->client; + int ret = 0; + + if (chan->type != IIO_CONCENTRATION) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + long tmp; + + mutex_lock(&data->lock); + + tmp = i2c_smbus_write_byte(client, 'R'); + + if (tmp < 0) { + mutex_unlock(&data->lock); + return tmp; + } + + msleep(data->chip->delay); + + tmp = i2c_master_recv(client, data->buffer, sizeof(data->buffer)); + + if (tmp < 0 || data->buffer[0] != 1) { + mutex_unlock(&data->lock); + return -EBUSY; + } + + ret = kstrtol(data->buffer + 1, 10, &tmp); + + *val = tmp; + + mutex_unlock(&data->lock); + + return ret ? ret : IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = 100; /* 0.0001 */ + return IIO_VAL_INT_PLUS_MICRO; + } + + return ret; +} + +static const struct iio_info atlas_info = { + .read_raw = atlas_ezo_read_raw, +}; + +static const struct i2c_device_id atlas_ezo_id[] = { + { "atlas-co2-ezo", ATLAS_CO2_EZO }, + {} +}; +MODULE_DEVICE_TABLE(i2c, atlas_ezo_id); + +static const struct of_device_id atlas_ezo_dt_ids[] = { + { .compatible = "atlas,co2-ezo", .data = (void *)ATLAS_CO2_EZO, }, + {} +}; +MODULE_DEVICE_TABLE(of, atlas_ezo_dt_ids); + +static int atlas_ezo_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct atlas_ezo_data *data; + struct atlas_ezo_device *chip; + const struct of_device_id *of_id; + struct iio_dev *indio_dev; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + of_id = of_match_device(atlas_ezo_dt_ids, &client->dev); + if (!of_id) + chip = &atlas_ezo_devices[id->driver_data]; + else + chip = &atlas_ezo_devices[(unsigned long)of_id->data]; + + indio_dev->info = &atlas_info; + indio_dev->name = ATLAS_EZO_DRV_NAME; + indio_dev->channels = chip->channels; + indio_dev->num_channels = chip->num_channels; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->dev.parent = &client->dev; + + data = iio_priv(indio_dev); + data->client = client; + data->chip = chip; + mutex_init(&data->lock); + + return devm_iio_device_register(&client->dev, indio_dev); +}; + +static struct i2c_driver atlas_ezo_driver = { + .driver = { + .name = ATLAS_EZO_DRV_NAME, + .of_match_table = atlas_ezo_dt_ids, + }, + .probe = atlas_ezo_probe, + .id_table = atlas_ezo_id, +}; +module_i2c_driver(atlas_ezo_driver); + +MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>"); +MODULE_DESCRIPTION("Atlas Scientific EZO sensors"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/chemical/pms7003.c b/drivers/iio/chemical/pms7003.c index 23c9ab252470..07bb90d72434 100644 --- a/drivers/iio/chemical/pms7003.c +++ b/drivers/iio/chemical/pms7003.c @@ -73,6 +73,11 @@ struct pms7003_state { struct pms7003_frame frame; struct completion frame_ready; struct mutex lock; /* must be held whenever state gets touched */ + /* Used to construct scan to push to the IIO buffer */ + struct { + u16 data[3]; /* PM1, PM2P5, PM10 */ + s64 ts; + } scan; }; static int pms7003_do_cmd(struct pms7003_state *state, enum pms7003_cmd cmd) @@ -104,7 +109,6 @@ static irqreturn_t pms7003_trigger_handler(int irq, void *p) struct iio_dev *indio_dev = pf->indio_dev; struct pms7003_state *state = iio_priv(indio_dev); struct pms7003_frame *frame = &state->frame; - u16 data[3 + 1 + 4]; /* PM1, PM2P5, PM10, padding, timestamp */ int ret; mutex_lock(&state->lock); @@ -114,12 +118,15 @@ static irqreturn_t pms7003_trigger_handler(int irq, void *p) goto err; } - data[PM1] = pms7003_get_pm(frame->data + PMS7003_PM1_OFFSET); - data[PM2P5] = pms7003_get_pm(frame->data + PMS7003_PM2P5_OFFSET); - data[PM10] = pms7003_get_pm(frame->data + PMS7003_PM10_OFFSET); + state->scan.data[PM1] = + pms7003_get_pm(frame->data + PMS7003_PM1_OFFSET); + state->scan.data[PM2P5] = + pms7003_get_pm(frame->data + PMS7003_PM2P5_OFFSET); + state->scan.data[PM10] = + pms7003_get_pm(frame->data + PMS7003_PM10_OFFSET); mutex_unlock(&state->lock); - iio_push_to_buffers_with_timestamp(indio_dev, data, + iio_push_to_buffers_with_timestamp(indio_dev, &state->scan, iio_get_time_ns(indio_dev)); err: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/chemical/sps30.c b/drivers/iio/chemical/sps30.c index acb9f8ecbb3d..a88c1fb875a0 100644 --- a/drivers/iio/chemical/sps30.c +++ b/drivers/iio/chemical/sps30.c @@ -230,15 +230,18 @@ static irqreturn_t sps30_trigger_handler(int irq, void *p) struct iio_dev *indio_dev = pf->indio_dev; struct sps30_state *state = iio_priv(indio_dev); int ret; - s32 data[4 + 2]; /* PM1, PM2P5, PM4, PM10, timestamp */ + struct { + s32 data[4]; /* PM1, PM2P5, PM4, PM10 */ + s64 ts; + } scan; mutex_lock(&state->lock); - ret = sps30_do_meas(state, data, 4); + ret = sps30_do_meas(state, scan.data, ARRAY_SIZE(scan.data)); mutex_unlock(&state->lock); if (ret) goto err; - iio_push_to_buffers_with_timestamp(indio_dev, data, + iio_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev)); err: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/dac/ad5360.c b/drivers/iio/dac/ad5360.c index 2ac428b957e3..3e0c9e84e8da 100644 --- a/drivers/iio/dac/ad5360.c +++ b/drivers/iio/dac/ad5360.c @@ -67,6 +67,7 @@ struct ad5360_chip_info { * @chip_info: chip model specific constants, available modes etc * @vref_reg: vref supply regulators * @ctrl: control register cache + * @lock lock to protect the data buffer during SPI ops * @data: spi transfer buffers */ @@ -75,6 +76,7 @@ struct ad5360_state { const struct ad5360_chip_info *chip_info; struct regulator_bulk_data vref_reg[3]; unsigned int ctrl; + struct mutex lock; /* * DMA (thus cache coherency maintenance) requires the @@ -205,10 +207,11 @@ static int ad5360_write(struct iio_dev *indio_dev, unsigned int cmd, unsigned int addr, unsigned int val, unsigned int shift) { int ret; + struct ad5360_state *st = iio_priv(indio_dev); - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); ret = ad5360_write_unlocked(indio_dev, cmd, addr, val, shift); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -229,7 +232,7 @@ static int ad5360_read(struct iio_dev *indio_dev, unsigned int type, }, }; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); st->data[0].d32 = cpu_to_be32(AD5360_CMD(AD5360_CMD_SPECIAL_FUNCTION) | AD5360_ADDR(AD5360_REG_SF_READBACK) | @@ -240,7 +243,7 @@ static int ad5360_read(struct iio_dev *indio_dev, unsigned int type, if (ret >= 0) ret = be32_to_cpu(st->data[1].d32) & 0xffff; - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -261,7 +264,7 @@ static int ad5360_update_ctrl(struct iio_dev *indio_dev, unsigned int set, struct ad5360_state *st = iio_priv(indio_dev); unsigned int ret; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); st->ctrl |= set; st->ctrl &= ~clr; @@ -269,7 +272,7 @@ static int ad5360_update_ctrl(struct iio_dev *indio_dev, unsigned int set, ret = ad5360_write_unlocked(indio_dev, AD5360_CMD_SPECIAL_FUNCTION, AD5360_REG_SF_CTRL, st->ctrl, 0); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -479,6 +482,8 @@ static int ad5360_probe(struct spi_device *spi) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->num_channels = st->chip_info->num_channels; + mutex_init(&st->lock); + ret = ad5360_alloc_channels(indio_dev); if (ret) { dev_err(&spi->dev, "Failed to allocate channel spec: %d\n", ret); diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c index 2ebe08326048..b37e5675f716 100644 --- a/drivers/iio/dac/ad5380.c +++ b/drivers/iio/dac/ad5380.c @@ -51,6 +51,7 @@ struct ad5380_chip_info { * @vref_reg: vref supply regulator * @vref: actual reference voltage used in uA * @pwr_down: whether the chip is currently in power down mode + * @lock lock to protect the data buffer during regmap ops */ struct ad5380_state { @@ -59,6 +60,7 @@ struct ad5380_state { struct regulator *vref_reg; int vref; bool pwr_down; + struct mutex lock; }; enum ad5380_type { @@ -98,7 +100,7 @@ static ssize_t ad5380_write_dac_powerdown(struct iio_dev *indio_dev, if (ret) return ret; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); if (pwr_down) ret = regmap_write(st->regmap, AD5380_REG_SF_PWR_DOWN, 0); @@ -107,7 +109,7 @@ static ssize_t ad5380_write_dac_powerdown(struct iio_dev *indio_dev, st->pwr_down = pwr_down; - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret ? ret : len; } @@ -390,6 +392,8 @@ static int ad5380_probe(struct device *dev, struct regmap *regmap, indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->num_channels = st->chip_info->num_channels; + mutex_init(&st->lock); + ret = ad5380_alloc_channels(indio_dev); if (ret) { dev_err(dev, "Failed to allocate channel spec: %d\n", ret); diff --git a/drivers/iio/dac/ad5421.c b/drivers/iio/dac/ad5421.c index 63063e85cd0a..fec27764cea8 100644 --- a/drivers/iio/dac/ad5421.c +++ b/drivers/iio/dac/ad5421.c @@ -62,12 +62,14 @@ * @current_range: current range which the device is configured for * @data: spi transfer buffers * @fault_mask: software masking of events + * @lock lock to protect the data buffer during SPI ops */ struct ad5421_state { struct spi_device *spi; unsigned int ctrl; enum ad5421_current_range current_range; unsigned int fault_mask; + struct mutex lock; /* * DMA (thus cache coherency maintenance) requires the @@ -142,11 +144,12 @@ static int ad5421_write_unlocked(struct iio_dev *indio_dev, static int ad5421_write(struct iio_dev *indio_dev, unsigned int reg, unsigned int val) { + struct ad5421_state *st = iio_priv(indio_dev); int ret; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); ret = ad5421_write_unlocked(indio_dev, reg, val); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -166,7 +169,7 @@ static int ad5421_read(struct iio_dev *indio_dev, unsigned int reg) }, }; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16)); @@ -174,7 +177,7 @@ static int ad5421_read(struct iio_dev *indio_dev, unsigned int reg) if (ret >= 0) ret = be32_to_cpu(st->data[1].d32) & 0xffff; - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -185,14 +188,14 @@ static int ad5421_update_ctrl(struct iio_dev *indio_dev, unsigned int set, struct ad5421_state *st = iio_priv(indio_dev); unsigned int ret; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); st->ctrl &= ~clr; st->ctrl |= set; ret = ad5421_write_unlocked(indio_dev, AD5421_REG_CTRL, st->ctrl); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -400,12 +403,12 @@ static int ad5421_write_event_config(struct iio_dev *indio_dev, return -EINVAL; } - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); if (state) st->fault_mask |= mask; else st->fault_mask &= ~mask; - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return 0; } @@ -491,6 +494,8 @@ static int ad5421_probe(struct spi_device *spi) indio_dev->channels = ad5421_channels; indio_dev->num_channels = ARRAY_SIZE(ad5421_channels); + mutex_init(&st->lock); + st->ctrl = AD5421_CTRL_WATCHDOG_DISABLE | AD5421_CTRL_AUTO_FAULT_READBACK; diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c index 9884e29b19b7..8f8afc8999bc 100644 --- a/drivers/iio/dac/ad5446.c +++ b/drivers/iio/dac/ad5446.c @@ -33,6 +33,7 @@ * @chip_info: chip model specific constants, available modes etc * @reg: supply regulator * @vref_mv: actual reference voltage used + * @lock lock to protect the data buffer during write ops */ struct ad5446_state { @@ -43,6 +44,7 @@ struct ad5446_state { unsigned cached_val; unsigned pwr_down_mode; unsigned pwr_down; + struct mutex lock; }; /** @@ -112,7 +114,7 @@ static ssize_t ad5446_write_dac_powerdown(struct iio_dev *indio_dev, if (ret) return ret; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); st->pwr_down = powerdown; if (st->pwr_down) { @@ -123,7 +125,7 @@ static ssize_t ad5446_write_dac_powerdown(struct iio_dev *indio_dev, } ret = st->chip_info->write(st, val); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret ? ret : len; } @@ -197,11 +199,11 @@ static int ad5446_write_raw(struct iio_dev *indio_dev, return -EINVAL; val <<= chan->scan_type.shift; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); st->cached_val = val; if (!st->pwr_down) ret = st->chip_info->write(st, val); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); break; default: ret = -EINVAL; @@ -256,6 +258,8 @@ static int ad5446_probe(struct device *dev, const char *name, indio_dev->channels = &st->chip_info->channel; indio_dev->num_channels = 1; + mutex_init(&st->lock); + st->pwr_down_mode = MODE_PWRDWN_1k; if (st->chip_info->int_vref_mv) diff --git a/drivers/iio/dac/ad5449.c b/drivers/iio/dac/ad5449.c index fed3ebaccac4..d739b10e5236 100644 --- a/drivers/iio/dac/ad5449.c +++ b/drivers/iio/dac/ad5449.c @@ -56,11 +56,13 @@ struct ad5449_chip_info { * @has_sdo: whether the SDO line is connected * @dac_cache: Cache for the DAC values * @data: spi transfer buffers + * @lock lock to protect the data buffer during SPI ops */ struct ad5449 { struct spi_device *spi; const struct ad5449_chip_info *chip_info; struct regulator_bulk_data vref_reg[AD5449_MAX_VREFS]; + struct mutex lock; bool has_sdo; uint16_t dac_cache[AD5449_MAX_CHANNELS]; @@ -87,10 +89,10 @@ static int ad5449_write(struct iio_dev *indio_dev, unsigned int addr, struct ad5449 *st = iio_priv(indio_dev); int ret; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); st->data[0] = cpu_to_be16((addr << 12) | val); ret = spi_write(st->spi, st->data, 2); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -112,7 +114,7 @@ static int ad5449_read(struct iio_dev *indio_dev, unsigned int addr, }, }; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); st->data[0] = cpu_to_be16(addr << 12); st->data[1] = cpu_to_be16(AD5449_CMD_NOOP); @@ -123,7 +125,7 @@ static int ad5449_read(struct iio_dev *indio_dev, unsigned int addr, *val = be16_to_cpu(st->data[1]); out_unlock: - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -302,6 +304,8 @@ static int ad5449_spi_probe(struct spi_device *spi) indio_dev->channels = st->chip_info->channels; indio_dev->num_channels = st->chip_info->num_channels; + mutex_init(&st->lock); + if (st->chip_info->has_ctrl) { unsigned int ctrl = 0x00; if (pdata) { diff --git a/drivers/iio/dac/ad5592r-base.c b/drivers/iio/dac/ad5592r-base.c index e2110113e884..410e90e5f75f 100644 --- a/drivers/iio/dac/ad5592r-base.c +++ b/drivers/iio/dac/ad5592r-base.c @@ -156,7 +156,6 @@ static void ad5592r_gpio_cleanup(struct ad5592r_state *st) static int ad5592r_reset(struct ad5592r_state *st) { struct gpio_desc *gpio; - struct iio_dev *iio_dev = iio_priv_to_dev(st); gpio = devm_gpiod_get_optional(st->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(gpio)) @@ -166,10 +165,10 @@ static int ad5592r_reset(struct ad5592r_state *st) udelay(1); gpiod_set_value(gpio, 1); } else { - mutex_lock(&iio_dev->mlock); + mutex_lock(&st->lock); /* Writing this magic value resets the device */ st->ops->reg_write(st, AD5592R_REG_RESET, 0xdac); - mutex_unlock(&iio_dev->mlock); + mutex_unlock(&st->lock); } udelay(250); @@ -197,7 +196,6 @@ static int ad5592r_set_channel_modes(struct ad5592r_state *st) const struct ad5592r_rw_ops *ops = st->ops; int ret; unsigned i; - struct iio_dev *iio_dev = iio_priv_to_dev(st); u8 pulldown = 0, tristate = 0, dac = 0, adc = 0; u16 read_back; @@ -247,7 +245,7 @@ static int ad5592r_set_channel_modes(struct ad5592r_state *st) } } - mutex_lock(&iio_dev->mlock); + mutex_lock(&st->lock); /* Pull down unused pins to GND */ ret = ops->reg_write(st, AD5592R_REG_PULLDOWN, pulldown); @@ -285,7 +283,7 @@ static int ad5592r_set_channel_modes(struct ad5592r_state *st) ret = -EIO; err_unlock: - mutex_unlock(&iio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -314,11 +312,11 @@ static int ad5592r_write_raw(struct iio_dev *iio_dev, if (!chan->output) return -EINVAL; - mutex_lock(&iio_dev->mlock); + mutex_lock(&st->lock); ret = st->ops->write_dac(st, chan->channel, val); if (!ret) st->cached_dac[chan->channel] = val; - mutex_unlock(&iio_dev->mlock); + mutex_unlock(&st->lock); return ret; case IIO_CHAN_INFO_SCALE: if (chan->type == IIO_VOLTAGE) { @@ -333,12 +331,12 @@ static int ad5592r_write_raw(struct iio_dev *iio_dev, else return -EINVAL; - mutex_lock(&iio_dev->mlock); + mutex_lock(&st->lock); ret = st->ops->reg_read(st, AD5592R_REG_CTRL, &st->cached_gp_ctrl); if (ret < 0) { - mutex_unlock(&iio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -360,7 +358,7 @@ static int ad5592r_write_raw(struct iio_dev *iio_dev, ret = st->ops->reg_write(st, AD5592R_REG_CTRL, st->cached_gp_ctrl); - mutex_unlock(&iio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -382,7 +380,7 @@ static int ad5592r_read_raw(struct iio_dev *iio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - mutex_lock(&iio_dev->mlock); + mutex_lock(&st->lock); if (!chan->output) { ret = st->ops->read_adc(st, chan->channel, &read_val); @@ -419,7 +417,7 @@ static int ad5592r_read_raw(struct iio_dev *iio_dev, } else { int mult; - mutex_lock(&iio_dev->mlock); + mutex_lock(&st->lock); if (chan->output) mult = !!(st->cached_gp_ctrl & @@ -437,7 +435,7 @@ static int ad5592r_read_raw(struct iio_dev *iio_dev, case IIO_CHAN_INFO_OFFSET: ret = ad5592r_get_vref(st); - mutex_lock(&iio_dev->mlock); + mutex_lock(&st->lock); if (st->cached_gp_ctrl & AD5592R_REG_CTRL_ADC_RANGE) *val = (-34365 * 25) / ret; @@ -450,7 +448,7 @@ static int ad5592r_read_raw(struct iio_dev *iio_dev, } unlock: - mutex_unlock(&iio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -625,6 +623,8 @@ int ad5592r_probe(struct device *dev, const char *name, iio_dev->info = &ad5592r_info; iio_dev->modes = INDIO_DIRECT_MODE; + mutex_init(&st->lock); + ad5592r_init_scales(st, ad5592r_get_vref(st)); ret = ad5592r_reset(st); diff --git a/drivers/iio/dac/ad5592r-base.h b/drivers/iio/dac/ad5592r-base.h index 4774e4cd9c11..23dac2f1ff8a 100644 --- a/drivers/iio/dac/ad5592r-base.h +++ b/drivers/iio/dac/ad5592r-base.h @@ -52,6 +52,7 @@ struct ad5592r_state { struct regulator *reg; struct gpio_chip gpiochip; struct mutex gpio_lock; /* Protect cached gpio_out, gpio_val, etc. */ + struct mutex lock; unsigned int num_channels; const struct ad5592r_rw_ops *ops; int scale_avail[2][2]; diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index e06b29c565b9..8dd67da0a7da 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -127,9 +127,9 @@ static int ad5686_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); ret = st->read(st, chan->address); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); if (ret < 0) return ret; *val = (ret >> chan->scan_type.shift) & @@ -157,12 +157,12 @@ static int ad5686_write_raw(struct iio_dev *indio_dev, if (val > (1 << chan->scan_type.realbits) || val < 0) return -EINVAL; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); ret = st->write(st, AD5686_CMD_WRITE_INPUT_N_UPDATE_N, chan->address, val << chan->scan_type.shift); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); break; default: ret = -EINVAL; @@ -468,6 +468,8 @@ int ad5686_probe(struct device *dev, indio_dev->channels = st->chip_info->channels; indio_dev->num_channels = st->chip_info->num_channels; + mutex_init(&st->lock); + switch (st->chip_info->regmap_type) { case AD5310_REGMAP: cmd = AD5686_CMD_CONTROL_REG; diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index 70a779939ddb..52009b5eef88 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -117,6 +117,7 @@ struct ad5686_chip_info { * @pwr_down_mask: power down mask * @pwr_down_mode: current power down mode * @use_internal_vref: set to true if the internal reference voltage is used + * @lock lock to protect the data buffer during regmap ops * @data: spi transfer buffers */ @@ -130,6 +131,7 @@ struct ad5686_state { ad5686_write_func write; ad5686_read_func read; bool use_internal_vref; + struct mutex lock; /* * DMA (thus cache coherency maintenance) requires the diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c index 388ddd14bfd0..7723bd313fc6 100644 --- a/drivers/iio/dac/ad5755.c +++ b/drivers/iio/dac/ad5755.c @@ -82,6 +82,7 @@ struct ad5755_chip_info { * @pwr_down: bitmask which contains hether a channel is powered down or not * @ctrl: software shadow of the channel ctrl registers * @channels: iio channel spec for the device + * @lock lock to protect the data buffer during SPI ops * @data: spi transfer buffers */ struct ad5755_state { @@ -90,6 +91,7 @@ struct ad5755_state { unsigned int pwr_down; unsigned int ctrl[AD5755_NUM_CHANNELS]; struct iio_chan_spec channels[AD5755_NUM_CHANNELS]; + struct mutex lock; /* * DMA (thus cache coherency maintenance) requires the @@ -174,11 +176,12 @@ static int ad5755_write_ctrl_unlocked(struct iio_dev *indio_dev, static int ad5755_write(struct iio_dev *indio_dev, unsigned int reg, unsigned int val) { + struct ad5755_state *st = iio_priv(indio_dev); int ret; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); ret = ad5755_write_unlocked(indio_dev, reg, val); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -186,11 +189,12 @@ static int ad5755_write(struct iio_dev *indio_dev, unsigned int reg, static int ad5755_write_ctrl(struct iio_dev *indio_dev, unsigned int channel, unsigned int reg, unsigned int val) { + struct ad5755_state *st = iio_priv(indio_dev); int ret; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); ret = ad5755_write_ctrl_unlocked(indio_dev, channel, reg, val); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -211,7 +215,7 @@ static int ad5755_read(struct iio_dev *indio_dev, unsigned int addr) }, }; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); st->data[0].d32 = cpu_to_be32(AD5755_READ_FLAG | (addr << 16)); st->data[1].d32 = cpu_to_be32(AD5755_NOOP); @@ -220,7 +224,7 @@ static int ad5755_read(struct iio_dev *indio_dev, unsigned int addr) if (ret >= 0) ret = be32_to_cpu(st->data[1].d32) & 0xffff; - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -246,7 +250,7 @@ static int ad5755_set_channel_pwr_down(struct iio_dev *indio_dev, struct ad5755_state *st = iio_priv(indio_dev); unsigned int mask = BIT(channel); - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); if ((bool)(st->pwr_down & mask) == pwr_down) goto out_unlock; @@ -266,7 +270,7 @@ static int ad5755_set_channel_pwr_down(struct iio_dev *indio_dev, } out_unlock: - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return 0; } @@ -746,6 +750,8 @@ static int ad5755_probe(struct spi_device *spi) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->num_channels = AD5755_NUM_CHANNELS; + mutex_init(&st->lock); + if (spi->dev.of_node) pdata = ad5755_parse_dt(&spi->dev); else diff --git a/drivers/iio/dac/ad5761.c b/drivers/iio/dac/ad5761.c index 4fb42b743f0f..67179944e5c5 100644 --- a/drivers/iio/dac/ad5761.c +++ b/drivers/iio/dac/ad5761.c @@ -57,11 +57,13 @@ enum ad5761_supported_device_ids { * @use_intref: true when the internal voltage reference is used * @vref: actual voltage reference in mVolts * @range: output range mode used + * @lock lock to protect the data buffer during SPI ops * @data: cache aligned spi buffer */ struct ad5761_state { struct spi_device *spi; struct regulator *vref_reg; + struct mutex lock; bool use_intref; int vref; @@ -124,9 +126,9 @@ static int ad5761_spi_write(struct iio_dev *indio_dev, u8 addr, u16 val) struct ad5761_state *st = iio_priv(indio_dev); int ret; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); ret = _ad5761_spi_write(st, addr, val); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -163,9 +165,9 @@ static int ad5761_spi_read(struct iio_dev *indio_dev, u8 addr, u16 *val) struct ad5761_state *st = iio_priv(indio_dev); int ret; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); ret = _ad5761_spi_read(st, addr, val); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -368,6 +370,8 @@ static int ad5761_probe(struct spi_device *spi) if (pdata) voltage_range = pdata->voltage_range; + mutex_init(&st->lock); + ret = ad5761_spi_set_range(st, voltage_range); if (ret) goto disable_regulator_err; diff --git a/drivers/iio/dac/ad5764.c b/drivers/iio/dac/ad5764.c index f7ab211604a1..5b0f0fe354f6 100644 --- a/drivers/iio/dac/ad5764.c +++ b/drivers/iio/dac/ad5764.c @@ -46,6 +46,7 @@ struct ad5764_chip_info { * @spi: spi_device * @chip_info: chip info * @vref_reg: vref supply regulators + * @lock lock to protect the data buffer during SPI ops * @data: spi transfer buffers */ @@ -53,6 +54,7 @@ struct ad5764_state { struct spi_device *spi; const struct ad5764_chip_info *chip_info; struct regulator_bulk_data vref_reg[2]; + struct mutex lock; /* * DMA (thus cache coherency maintenance) requires the @@ -126,11 +128,11 @@ static int ad5764_write(struct iio_dev *indio_dev, unsigned int reg, struct ad5764_state *st = iio_priv(indio_dev); int ret; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); st->data[0].d32 = cpu_to_be32((reg << 16) | val); ret = spi_write(st->spi, &st->data[0].d8[1], 3); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -151,7 +153,7 @@ static int ad5764_read(struct iio_dev *indio_dev, unsigned int reg, }, }; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16)); @@ -159,7 +161,7 @@ static int ad5764_read(struct iio_dev *indio_dev, unsigned int reg, if (ret >= 0) *val = be32_to_cpu(st->data[1].d32) & 0xffff; - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -295,6 +297,8 @@ static int ad5764_probe(struct spi_device *spi) indio_dev->num_channels = AD5764_NUM_CHANNELS; indio_dev->channels = st->chip_info->channels; + mutex_init(&st->lock); + if (st->chip_info->int_vref == 0) { st->vref_reg[0].supply = "vrefAB"; st->vref_reg[1].supply = "vrefCD"; diff --git a/drivers/iio/dac/vf610_dac.c b/drivers/iio/dac/vf610_dac.c index 71f8a5c471c4..c1e15ede0e8e 100644 --- a/drivers/iio/dac/vf610_dac.c +++ b/drivers/iio/dac/vf610_dac.c @@ -36,6 +36,7 @@ struct vf610_dac { struct device *dev; enum vf610_conversion_mode_sel conv_mode; void __iomem *regs; + struct mutex lock; }; static void vf610_dac_init(struct vf610_dac *info) @@ -64,7 +65,7 @@ static int vf610_set_conversion_mode(struct iio_dev *indio_dev, struct vf610_dac *info = iio_priv(indio_dev); int val; - mutex_lock(&indio_dev->mlock); + mutex_lock(&info->lock); info->conv_mode = mode; val = readl(info->regs + VF610_DACx_STATCTRL); if (mode) @@ -72,7 +73,7 @@ static int vf610_set_conversion_mode(struct iio_dev *indio_dev, else val &= ~VF610_DAC_LPEN; writel(val, info->regs + VF610_DACx_STATCTRL); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&info->lock); return 0; } @@ -147,9 +148,9 @@ static int vf610_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); + mutex_lock(&info->lock); writel(VF610_DAC_DAT0(val), info->regs); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&info->lock); return 0; default: @@ -205,6 +206,8 @@ static int vf610_dac_probe(struct platform_device *pdev) indio_dev->channels = vf610_dac_iio_channels; indio_dev->num_channels = ARRAY_SIZE(vf610_dac_iio_channels); + mutex_init(&info->lock); + ret = clk_prepare_enable(info->clk); if (ret) { dev_err(&pdev->dev, diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index eae39eaf49af..9fa238c0a7d4 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -927,6 +927,7 @@ static int iio_enable_buffers(struct iio_dev *indio_dev, indio_dev->active_scan_mask = config->scan_mask; indio_dev->scan_timestamp = config->scan_timestamp; indio_dev->scan_bytes = config->scan_bytes; + indio_dev->currentmode = config->mode; iio_update_demux(indio_dev); @@ -962,8 +963,6 @@ static int iio_enable_buffers(struct iio_dev *indio_dev, goto err_disable_buffers; } - indio_dev->currentmode = config->mode; - if (indio_dev->setup_ops->postenable) { ret = indio_dev->setup_ops->postenable(indio_dev); if (ret) { @@ -980,10 +979,10 @@ err_disable_buffers: buffer_list) iio_buffer_disable(buffer, indio_dev); err_run_postdisable: - indio_dev->currentmode = INDIO_DIRECT_MODE; if (indio_dev->setup_ops->postdisable) indio_dev->setup_ops->postdisable(indio_dev); err_undo_config: + indio_dev->currentmode = INDIO_DIRECT_MODE; indio_dev->active_scan_mask = NULL; return ret; @@ -1018,8 +1017,6 @@ static int iio_disable_buffers(struct iio_dev *indio_dev) ret = ret2; } - indio_dev->currentmode = INDIO_DIRECT_MODE; - if (indio_dev->setup_ops->postdisable) { ret2 = indio_dev->setup_ops->postdisable(indio_dev); if (ret2 && !ret) @@ -1028,6 +1025,7 @@ static int iio_disable_buffers(struct iio_dev *indio_dev) iio_free_scan_mask(indio_dev, indio_dev->active_scan_mask); indio_dev->active_scan_mask = NULL; + indio_dev->currentmode = INDIO_DIRECT_MODE; return ret; } @@ -1244,7 +1242,7 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) struct iio_dev_attr *p; struct attribute **attr; struct iio_buffer *buffer = indio_dev->buffer; - int ret, i, attrn, attrcount, attrcount_orig = 0; + int ret, i, attrn, attrcount; const struct iio_chan_spec *channels; channels = indio_dev->channels; @@ -1288,7 +1286,7 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) indio_dev->groups[indio_dev->groupcounter++] = &buffer->buffer_group; - attrcount = attrcount_orig; + attrcount = 0; INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list); channels = indio_dev->channels; if (channels) { @@ -1325,7 +1323,7 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) ret = -ENOMEM; goto error_free_scan_mask; } - attrn = attrcount_orig; + attrn = 0; list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l) buffer->scan_el_group.attrs[attrn++] = &p->dev_attr.attr; diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index b7ef16b28280..7a2679bdc987 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -158,6 +158,9 @@ static irqreturn_t gp2ap002_prox_irq(int irq, void *d) int val; int ret; + if (!gp2ap002->enabled) + goto err_retrig; + ret = regmap_read(gp2ap002->map, GP2AP002_PROX, &val); if (ret) { dev_err(gp2ap002->dev, "error reading proximity\n"); @@ -247,6 +250,8 @@ static int gp2ap002_read_raw(struct iio_dev *indio_dev, struct gp2ap002 *gp2ap002 = iio_priv(indio_dev); int ret; + pm_runtime_get_sync(gp2ap002->dev); + switch (mask) { case IIO_CHAN_INFO_RAW: switch (chan->type) { @@ -255,13 +260,21 @@ static int gp2ap002_read_raw(struct iio_dev *indio_dev, if (ret < 0) return ret; *val = ret; - return IIO_VAL_INT; + ret = IIO_VAL_INT; + goto out; default: - return -EINVAL; + ret = -EINVAL; + goto out; } default: - return -EINVAL; + ret = -EINVAL; } + +out: + pm_runtime_mark_last_busy(gp2ap002->dev); + pm_runtime_put_autosuspend(gp2ap002->dev); + + return ret; } static int gp2ap002_init(struct gp2ap002 *gp2ap002) diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig index d57e8cc17e42..12672a0e89ed 100644 --- a/drivers/iio/proximity/Kconfig +++ b/drivers/iio/proximity/Kconfig @@ -140,6 +140,17 @@ config SRF08 To compile this driver as a module, choose M here: the module will be called srf08. +config VCNL3020 + tristate "VCNL3020 proximity sensor" + select REGMAP_I2C + depends on I2C + help + Say Y here if you want to build a driver for the Vishay VCNL3020 + proximity sensor. + + To compile this driver as a module, choose M here: the + module will be called vcnl3020. + config VL53L0X_I2C tristate "STMicroelectronics VL53L0X ToF ranger sensor (I2C)" depends on I2C diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile index 25e5a04da101..9c1aca1a8b79 100644 --- a/drivers/iio/proximity/Makefile +++ b/drivers/iio/proximity/Makefile @@ -14,5 +14,6 @@ obj-$(CONFIG_SRF04) += srf04.o obj-$(CONFIG_SRF08) += srf08.o obj-$(CONFIG_SX9310) += sx9310.o obj-$(CONFIG_SX9500) += sx9500.o +obj-$(CONFIG_VCNL3020) += vcnl3020.o obj-$(CONFIG_VL53L0X_I2C) += vl53l0x-i2c.o diff --git a/drivers/iio/proximity/ping.c b/drivers/iio/proximity/ping.c index 12b893c5b0ee..2e99eeb27f2e 100644 --- a/drivers/iio/proximity/ping.c +++ b/drivers/iio/proximity/ping.c @@ -89,14 +89,14 @@ static irqreturn_t ping_handle_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static int ping_read(struct ping_data *data) +static int ping_read(struct iio_dev *indio_dev) { + struct ping_data *data = iio_priv(indio_dev); int ret; ktime_t ktime_dt; s64 dt_ns; u32 time_ns, distance_mm; struct platform_device *pdev = to_platform_device(data->dev); - struct iio_dev *indio_dev = iio_priv_to_dev(data); /* * just one read-echo-cycle can take place at a time @@ -228,7 +228,6 @@ static int ping_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *channel, int *val, int *val2, long info) { - struct ping_data *data = iio_priv(indio_dev); int ret; if (channel->type != IIO_DISTANCE) @@ -236,7 +235,7 @@ static int ping_read_raw(struct iio_dev *indio_dev, switch (info) { case IIO_CHAN_INFO_RAW: - ret = ping_read(data); + ret = ping_read(indio_dev); if (ret < 0) return ret; *val = ret; diff --git a/drivers/iio/proximity/vcnl3020.c b/drivers/iio/proximity/vcnl3020.c new file mode 100644 index 000000000000..9ff1a164c2e6 --- /dev/null +++ b/drivers/iio/proximity/vcnl3020.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Support for Vishay VCNL3020 proximity sensor on i2c bus. + * Based on Vishay VCNL4000 driver code. + * + * TODO: interrupts. + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/regmap.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define VCNL3020_PROD_ID 0x21 + +#define VCNL_COMMAND 0x80 /* Command register */ +#define VCNL_PROD_REV 0x81 /* Product ID and Revision ID */ +#define VCNL_PROXIMITY_RATE 0x82 /* Rate of Proximity Measurement */ +#define VCNL_LED_CURRENT 0x83 /* IR LED current for proximity mode */ +#define VCNL_PS_RESULT_HI 0x87 /* Proximity result register, MSB */ +#define VCNL_PS_RESULT_LO 0x88 /* Proximity result register, LSB */ +#define VCNL_PS_ICR 0x89 /* Interrupt Control Register */ +#define VCNL_PS_LO_THR_HI 0x8a /* High byte of low threshold value */ +#define VCNL_PS_LO_THR_LO 0x8b /* Low byte of low threshold value */ +#define VCNL_PS_HI_THR_HI 0x8c /* High byte of high threshold value */ +#define VCNL_PS_HI_THR_LO 0x8d /* Low byte of high threshold value */ +#define VCNL_ISR 0x8e /* Interrupt Status Register */ +#define VCNL_PS_MOD_ADJ 0x8f /* Proximity Modulator Timing Adjustment */ + +/* Bit masks for COMMAND register */ +#define VCNL_PS_RDY BIT(5) /* proximity data ready? */ +#define VCNL_PS_OD BIT(3) /* start on-demand proximity + * measurement + */ + +#define VCNL_ON_DEMAND_TIMEOUT_US 100000 +#define VCNL_POLL_US 20000 + +/** + * struct vcnl3020_data - vcnl3020 specific data. + * @regmap: device register map. + * @dev: vcnl3020 device. + * @rev: revision id. + * @lock: lock for protecting access to device hardware registers. + */ +struct vcnl3020_data { + struct regmap *regmap; + struct device *dev; + u8 rev; + struct mutex lock; +}; + +/** + * struct vcnl3020_property - vcnl3020 property. + * @name: property name. + * @reg: i2c register offset. + * @conversion_func: conversion function. + */ +struct vcnl3020_property { + const char *name; + u32 reg; + u32 (*conversion_func)(u32 *val); +}; + +static u32 microamp_to_reg(u32 *val) +{ + /* + * An example of conversion from uA to reg val: + * 200000 uA == 200 mA == 20 + */ + return *val /= 10000; +}; + +static struct vcnl3020_property vcnl3020_led_current_property = { + .name = "vishay,led-current-microamp", + .reg = VCNL_LED_CURRENT, + .conversion_func = microamp_to_reg, +}; + +static int vcnl3020_get_and_apply_property(struct vcnl3020_data *data, + struct vcnl3020_property prop) +{ + int rc; + u32 val; + + rc = device_property_read_u32(data->dev, prop.name, &val); + if (rc) + return 0; + + if (prop.conversion_func) + prop.conversion_func(&val); + + rc = regmap_write(data->regmap, prop.reg, val); + if (rc) { + dev_err(data->dev, "Error (%d) setting property (%s)\n", + rc, prop.name); + } + + return rc; +} + +static int vcnl3020_init(struct vcnl3020_data *data) +{ + int rc; + unsigned int reg; + + rc = regmap_read(data->regmap, VCNL_PROD_REV, ®); + if (rc) { + dev_err(data->dev, + "Error (%d) reading product revision\n", rc); + return rc; + } + + if (reg != VCNL3020_PROD_ID) { + dev_err(data->dev, + "Product id (%x) did not match vcnl3020 (%x)\n", reg, + VCNL3020_PROD_ID); + return -ENODEV; + } + + data->rev = reg; + mutex_init(&data->lock); + + return vcnl3020_get_and_apply_property(data, + vcnl3020_led_current_property); +}; + +static int vcnl3020_measure_proximity(struct vcnl3020_data *data, int *val) +{ + int rc; + unsigned int reg; + __be16 res; + + mutex_lock(&data->lock); + + rc = regmap_write(data->regmap, VCNL_COMMAND, VCNL_PS_OD); + if (rc) + goto err_unlock; + + /* wait for data to become ready */ + rc = regmap_read_poll_timeout(data->regmap, VCNL_COMMAND, reg, + reg & VCNL_PS_RDY, VCNL_POLL_US, + VCNL_ON_DEMAND_TIMEOUT_US); + if (rc) { + dev_err(data->dev, + "Error (%d) reading vcnl3020 command register\n", rc); + goto err_unlock; + } + + /* high & low result bytes read */ + rc = regmap_bulk_read(data->regmap, VCNL_PS_RESULT_HI, &res, + sizeof(res)); + if (rc) + goto err_unlock; + + *val = be16_to_cpu(res); + +err_unlock: + mutex_unlock(&data->lock); + + return rc; +} + +static const struct iio_chan_spec vcnl3020_channels[] = { + { + .type = IIO_PROXIMITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, +}; + +static int vcnl3020_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int rc; + struct vcnl3020_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + rc = vcnl3020_measure_proximity(data, val); + if (rc) + return rc; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_info vcnl3020_info = { + .read_raw = vcnl3020_read_raw, +}; + +static const struct regmap_config vcnl3020_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = VCNL_PS_MOD_ADJ, +}; + +static int vcnl3020_probe(struct i2c_client *client) +{ + struct vcnl3020_data *data; + struct iio_dev *indio_dev; + struct regmap *regmap; + int rc; + + regmap = devm_regmap_init_i2c(client, &vcnl3020_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "regmap_init failed\n"); + return PTR_ERR(regmap); + } + + 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->regmap = regmap; + data->dev = &client->dev; + + rc = vcnl3020_init(data); + if (rc) + return rc; + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &vcnl3020_info; + indio_dev->channels = vcnl3020_channels; + indio_dev->num_channels = ARRAY_SIZE(vcnl3020_channels); + indio_dev->name = "vcnl3020"; + indio_dev->modes = INDIO_DIRECT_MODE; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct of_device_id vcnl3020_of_match[] = { + { + .compatible = "vishay,vcnl3020", + }, + {} +}; +MODULE_DEVICE_TABLE(of, vcnl3020_of_match); + +static struct i2c_driver vcnl3020_driver = { + .driver = { + .name = "vcnl3020", + .of_match_table = vcnl3020_of_match, + }, + .probe_new = vcnl3020_probe, +}; +module_i2c_driver(vcnl3020_driver); + +MODULE_AUTHOR("Ivan Mikhaylov <i.mikhaylov@yadro.com>"); +MODULE_DESCRIPTION("Vishay VCNL3020 proximity sensor driver"); +MODULE_LICENSE("GPL"); |