From 5e45729608dd231a771817c7af4e2be61ef3b659 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 3 Aug 2018 12:55:51 +0100 Subject: [PATCH 01/68] iio: chemical: fix spelling mistake "failted" -> "failed" fix spelling mistake in dev_err error message text Signed-off-by: Colin Ian King Reviewed-by: Himanshu Jha Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/bme680_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c index 7d9bb62baa3f..5de9e27c914a 100644 --- a/drivers/iio/chemical/bme680_core.c +++ b/drivers/iio/chemical/bme680_core.c @@ -577,7 +577,7 @@ static int bme680_gas_config(struct bme680_data *data) /* set target heating duration */ ret = regmap_write(data->regmap, BME680_REG_GAS_WAIT_0, heatr_dur); if (ret < 0) { - dev_err(dev, "failted to write gas_wait_0 register\n"); + dev_err(dev, "failed to write gas_wait_0 register\n"); return ret; } From 2fca585502716c25c52ad4fe54207f80762bc7b4 Mon Sep 17 00:00:00 2001 From: Siddartha Mohanadoss Date: Thu, 2 Aug 2018 18:43:38 -0700 Subject: [PATCH 02/68] dt-bindings: iio: adc: Add DT binding document for PMIC5 ADC PMIC5 ADC has support for clients to measure voltage and current on inputs connected to the PMIC. Clients include reading voltage phone power and on board system thermistors for thermal management. ADC5 on certain PMIC has support to read battery current. This change adds documentation. Signed-off-by: Siddartha Mohanadoss Reviewed-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/qcom,spmi-vadc.txt | 77 +++++++++--- include/dt-bindings/iio/qcom,spmi-vadc.h | 115 +++++++++++++++++- 2 files changed, 172 insertions(+), 20 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt index 0fb46137f936..8498f11d06cd 100644 --- a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt +++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt @@ -1,7 +1,9 @@ -Qualcomm's SPMI PMIC voltage ADC +Qualcomm's SPMI PMIC ADC -SPMI PMIC voltage ADC (VADC) provides interface to clients to read -voltage. The VADC is a 15-bit sigma-delta ADC. +- SPMI PMIC voltage ADC (VADC) provides interface to clients to read + voltage. The VADC is a 15-bit sigma-delta ADC. +- SPMI PMIC5 voltage ADC (ADC) provides interface to clients to read + voltage. The VADC is a 16-bit sigma-delta ADC. VADC node: @@ -9,6 +11,8 @@ VADC node: Usage: required Value type: Definition: Should contain "qcom,spmi-vadc". + Should contain "qcom,spmi-adc5" for PMIC5 ADC driver. + Should contain "qcom,spmi-adc-rev2" for PMIC rev2 ADC driver. - reg: Usage: required @@ -45,13 +49,26 @@ Channel node properties: Definition: ADC channel number. See include/dt-bindings/iio/qcom,spmi-vadc.h +- label: + Usage: required for "qcom,spmi-adc5" and "qcom,spmi-adc-rev2" + Value type: + Definition: ADC input of the platform as seen in the schematics. + For thermistor inputs connected to generic AMUX or GPIO inputs + these can vary across platform for the same pins. Hence select + the platform schematics name for this channel. + - qcom,decimation: Usage: optional Value type: Definition: This parameter is used to decrease ADC sampling rate. Quicker measurements can be made by reducing decimation ratio. - Valid values are 512, 1024, 2048, 4096. - If property is not found, default value of 512 will be used. + - For compatible property "qcom,spmi-vadc", valid values are + 512, 1024, 2048, 4096. If property is not found, default value + of 512 will be used. + - For compatible property "qcom,spmi-adc5", valid values are 250, 420 + and 840. If property is not found, default value of 840 is used. + - For compatible property "qcom,spmi-adc-rev2", valid values are 256, + 512 and 1024. If property is not present, default value is 1024. - qcom,pre-scaling: Usage: optional @@ -66,21 +83,38 @@ Channel node properties: - qcom,ratiometric: Usage: optional Value type: - Definition: Channel calibration type. If this property is specified - VADC will use the VDD reference (1.8V) and GND for channel - calibration. If property is not found, channel will be - calibrated with 0.625V and 1.25V reference channels, also - known as absolute calibration. + Definition: Channel calibration type. + - For compatible property "qcom,spmi-vadc", if this property is + specified VADC will use the VDD reference (1.8V) and GND for + channel calibration. If property is not found, channel will be + calibrated with 0.625V and 1.25V reference channels, also + known as absolute calibration. + - For compatible property "qcom,spmi-adc5" and "qcom,spmi-adc-rev2", + if this property is specified VADC will use the VDD reference + (1.875V) and GND for channel calibration. If property is not found, + channel will be calibrated with 0V and 1.25V reference channels, + also known as absolute calibration. - qcom,hw-settle-time: Usage: optional Value type: Definition: Time between AMUX getting configured and the ADC starting - conversion. Delay = 100us * (value) for value < 11, and - 2ms * (value - 10) otherwise. - Valid values are: 0, 100, 200, 300, 400, 500, 600, 700, 800, - 900 us and 1, 2, 4, 6, 8, 10 ms - If property is not found, channel will use 0us. + conversion. The 'hw_settle_time' is an index used from valid values + and programmed in hardware to achieve the hardware settling delay. + - For compatible property "qcom,spmi-vadc" and "qcom,spmi-adc-rev2", + Delay = 100us * (hw_settle_time) for hw_settle_time < 11, + and 2ms * (hw_settle_time - 10) otherwise. + Valid values are: 0, 100, 200, 300, 400, 500, 600, 700, 800, + 900 us and 1, 2, 4, 6, 8, 10 ms. + If property is not found, channel will use 0us. + - For compatible property "qcom,spmi-adc5", delay = 15us for + value 0, 100us * (value) for values < 11, + and 2ms * (value - 10) otherwise. + Valid values are: 15, 100, 200, 300, 400, 500, 600, 700, 800, + 900 us and 1, 2, 4, 6, 8, 10 ms + Certain controller digital versions have valid values of + 15, 100, 200, 300, 400, 500, 600, 700, 1, 2, 4, 8, 16, 32, 64, 128 ms + If property is not found, channel will use 15us. - qcom,avg-samples: Usage: optional @@ -89,13 +123,18 @@ Channel node properties: Averaging provides the option to obtain a single measurement from the ADC that is an average of multiple samples. The value selected is 2^(value). - Valid values are: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 - If property is not found, 1 sample will be used. + - For compatible property "qcom,spmi-vadc", valid values + are: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 + If property is not found, 1 sample will be used. + - For compatible property "qcom,spmi-adc5" and "qcom,spmi-adc-rev2", + valid values are: 1, 2, 4, 8, 16 + If property is not found, 1 sample will be used. NOTE: -Following channels, also known as reference point channels, are used for -result calibration and their channel configuration nodes should be defined: +For compatible property "qcom,spmi-vadc" following channels, also known as +reference point channels, are used for result calibration and their channel +configuration nodes should be defined: VADC_REF_625MV and/or VADC_SPARE1(based on PMIC version) VADC_REF_1250MV, VADC_GND_REF and VADC_VDD_VADC. diff --git a/include/dt-bindings/iio/qcom,spmi-vadc.h b/include/dt-bindings/iio/qcom,spmi-vadc.h index 42121fa238fa..bf54b5adc065 100644 --- a/include/dt-bindings/iio/qcom,spmi-vadc.h +++ b/include/dt-bindings/iio/qcom,spmi-vadc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2014,2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -116,4 +116,117 @@ #define VADC_LR_MUX10_PU1_PU2_AMUX_USB_ID 0xf9 #define VADC_LR_MUX3_BUF_PU1_PU2_XO_THERM 0xfc +/* ADC channels for SPMI PMIC5 */ + +#define ADC5_REF_GND 0x00 +#define ADC5_1P25VREF 0x01 +#define ADC5_VREF_VADC 0x02 +#define ADC5_VREF_VADC5_DIV_3 0x82 +#define ADC5_VPH_PWR 0x83 +#define ADC5_VBAT_SNS 0x84 +#define ADC5_VCOIN 0x85 +#define ADC5_DIE_TEMP 0x06 +#define ADC5_USB_IN_I 0x07 +#define ADC5_USB_IN_V_16 0x08 +#define ADC5_CHG_TEMP 0x09 +#define ADC5_BAT_THERM 0x0a +#define ADC5_BAT_ID 0x0b +#define ADC5_XO_THERM 0x0c +#define ADC5_AMUX_THM1 0x0d +#define ADC5_AMUX_THM2 0x0e +#define ADC5_AMUX_THM3 0x0f +#define ADC5_AMUX_THM4 0x10 +#define ADC5_AMUX_THM5 0x11 +#define ADC5_GPIO1 0x12 +#define ADC5_GPIO2 0x13 +#define ADC5_GPIO3 0x14 +#define ADC5_GPIO4 0x15 +#define ADC5_GPIO5 0x16 +#define ADC5_GPIO6 0x17 +#define ADC5_GPIO7 0x18 +#define ADC5_SBUx 0x99 +#define ADC5_MID_CHG_DIV6 0x1e +#define ADC5_OFF 0xff + +/* 30k pull-up1 */ +#define ADC5_BAT_THERM_30K_PU 0x2a +#define ADC5_BAT_ID_30K_PU 0x2b +#define ADC5_XO_THERM_30K_PU 0x2c +#define ADC5_AMUX_THM1_30K_PU 0x2d +#define ADC5_AMUX_THM2_30K_PU 0x2e +#define ADC5_AMUX_THM3_30K_PU 0x2f +#define ADC5_AMUX_THM4_30K_PU 0x30 +#define ADC5_AMUX_THM5_30K_PU 0x31 +#define ADC5_GPIO1_30K_PU 0x32 +#define ADC5_GPIO2_30K_PU 0x33 +#define ADC5_GPIO3_30K_PU 0x34 +#define ADC5_GPIO4_30K_PU 0x35 +#define ADC5_GPIO5_30K_PU 0x36 +#define ADC5_GPIO6_30K_PU 0x37 +#define ADC5_GPIO7_30K_PU 0x38 +#define ADC5_SBUx_30K_PU 0x39 + +/* 100k pull-up2 */ +#define ADC5_BAT_THERM_100K_PU 0x4a +#define ADC5_BAT_ID_100K_PU 0x4b +#define ADC5_XO_THERM_100K_PU 0x4c +#define ADC5_AMUX_THM1_100K_PU 0x4d +#define ADC5_AMUX_THM2_100K_PU 0x4e +#define ADC5_AMUX_THM3_100K_PU 0x4f +#define ADC5_AMUX_THM4_100K_PU 0x50 +#define ADC5_AMUX_THM5_100K_PU 0x51 +#define ADC5_GPIO1_100K_PU 0x52 +#define ADC5_GPIO2_100K_PU 0x53 +#define ADC5_GPIO3_100K_PU 0x54 +#define ADC5_GPIO4_100K_PU 0x55 +#define ADC5_GPIO5_100K_PU 0x56 +#define ADC5_GPIO6_100K_PU 0x57 +#define ADC5_GPIO7_100K_PU 0x58 +#define ADC5_SBUx_100K_PU 0x59 + +/* 400k pull-up3 */ +#define ADC5_BAT_THERM_400K_PU 0x6a +#define ADC5_BAT_ID_400K_PU 0x6b +#define ADC5_XO_THERM_400K_PU 0x6c +#define ADC5_AMUX_THM1_400K_PU 0x6d +#define ADC5_AMUX_THM2_400K_PU 0x6e +#define ADC5_AMUX_THM3_400K_PU 0x6f +#define ADC5_AMUX_THM4_400K_PU 0x70 +#define ADC5_AMUX_THM5_400K_PU 0x71 +#define ADC5_GPIO1_400K_PU 0x72 +#define ADC5_GPIO2_400K_PU 0x73 +#define ADC5_GPIO3_400K_PU 0x74 +#define ADC5_GPIO4_400K_PU 0x75 +#define ADC5_GPIO5_400K_PU 0x76 +#define ADC5_GPIO6_400K_PU 0x77 +#define ADC5_GPIO7_400K_PU 0x78 +#define ADC5_SBUx_400K_PU 0x79 + +/* 1/3 Divider */ +#define ADC5_GPIO1_DIV3 0x92 +#define ADC5_GPIO2_DIV3 0x93 +#define ADC5_GPIO3_DIV3 0x94 +#define ADC5_GPIO4_DIV3 0x95 +#define ADC5_GPIO5_DIV3 0x96 +#define ADC5_GPIO6_DIV3 0x97 +#define ADC5_GPIO7_DIV3 0x98 +#define ADC5_SBUx_DIV3 0x99 + +/* Current and combined current/voltage channels */ +#define ADC5_INT_EXT_ISENSE 0xa1 +#define ADC5_PARALLEL_ISENSE 0xa5 +#define ADC5_CUR_REPLICA_VDS 0xa7 +#define ADC5_CUR_SENS_BATFET_VDS_OFFSET 0xa9 +#define ADC5_CUR_SENS_REPLICA_VDS_OFFSET 0xab +#define ADC5_EXT_SENS_OFFSET 0xad + +#define ADC5_INT_EXT_ISENSE_VBAT_VDATA 0xb0 +#define ADC5_INT_EXT_ISENSE_VBAT_IDATA 0xb1 +#define ADC5_EXT_ISENSE_VBAT_VDATA 0xb2 +#define ADC5_EXT_ISENSE_VBAT_IDATA 0xb3 +#define ADC5_PARALLEL_ISENSE_VBAT_VDATA 0xb4 +#define ADC5_PARALLEL_ISENSE_VBAT_IDATA 0xb5 + +#define ADC5_MAX_CHANNEL 0xc0 + #endif /* _DT_BINDINGS_QCOM_SPMI_VADC_H */ From e13d757279bbc59776c8435fb94e54b5a58bdd0b Mon Sep 17 00:00:00 2001 From: Siddartha Mohanadoss Date: Thu, 2 Aug 2018 18:43:53 -0700 Subject: [PATCH 03/68] iio: adc: Add QCOM SPMI PMIC5 ADC driver This patch adds support for QCOM SPMI PMIC5 family of ADC driver that supports hardware based offset and gain compensation. The ADC peripheral can measure both voltage and current channels whose input signal is connected to the PMIC ADC AMUX. The register set and configuration has been refreshed compared to the prior QCOM PMIC ADC family. Register ADC5 as part of the IIO framework. Signed-off-by: Siddartha Mohanadoss Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 20 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/qcom-spmi-adc5.c | 788 +++++++++++++++++++++++++++++ drivers/iio/adc/qcom-vadc-common.c | 189 ++++++- drivers/iio/adc/qcom-vadc-common.h | 54 ++ 5 files changed, 1047 insertions(+), 5 deletions(-) create mode 100644 drivers/iio/adc/qcom-spmi-adc5.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 4a754921fb6f..690fdb249565 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -596,6 +596,26 @@ config QCOM_SPMI_VADC To compile this driver as a module, choose M here: the module will be called qcom-spmi-vadc. +config QCOM_SPMI_ADC5 + tristate "Qualcomm Technologies Inc. SPMI PMIC5 ADC" + depends on SPMI + select REGMAP_SPMI + select QCOM_VADC_COMMON + help + This is the IIO Voltage PMIC5 ADC driver for Qualcomm Technologies Inc. + + The driver supports multiple channels read. The ADC is a 16-bit + sigma-delta ADC. The hardware supports calibrated results for + conversion requests and clients include reading voltage phone + power, on board system thermistors connected to the PMIC ADC, + PMIC die temperature, charger temperature, battery current, USB voltage + input, voltage signals connected to supported PMIC GPIO inputs. The + hardware supports internal pull-up for thermistors and can choose between + a 100k, 30k and 400k pull up using the ADC channels. + + To compile this driver as a module, choose M here: the module will + be called qcom-spmi-adc5. + config RCAR_GYRO_ADC tristate "Renesas R-Car GyroADC driver" depends on ARCH_RCAR_GEN2 || COMPILE_TEST diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 03db7b578f9c..e478b3065ecd 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_MESON_SARADC) += meson_saradc.o obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o obj-$(CONFIG_NAU7802) += nau7802.o obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o +obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c new file mode 100644 index 000000000000..a4299417f3de --- /dev/null +++ b/drivers/iio/adc/qcom-spmi-adc5.c @@ -0,0 +1,788 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "qcom-vadc-common.h" + +#define ADC5_USR_REVISION1 0x0 +#define ADC5_USR_STATUS1 0x8 +#define ADC5_USR_STATUS1_REQ_STS BIT(1) +#define ADC5_USR_STATUS1_EOC BIT(0) +#define ADC5_USR_STATUS1_REQ_STS_EOC_MASK 0x3 + +#define ADC5_USR_STATUS2 0x9 +#define ADC5_USR_STATUS2_CONV_SEQ_MASK 0x70 +#define ADC5_USR_STATUS2_CONV_SEQ_MASK_SHIFT 0x5 + +#define ADC5_USR_IBAT_MEAS 0xf +#define ADC5_USR_IBAT_MEAS_SUPPORTED BIT(0) + +#define ADC5_USR_DIG_PARAM 0x42 +#define ADC5_USR_DIG_PARAM_CAL_VAL BIT(6) +#define ADC5_USR_DIG_PARAM_CAL_VAL_SHIFT 6 +#define ADC5_USR_DIG_PARAM_CAL_SEL 0x30 +#define ADC5_USR_DIG_PARAM_CAL_SEL_SHIFT 4 +#define ADC5_USR_DIG_PARAM_DEC_RATIO_SEL 0xc +#define ADC5_USR_DIG_PARAM_DEC_RATIO_SEL_SHIFT 2 + +#define ADC5_USR_FAST_AVG_CTL 0x43 +#define ADC5_USR_FAST_AVG_CTL_EN BIT(7) +#define ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK 0x7 + +#define ADC5_USR_CH_SEL_CTL 0x44 + +#define ADC5_USR_DELAY_CTL 0x45 +#define ADC5_USR_HW_SETTLE_DELAY_MASK 0xf + +#define ADC5_USR_EN_CTL1 0x46 +#define ADC5_USR_EN_CTL1_ADC_EN BIT(7) + +#define ADC5_USR_CONV_REQ 0x47 +#define ADC5_USR_CONV_REQ_REQ BIT(7) + +#define ADC5_USR_DATA0 0x50 + +#define ADC5_USR_DATA1 0x51 + +#define ADC5_USR_IBAT_DATA0 0x52 + +#define ADC5_USR_IBAT_DATA1 0x53 + +/* + * Conversion time varies based on the decimation, clock rate, fast average + * samples and measurements queued across different VADC peripherals. + * Set the timeout to a max of 100ms. + */ +#define ADC5_CONV_TIME_MIN_US 263 +#define ADC5_CONV_TIME_MAX_US 264 +#define ADC5_CONV_TIME_RETRY 400 +#define ADC5_CONV_TIMEOUT msecs_to_jiffies(100) + +/* Digital version >= 5.3 supports hw_settle_2 */ +#define ADC5_HW_SETTLE_DIFF_MINOR 3 +#define ADC5_HW_SETTLE_DIFF_MAJOR 5 + +enum adc5_cal_method { + ADC5_NO_CAL = 0, + ADC5_RATIOMETRIC_CAL, + ADC5_ABSOLUTE_CAL +}; + +enum adc5_cal_val { + ADC5_TIMER_CAL = 0, + ADC5_NEW_CAL +}; + +/** + * struct adc5_channel_prop - ADC channel property. + * @channel: channel number, refer to the channel list. + * @cal_method: calibration method. + * @cal_val: calibration value + * @decimation: sampling rate supported for the channel. + * @prescale: channel scaling performed on the input signal. + * @hw_settle_time: the time between AMUX being configured and the + * start of conversion. + * @avg_samples: ability to provide single result from the ADC + * that is an average of multiple measurements. + * @scale_fn_type: Represents the scaling function to convert voltage + * physical units desired by the client for the channel. + * @datasheet_name: Channel name used in device tree. + */ +struct adc5_channel_prop { + unsigned int channel; + enum adc5_cal_method cal_method; + enum adc5_cal_val cal_val; + unsigned int decimation; + unsigned int prescale; + unsigned int hw_settle_time; + unsigned int avg_samples; + enum vadc_scale_fn_type scale_fn_type; + const char *datasheet_name; +}; + +/** + * struct adc5_chip - ADC private structure. + * @regmap: SPMI ADC5 peripheral register map field. + * @dev: SPMI ADC5 device. + * @base: base address for the ADC peripheral. + * @nchannels: number of ADC channels. + * @chan_props: array of ADC channel properties. + * @iio_chans: array of IIO channels specification. + * @poll_eoc: use polling instead of interrupt. + * @complete: ADC result notification after interrupt is received. + * @lock: ADC lock for access to the peripheral. + * @data: software configuration data. + */ +struct adc5_chip { + struct regmap *regmap; + struct device *dev; + u16 base; + unsigned int nchannels; + struct adc5_channel_prop *chan_props; + struct iio_chan_spec *iio_chans; + bool poll_eoc; + struct completion complete; + struct mutex lock; + const struct adc5_data *data; +}; + +static const struct vadc_prescale_ratio adc5_prescale_ratios[] = { + {.num = 1, .den = 1}, + {.num = 1, .den = 3}, + {.num = 1, .den = 4}, + {.num = 1, .den = 6}, + {.num = 1, .den = 20}, + {.num = 1, .den = 8}, + {.num = 10, .den = 81}, + {.num = 1, .den = 10}, + {.num = 1, .den = 16} +}; + +static int adc5_read(struct adc5_chip *adc, u16 offset, u8 *data, int len) +{ + return regmap_bulk_read(adc->regmap, adc->base + offset, data, len); +} + +static int adc5_write(struct adc5_chip *adc, u16 offset, u8 *data, int len) +{ + return regmap_bulk_write(adc->regmap, adc->base + offset, data, len); +} + +static int adc5_prescaling_from_dt(u32 num, u32 den) +{ + unsigned int pre; + + for (pre = 0; pre < ARRAY_SIZE(adc5_prescale_ratios); pre++) + if (adc5_prescale_ratios[pre].num == num && + adc5_prescale_ratios[pre].den == den) + break; + + if (pre == ARRAY_SIZE(adc5_prescale_ratios)) + return -EINVAL; + + return pre; +} + +static int adc5_hw_settle_time_from_dt(u32 value, + const unsigned int *hw_settle) +{ + unsigned int i; + + for (i = 0; i < VADC_HW_SETTLE_SAMPLES_MAX; i++) { + if (value == hw_settle[i]) + return i; + } + + return -EINVAL; +} + +static int adc5_avg_samples_from_dt(u32 value) +{ + if (!is_power_of_2(value) || value > ADC5_AVG_SAMPLES_MAX) + return -EINVAL; + + return __ffs(value); +} + +static int adc5_decimation_from_dt(u32 value, + const unsigned int *decimation) +{ + unsigned int i; + + for (i = 0; i < ADC5_DECIMATION_SAMPLES_MAX; i++) { + if (value == decimation[i]) + return i; + } + + return -EINVAL; +} + +static int adc5_read_voltage_data(struct adc5_chip *adc, u16 *data) +{ + int ret; + u8 rslt_lsb, rslt_msb; + + ret = adc5_read(adc, ADC5_USR_DATA0, &rslt_lsb, sizeof(rslt_lsb)); + if (ret) + return ret; + + ret = adc5_read(adc, ADC5_USR_DATA1, &rslt_msb, sizeof(rslt_lsb)); + if (ret) + return ret; + + *data = (rslt_msb << 8) | rslt_lsb; + + if (*data == ADC5_USR_DATA_CHECK) { + pr_err("Invalid data:0x%x\n", *data); + return -EINVAL; + } + + pr_debug("voltage raw code:0x%x\n", *data); + + return 0; +} + +static int adc5_poll_wait_eoc(struct adc5_chip *adc) +{ + unsigned int count, retry = ADC5_CONV_TIME_RETRY; + u8 status1; + int ret; + + for (count = 0; count < retry; count++) { + ret = adc5_read(adc, ADC5_USR_STATUS1, &status1, + sizeof(status1)); + if (ret) + return ret; + + status1 &= ADC5_USR_STATUS1_REQ_STS_EOC_MASK; + if (status1 == ADC5_USR_STATUS1_EOC) + return 0; + + usleep_range(ADC5_CONV_TIME_MIN_US, ADC5_CONV_TIME_MAX_US); + } + + return -ETIMEDOUT; +} + +static void adc5_update_dig_param(struct adc5_chip *adc, + struct adc5_channel_prop *prop, u8 *data) +{ + /* Update calibration value */ + *data &= ~ADC5_USR_DIG_PARAM_CAL_VAL; + *data |= (prop->cal_val << ADC5_USR_DIG_PARAM_CAL_VAL_SHIFT); + + /* Update calibration select */ + *data &= ~ADC5_USR_DIG_PARAM_CAL_SEL; + *data |= (prop->cal_method << ADC5_USR_DIG_PARAM_CAL_SEL_SHIFT); + + /* Update decimation ratio select */ + *data &= ~ADC5_USR_DIG_PARAM_DEC_RATIO_SEL; + *data |= (prop->decimation << ADC5_USR_DIG_PARAM_DEC_RATIO_SEL_SHIFT); +} + +static int adc5_configure(struct adc5_chip *adc, + struct adc5_channel_prop *prop) +{ + int ret; + u8 buf[6]; + + /* Read registers 0x42 through 0x46 */ + ret = adc5_read(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf)); + if (ret < 0) + return ret; + + /* Digital param selection */ + adc5_update_dig_param(adc, prop, &buf[0]); + + /* Update fast average sample value */ + buf[1] &= (u8) ~ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK; + buf[1] |= prop->avg_samples; + + /* Select ADC channel */ + buf[2] = prop->channel; + + /* Select HW settle delay for channel */ + buf[3] &= (u8) ~ADC5_USR_HW_SETTLE_DELAY_MASK; + buf[3] |= prop->hw_settle_time; + + /* Select ADC enable */ + buf[4] |= ADC5_USR_EN_CTL1_ADC_EN; + + /* Select CONV request */ + buf[5] |= ADC5_USR_CONV_REQ_REQ; + + if (!adc->poll_eoc) + reinit_completion(&adc->complete); + + return adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf)); +} + +static int adc5_do_conversion(struct adc5_chip *adc, + struct adc5_channel_prop *prop, + struct iio_chan_spec const *chan, + u16 *data_volt, u16 *data_cur) +{ + int ret; + + mutex_lock(&adc->lock); + + ret = adc5_configure(adc, prop); + if (ret) { + pr_err("ADC configure failed with %d\n", ret); + goto unlock; + } + + if (adc->poll_eoc) { + ret = adc5_poll_wait_eoc(adc); + if (ret < 0) { + pr_err("EOC bit not set\n"); + goto unlock; + } + } else { + ret = wait_for_completion_timeout(&adc->complete, + ADC5_CONV_TIMEOUT); + if (!ret) { + pr_debug("Did not get completion timeout.\n"); + ret = adc5_poll_wait_eoc(adc); + if (ret < 0) { + pr_err("EOC bit not set\n"); + goto unlock; + } + } + } + + ret = adc5_read_voltage_data(adc, data_volt); +unlock: + mutex_unlock(&adc->lock); + + return ret; +} + +static irqreturn_t adc5_isr(int irq, void *dev_id) +{ + struct adc5_chip *adc = dev_id; + + complete(&adc->complete); + + return IRQ_HANDLED; +} + +static int adc5_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) +{ + struct adc5_chip *adc = iio_priv(indio_dev); + int i; + + for (i = 0; i < adc->nchannels; i++) + if (adc->chan_props[i].channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static int adc5_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, + long mask) +{ + struct adc5_chip *adc = iio_priv(indio_dev); + struct adc5_channel_prop *prop; + u16 adc_code_volt, adc_code_cur; + int ret; + + prop = &adc->chan_props[chan->address]; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + ret = adc5_do_conversion(adc, prop, chan, + &adc_code_volt, &adc_code_cur); + if (ret) + return ret; + + ret = qcom_adc5_hw_scale(prop->scale_fn_type, + &adc5_prescale_ratios[prop->prescale], + adc->data, + adc_code_volt, val); + if (ret) + return ret; + + return IIO_VAL_INT; + default: + return -EINVAL; + } + + return 0; +} + +static const struct iio_info adc5_info = { + .read_raw = adc5_read_raw, + .of_xlate = adc5_of_xlate, +}; + +struct adc5_channels { + const char *datasheet_name; + unsigned int prescale_index; + enum iio_chan_type type; + long info_mask; + enum vadc_scale_fn_type scale_fn_type; +}; + +#define ADC5_CHAN(_dname, _type, _mask, _pre, _scale) \ + { \ + .datasheet_name = _dname, \ + .prescale_index = _pre, \ + .type = _type, \ + .info_mask = _mask, \ + .scale_fn_type = _scale, \ + }, \ + +#define ADC5_CHAN_TEMP(_dname, _pre, _scale) \ + ADC5_CHAN(_dname, IIO_TEMP, \ + BIT(IIO_CHAN_INFO_PROCESSED), \ + _pre, _scale) \ + +#define ADC5_CHAN_VOLT(_dname, _pre, _scale) \ + ADC5_CHAN(_dname, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_PROCESSED), \ + _pre, _scale) \ + +static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = { + [ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 1, + SCALE_HW_CALIB_DEFAULT) + [ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 1, + SCALE_HW_CALIB_DEFAULT) + [ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 3, + SCALE_HW_CALIB_DEFAULT) + [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 3, + SCALE_HW_CALIB_DEFAULT) + [ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 1, + SCALE_HW_CALIB_PMIC_THERM) + [ADC5_USB_IN_I] = ADC5_CHAN_VOLT("usb_in_i_uv", 1, + SCALE_HW_CALIB_DEFAULT) + [ADC5_USB_IN_V_16] = ADC5_CHAN_VOLT("usb_in_v_div_16", 16, + SCALE_HW_CALIB_DEFAULT) + [ADC5_CHG_TEMP] = ADC5_CHAN_TEMP("chg_temp", 1, + SCALE_HW_CALIB_PM5_CHG_TEMP) + /* Charger prescales SBUx and MID_CHG to fit within 1.8V upper unit */ + [ADC5_SBUx] = ADC5_CHAN_VOLT("chg_sbux", 3, + SCALE_HW_CALIB_DEFAULT) + [ADC5_MID_CHG_DIV6] = ADC5_CHAN_VOLT("chg_mid_chg", 6, + SCALE_HW_CALIB_DEFAULT) + [ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm", 1, + SCALE_HW_CALIB_XOTHERM) + [ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 1, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 1, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 1, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM2] = ADC5_CHAN_TEMP("amux_thm2", 1, + SCALE_HW_CALIB_PM5_SMB_TEMP) +}; + +static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = { + [ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 1, + SCALE_HW_CALIB_DEFAULT) + [ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 1, + SCALE_HW_CALIB_DEFAULT) + [ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 3, + SCALE_HW_CALIB_DEFAULT) + [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 3, + SCALE_HW_CALIB_DEFAULT) + [ADC5_VCOIN] = ADC5_CHAN_VOLT("vcoin", 3, + SCALE_HW_CALIB_DEFAULT) + [ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 1, + SCALE_HW_CALIB_PMIC_THERM) + [ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 1, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 1, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM5_100K_PU] = ADC5_CHAN_TEMP("amux_thm5_100k_pu", 1, + SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm_100k_pu", 1, + SCALE_HW_CALIB_THERM_100K_PULLUP) +}; + +static int adc5_get_dt_channel_data(struct adc5_chip *adc, + struct adc5_channel_prop *prop, + struct device_node *node, + const struct adc5_data *data) +{ + const char *name = node->name, *channel_name; + u32 chan, value, varr[2]; + int ret; + struct device *dev = adc->dev; + + ret = of_property_read_u32(node, "reg", &chan); + if (ret) { + dev_err(dev, "invalid channel number %s\n", name); + return ret; + } + + if (chan > ADC5_PARALLEL_ISENSE_VBAT_IDATA) { + dev_err(dev, "%s invalid channel number %d\n", name, chan); + return -EINVAL; + } + + /* the channel has DT description */ + prop->channel = chan; + + channel_name = of_get_property(node, + "label", NULL) ? : node->name; + if (!channel_name) { + pr_err("Invalid channel name\n"); + return -EINVAL; + } + prop->datasheet_name = channel_name; + + ret = of_property_read_u32(node, "qcom,decimation", &value); + if (!ret) { + ret = adc5_decimation_from_dt(value, data->decimation); + if (ret < 0) { + dev_err(dev, "%02x invalid decimation %d\n", + chan, value); + return ret; + } + prop->decimation = ret; + } else { + prop->decimation = ADC5_DECIMATION_DEFAULT; + } + + ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2); + if (!ret) { + ret = adc5_prescaling_from_dt(varr[0], varr[1]); + if (ret < 0) { + dev_err(dev, "%02x invalid pre-scaling <%d %d>\n", + chan, varr[0], varr[1]); + return ret; + } + prop->prescale = ret; + } + + ret = of_property_read_u32(node, "qcom,hw-settle-time", &value); + if (!ret) { + u8 dig_version[2]; + + ret = adc5_read(adc, ADC5_USR_REVISION1, dig_version, + sizeof(dig_version)); + if (ret < 0) { + dev_err(dev, "Invalid dig version read %d\n", ret); + return ret; + } + + pr_debug("dig_ver:minor:%d, major:%d\n", dig_version[0], + dig_version[1]); + /* Digital controller >= 5.3 have hw_settle_2 option */ + if (dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR && + dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR) + ret = adc5_hw_settle_time_from_dt(value, + data->hw_settle_2); + else + ret = adc5_hw_settle_time_from_dt(value, + data->hw_settle_1); + + if (ret < 0) { + dev_err(dev, "%02x invalid hw-settle-time %d us\n", + chan, value); + return ret; + } + prop->hw_settle_time = ret; + } else { + prop->hw_settle_time = VADC_DEF_HW_SETTLE_TIME; + } + + ret = of_property_read_u32(node, "qcom,avg-samples", &value); + if (!ret) { + ret = adc5_avg_samples_from_dt(value); + if (ret < 0) { + dev_err(dev, "%02x invalid avg-samples %d\n", + chan, value); + return ret; + } + prop->avg_samples = ret; + } else { + prop->avg_samples = VADC_DEF_AVG_SAMPLES; + } + + if (of_property_read_bool(node, "qcom,ratiometric")) + prop->cal_method = ADC5_RATIOMETRIC_CAL; + else + prop->cal_method = ADC5_ABSOLUTE_CAL; + + /* + * Default to using timer calibration. Using a fresh calibration value + * for every conversion will increase the overall time for a request. + */ + prop->cal_val = ADC5_TIMER_CAL; + + dev_dbg(dev, "%02x name %s\n", chan, name); + + return 0; +} + +static const struct adc5_data adc5_data_pmic = { + .full_scale_code_volt = 0x70e4, + .full_scale_code_cur = 0x2710, + .adc_chans = adc5_chans_pmic, + .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX]) + {250, 420, 840}, + .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX]) + {15, 100, 200, 300, 400, 500, 600, 700, + 800, 900, 1, 2, 4, 6, 8, 10}, + .hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX]) + {15, 100, 200, 300, 400, 500, 600, 700, + 1, 2, 4, 8, 16, 32, 64, 128}, +}; + +static const struct adc5_data adc5_data_pmic_rev2 = { + .full_scale_code_volt = 0x4000, + .full_scale_code_cur = 0x1800, + .adc_chans = adc5_chans_rev2, + .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX]) + {256, 512, 1024}, + .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX]) + {0, 100, 200, 300, 400, 500, 600, 700, + 800, 900, 1, 2, 4, 6, 8, 10}, + .hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX]) + {15, 100, 200, 300, 400, 500, 600, 700, + 1, 2, 4, 8, 16, 32, 64, 128}, +}; + +static const struct of_device_id adc5_match_table[] = { + { + .compatible = "qcom,spmi-adc5", + .data = &adc5_data_pmic, + }, + { + .compatible = "qcom,spmi-adc-rev2", + .data = &adc5_data_pmic_rev2, + }, + { } +}; + +static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node) +{ + const struct adc5_channels *adc_chan; + struct iio_chan_spec *iio_chan; + struct adc5_channel_prop prop, *chan_props; + struct device_node *child; + unsigned int index = 0; + const struct of_device_id *id; + const struct adc5_data *data; + int ret; + + adc->nchannels = of_get_available_child_count(node); + if (!adc->nchannels) + return -EINVAL; + + adc->iio_chans = devm_kcalloc(adc->dev, adc->nchannels, + sizeof(*adc->iio_chans), GFP_KERNEL); + if (!adc->iio_chans) + return -ENOMEM; + + adc->chan_props = devm_kcalloc(adc->dev, adc->nchannels, + sizeof(*adc->chan_props), GFP_KERNEL); + if (!adc->chan_props) + return -ENOMEM; + + chan_props = adc->chan_props; + iio_chan = adc->iio_chans; + id = of_match_node(adc5_match_table, node); + if (id) + data = id->data; + else + data = &adc5_data_pmic; + adc->data = data; + + for_each_available_child_of_node(node, child) { + ret = adc5_get_dt_channel_data(adc, &prop, child, data); + if (ret) { + of_node_put(child); + return ret; + } + + prop.scale_fn_type = + data->adc_chans[prop.channel].scale_fn_type; + *chan_props = prop; + adc_chan = &data->adc_chans[prop.channel]; + + iio_chan->channel = prop.channel; + iio_chan->datasheet_name = prop.datasheet_name; + iio_chan->extend_name = prop.datasheet_name; + iio_chan->info_mask_separate = adc_chan->info_mask; + iio_chan->type = adc_chan->type; + iio_chan->address = index; + iio_chan++; + chan_props++; + index++; + } + + return 0; +} + +static int adc5_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct adc5_chip *adc; + struct regmap *regmap; + int ret, irq_eoc; + u32 reg; + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) + return -ENODEV; + + ret = of_property_read_u32(node, "reg", ®); + if (ret < 0) + return ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->regmap = regmap; + adc->dev = dev; + adc->base = reg; + init_completion(&adc->complete); + mutex_init(&adc->lock); + + ret = adc5_get_dt_data(adc, node); + if (ret) { + pr_err("adc get dt data failed\n"); + return ret; + } + + irq_eoc = platform_get_irq(pdev, 0); + if (irq_eoc < 0) { + if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL) + return irq_eoc; + adc->poll_eoc = true; + } else { + ret = devm_request_irq(dev, irq_eoc, adc5_isr, 0, + "pm-adc5", adc); + if (ret) + return ret; + } + + indio_dev->dev.parent = dev; + indio_dev->dev.of_node = node; + indio_dev->name = pdev->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &adc5_info; + indio_dev->channels = adc->iio_chans; + indio_dev->num_channels = adc->nchannels; + + return devm_iio_device_register(dev, indio_dev); +} + +static struct platform_driver adc5_driver = { + .driver = { + .name = "qcom-spmi-adc5.c", + .of_match_table = adc5_match_table, + }, + .probe = adc5_probe, +}; +module_platform_driver(adc5_driver); + +MODULE_ALIAS("platform:qcom-spmi-adc5"); +MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC5 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c index fe3d7826783c..dcd7fb5b9fb2 100644 --- a/drivers/iio/adc/qcom-vadc-common.c +++ b/drivers/iio/adc/qcom-vadc-common.c @@ -47,8 +47,79 @@ static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = { {44, 125} }; +/* + * Voltage to temperature table for 100k pull up for NTCG104EF104 with + * 1.875V reference. + */ +static const struct vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = { + { 1831, -40000 }, + { 1814, -35000 }, + { 1791, -30000 }, + { 1761, -25000 }, + { 1723, -20000 }, + { 1675, -15000 }, + { 1616, -10000 }, + { 1545, -5000 }, + { 1463, 0 }, + { 1370, 5000 }, + { 1268, 10000 }, + { 1160, 15000 }, + { 1049, 20000 }, + { 937, 25000 }, + { 828, 30000 }, + { 726, 35000 }, + { 630, 40000 }, + { 544, 45000 }, + { 467, 50000 }, + { 399, 55000 }, + { 340, 60000 }, + { 290, 65000 }, + { 247, 70000 }, + { 209, 75000 }, + { 179, 80000 }, + { 153, 85000 }, + { 130, 90000 }, + { 112, 95000 }, + { 96, 100000 }, + { 82, 105000 }, + { 71, 110000 }, + { 62, 115000 }, + { 53, 120000 }, + { 46, 125000 }, +}; + +static int qcom_vadc_scale_hw_calib_volt( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_uv); +static int qcom_vadc_scale_hw_calib_therm( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec); +static int qcom_vadc_scale_hw_smb_temp( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec); +static int qcom_vadc_scale_hw_chg5_temp( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec); +static int qcom_vadc_scale_hw_calib_die_temp( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec); + +static struct qcom_adc5_scale_type scale_adc5_fn[] = { + [SCALE_HW_CALIB_DEFAULT] = {qcom_vadc_scale_hw_calib_volt}, + [SCALE_HW_CALIB_THERM_100K_PULLUP] = {qcom_vadc_scale_hw_calib_therm}, + [SCALE_HW_CALIB_XOTHERM] = {qcom_vadc_scale_hw_calib_therm}, + [SCALE_HW_CALIB_PMIC_THERM] = {qcom_vadc_scale_hw_calib_die_temp}, + [SCALE_HW_CALIB_PM5_CHG_TEMP] = {qcom_vadc_scale_hw_chg5_temp}, + [SCALE_HW_CALIB_PM5_SMB_TEMP] = {qcom_vadc_scale_hw_smb_temp}, +}; + static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts, - u32 tablesize, s32 input, s64 *output) + u32 tablesize, s32 input, int *output) { bool descending = 1; u32 i = 0; @@ -128,7 +199,7 @@ static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph, bool absolute, u16 adc_code, int *result_mdec) { - s64 voltage = 0, result = 0; + s64 voltage = 0; int ret; qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); @@ -138,12 +209,11 @@ static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph, ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb, ARRAY_SIZE(adcmap_100k_104ef_104fb), - voltage, &result); + voltage, result_mdec); if (ret) return ret; - result *= 1000; - *result_mdec = result; + *result_mdec *= 1000; return 0; } @@ -191,6 +261,99 @@ static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph, return 0; } +static int qcom_vadc_scale_code_voltage_factor(u16 adc_code, + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + unsigned int factor) +{ + s64 voltage, temp, adc_vdd_ref_mv = 1875; + + /* + * The normal data range is between 0V to 1.875V. On cases where + * we read low voltage values, the ADC code can go beyond the + * range and the scale result is incorrect so we clamp the values + * for the cases where the code represents a value below 0V + */ + if (adc_code > VADC5_MAX_CODE) + adc_code = 0; + + /* (ADC code * vref_vadc (1.875V)) / full_scale_code */ + voltage = (s64) adc_code * adc_vdd_ref_mv * 1000; + voltage = div64_s64(voltage, data->full_scale_code_volt); + if (voltage > 0) { + voltage *= prescale->den; + temp = prescale->num * factor; + voltage = div64_s64(voltage, temp); + } else { + voltage = 0; + } + + return (int) voltage; +} + +static int qcom_vadc_scale_hw_calib_volt( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_uv) +{ + *result_uv = qcom_vadc_scale_code_voltage_factor(adc_code, + prescale, data, 1); + + return 0; +} + +static int qcom_vadc_scale_hw_calib_therm( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec) +{ + int voltage; + + voltage = qcom_vadc_scale_code_voltage_factor(adc_code, + prescale, data, 1000); + + /* Map voltage to temperature from look-up table */ + return qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref, + ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref), + voltage, result_mdec); +} + +static int qcom_vadc_scale_hw_calib_die_temp( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec) +{ + *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code, + prescale, data, 2); + *result_mdec -= KELVINMIL_CELSIUSMIL; + + return 0; +} + +static int qcom_vadc_scale_hw_smb_temp( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec) +{ + *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code * 100, + prescale, data, PMIC5_SMB_TEMP_SCALE_FACTOR); + *result_mdec = PMIC5_SMB_TEMP_CONSTANT - *result_mdec; + + return 0; +} + +static int qcom_vadc_scale_hw_chg5_temp( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec) +{ + *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code, + prescale, data, 4); + *result_mdec = PMIC5_CHG_TEMP_SCALE_FACTOR - *result_mdec; + + return 0; +} + int qcom_vadc_scale(enum vadc_scale_fn_type scaletype, const struct vadc_linear_graph *calib_graph, const struct vadc_prescale_ratio *prescale, @@ -221,6 +384,22 @@ int qcom_vadc_scale(enum vadc_scale_fn_type scaletype, } EXPORT_SYMBOL(qcom_vadc_scale); +int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype, + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result) +{ + if (!(scaletype >= SCALE_HW_CALIB_DEFAULT && + scaletype < SCALE_HW_CALIB_INVALID)) { + pr_err("Invalid scale type %d\n", scaletype); + return -EINVAL; + } + + return scale_adc5_fn[scaletype].scale_fn(prescale, data, + adc_code, result); +} +EXPORT_SYMBOL(qcom_adc5_hw_scale); + int qcom_vadc_decimation_from_dt(u32 value) { if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN || diff --git a/drivers/iio/adc/qcom-vadc-common.h b/drivers/iio/adc/qcom-vadc-common.h index 1d5354ff5c72..bbb1fa02b382 100644 --- a/drivers/iio/adc/qcom-vadc-common.h +++ b/drivers/iio/adc/qcom-vadc-common.h @@ -25,15 +25,31 @@ #define VADC_DECIMATION_MIN 512 #define VADC_DECIMATION_MAX 4096 +#define ADC5_DEF_VBAT_PRESCALING 1 /* 1:3 */ +#define ADC5_DECIMATION_SHORT 250 +#define ADC5_DECIMATION_MEDIUM 420 +#define ADC5_DECIMATION_LONG 840 +/* Default decimation - 1024 for rev2, 840 for pmic5 */ +#define ADC5_DECIMATION_DEFAULT 2 +#define ADC5_DECIMATION_SAMPLES_MAX 3 #define VADC_HW_SETTLE_DELAY_MAX 10000 +#define VADC_HW_SETTLE_SAMPLES_MAX 16 #define VADC_AVG_SAMPLES_MAX 512 +#define ADC5_AVG_SAMPLES_MAX 16 #define KELVINMIL_CELSIUSMIL 273150 +#define PMIC5_CHG_TEMP_SCALE_FACTOR 377500 +#define PMIC5_SMB_TEMP_CONSTANT 419400 +#define PMIC5_SMB_TEMP_SCALE_FACTOR 356 #define PMI_CHG_SCALE_1 -138890 #define PMI_CHG_SCALE_2 391750000000LL +#define VADC5_MAX_CODE 0x7fff +#define ADC5_FULL_SCALE_CODE 0x70e4 +#define ADC5_USR_DATA_CHECK 0x8000 + /** * struct vadc_map_pt - Map the graph representation for ADC channel * @x: Represent the ADC digitized code. @@ -89,6 +105,18 @@ struct vadc_prescale_ratio { * SCALE_PMIC_THERM: Returns result in milli degree's Centigrade. * SCALE_XOTHERM: Returns XO thermistor voltage in millidegC. * SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp + * SCALE_HW_CALIB_DEFAULT: Default scaling to convert raw adc code to + * voltage (uV) with hardware applied offset/slope values to adc code. + * SCALE_HW_CALIB_THERM_100K_PULLUP: Returns temperature in millidegC using + * lookup table. The hardware applies offset/slope to adc code. + * SCALE_HW_CALIB_XOTHERM: Returns XO thermistor voltage in millidegC using + * 100k pullup. The hardware applies offset/slope to adc code. + * SCALE_HW_CALIB_PMIC_THERM: Returns result in milli degree's Centigrade. + * The hardware applies offset/slope to adc code. + * SCALE_HW_CALIB_PM5_CHG_TEMP: Returns result in millidegrees for PMIC5 + * charger temperature. + * SCALE_HW_CALIB_PM5_SMB_TEMP: Returns result in millidegrees for PMIC5 + * SMB1390 temperature. */ enum vadc_scale_fn_type { SCALE_DEFAULT = 0, @@ -96,6 +124,22 @@ enum vadc_scale_fn_type { SCALE_PMIC_THERM, SCALE_XOTHERM, SCALE_PMI_CHG_TEMP, + SCALE_HW_CALIB_DEFAULT, + SCALE_HW_CALIB_THERM_100K_PULLUP, + SCALE_HW_CALIB_XOTHERM, + SCALE_HW_CALIB_PMIC_THERM, + SCALE_HW_CALIB_PM5_CHG_TEMP, + SCALE_HW_CALIB_PM5_SMB_TEMP, + SCALE_HW_CALIB_INVALID, +}; + +struct adc5_data { + const u32 full_scale_code_volt; + const u32 full_scale_code_cur; + const struct adc5_channels *adc_chans; + unsigned int *decimation; + unsigned int *hw_settle_1; + unsigned int *hw_settle_2; }; int qcom_vadc_scale(enum vadc_scale_fn_type scaletype, @@ -104,6 +148,16 @@ int qcom_vadc_scale(enum vadc_scale_fn_type scaletype, bool absolute, u16 adc_code, int *result_mdec); +struct qcom_adc5_scale_type { + int (*scale_fn)(const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, u16 adc_code, int *result); +}; + +int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype, + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec); + int qcom_vadc_decimation_from_dt(u32 value); #endif /* QCOM_VADC_COMMON_H */ From 2c8909b95b3e2e9f3c312bf8385c2099dacd6d63 Mon Sep 17 00:00:00 2001 From: Siddartha Mohanadoss Date: Thu, 2 Aug 2018 18:44:06 -0700 Subject: [PATCH 04/68] iio: adc: Update QCOM ADC license to SPDX format Update QCOM ADC header file to SPDX format. Signed-off-by: Siddartha Mohanadoss Reviewed-by: Rob Herring Signed-off-by: Jonathan Cameron --- include/dt-bindings/iio/qcom,spmi-vadc.h | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/include/dt-bindings/iio/qcom,spmi-vadc.h b/include/dt-bindings/iio/qcom,spmi-vadc.h index bf54b5adc065..61d556db1542 100644 --- a/include/dt-bindings/iio/qcom,spmi-vadc.h +++ b/include/dt-bindings/iio/qcom,spmi-vadc.h @@ -1,14 +1,6 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2012-2014,2018 The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _DT_BINDINGS_QCOM_SPMI_VADC_H From 07c12b1c007c5c1d9c434ec9a19373ce5d87fe04 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 2 Aug 2018 20:18:52 -0400 Subject: [PATCH 05/68] iio: imu: mpu6050: add support for regulator framework This patch adds support for the regulator framework to the mpu6050 driver. Signed-off-by: Brian Masney Signed-off-by: Jonathan Marek Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../bindings/iio/imu/inv_mpu6050.txt | 1 + drivers/iio/imu/inv_mpu6050/inv_mpu_core.c | 62 +++++++++++++++++++ drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h | 2 + 3 files changed, 65 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt b/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt index b7def51c8ad9..d39907b12a46 100644 --- a/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt +++ b/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt @@ -21,6 +21,7 @@ Required properties: bindings. Optional properties: + - vddio-supply: regulator phandle for VDDIO supply - mount-matrix: an optional 3x3 mounting rotation matrix - i2c-gate node. These devices also support an auxiliary i2c bus. This is simple enough to be described using the i2c-gate binding. See diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index d80ef468508a..1e428c196a82 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "inv_mpu_iio.h" /* @@ -926,6 +927,39 @@ error_power_off: return result; } +static int inv_mpu_core_enable_regulator(struct inv_mpu6050_state *st) +{ + int result; + + result = regulator_enable(st->vddio_supply); + if (result) { + dev_err(regmap_get_device(st->map), + "Failed to enable regulator: %d\n", result); + } else { + /* Give the device a little bit of time to start up. */ + usleep_range(35000, 70000); + } + + return result; +} + +static int inv_mpu_core_disable_regulator(struct inv_mpu6050_state *st) +{ + int result; + + result = regulator_disable(st->vddio_supply); + if (result) + dev_err(regmap_get_device(st->map), + "Failed to disable regulator: %d\n", result); + + return result; +} + +static void inv_mpu_core_disable_regulator_action(void *_data) +{ + inv_mpu_core_disable_regulator(_data); +} + int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type) { @@ -992,6 +1026,28 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, return -EINVAL; } + st->vddio_supply = devm_regulator_get(dev, "vddio"); + if (IS_ERR(st->vddio_supply)) { + if (PTR_ERR(st->vddio_supply) != -EPROBE_DEFER) + dev_err(dev, "Failed to get vddio regulator %d\n", + (int)PTR_ERR(st->vddio_supply)); + + return PTR_ERR(st->vddio_supply); + } + + result = inv_mpu_core_enable_regulator(st); + if (result) + return result; + + result = devm_add_action(dev, inv_mpu_core_disable_regulator_action, + st); + if (result) { + inv_mpu_core_disable_regulator_action(st); + dev_err(dev, "Failed to setup regulator cleanup action %d\n", + result); + return result; + } + /* power is turned on inside check chip type*/ result = inv_check_and_setup_chip(st); if (result) @@ -1051,7 +1107,12 @@ static int inv_mpu_resume(struct device *dev) int result; mutex_lock(&st->lock); + result = inv_mpu_core_enable_regulator(st); + if (result) + goto out_unlock; + result = inv_mpu6050_set_power_itg(st, true); +out_unlock: mutex_unlock(&st->lock); return result; @@ -1064,6 +1125,7 @@ static int inv_mpu_suspend(struct device *dev) mutex_lock(&st->lock); result = inv_mpu6050_set_power_itg(st, false); + inv_mpu_core_disable_regulator(st); mutex_unlock(&st->lock); return result; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index e69a59659dbc..6bcc11fc1b88 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -129,6 +129,7 @@ struct inv_mpu6050_hw { * @chip_period: chip internal period estimation (~1kHz). * @it_timestamp: timestamp from previous interrupt. * @data_timestamp: timestamp for next data sample. + * @vddio_supply voltage regulator for the chip. */ struct inv_mpu6050_state { struct mutex lock; @@ -149,6 +150,7 @@ struct inv_mpu6050_state { s64 chip_period; s64 it_timestamp; s64 data_timestamp; + struct regulator *vddio_supply; }; /*register and associated bit definition*/ From 1ed80a817bc42de91701cc60e58d968077359a58 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 2 Aug 2018 20:18:55 -0400 Subject: [PATCH 06/68] dt-bindings: iio: tsl2772: add new bindings This patch adds the new properties amstaos,proximity-diodes and led-max-microamp to the tsl2772 driver. This patch also removes the driver from the trivial-devices.txt. Signed-off-by: Brian Masney Reviewed-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/light/tsl2772.txt | 41 +++++++++++++++++++ .../devicetree/bindings/trivial-devices.txt | 10 ----- 2 files changed, 41 insertions(+), 10 deletions(-) create mode 100644 Documentation/devicetree/bindings/iio/light/tsl2772.txt diff --git a/Documentation/devicetree/bindings/iio/light/tsl2772.txt b/Documentation/devicetree/bindings/iio/light/tsl2772.txt new file mode 100644 index 000000000000..4e7d98627cbf --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/tsl2772.txt @@ -0,0 +1,41 @@ +* AMS/TAOS ALS and proximity sensor + +Required properties: + + - compatible: Should be one of + "amstaos,tsl2571" + "amstaos,tsl2671" + "amstaos,tmd2671" + "amstaos,tsl2771" + "amstaos,tmd2771" + "amstaos,tsl2572" + "amstaos,tsl2672" + "amstaos,tmd2672" + "amstaos,tsl2772" + "amstaos,tmd2772" + - reg: the I2C address of the device + +Optional properties: + + - amstaos,proximity-diodes - proximity diodes to enable. <0>, <1>, or <0 1> + are the only valid values. + - led-max-microamp - current for the proximity LED. Must be 100000, 50000, + 25000, or 13000. + - vdd-supply: phandle to the regulator that provides power to the sensor. + - vddio-supply: phandle to the regulator that provides power to the bus. + - interrupts: the sole interrupt generated by the device + + Refer to interrupt-controller/interrupts.txt for generic interrupt client + node bindings. + +Example: + +tsl2772@39 { + compatible = "amstaos,tsl2772"; + reg = <0x39>; + interrupts-extended = <&msmgpio 61 IRQ_TYPE_EDGE_FALLING>; + vdd-supply = <&pm8941_l17>; + vddio-supply = <&pm8941_lvs1>; + amstaos,proximity-diodes = <0>; + led-max-microamp = <100000>; +}; diff --git a/Documentation/devicetree/bindings/trivial-devices.txt b/Documentation/devicetree/bindings/trivial-devices.txt index 763a2808a95c..a977ccef7230 100644 --- a/Documentation/devicetree/bindings/trivial-devices.txt +++ b/Documentation/devicetree/bindings/trivial-devices.txt @@ -21,16 +21,6 @@ adi,adt7490 +/-1C TDM Extended Temp Range I.C adi,adxl345 Three-Axis Digital Accelerometer adi,adxl346 Three-Axis Digital Accelerometer (backward-compatibility value "adi,adxl345" must be listed too) ams,iaq-core AMS iAQ-Core VOC Sensor -amstaos,tsl2571 AMS/TAOS ALS and proximity sensor -amstaos,tsl2671 AMS/TAOS ALS and proximity sensor -amstaos,tmd2671 AMS/TAOS ALS and proximity sensor -amstaos,tsl2771 AMS/TAOS ALS and proximity sensor -amstaos,tmd2771 AMS/TAOS ALS and proximity sensor -amstaos,tsl2572 AMS/TAOS ALS and proximity sensor -amstaos,tsl2672 AMS/TAOS ALS and proximity sensor -amstaos,tmd2672 AMS/TAOS ALS and proximity sensor -amstaos,tsl2772 AMS/TAOS ALS and proximity sensor -amstaos,tmd2772 AMS/TAOS ALS and proximity sensor at,24c08 i2c serial eeprom (24cxx) atmel,at97sc3204t i2c trusted platform module (TPM) capella,cm32181 CM32181: Ambient Light Sensor From 94cd1113aaa07762c57032e2e6212531f5308893 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 2 Aug 2018 20:18:54 -0400 Subject: [PATCH 07/68] iio: tsl2772: add support for reading proximity led settings from device tree This patch adds support for optionally reading the proximity led diode and current settings from device tree. This was tested using a LG Nexus 5 (hammerhead) which requires a different diode than the driver default for the IR LED. Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2772.c | 81 +++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c index df5b2a0da96c..ae00edf0d87e 100644 --- a/drivers/iio/light/tsl2772.c +++ b/drivers/iio/light/tsl2772.c @@ -107,6 +107,8 @@ #define TSL2772_ALS_GAIN_TRIM_MIN 250 #define TSL2772_ALS_GAIN_TRIM_MAX 4000 +#define TSL2772_MAX_PROX_LEDS 2 + /* Device family members */ enum { tsl2571, @@ -141,6 +143,14 @@ struct tsl2772_chip_info { const struct iio_info *info; }; +static const int tsl2772_led_currents[][2] = { + { 100000, TSL2772_100_mA }, + { 50000, TSL2772_50_mA }, + { 25000, TSL2772_25_mA }, + { 13000, TSL2772_13_mA }, + { 0, 0 } +}; + struct tsl2772_chip { kernel_ulong_t id; struct mutex prox_mutex; @@ -515,6 +525,75 @@ prox_poll_err: return ret; } +static int tsl2772_read_prox_led_current(struct tsl2772_chip *chip) +{ + struct device_node *of_node = chip->client->dev.of_node; + int ret, tmp, i; + + ret = of_property_read_u32(of_node, "led-max-microamp", &tmp); + if (ret < 0) + return ret; + + for (i = 0; tsl2772_led_currents[i][0] != 0; i++) { + if (tmp == tsl2772_led_currents[i][0]) { + chip->settings.prox_power = tsl2772_led_currents[i][1]; + return 0; + } + } + + dev_err(&chip->client->dev, "Invalid value %d for led-max-microamp\n", + tmp); + + return -EINVAL; + +} + +static int tsl2772_read_prox_diodes(struct tsl2772_chip *chip) +{ + struct device_node *of_node = chip->client->dev.of_node; + int i, ret, num_leds, prox_diode_mask; + u32 leds[TSL2772_MAX_PROX_LEDS]; + + ret = of_property_count_u32_elems(of_node, "amstaos,proximity-diodes"); + if (ret < 0) + return ret; + + num_leds = ret; + if (num_leds > TSL2772_MAX_PROX_LEDS) + num_leds = TSL2772_MAX_PROX_LEDS; + + ret = of_property_read_u32_array(of_node, "amstaos,proximity-diodes", + leds, num_leds); + if (ret < 0) { + dev_err(&chip->client->dev, + "Invalid value for amstaos,proximity-diodes: %d.\n", + ret); + return ret; + } + + prox_diode_mask = 0; + for (i = 0; i < num_leds; i++) { + if (leds[i] == 0) + prox_diode_mask |= TSL2772_DIODE0; + else if (leds[i] == 1) + prox_diode_mask |= TSL2772_DIODE1; + else { + dev_err(&chip->client->dev, + "Invalid value %d in amstaos,proximity-diodes.\n", + leds[i]); + return -EINVAL; + } + } + + return 0; +} + +static void tsl2772_parse_dt(struct tsl2772_chip *chip) +{ + tsl2772_read_prox_led_current(chip); + tsl2772_read_prox_diodes(chip); +} + /** * tsl2772_defaults() - Populates the device nominal operating parameters * with those provided by a 'platform' data struct or @@ -541,6 +620,8 @@ static void tsl2772_defaults(struct tsl2772_chip *chip) memcpy(chip->tsl2772_device_lux, tsl2772_default_lux_table_group[chip->id], TSL2772_DEFAULT_TABLE_BYTES); + + tsl2772_parse_dt(chip); } /** From 28b6977e089dda97f8f32ac1a6a223f59e7065f4 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 2 Aug 2018 20:18:59 -0400 Subject: [PATCH 08/68] dt-bindings: iio: tsl2772: add binding for avago,apds9930 This patch adds avago,apds9930 to the tsl2772 bindings. Signed-off-by: Brian Masney Reviewed-by: Rob Herring Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/light/tsl2772.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/iio/light/tsl2772.txt b/Documentation/devicetree/bindings/iio/light/tsl2772.txt index 4e7d98627cbf..1c5e6f17a1df 100644 --- a/Documentation/devicetree/bindings/iio/light/tsl2772.txt +++ b/Documentation/devicetree/bindings/iio/light/tsl2772.txt @@ -13,6 +13,7 @@ Required properties: "amstaos,tmd2672" "amstaos,tsl2772" "amstaos,tmd2772" + "avago,apds9930" - reg: the I2C address of the device Optional properties: From 75de3b570b1c80f185df5289cb781e453fd64502 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 2 Aug 2018 20:18:58 -0400 Subject: [PATCH 09/68] iio: tsl2772: add support for avago,apds9930 The Avago APDS9930 has the same register set as the TAOS/AMS TSL2772 so this patch adds the correct bindings and the appropriate LUX table values derived from the values in the datasheet. Driver was tested on a LG Nexus 5 (hammerhead) phone. avago,apds9930 datasheet: https://www.mouser.com/datasheet/2/678/avago_AV02-3190EN_DS_APDS-9930_2014-03-25[1]-1217273.pdf tsl2772 datasheet: https://ams.com/eng/content/download/291503/1066377/file/TSL2772_DS000181_2-00.pdf Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2772.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c index ae00edf0d87e..66dc3254cd9a 100644 --- a/drivers/iio/light/tsl2772.c +++ b/drivers/iio/light/tsl2772.c @@ -120,7 +120,8 @@ enum { tsl2672, tmd2672, tsl2772, - tmd2772 + tmd2772, + apds9930, }; enum { @@ -207,6 +208,12 @@ static const struct tsl2772_lux tmd2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { { 0, 0 }, }; +static const struct tsl2772_lux apds9930_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { + { 52000, 96824 }, + { 38792, 67132 }, + { 0, 0 }, +}; + static const struct tsl2772_lux *tsl2772_default_lux_table_group[] = { [tsl2571] = tsl2x71_lux_table, [tsl2671] = tsl2x71_lux_table, @@ -218,6 +225,7 @@ static const struct tsl2772_lux *tsl2772_default_lux_table_group[] = { [tmd2672] = tmd2x72_lux_table, [tsl2772] = tsl2x72_lux_table, [tmd2772] = tmd2x72_lux_table, + [apds9930] = apds9930_lux_table, }; static const struct tsl2772_settings tsl2772_default_settings = { @@ -268,6 +276,7 @@ static const int tsl2772_int_time_avail[][6] = { [tmd2672] = { 0, 2730, 0, 2730, 0, 699000 }, [tsl2772] = { 0, 2730, 0, 2730, 0, 699000 }, [tmd2772] = { 0, 2730, 0, 2730, 0, 699000 }, + [apds9930] = { 0, 2730, 0, 2730, 0, 699000 }, }; static int tsl2772_int_calibscale_avail[] = { 1, 8, 16, 120 }; @@ -293,7 +302,8 @@ static const u8 device_channel_config[] = { [tsl2672] = PRX2, [tmd2672] = PRX2, [tsl2772] = ALSPRX2, - [tmd2772] = ALSPRX2 + [tmd2772] = ALSPRX2, + [apds9930] = ALSPRX2, }; static int tsl2772_read_status(struct tsl2772_chip *chip) @@ -507,6 +517,7 @@ static int tsl2772_get_prox(struct iio_dev *indio_dev) case tmd2672: case tsl2772: case tmd2772: + case apds9930: if (!(ret & TSL2772_STA_PRX_VALID)) { ret = -EINVAL; goto prox_poll_err; @@ -1341,6 +1352,7 @@ static int tsl2772_device_id_verif(int id, int target) case tmd2672: case tsl2772: case tmd2772: + case apds9930: return (id & 0xf0) == SWORDFISH_ID; } @@ -1839,6 +1851,7 @@ static const struct i2c_device_id tsl2772_idtable[] = { { "tmd2672", tmd2672 }, { "tsl2772", tsl2772 }, { "tmd2772", tmd2772 }, + { "apds9930", apds9930}, {} }; @@ -1855,6 +1868,7 @@ static const struct of_device_id tsl2772_of_match[] = { { .compatible = "amstaos,tmd2672" }, { .compatible = "amstaos,tsl2772" }, { .compatible = "amstaos,tmd2772" }, + { .compatible = "avago,apds9930" }, {} }; MODULE_DEVICE_TABLE(of, tsl2772_of_match); From 7c14947e4d3d8585cc047b132cd1a4ac3167928c Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 2 Aug 2018 20:18:56 -0400 Subject: [PATCH 10/68] iio: tsl2772: add support for regulator framework This patch adds support for the regulator framework to the tsl2772 driver. Driver was tested using a LG Nexus 5 (hammerhead) phone with the two regulators and on a Raspberry Pi 2 without any regulators controlling the power to the sensor. Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2772.c | 95 ++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c index 66dc3254cd9a..83cece921843 100644 --- a/drivers/iio/light/tsl2772.c +++ b/drivers/iio/light/tsl2772.c @@ -20,6 +20,7 @@ #include #include #include +#include /* Cal defs */ #define PROX_STAT_CAL 0 @@ -109,6 +110,9 @@ #define TSL2772_MAX_PROX_LEDS 2 +#define TSL2772_BOOT_MIN_SLEEP_TIME 10000 +#define TSL2772_BOOT_MAX_SLEEP_TIME 28000 + /* Device family members */ enum { tsl2571, @@ -157,6 +161,8 @@ struct tsl2772_chip { struct mutex prox_mutex; struct mutex als_mutex; struct i2c_client *client; + struct regulator *vdd_supply; + struct regulator *vddio_supply; u16 prox_data; struct tsl2772_als_info als_cur_info; struct tsl2772_settings settings; @@ -687,6 +693,52 @@ static int tsl2772_als_calibrate(struct iio_dev *indio_dev) return ret; } +static void tsl2772_disable_regulators_action(void *_data) +{ + struct tsl2772_chip *chip = _data; + + regulator_disable(chip->vdd_supply); + regulator_disable(chip->vddio_supply); +} + +static int tsl2772_enable_regulator(struct tsl2772_chip *chip, + struct regulator *regulator) +{ + int ret; + + ret = regulator_enable(regulator); + if (ret < 0) { + dev_err(&chip->client->dev, "Failed to enable regulator: %d\n", + ret); + return ret; + } + + return 0; +} + +static struct regulator *tsl2772_get_regulator(struct tsl2772_chip *chip, + char *name) +{ + struct regulator *regulator; + int ret; + + regulator = devm_regulator_get(&chip->client->dev, name); + if (IS_ERR(regulator)) { + if (PTR_ERR(regulator) != -EPROBE_DEFER) + dev_err(&chip->client->dev, + "Failed to get %s regulator %d\n", + name, (int)PTR_ERR(regulator)); + + return regulator; + } + + ret = tsl2772_enable_regulator(chip, regulator); + if (ret < 0) + return ERR_PTR(ret); + + return regulator; +} + static int tsl2772_chip_on(struct iio_dev *indio_dev) { struct tsl2772_chip *chip = iio_priv(indio_dev); @@ -1745,6 +1797,27 @@ static int tsl2772_probe(struct i2c_client *clientp, chip->client = clientp; i2c_set_clientdata(clientp, indio_dev); + chip->vddio_supply = tsl2772_get_regulator(chip, "vddio"); + if (IS_ERR(chip->vddio_supply)) + return PTR_ERR(chip->vddio_supply); + + chip->vdd_supply = tsl2772_get_regulator(chip, "vdd"); + if (IS_ERR(chip->vdd_supply)) { + regulator_disable(chip->vddio_supply); + return PTR_ERR(chip->vdd_supply); + } + + ret = devm_add_action(&clientp->dev, tsl2772_disable_regulators_action, + chip); + if (ret < 0) { + tsl2772_disable_regulators_action(chip); + dev_err(&clientp->dev, "Failed to setup regulator cleanup action %d\n", + ret); + return ret; + } + + usleep_range(TSL2772_BOOT_MIN_SLEEP_TIME, TSL2772_BOOT_MAX_SLEEP_TIME); + ret = i2c_smbus_read_byte_data(chip->client, TSL2772_CMD_REG | TSL2772_CHIPID); if (ret < 0) @@ -1818,13 +1891,33 @@ static int tsl2772_probe(struct i2c_client *clientp, static int tsl2772_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tsl2772_chip *chip = iio_priv(indio_dev); + int ret; - return tsl2772_chip_off(indio_dev); + ret = tsl2772_chip_off(indio_dev); + regulator_disable(chip->vdd_supply); + regulator_disable(chip->vddio_supply); + + return ret; } static int tsl2772_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tsl2772_chip *chip = iio_priv(indio_dev); + int ret; + + ret = tsl2772_enable_regulator(chip, chip->vddio_supply); + if (ret < 0) + return ret; + + ret = tsl2772_enable_regulator(chip, chip->vdd_supply); + if (ret < 0) { + regulator_disable(chip->vddio_supply); + return ret; + } + + usleep_range(TSL2772_BOOT_MIN_SLEEP_TIME, TSL2772_BOOT_MAX_SLEEP_TIME); return tsl2772_chip_on(indio_dev); } From 8a0ae7d83aa8427fd264404da3045994521b621f Mon Sep 17 00:00:00 2001 From: David Frey Date: Fri, 17 Aug 2018 12:03:13 -0700 Subject: [PATCH 11/68] iio: chemical: bme680: use clamp macro Signed-off-by: David Frey Reviewed-by: Himanshu Jha Tested-by: Himanshu Jha Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/bme680_core.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c index 5de9e27c914a..4d98c2155810 100644 --- a/drivers/iio/chemical/bme680_core.c +++ b/drivers/iio/chemical/bme680_core.c @@ -408,10 +408,7 @@ static u32 bme680_compensate_humid(struct bme680_data *data, var6 = (var4 * var5) >> 1; calc_hum = (((var3 + var6) >> 10) * 1000) >> 12; - if (calc_hum > 100000) /* Cap at 100%rH */ - calc_hum = 100000; - else if (calc_hum < 0) - calc_hum = 0; + calc_hum = clamp(calc_hum, 0, 100000); /* clamp between 0-100 %rH */ return calc_hum; } From 6784ba340a0ea48e276f888143d444ec631ff4a6 Mon Sep 17 00:00:00 2001 From: David Frey Date: Fri, 17 Aug 2018 12:03:14 -0700 Subject: [PATCH 12/68] iio: chemical: bme680: cleanup bme680_read_calib formatting Use the full 80 char width to reduce the number of lines taken by function calls. Remove blank lines where it aids clarity by bringing together related code blocks (such as read hi and low bytes and then combine them into one value). Signed-off-by: David Frey Reviewed-by: Himanshu Jha Tested-by: Himanshu Jha Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/bme680_core.c | 36 ++++++++++-------------------- 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c index 4d98c2155810..103085360415 100644 --- a/drivers/iio/chemical/bme680_core.c +++ b/drivers/iio/chemical/bme680_core.c @@ -102,16 +102,14 @@ static int bme680_read_calib(struct bme680_data *data, __le16 buf; /* Temperature related coefficients */ - ret = regmap_bulk_read(data->regmap, BME680_T1_LSB_REG, - (u8 *) &buf, 2); + ret = regmap_bulk_read(data->regmap, BME680_T1_LSB_REG, (u8 *) &buf, 2); if (ret < 0) { dev_err(dev, "failed to read BME680_T1_LSB_REG\n"); return ret; } calib->par_t1 = le16_to_cpu(buf); - ret = regmap_bulk_read(data->regmap, BME680_T2_LSB_REG, - (u8 *) &buf, 2); + ret = regmap_bulk_read(data->regmap, BME680_T2_LSB_REG, (u8 *) &buf, 2); if (ret < 0) { dev_err(dev, "failed to read BME680_T2_LSB_REG\n"); return ret; @@ -126,16 +124,14 @@ static int bme680_read_calib(struct bme680_data *data, calib->par_t3 = tmp; /* Pressure related coefficients */ - ret = regmap_bulk_read(data->regmap, BME680_P1_LSB_REG, - (u8 *) &buf, 2); + ret = regmap_bulk_read(data->regmap, BME680_P1_LSB_REG, (u8 *) &buf, 2); if (ret < 0) { dev_err(dev, "failed to read BME680_P1_LSB_REG\n"); return ret; } calib->par_p1 = le16_to_cpu(buf); - ret = regmap_bulk_read(data->regmap, BME680_P2_LSB_REG, - (u8 *) &buf, 2); + ret = regmap_bulk_read(data->regmap, BME680_P2_LSB_REG, (u8 *) &buf, 2); if (ret < 0) { dev_err(dev, "failed to read BME680_P2_LSB_REG\n"); return ret; @@ -149,16 +145,14 @@ static int bme680_read_calib(struct bme680_data *data, } calib->par_p3 = tmp; - ret = regmap_bulk_read(data->regmap, BME680_P4_LSB_REG, - (u8 *) &buf, 2); + ret = regmap_bulk_read(data->regmap, BME680_P4_LSB_REG, (u8 *) &buf, 2); if (ret < 0) { dev_err(dev, "failed to read BME680_P4_LSB_REG\n"); return ret; } calib->par_p4 = le16_to_cpu(buf); - ret = regmap_bulk_read(data->regmap, BME680_P5_LSB_REG, - (u8 *) &buf, 2); + ret = regmap_bulk_read(data->regmap, BME680_P5_LSB_REG, (u8 *) &buf, 2); if (ret < 0) { dev_err(dev, "failed to read BME680_P5_LSB_REG\n"); return ret; @@ -179,16 +173,14 @@ static int bme680_read_calib(struct bme680_data *data, } calib->par_p7 = tmp; - ret = regmap_bulk_read(data->regmap, BME680_P8_LSB_REG, - (u8 *) &buf, 2); + ret = regmap_bulk_read(data->regmap, BME680_P8_LSB_REG, (u8 *) &buf, 2); if (ret < 0) { dev_err(dev, "failed to read BME680_P8_LSB_REG\n"); return ret; } calib->par_p8 = le16_to_cpu(buf); - ret = regmap_bulk_read(data->regmap, BME680_P9_LSB_REG, - (u8 *) &buf, 2); + ret = regmap_bulk_read(data->regmap, BME680_P9_LSB_REG, (u8 *) &buf, 2); if (ret < 0) { dev_err(dev, "failed to read BME680_P9_LSB_REG\n"); return ret; @@ -208,30 +200,26 @@ static int bme680_read_calib(struct bme680_data *data, dev_err(dev, "failed to read BME680_H1_MSB_REG\n"); return ret; } - ret = regmap_read(data->regmap, BME680_H1_LSB_REG, &tmp_lsb); if (ret < 0) { dev_err(dev, "failed to read BME680_H1_LSB_REG\n"); return ret; } - calib->par_h1 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) | - (tmp_lsb & BME680_BIT_H1_DATA_MSK); + (tmp_lsb & BME680_BIT_H1_DATA_MSK); ret = regmap_read(data->regmap, BME680_H2_MSB_REG, &tmp_msb); if (ret < 0) { dev_err(dev, "failed to read BME680_H2_MSB_REG\n"); return ret; } - ret = regmap_read(data->regmap, BME680_H2_LSB_REG, &tmp_lsb); if (ret < 0) { dev_err(dev, "failed to read BME680_H2_LSB_REG\n"); return ret; } - calib->par_h2 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) | - (tmp_lsb >> BME680_HUM_REG_SHIFT_VAL); + (tmp_lsb >> BME680_HUM_REG_SHIFT_VAL); ret = regmap_read(data->regmap, BME680_H3_REG, &tmp); if (ret < 0) { @@ -276,8 +264,8 @@ static int bme680_read_calib(struct bme680_data *data, } calib->par_gh1 = tmp; - ret = regmap_bulk_read(data->regmap, BME680_GH2_LSB_REG, - (u8 *) &buf, 2); + ret = regmap_bulk_read(data->regmap, BME680_GH2_LSB_REG, (u8 *) &buf, + 2); if (ret < 0) { dev_err(dev, "failed to read BME680_GH2_LSB_REG\n"); return ret; From 70f1cbddb265c6420b149f55e7e9c67a1e77a294 Mon Sep 17 00:00:00 2001 From: David Frey Date: Fri, 17 Aug 2018 12:03:15 -0700 Subject: [PATCH 13/68] iio: chemical: bme680: indent #defines consistently Signed-off-by: David Frey Reviewed-by: Himanshu Jha Tested-by: Himanshu Jha Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/bme680.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/iio/chemical/bme680.h b/drivers/iio/chemical/bme680.h index e049323f209a..dd4247d364a0 100644 --- a/drivers/iio/chemical/bme680.h +++ b/drivers/iio/chemical/bme680.h @@ -4,10 +4,10 @@ #define BME680_REG_CHIP_I2C_ID 0xD0 #define BME680_REG_CHIP_SPI_ID 0x50 -#define BME680_CHIP_ID_VAL 0x61 +#define BME680_CHIP_ID_VAL 0x61 #define BME680_REG_SOFT_RESET_I2C 0xE0 #define BME680_REG_SOFT_RESET_SPI 0x60 -#define BME680_CMD_SOFTRESET 0xB6 +#define BME680_CMD_SOFTRESET 0xB6 #define BME680_REG_STATUS 0x73 #define BME680_SPI_MEM_PAGE_BIT BIT(4) #define BME680_SPI_MEM_PAGE_1_VAL 1 @@ -18,6 +18,7 @@ #define BME680_REG_GAS_MSB 0x2A #define BME680_REG_GAS_R_LSB 0x2B #define BME680_GAS_STAB_BIT BIT(4) +#define BME680_GAS_RANGE_MASK 0x0F #define BME680_REG_CTRL_HUMIDITY 0x72 #define BME680_OSRS_HUMIDITY_MASK GENMASK(2, 0) @@ -26,9 +27,8 @@ #define BME680_OSRS_TEMP_MASK GENMASK(7, 5) #define BME680_OSRS_PRESS_MASK GENMASK(4, 2) #define BME680_MODE_MASK GENMASK(1, 0) - -#define BME680_MODE_FORCED 1 -#define BME680_MODE_SLEEP 0 +#define BME680_MODE_FORCED 1 +#define BME680_MODE_SLEEP 0 #define BME680_REG_CONFIG 0x75 #define BME680_FILTER_MASK GENMASK(4, 2) @@ -42,13 +42,12 @@ #define BME680_BIT_H1_DATA_MSK 0x0F #define BME680_REG_RES_HEAT_RANGE 0x02 -#define BME680_RHRANGE_MSK 0x30 +#define BME680_RHRANGE_MSK 0x30 #define BME680_REG_RES_HEAT_VAL 0x00 #define BME680_REG_RANGE_SW_ERR 0x04 -#define BME680_RSERROR_MSK 0xF0 +#define BME680_RSERROR_MSK 0xF0 #define BME680_REG_RES_HEAT_0 0x5A #define BME680_REG_GAS_WAIT_0 0x64 -#define BME680_GAS_RANGE_MASK 0x0F #define BME680_ADC_GAS_RES_SHIFT 6 #define BME680_AMB_TEMP 25 From a24b4d70c7b2f4b4fed807b9283ba7ae8b114e2f Mon Sep 17 00:00:00 2001 From: David Frey Date: Fri, 17 Aug 2018 12:03:16 -0700 Subject: [PATCH 14/68] iio: chemical: bme680: change MSK->MASK in #defines Convert all defines to use "MASK" instead of a mix of "MSK" and "MASK" Signed-off-by: David Frey Reviewed-by: Himanshu Jha Tested-by: Himanshu Jha Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/bme680.h | 6 +++--- drivers/iio/chemical/bme680_core.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/iio/chemical/bme680.h b/drivers/iio/chemical/bme680.h index dd4247d364a0..437d75c2bad5 100644 --- a/drivers/iio/chemical/bme680.h +++ b/drivers/iio/chemical/bme680.h @@ -39,13 +39,13 @@ #define BME680_MAX_OVERFLOW_VAL 0x40000000 #define BME680_HUM_REG_SHIFT_VAL 4 -#define BME680_BIT_H1_DATA_MSK 0x0F +#define BME680_BIT_H1_DATA_MASK 0x0F #define BME680_REG_RES_HEAT_RANGE 0x02 -#define BME680_RHRANGE_MSK 0x30 +#define BME680_RHRANGE_MASK 0x30 #define BME680_REG_RES_HEAT_VAL 0x00 #define BME680_REG_RANGE_SW_ERR 0x04 -#define BME680_RSERROR_MSK 0xF0 +#define BME680_RSERROR_MASK 0xF0 #define BME680_REG_RES_HEAT_0 0x5A #define BME680_REG_GAS_WAIT_0 0x64 #define BME680_ADC_GAS_RES_SHIFT 6 diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c index 103085360415..0ebe2d26ff1b 100644 --- a/drivers/iio/chemical/bme680_core.c +++ b/drivers/iio/chemical/bme680_core.c @@ -206,7 +206,7 @@ static int bme680_read_calib(struct bme680_data *data, return ret; } calib->par_h1 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) | - (tmp_lsb & BME680_BIT_H1_DATA_MSK); + (tmp_lsb & BME680_BIT_H1_DATA_MASK); ret = regmap_read(data->regmap, BME680_H2_MSB_REG, &tmp_msb); if (ret < 0) { @@ -285,7 +285,7 @@ static int bme680_read_calib(struct bme680_data *data, dev_err(dev, "failed to read resistance heat range\n"); return ret; } - calib->res_heat_range = (tmp & BME680_RHRANGE_MSK) / 16; + calib->res_heat_range = (tmp & BME680_RHRANGE_MASK) / 16; ret = regmap_read(data->regmap, BME680_REG_RES_HEAT_VAL, &tmp); if (ret < 0) { @@ -299,7 +299,7 @@ static int bme680_read_calib(struct bme680_data *data, dev_err(dev, "failed to read range software error\n"); return ret; } - calib->range_sw_err = (tmp & BME680_RSERROR_MSK) / 16; + calib->range_sw_err = (tmp & BME680_RSERROR_MASK) / 16; return 0; } From 8d3032c3692d247ad625863e949d419952db8cbd Mon Sep 17 00:00:00 2001 From: David Frey Date: Fri, 17 Aug 2018 12:03:17 -0700 Subject: [PATCH 15/68] iio: chemical: bme680: use GENMASK macro Replace hardcoded bit masks with GENMASK macro Signed-off-by: David Frey Reviewed-by: Himanshu Jha Tested-by: Himanshu Jha Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/bme680.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/chemical/bme680.h b/drivers/iio/chemical/bme680.h index 437d75c2bad5..a9f2a9a6abc5 100644 --- a/drivers/iio/chemical/bme680.h +++ b/drivers/iio/chemical/bme680.h @@ -18,7 +18,7 @@ #define BME680_REG_GAS_MSB 0x2A #define BME680_REG_GAS_R_LSB 0x2B #define BME680_GAS_STAB_BIT BIT(4) -#define BME680_GAS_RANGE_MASK 0x0F +#define BME680_GAS_RANGE_MASK GENMASK(3, 0) #define BME680_REG_CTRL_HUMIDITY 0x72 #define BME680_OSRS_HUMIDITY_MASK GENMASK(2, 0) @@ -39,13 +39,13 @@ #define BME680_MAX_OVERFLOW_VAL 0x40000000 #define BME680_HUM_REG_SHIFT_VAL 4 -#define BME680_BIT_H1_DATA_MASK 0x0F +#define BME680_BIT_H1_DATA_MASK GENMASK(3, 0) #define BME680_REG_RES_HEAT_RANGE 0x02 -#define BME680_RHRANGE_MASK 0x30 +#define BME680_RHRANGE_MASK GENMASK(5, 4) #define BME680_REG_RES_HEAT_VAL 0x00 #define BME680_REG_RANGE_SW_ERR 0x04 -#define BME680_RSERROR_MASK 0xF0 +#define BME680_RSERROR_MASK GENMASK(7, 4) #define BME680_REG_RES_HEAT_0 0x5A #define BME680_REG_GAS_WAIT_0 0x64 #define BME680_ADC_GAS_RES_SHIFT 6 From 3dcb60cd926ebfc8f85603d226457c9a38794209 Mon Sep 17 00:00:00 2001 From: David Frey Date: Fri, 17 Aug 2018 12:03:18 -0700 Subject: [PATCH 16/68] iio: chemical: bme680: use FIELD_GET macro Use the FIELD_GET macro instead of explicit mask and shift. Signed-off-by: David Frey Reviewed-by: Himanshu Jha Tested-by: Himanshu Jha Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/bme680_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c index 0ebe2d26ff1b..ced63e3035be 100644 --- a/drivers/iio/chemical/bme680_core.c +++ b/drivers/iio/chemical/bme680_core.c @@ -285,7 +285,7 @@ static int bme680_read_calib(struct bme680_data *data, dev_err(dev, "failed to read resistance heat range\n"); return ret; } - calib->res_heat_range = (tmp & BME680_RHRANGE_MASK) / 16; + calib->res_heat_range = FIELD_GET(BME680_RHRANGE_MASK, tmp); ret = regmap_read(data->regmap, BME680_REG_RES_HEAT_VAL, &tmp); if (ret < 0) { @@ -299,7 +299,7 @@ static int bme680_read_calib(struct bme680_data *data, dev_err(dev, "failed to read range software error\n"); return ret; } - calib->range_sw_err = (tmp & BME680_RSERROR_MASK) / 16; + calib->range_sw_err = FIELD_GET(BME680_RSERROR_MASK, tmp); return 0; } From 79fd571b7a4915cee0de64314ea08d8a2ffe8e78 Mon Sep 17 00:00:00 2001 From: David Frey Date: Fri, 17 Aug 2018 12:03:19 -0700 Subject: [PATCH 17/68] iio: chemical: bme680: simplify oversampling handling Temperature, pressure and humidity all expose and oversampling setting that works in the same way. Provide common handling for the oversampling sysfs attributes. Signed-off-by: David Frey Reviewed-by: Himanshu Jha Tested-by: Himanshu Jha Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/bme680_core.c | 99 +++++++++++------------------- 1 file changed, 36 insertions(+), 63 deletions(-) diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c index ced63e3035be..5ddeffb29fb0 100644 --- a/drivers/iio/chemical/bme680_core.c +++ b/drivers/iio/chemical/bme680_core.c @@ -91,8 +91,6 @@ static const struct iio_chan_spec bme680_channels[] = { }, }; -static const int bme680_oversampling_avail[] = { 1, 2, 4, 8, 16 }; - static int bme680_read_calib(struct bme680_data *data, struct bme680_calib *calib) { @@ -503,12 +501,20 @@ static int bme680_set_mode(struct bme680_data *data, bool mode) return ret; } +static u8 bme680_oversampling_to_reg(u8 val) +{ + return ilog2(val) + 1; +} + static int bme680_chip_config(struct bme680_data *data) { struct device *dev = regmap_get_device(data->regmap); int ret; - u8 osrs = FIELD_PREP(BME680_OSRS_HUMIDITY_MASK, - data->oversampling_humid + 1); + u8 osrs; + + osrs = FIELD_PREP( + BME680_OSRS_HUMIDITY_MASK, + bme680_oversampling_to_reg(data->oversampling_humid)); /* * Highly recommended to set oversampling of humidity before * temperature/pressure oversampling. @@ -529,12 +535,12 @@ static int bme680_chip_config(struct bme680_data *data) return ret; } - osrs = FIELD_PREP(BME680_OSRS_TEMP_MASK, data->oversampling_temp + 1) | - FIELD_PREP(BME680_OSRS_PRESS_MASK, data->oversampling_press + 1); - + osrs = FIELD_PREP(BME680_OSRS_TEMP_MASK, + bme680_oversampling_to_reg(data->oversampling_temp)) | + FIELD_PREP(BME680_OSRS_PRESS_MASK, + bme680_oversampling_to_reg(data->oversampling_press)); ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS, - BME680_OSRS_TEMP_MASK | - BME680_OSRS_PRESS_MASK, + BME680_OSRS_TEMP_MASK | BME680_OSRS_PRESS_MASK, osrs); if (ret < 0) dev_err(dev, "failed to write ctrl_meas register\n"); @@ -767,13 +773,13 @@ static int bme680_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_OVERSAMPLING_RATIO: switch (chan->type) { case IIO_TEMP: - *val = 1 << data->oversampling_temp; + *val = data->oversampling_temp; return IIO_VAL_INT; case IIO_PRESSURE: - *val = 1 << data->oversampling_press; + *val = data->oversampling_press; return IIO_VAL_INT; case IIO_HUMIDITYRELATIVE: - *val = 1 << data->oversampling_humid; + *val = data->oversampling_humid; return IIO_VAL_INT; default: return -EINVAL; @@ -783,52 +789,9 @@ static int bme680_read_raw(struct iio_dev *indio_dev, } } -static int bme680_write_oversampling_ratio_temp(struct bme680_data *data, - int val) +static bool bme680_is_valid_oversampling(int rate) { - int i; - - for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); i++) { - if (bme680_oversampling_avail[i] == val) { - data->oversampling_temp = ilog2(val); - - return bme680_chip_config(data); - } - } - - return -EINVAL; -} - -static int bme680_write_oversampling_ratio_press(struct bme680_data *data, - int val) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); i++) { - if (bme680_oversampling_avail[i] == val) { - data->oversampling_press = ilog2(val); - - return bme680_chip_config(data); - } - } - - return -EINVAL; -} - -static int bme680_write_oversampling_ratio_humid(struct bme680_data *data, - int val) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); i++) { - if (bme680_oversampling_avail[i] == val) { - data->oversampling_humid = ilog2(val); - - return bme680_chip_config(data); - } - } - - return -EINVAL; + return (rate > 0 && rate <= 16 && is_power_of_2(rate)); } static int bme680_write_raw(struct iio_dev *indio_dev, @@ -839,16 +802,26 @@ static int bme680_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + { + if (!bme680_is_valid_oversampling(val)) + return -EINVAL; + switch (chan->type) { case IIO_TEMP: - return bme680_write_oversampling_ratio_temp(data, val); + data->oversampling_temp = val; + break; case IIO_PRESSURE: - return bme680_write_oversampling_ratio_press(data, val); + data->oversampling_press = val; + break; case IIO_HUMIDITYRELATIVE: - return bme680_write_oversampling_ratio_humid(data, val); + data->oversampling_humid = val; + break; default: return -EINVAL; } + + return bme680_chip_config(data); + } default: return -EINVAL; } @@ -910,9 +883,9 @@ int bme680_core_probe(struct device *dev, struct regmap *regmap, indio_dev->modes = INDIO_DIRECT_MODE; /* default values for the sensor */ - data->oversampling_humid = ilog2(2); /* 2X oversampling rate */ - data->oversampling_press = ilog2(4); /* 4X oversampling rate */ - data->oversampling_temp = ilog2(8); /* 8X oversampling rate */ + data->oversampling_humid = 2; /* 2X oversampling rate */ + data->oversampling_press = 4; /* 4X oversampling rate */ + data->oversampling_temp = 8; /* 8X oversampling rate */ data->heater_temp = 320; /* degree Celsius */ data->heater_dur = 150; /* milliseconds */ From a18c66c405c740291ea263412f2e8b6c66fa5966 Mon Sep 17 00:00:00 2001 From: Tomasz Duszynski Date: Thu, 16 Aug 2018 20:49:15 +0200 Subject: [PATCH 18/68] iio: pressure: ms5611: remove deprecated compatible strings Compatible strings tend to follow manufacturer,model format. In case one wants to do a matching with manufacturer stripped off he can still do so since SPI/I2C core will try id_table based matching anyway. Signed-off-by: Tomasz Duszynski Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/ms5611_i2c.c | 2 -- drivers/iio/pressure/ms5611_spi.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/drivers/iio/pressure/ms5611_i2c.c b/drivers/iio/pressure/ms5611_i2c.c index 55fb5fc0b6ea..0469c8ae1134 100644 --- a/drivers/iio/pressure/ms5611_i2c.c +++ b/drivers/iio/pressure/ms5611_i2c.c @@ -117,9 +117,7 @@ static int ms5611_i2c_remove(struct i2c_client *client) #if defined(CONFIG_OF) static const struct of_device_id ms5611_i2c_matches[] = { { .compatible = "meas,ms5611" }, - { .compatible = "ms5611" }, { .compatible = "meas,ms5607" }, - { .compatible = "ms5607" }, { } }; MODULE_DEVICE_TABLE(of, ms5611_i2c_matches); diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c index 932e05001e1a..cd11d022208e 100644 --- a/drivers/iio/pressure/ms5611_spi.c +++ b/drivers/iio/pressure/ms5611_spi.c @@ -119,9 +119,7 @@ static int ms5611_spi_remove(struct spi_device *spi) #if defined(CONFIG_OF) static const struct of_device_id ms5611_spi_matches[] = { { .compatible = "meas,ms5611" }, - { .compatible = "ms5611" }, { .compatible = "meas,ms5607" }, - { .compatible = "ms5607" }, { } }; MODULE_DEVICE_TABLE(of, ms5611_spi_matches); From 9ffa68f65416094714c63ed1db219493e84fe21d Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 15 Aug 2018 11:23:49 -0500 Subject: [PATCH 19/68] iio: health: max30102: Mark expected switch fall-throughs In preparation to enabling -Wimplicit-fallthrough, mark switch cases where we are expecting to fall through. Notice that in this particular case, I placed the "fall through" annotation at the bottom of the case, which is what GCC is expecting to find. Addresses-Coverity-ID: 1458342 ("Missing break in switch") Addresses-Coverity-ID: 1458345 ("Missing break in switch") Signed-off-by: Gustavo A. R. Silva Signed-off-by: Jonathan Cameron --- drivers/iio/health/max30102.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index 15ccadc74891..3e29562ce374 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -282,9 +282,11 @@ static int max30102_read_measurement(struct max30102_data *data, switch (measurements) { case 3: MAX30102_COPY_DATA(2); - case 2: /* fall-through */ + /* fall through */ + case 2: MAX30102_COPY_DATA(1); - case 1: /* fall-through */ + /* fall through */ + case 1: MAX30102_COPY_DATA(0); break; default: From 9920ed25eccc9b494926be5372ba745db83481ab Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Tue, 14 Aug 2018 13:23:17 +0200 Subject: [PATCH 20/68] drivers: iio: Update MODULE AUTHOR email address no functional changes Signed-off-by: Michael Hennerich Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7298.c | 2 +- drivers/iio/adc/ad7476.c | 2 +- drivers/iio/adc/ad7793.c | 2 +- drivers/iio/adc/ad7887.c | 2 +- drivers/iio/adc/ad7923.c | 2 +- drivers/iio/adc/ad799x.c | 2 +- drivers/iio/amplifiers/ad8366.c | 2 +- drivers/iio/dac/ad5446.c | 2 +- drivers/iio/dac/ad5504.c | 2 +- drivers/iio/dac/ad5686.c | 2 +- drivers/iio/dac/ad5791.c | 2 +- drivers/iio/frequency/ad9523.c | 2 +- drivers/iio/trigger/iio-trig-sysfs.c | 2 +- drivers/staging/iio/adc/ad7192.c | 2 +- drivers/staging/iio/adc/ad7280a.c | 2 +- drivers/staging/iio/adc/ad7606.c | 2 +- drivers/staging/iio/adc/ad7606_par.c | 2 +- drivers/staging/iio/adc/ad7606_spi.c | 2 +- drivers/staging/iio/adc/ad7780.c | 2 +- drivers/staging/iio/cdc/ad7746.c | 2 +- drivers/staging/iio/frequency/ad9832.c | 2 +- drivers/staging/iio/frequency/ad9834.c | 2 +- drivers/staging/iio/impedance-analyzer/ad5933.c | 2 +- 23 files changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c index 2b20c6c8ec7f..e0220825fde0 100644 --- a/drivers/iio/adc/ad7298.c +++ b/drivers/iio/adc/ad7298.c @@ -385,6 +385,6 @@ static struct spi_driver ad7298_driver = { }; module_spi_driver(ad7298_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD7298 ADC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index fbaae47746a8..0549686b9ef8 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -328,6 +328,6 @@ static struct spi_driver ad7476_driver = { }; module_spi_driver(ad7476_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD7476 and similar 1-channel ADCs"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index d4bbe5b53318..4ac3ae62f56f 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -822,6 +822,6 @@ static struct spi_driver ad7793_driver = { }; module_spi_driver(ad7793_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD7793 and similar ADCs"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c index 205c0f1761aa..9d4c2467d362 100644 --- a/drivers/iio/adc/ad7887.c +++ b/drivers/iio/adc/ad7887.c @@ -362,6 +362,6 @@ static struct spi_driver ad7887_driver = { }; module_spi_driver(ad7887_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD7887 ADC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index ffb7e089969c..d62dbb62be45 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -363,7 +363,7 @@ static struct spi_driver ad7923_driver = { }; module_spi_driver(ad7923_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_AUTHOR("Patrick Vasseur "); MODULE_DESCRIPTION("Analog Devices AD7904/AD7914/AD7923/AD7924 ADC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index e1da67d5ee22..7a5b5d00a87d 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -892,6 +892,6 @@ static struct i2c_driver ad799x_driver = { }; module_i2c_driver(ad799x_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD799x ADC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index 0138337aedd1..4b76b61ba4be 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -209,6 +209,6 @@ static struct spi_driver ad8366_driver = { module_spi_driver(ad8366_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD8366 VGA"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c index fd26a4272fc5..c3426708b6b5 100644 --- a/drivers/iio/dac/ad5446.c +++ b/drivers/iio/dac/ad5446.c @@ -628,6 +628,6 @@ static void __exit ad5446_exit(void) } module_exit(ad5446_exit); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index d9037ea59168..0ae23a268017 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -369,6 +369,6 @@ static struct spi_driver ad5504_driver = { }; module_spi_driver(ad5504_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD5501/AD5501 DAC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 2ddbfc3fdbae..0e134b13967a 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -470,6 +470,6 @@ int ad5686_remove(struct device *dev) } EXPORT_SYMBOL_GPL(ad5686_remove); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c index 7569bf6868c2..84ce5e6ecf3f 100644 --- a/drivers/iio/dac/ad5791.c +++ b/drivers/iio/dac/ad5791.c @@ -467,6 +467,6 @@ static struct spi_driver ad5791_driver = { }; module_spi_driver(ad5791_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c index f4a508107f0d..f3f94fbdd20a 100644 --- a/drivers/iio/frequency/ad9523.c +++ b/drivers/iio/frequency/ad9523.c @@ -1078,6 +1078,6 @@ static struct spi_driver ad9523_driver = { }; module_spi_driver(ad9523_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD9523 CLOCKDIST/PLL"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/trigger/iio-trig-sysfs.c b/drivers/iio/trigger/iio-trig-sysfs.c index 3f0dc9a1a514..45c4897295d6 100644 --- a/drivers/iio/trigger/iio-trig-sysfs.c +++ b/drivers/iio/trigger/iio-trig-sysfs.c @@ -222,7 +222,7 @@ static void __exit iio_sysfs_trig_exit(void) } module_exit(iio_sysfs_trig_exit); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Sysfs based trigger for the iio subsystem"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:iio-trig-sysfs"); diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c index df0499fc4802..acdbc07fd259 100644 --- a/drivers/staging/iio/adc/ad7192.c +++ b/drivers/staging/iio/adc/ad7192.c @@ -761,6 +761,6 @@ static struct spi_driver ad7192_driver = { }; module_spi_driver(ad7192_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD7190, AD7192, AD7193, AD7195 ADC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/ad7280a.c b/drivers/staging/iio/adc/ad7280a.c index b736275c10f5..58420dcb406d 100644 --- a/drivers/staging/iio/adc/ad7280a.c +++ b/drivers/staging/iio/adc/ad7280a.c @@ -987,6 +987,6 @@ static struct spi_driver ad7280_driver = { }; module_spi_driver(ad7280_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD7280A"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/ad7606.c b/drivers/staging/iio/adc/ad7606.c index 25b9fcd5e3a4..c5fe3003075b 100644 --- a/drivers/staging/iio/adc/ad7606.c +++ b/drivers/staging/iio/adc/ad7606.c @@ -534,6 +534,6 @@ EXPORT_SYMBOL_GPL(ad7606_pm_ops); #endif -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/ad7606_par.c b/drivers/staging/iio/adc/ad7606_par.c index a34c2a1d5373..956e38774767 100644 --- a/drivers/staging/iio/adc/ad7606_par.c +++ b/drivers/staging/iio/adc/ad7606_par.c @@ -105,6 +105,6 @@ static struct platform_driver ad7606_driver = { module_platform_driver(ad7606_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/ad7606_spi.c b/drivers/staging/iio/adc/ad7606_spi.c index c9b1f26685f4..ffd9d0626ec2 100644 --- a/drivers/staging/iio/adc/ad7606_spi.c +++ b/drivers/staging/iio/adc/ad7606_spi.c @@ -73,6 +73,6 @@ static struct spi_driver ad7606_driver = { }; module_spi_driver(ad7606_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD7606 ADC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/adc/ad7780.c b/drivers/staging/iio/adc/ad7780.c index 16d72072c076..b67412db0318 100644 --- a/drivers/staging/iio/adc/ad7780.c +++ b/drivers/staging/iio/adc/ad7780.c @@ -260,6 +260,6 @@ static struct spi_driver ad7780_driver = { }; module_spi_driver(ad7780_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD7780 and similar ADCs"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c index f53612a6461d..0eb28fea876e 100644 --- a/drivers/staging/iio/cdc/ad7746.c +++ b/drivers/staging/iio/cdc/ad7746.c @@ -758,6 +758,6 @@ static struct i2c_driver ad7746_driver = { }; module_i2c_driver(ad7746_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD7746/5/7 capacitive sensor driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c index c73eff1f8d73..a3ce50427724 100644 --- a/drivers/staging/iio/frequency/ad9832.c +++ b/drivers/staging/iio/frequency/ad9832.c @@ -454,6 +454,6 @@ static struct spi_driver ad9832_driver = { }; module_spi_driver(ad9832_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD9832/AD9835 DDS"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c index 4c6d4043903e..1e977014fe5f 100644 --- a/drivers/staging/iio/frequency/ad9834.c +++ b/drivers/staging/iio/frequency/ad9834.c @@ -526,6 +526,6 @@ static struct spi_driver ad9834_driver = { }; module_spi_driver(ad9834_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD9833/AD9834/AD9837/AD9838 DDS"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c index 14df89510396..a2370dd1e1a8 100644 --- a/drivers/staging/iio/impedance-analyzer/ad5933.c +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -797,6 +797,6 @@ static struct i2c_driver ad5933_driver = { }; module_i2c_driver(ad5933_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("Analog Devices AD5933 Impedance Conv. Network Analyzer"); MODULE_LICENSE("GPL v2"); From fe8a5420749f5d1e1bd1be15942281bf1b6e3d4e Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Sat, 11 Aug 2018 10:47:09 +0200 Subject: [PATCH 21/68] iio: dac: max517: avoid using CONFIG_PM_SLEEP This is already handled by SIMPLE_DEV_PM_OPS(). Signed-off-by: Marcus Folkesson Signed-off-by: Jonathan Cameron --- drivers/iio/dac/max517.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/iio/dac/max517.c b/drivers/iio/dac/max517.c index 1d853247a205..451d10e323cf 100644 --- a/drivers/iio/dac/max517.c +++ b/drivers/iio/dac/max517.c @@ -113,15 +113,14 @@ static int max517_write_raw(struct iio_dev *indio_dev, return ret; } -#ifdef CONFIG_PM_SLEEP -static int max517_suspend(struct device *dev) +static int __maybe_unused max517_suspend(struct device *dev) { u8 outbuf = COMMAND_PD; return i2c_master_send(to_i2c_client(dev), &outbuf, 1); } -static int max517_resume(struct device *dev) +static int __maybe_unused max517_resume(struct device *dev) { u8 outbuf = 0; @@ -129,10 +128,6 @@ static int max517_resume(struct device *dev) } static SIMPLE_DEV_PM_OPS(max517_pm_ops, max517_suspend, max517_resume); -#define MAX517_PM_OPS (&max517_pm_ops) -#else -#define MAX517_PM_OPS NULL -#endif static const struct iio_info max517_info = { .read_raw = max517_read_raw, @@ -229,7 +224,7 @@ MODULE_DEVICE_TABLE(i2c, max517_id); static struct i2c_driver max517_driver = { .driver = { .name = MAX517_DRV_NAME, - .pm = MAX517_PM_OPS, + .pm = &max517_pm_ops, }, .probe = max517_probe, .remove = max517_remove, From b6ec220c28268872cad624aa824463a52c50ed47 Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Sat, 11 Aug 2018 10:47:18 +0200 Subject: [PATCH 22/68] iio: dac: max5821: avoid using CONFIG_PM_SLEEP This is already handled by SIMPLE_DEV_PM_OPS(). Signed-off-by: Marcus Folkesson Signed-off-by: Jonathan Cameron --- drivers/iio/dac/max5821.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/iio/dac/max5821.c b/drivers/iio/dac/max5821.c index d0ecc1fdd8fc..f0cf6903dcd2 100644 --- a/drivers/iio/dac/max5821.c +++ b/drivers/iio/dac/max5821.c @@ -270,8 +270,7 @@ static int max5821_write_raw(struct iio_dev *indio_dev, } } -#ifdef CONFIG_PM_SLEEP -static int max5821_suspend(struct device *dev) +static int __maybe_unused max5821_suspend(struct device *dev) { u8 outbuf[2] = { MAX5821_EXTENDED_COMMAND_MODE, MAX5821_EXTENDED_DAC_A | @@ -281,7 +280,7 @@ static int max5821_suspend(struct device *dev) return i2c_master_send(to_i2c_client(dev), outbuf, 2); } -static int max5821_resume(struct device *dev) +static int __maybe_unused max5821_resume(struct device *dev) { u8 outbuf[2] = { MAX5821_EXTENDED_COMMAND_MODE, MAX5821_EXTENDED_DAC_A | @@ -292,10 +291,6 @@ static int max5821_resume(struct device *dev) } static SIMPLE_DEV_PM_OPS(max5821_pm_ops, max5821_suspend, max5821_resume); -#define MAX5821_PM_OPS (&max5821_pm_ops) -#else -#define MAX5821_PM_OPS NULL -#endif /* CONFIG_PM_SLEEP */ static const struct iio_info max5821_info = { .read_raw = max5821_read_raw, @@ -392,7 +387,7 @@ static struct i2c_driver max5821_driver = { .driver = { .name = "max5821", .of_match_table = max5821_of_match, - .pm = MAX5821_PM_OPS, + .pm = &max5821_pm_ops, }, .probe = max5821_probe, .remove = max5821_remove, From 1a5bc41a3d23745577fa26f1ee34d50c205e2926 Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Sat, 11 Aug 2018 10:47:25 +0200 Subject: [PATCH 23/68] iio: dac: mcp4725: avoid using CONFIG_PM_SLEEP This is already handled by SIMPLE_DEV_PM_OPS(). Signed-off-by: Marcus Folkesson Signed-off-by: Jonathan Cameron --- drivers/iio/dac/mcp4725.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index 8b5aad4c32d9..6d71fd905e29 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -45,7 +45,7 @@ struct mcp4725_data { struct regulator *vref_reg; }; -static int mcp4725_suspend(struct device *dev) +static int __maybe_unused mcp4725_suspend(struct device *dev) { struct mcp4725_data *data = iio_priv(i2c_get_clientdata( to_i2c_client(dev))); @@ -58,7 +58,7 @@ static int mcp4725_suspend(struct device *dev) return i2c_master_send(data->client, outbuf, 2); } -static int mcp4725_resume(struct device *dev) +static int __maybe_unused mcp4725_resume(struct device *dev) { struct mcp4725_data *data = iio_priv(i2c_get_clientdata( to_i2c_client(dev))); @@ -71,13 +71,7 @@ static int mcp4725_resume(struct device *dev) return i2c_master_send(data->client, outbuf, 2); } - -#ifdef CONFIG_PM_SLEEP static SIMPLE_DEV_PM_OPS(mcp4725_pm_ops, mcp4725_suspend, mcp4725_resume); -#define MCP4725_PM_OPS (&mcp4725_pm_ops) -#else -#define MCP4725_PM_OPS NULL -#endif static ssize_t mcp4725_store_eeprom(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) @@ -547,7 +541,7 @@ static struct i2c_driver mcp4725_driver = { .driver = { .name = MCP4725_DRV_NAME, .of_match_table = of_match_ptr(mcp4725_of_match), - .pm = MCP4725_PM_OPS, + .pm = &mcp4725_pm_ops, }, .probe = mcp4725_probe, .remove = mcp4725_remove, From b1ec0802503820ccbc894aadfd2a44da20232f5e Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Sat, 11 Aug 2018 11:12:19 +0200 Subject: [PATCH 24/68] iio: adc: max9611: explicitly cast gain_selectors After finding a reasonable gain, the function converts the configured gain to a gain configuration option selector enum max9611_csa_gain. Make the conversion clearly visible by using an explicit cast. This also avoids a warning seen with clang: drivers/iio/adc/max9611.c:292:16: warning: implicit conversion from enumeration type 'enum max9611_conf_ids' to different enumeration type 'enum max9611_csa_gain' [-Wenum-conversion] *csa_gain = gain_selectors[i]; ~ ^~~~~~~~~~~~~~~~~ Signed-off-by: Stefan Agner Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max9611.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c index 0538ff8c4ac1..643a4e66eb80 100644 --- a/drivers/iio/adc/max9611.c +++ b/drivers/iio/adc/max9611.c @@ -289,7 +289,7 @@ static int max9611_read_csa_voltage(struct max9611_dev *max9611, return ret; if (*adc_raw > 0) { - *csa_gain = gain_selectors[i]; + *csa_gain = (enum max9611_csa_gain)gain_selectors[i]; return 0; } } From 4d0434f1297c25c2adbd188888367b534e0aff87 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Fri, 10 Aug 2018 12:47:02 -0700 Subject: [PATCH 25/68] iio: adc: qcom-spmi-adc5: Add ADC5_AMUX_THM[24]_100K_PU to rev2 channel list Add ADC5_AMUX_THM2_100K_PU and ADC5_AMUX_THM4_100K_PU to the list of rev2 ADC channels. Signed-off-by: Matthias Kaehlcke Signed-off-by: Jonathan Cameron --- drivers/iio/adc/qcom-spmi-adc5.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c index a4299417f3de..1f9298a5c83d 100644 --- a/drivers/iio/adc/qcom-spmi-adc5.c +++ b/drivers/iio/adc/qcom-spmi-adc5.c @@ -491,8 +491,12 @@ static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = { SCALE_HW_CALIB_PMIC_THERM) [ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 1, SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 1, + SCALE_HW_CALIB_THERM_100K_PULLUP) [ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 1, SCALE_HW_CALIB_THERM_100K_PULLUP) + [ADC5_AMUX_THM4_100K_PU] = ADC5_CHAN_TEMP("amux_thm4_100k_pu", 1, + SCALE_HW_CALIB_THERM_100K_PULLUP) [ADC5_AMUX_THM5_100K_PU] = ADC5_CHAN_TEMP("amux_thm5_100k_pu", 1, SCALE_HW_CALIB_THERM_100K_PULLUP) [ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm_100k_pu", 1, From 4097da40f9264ab75f256ec7f097cfff2710cc0a Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Fri, 10 Aug 2018 11:46:18 +0300 Subject: [PATCH 26/68] iio: adxl372: New driver for Analog Devices ADXL372 Accelerometer This patch adds basic support for Analog Devices ADXL372 SPI-Bus Three-Axis Digital Accelerometer. The device is probed and configured the with some initial default values. With this basic driver, it is possible to read raw acceleration data. Datasheet: http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL372.pdf Signed-off-by: Stefan Popa Signed-off-by: Jonathan Cameron --- MAINTAINERS | 6 + drivers/iio/accel/Kconfig | 11 + drivers/iio/accel/Makefile | 1 + drivers/iio/accel/adxl372.c | 525 ++++++++++++++++++++++++++++++++++++ 4 files changed, 543 insertions(+) create mode 100644 drivers/iio/accel/adxl372.c diff --git a/MAINTAINERS b/MAINTAINERS index af64fe0f0b41..fe23f8b66712 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -543,6 +543,12 @@ W: http://ez.analog.com/community/linux-device-drivers S: Supported F: drivers/input/misc/adxl34x.c +ADXL372 THREE-AXIS DIGITAL ACCELEROMETER DRIVER +M: Stefan Popa +W: http://ez.analog.com/community/linux-device-drivers +S: Supported +F: drivers/iio/accel/adxl372.c + AF9013 MEDIA DRIVER M: Antti Palosaari L: linux-media@vger.kernel.org diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 829dc96c9dd6..eae23d66ce53 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -60,6 +60,17 @@ config ADXL345_SPI will be called adxl345_spi and you will also get adxl345_core for the core module. +config ADXL372 + tristate "Analog Devices ADXL372 3-Axis Accelerometer Driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to add support for the Analog Devices ADXL372 triaxial + acceleration sensor. + To compile this driver as a module, choose M here: the + module will be called adxl372. + config BMA180 tristate "Bosch BMA180/BMA250 3-Axis Accelerometer Driver" depends on I2C diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 636d4d1b2990..5758ffc113a6 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_ADIS16209) += adis16209.o obj-$(CONFIG_ADXL345) += adxl345_core.o obj-$(CONFIG_ADXL345_I2C) += adxl345_i2c.o obj-$(CONFIG_ADXL345_SPI) += adxl345_spi.o +obj-$(CONFIG_ADXL372) += adxl372.o obj-$(CONFIG_BMA180) += bma180.o obj-$(CONFIG_BMA220) += bma220_spi.o obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c new file mode 100644 index 000000000000..d6e926de955e --- /dev/null +++ b/drivers/iio/accel/adxl372.c @@ -0,0 +1,525 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ADXL372 3-Axis Digital Accelerometer SPI driver + * + * Copyright 2018 Analog Devices Inc. + */ + +#include +#include +#include +#include + +#include +#include + +/* ADXL372 registers definition */ +#define ADXL372_DEVID 0x00 +#define ADXL372_DEVID_MST 0x01 +#define ADXL372_PARTID 0x02 +#define ADXL372_REVID 0x03 +#define ADXL372_STATUS_1 0x04 +#define ADXL372_STATUS_2 0x05 +#define ADXL372_FIFO_ENTRIES_2 0x06 +#define ADXL372_FIFO_ENTRIES_1 0x07 +#define ADXL372_X_DATA_H 0x08 +#define ADXL372_X_DATA_L 0x09 +#define ADXL372_Y_DATA_H 0x0A +#define ADXL372_Y_DATA_L 0x0B +#define ADXL372_Z_DATA_H 0x0C +#define ADXL372_Z_DATA_L 0x0D +#define ADXL372_X_MAXPEAK_H 0x15 +#define ADXL372_X_MAXPEAK_L 0x16 +#define ADXL372_Y_MAXPEAK_H 0x17 +#define ADXL372_Y_MAXPEAK_L 0x18 +#define ADXL372_Z_MAXPEAK_H 0x19 +#define ADXL372_Z_MAXPEAK_L 0x1A +#define ADXL372_OFFSET_X 0x20 +#define ADXL372_OFFSET_Y 0x21 +#define ADXL372_OFFSET_Z 0x22 +#define ADXL372_X_THRESH_ACT_H 0x23 +#define ADXL372_X_THRESH_ACT_L 0x24 +#define ADXL372_Y_THRESH_ACT_H 0x25 +#define ADXL372_Y_THRESH_ACT_L 0x26 +#define ADXL372_Z_THRESH_ACT_H 0x27 +#define ADXL372_Z_THRESH_ACT_L 0x28 +#define ADXL372_TIME_ACT 0x29 +#define ADXL372_X_THRESH_INACT_H 0x2A +#define ADXL372_X_THRESH_INACT_L 0x2B +#define ADXL372_Y_THRESH_INACT_H 0x2C +#define ADXL372_Y_THRESH_INACT_L 0x2D +#define ADXL372_Z_THRESH_INACT_H 0x2E +#define ADXL372_Z_THRESH_INACT_L 0x2F +#define ADXL372_TIME_INACT_H 0x30 +#define ADXL372_TIME_INACT_L 0x31 +#define ADXL372_X_THRESH_ACT2_H 0x32 +#define ADXL372_X_THRESH_ACT2_L 0x33 +#define ADXL372_Y_THRESH_ACT2_H 0x34 +#define ADXL372_Y_THRESH_ACT2_L 0x35 +#define ADXL372_Z_THRESH_ACT2_H 0x36 +#define ADXL372_Z_THRESH_ACT2_L 0x37 +#define ADXL372_HPF 0x38 +#define ADXL372_FIFO_SAMPLES 0x39 +#define ADXL372_FIFO_CTL 0x3A +#define ADXL372_INT1_MAP 0x3B +#define ADXL372_INT2_MAP 0x3C +#define ADXL372_TIMING 0x3D +#define ADXL372_MEASURE 0x3E +#define ADXL372_POWER_CTL 0x3F +#define ADXL372_SELF_TEST 0x40 +#define ADXL372_RESET 0x41 +#define ADXL372_FIFO_DATA 0x42 + +#define ADXL372_DEVID_VAL 0xAD +#define ADXL372_PARTID_VAL 0xFA +#define ADXL372_RESET_CODE 0x52 + +/* ADXL372_POWER_CTL */ +#define ADXL372_POWER_CTL_MODE_MSK GENMASK_ULL(1, 0) +#define ADXL372_POWER_CTL_MODE(x) (((x) & 0x3) << 0) + +/* ADXL372_MEASURE */ +#define ADXL372_MEASURE_LINKLOOP_MSK GENMASK_ULL(5, 4) +#define ADXL372_MEASURE_LINKLOOP_MODE(x) (((x) & 0x3) << 4) +#define ADXL372_MEASURE_BANDWIDTH_MSK GENMASK_ULL(2, 0) +#define ADXL372_MEASURE_BANDWIDTH_MODE(x) (((x) & 0x7) << 0) + +/* ADXL372_TIMING */ +#define ADXL372_TIMING_ODR_MSK GENMASK_ULL(7, 5) +#define ADXL372_TIMING_ODR_MODE(x) (((x) & 0x7) << 5) + +/* ADXL372_FIFO_CTL */ +#define ADXL372_FIFO_CTL_FORMAT_MSK GENMASK(5, 3) +#define ADXL372_FIFO_CTL_FORMAT_MODE(x) (((x) & 0x7) << 3) +#define ADXL372_FIFO_CTL_MODE_MSK GENMASK(2, 1) +#define ADXL372_FIFO_CTL_MODE_MODE(x) (((x) & 0x3) << 1) +#define ADXL372_FIFO_CTL_SAMPLES_MSK BIT(1) +#define ADXL372_FIFO_CTL_SAMPLES_MODE(x) (((x) > 0xFF) ? 1 : 0) + +/* ADXL372_STATUS_1 */ +#define ADXL372_STATUS_1_DATA_RDY(x) (((x) >> 0) & 0x1) +#define ADXL372_STATUS_1_FIFO_RDY(x) (((x) >> 1) & 0x1) +#define ADXL372_STATUS_1_FIFO_FULL(x) (((x) >> 2) & 0x1) +#define ADXL372_STATUS_1_FIFO_OVR(x) (((x) >> 3) & 0x1) +#define ADXL372_STATUS_1_USR_NVM_BUSY(x) (((x) >> 5) & 0x1) +#define ADXL372_STATUS_1_AWAKE(x) (((x) >> 6) & 0x1) +#define ADXL372_STATUS_1_ERR_USR_REGS(x) (((x) >> 7) & 0x1) + +/* ADXL372_INT1_MAP */ +#define ADXL372_INT1_MAP_DATA_RDY_MSK BIT(0) +#define ADXL372_INT1_MAP_DATA_RDY_MODE(x) (((x) & 0x1) << 0) +#define ADXL372_INT1_MAP_FIFO_RDY_MSK BIT(1) +#define ADXL372_INT1_MAP_FIFO_RDY_MODE(x) (((x) & 0x1) << 1) +#define ADXL372_INT1_MAP_FIFO_FULL_MSK BIT(2) +#define ADXL372_INT1_MAP_FIFO_FULL_MODE(x) (((x) & 0x1) << 2) +#define ADXL372_INT1_MAP_FIFO_OVR_MSK BIT(3) +#define ADXL372_INT1_MAP_FIFO_OVR_MODE(x) (((x) & 0x1) << 3) +#define ADXL372_INT1_MAP_INACT_MSK BIT(4) +#define ADXL372_INT1_MAP_INACT_MODE(x) (((x) & 0x1) << 4) +#define ADXL372_INT1_MAP_ACT_MSK BIT(5) +#define ADXL372_INT1_MAP_ACT_MODE(x) (((x) & 0x1) << 5) +#define ADXL372_INT1_MAP_AWAKE_MSK BIT(6) +#define ADXL372_INT1_MAP_AWAKE_MODE(x) (((x) & 0x1) << 6) +#define ADXL372_INT1_MAP_LOW_MSK BIT(7) +#define ADXL372_INT1_MAP_LOW_MODE(x) (((x) & 0x1) << 7) + +/* + * At +/- 200g with 12-bit resolution, scale is computed as: + * (200 + 200) * 9.81 / (2^12 - 1) = 0.958241 + */ +#define ADXL372_USCALE 958241 + +enum adxl372_op_mode { + ADXL372_STANDBY, + ADXL372_WAKE_UP, + ADXL372_INSTANT_ON, + ADXL372_FULL_BW_MEASUREMENT, +}; + +enum adxl372_act_proc_mode { + ADXL372_DEFAULT, + ADXL372_LINKED, + ADXL372_LOOPED, +}; + +enum adxl372_th_activity { + ADXL372_ACTIVITY, + ADXL372_ACTIVITY2, + ADXL372_INACTIVITY, +}; + +enum adxl372_odr { + ADXL372_ODR_400HZ, + ADXL372_ODR_800HZ, + ADXL372_ODR_1600HZ, + ADXL372_ODR_3200HZ, + ADXL372_ODR_6400HZ, +}; + +enum adxl372_bandwidth { + ADXL372_BW_200HZ, + ADXL372_BW_400HZ, + ADXL372_BW_800HZ, + ADXL372_BW_1600HZ, + ADXL372_BW_3200HZ, +}; + +static const unsigned int adxl372_th_reg_high_addr[3] = { + [ADXL372_ACTIVITY] = ADXL372_X_THRESH_ACT_H, + [ADXL372_ACTIVITY2] = ADXL372_X_THRESH_ACT2_H, + [ADXL372_INACTIVITY] = ADXL372_X_THRESH_INACT_H, +}; + +#define ADXL372_ACCEL_CHANNEL(index, reg, axis) { \ + .type = IIO_ACCEL, \ + .address = reg, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec adxl372_channels[] = { + ADXL372_ACCEL_CHANNEL(0, ADXL372_X_DATA_H, X), + ADXL372_ACCEL_CHANNEL(1, ADXL372_Y_DATA_H, Y), + ADXL372_ACCEL_CHANNEL(2, ADXL372_Z_DATA_H, Z), +}; + +struct adxl372_state { + struct spi_device *spi; + struct regmap *regmap; + enum adxl372_op_mode op_mode; + enum adxl372_act_proc_mode act_proc_mode; + enum adxl372_odr odr; + enum adxl372_bandwidth bw; + u32 act_time_ms; + u32 inact_time_ms; +}; + +static int adxl372_read_axis(struct adxl372_state *st, u8 addr) +{ + __be16 regval; + int ret; + + ret = regmap_bulk_read(st->regmap, addr, ®val, sizeof(regval)); + if (ret < 0) + return ret; + + return be16_to_cpu(regval); +} + +static int adxl372_set_op_mode(struct adxl372_state *st, + enum adxl372_op_mode op_mode) +{ + int ret; + + ret = regmap_update_bits(st->regmap, ADXL372_POWER_CTL, + ADXL372_POWER_CTL_MODE_MSK, + ADXL372_POWER_CTL_MODE(op_mode)); + if (ret < 0) + return ret; + + st->op_mode = op_mode; + + return ret; +} + +static int adxl372_set_odr(struct adxl372_state *st, + enum adxl372_odr odr) +{ + int ret; + + ret = regmap_update_bits(st->regmap, ADXL372_TIMING, + ADXL372_TIMING_ODR_MSK, + ADXL372_TIMING_ODR_MODE(odr)); + if (ret < 0) + return ret; + + st->odr = odr; + + return ret; +} + +static int adxl372_set_bandwidth(struct adxl372_state *st, + enum adxl372_bandwidth bw) +{ + int ret; + + ret = regmap_update_bits(st->regmap, ADXL372_MEASURE, + ADXL372_MEASURE_BANDWIDTH_MSK, + ADXL372_MEASURE_BANDWIDTH_MODE(bw)); + if (ret < 0) + return ret; + + st->bw = bw; + + return ret; +} + +static int adxl372_set_act_proc_mode(struct adxl372_state *st, + enum adxl372_act_proc_mode mode) +{ + int ret; + + ret = regmap_update_bits(st->regmap, + ADXL372_MEASURE, + ADXL372_MEASURE_LINKLOOP_MSK, + ADXL372_MEASURE_LINKLOOP_MODE(mode)); + if (ret < 0) + return ret; + + st->act_proc_mode = mode; + + return ret; +} + +static int adxl372_set_activity_threshold(struct adxl372_state *st, + enum adxl372_th_activity act, + bool ref_en, bool enable, + unsigned int threshold) +{ + unsigned char buf[6]; + unsigned char th_reg_high_val, th_reg_low_val, th_reg_high_addr; + + /* scale factor is 100 mg/code */ + th_reg_high_val = (threshold / 100) >> 3; + th_reg_low_val = ((threshold / 100) << 5) | (ref_en << 1) | enable; + th_reg_high_addr = adxl372_th_reg_high_addr[act]; + + buf[0] = th_reg_high_val; + buf[1] = th_reg_low_val; + buf[2] = th_reg_high_val; + buf[3] = th_reg_low_val; + buf[4] = th_reg_high_val; + buf[5] = th_reg_low_val; + + return regmap_bulk_write(st->regmap, th_reg_high_addr, + buf, ARRAY_SIZE(buf)); +} + +static int adxl372_set_activity_time_ms(struct adxl372_state *st, + unsigned int act_time_ms) +{ + unsigned int reg_val, scale_factor; + int ret; + + /* + * 3.3 ms per code is the scale factor of the TIME_ACT register for + * ODR = 6400 Hz. It is 6.6 ms per code for ODR = 3200 Hz and below. + */ + if (st->odr == ADXL372_ODR_6400HZ) + scale_factor = 3300; + else + scale_factor = 6600; + + reg_val = DIV_ROUND_CLOSEST(act_time_ms * 1000, scale_factor); + + /* TIME_ACT register is 8 bits wide */ + if (reg_val > 0xFF) + reg_val = 0xFF; + + ret = regmap_write(st->regmap, ADXL372_TIME_ACT, reg_val); + if (ret < 0) + return ret; + + st->act_time_ms = act_time_ms; + + return ret; +} + +static int adxl372_set_inactivity_time_ms(struct adxl372_state *st, + unsigned int inact_time_ms) +{ + unsigned int reg_val_h, reg_val_l, res, scale_factor; + int ret; + + /* + * 13 ms per code is the scale factor of the TIME_INACT register for + * ODR = 6400 Hz. It is 26 ms per code for ODR = 3200 Hz and below. + */ + if (st->odr == ADXL372_ODR_6400HZ) + scale_factor = 13; + else + scale_factor = 26; + + res = DIV_ROUND_CLOSEST(inact_time_ms, scale_factor); + reg_val_h = (res >> 8) & 0xFF; + reg_val_l = res & 0xFF; + + ret = regmap_write(st->regmap, ADXL372_TIME_INACT_H, reg_val_h); + if (ret < 0) + return ret; + + ret = regmap_write(st->regmap, ADXL372_TIME_INACT_L, reg_val_l); + if (ret < 0) + return ret; + + st->inact_time_ms = inact_time_ms; + + return ret; +} + +static int adxl372_setup(struct adxl372_state *st) +{ + unsigned int regval; + int ret; + + ret = regmap_read(st->regmap, ADXL372_DEVID, ®val); + if (ret < 0) + return ret; + + if (regval != ADXL372_DEVID_VAL) { + dev_err(&st->spi->dev, "Invalid chip id %x\n", regval); + return -ENODEV; + } + + ret = adxl372_set_op_mode(st, ADXL372_STANDBY); + if (ret < 0) + return ret; + + /* Set threshold for activity detection to 1g */ + ret = adxl372_set_activity_threshold(st, ADXL372_ACTIVITY, + true, true, 1000); + if (ret < 0) + return ret; + + /* Set threshold for inactivity detection to 100mg */ + ret = adxl372_set_activity_threshold(st, ADXL372_INACTIVITY, + true, true, 100); + if (ret < 0) + return ret; + + /* Set activity processing in Looped mode */ + ret = adxl372_set_act_proc_mode(st, ADXL372_LOOPED); + if (ret < 0) + return ret; + + ret = adxl372_set_odr(st, ADXL372_ODR_6400HZ); + if (ret < 0) + return ret; + + ret = adxl372_set_bandwidth(st, ADXL372_BW_3200HZ); + if (ret < 0) + return ret; + + /* Set activity timer to 1ms */ + ret = adxl372_set_activity_time_ms(st, 1); + if (ret < 0) + return ret; + + /* Set inactivity timer to 10s */ + ret = adxl372_set_inactivity_time_ms(st, 10000); + if (ret < 0) + return ret; + + /* Set the mode of operation to full bandwidth measurement mode */ + return adxl372_set_op_mode(st, ADXL372_FULL_BW_MEASUREMENT); +} + +static int adxl372_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct adxl372_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + else + return regmap_write(st->regmap, reg, writeval); +} + +static int adxl372_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct adxl372_state *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = adxl372_read_axis(st, chan->address); + if (ret < 0) + return ret; + + *val = sign_extend32(ret >> chan->scan_type.shift, + chan->scan_type.realbits - 1); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = ADXL372_USCALE; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static const struct iio_info adxl372_info = { + .read_raw = adxl372_read_raw, + .debugfs_reg_access = &adxl372_reg_access, +}; + +static const struct regmap_config adxl372_spi_regmap_config = { + .reg_bits = 7, + .pad_bits = 1, + .val_bits = 8, + .read_flag_mask = BIT(0), +}; + +static int adxl372_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct adxl372_state *st; + struct regmap *regmap; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + + regmap = devm_regmap_init_spi(spi, &adxl372_spi_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + st->regmap = regmap; + + indio_dev->channels = adxl372_channels; + indio_dev->num_channels = ARRAY_SIZE(adxl372_channels); + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &adxl372_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adxl372_setup(st); + if (ret < 0) { + dev_err(&st->spi->dev, "ADXL372 setup failed\n"); + return ret; + } + + return devm_iio_device_register(&st->spi->dev, indio_dev); +} + +static const struct spi_device_id adxl372_id[] = { + { "adxl372", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, adxl372_id); + +static struct spi_driver adxl372_driver = { + .driver = { + .name = KBUILD_MODNAME, + }, + .probe = adxl372_probe, + .id_table = adxl372_id, +}; + +module_spi_driver(adxl372_driver); + +MODULE_AUTHOR("Stefan Popa "); +MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer driver"); +MODULE_LICENSE("GPL"); From ad6e1fb0312c5a4b4fc70e17b73ca1cdfc68c429 Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Fri, 10 Aug 2018 11:46:19 +0300 Subject: [PATCH 27/68] dt-bindings: iio: accel: Add docs for ADXL372 Add the device tree binding documentation for the ADXL372 3-axis digital accelerometer. Signed-off-by: Stefan Popa Reviewed-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/accel/adxl372.txt | 22 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/accel/adxl372.txt diff --git a/Documentation/devicetree/bindings/iio/accel/adxl372.txt b/Documentation/devicetree/bindings/iio/accel/adxl372.txt new file mode 100644 index 000000000000..9409984719e9 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/accel/adxl372.txt @@ -0,0 +1,22 @@ +Analog Devices ADXL372 3-Axis, +/-(200g) Digital Accelerometer + +http://www.analog.com/media/en/technical-documentation/data-sheets/adxl372.pdf + +Required properties: + - compatible : should be "adi,adxl372" + - reg: SPI chip select number for the device + - spi-max-frequency: Max SPI frequency to use + +Optional properties: + - interrupts: interrupt mapping for IRQ as documented in + Documentation/devicetree/bindings/interrupt-controller/interrupts.txt + +Example: + + accelerometer@0 { + compatible = "adi,adxl372"; + reg = <0>; + spi-max-frequency = <1000000>; + interrupt-parent = <&gpio>; + interrupts = <25 IRQ_TYPE_EDGE_FALLING>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index fe23f8b66712..2605b733f603 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -548,6 +548,7 @@ M: Stefan Popa W: http://ez.analog.com/community/linux-device-drivers S: Supported F: drivers/iio/accel/adxl372.c +F: Documentation/devicetree/bindings/iio/accel/adxl372.txt AF9013 MEDIA DRIVER M: Antti Palosaari From f4f55ce38e5f823a13662b12189627a5b9a34688 Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Fri, 10 Aug 2018 11:46:21 +0300 Subject: [PATCH 28/68] iio:adxl372: Add FIFO and interrupts support This patch adds support for the adxl372 FIFO. In order to accomplish this, triggered buffers were used. The number of FIFO samples which trigger the watermark interrupt can be configured by using the buffer watermark. The FIFO format is determined by configuring the scan elements for each axis. The FIFO data is pushed to the IIO device's buffer. Signed-off-by: Stefan Popa Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl372.c | 357 +++++++++++++++++++++++++++++++++++- 1 file changed, 356 insertions(+), 1 deletion(-) diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index d6e926de955e..d2fdc7581fe0 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -6,12 +6,19 @@ */ #include +#include +#include #include #include #include #include #include +#include +#include +#include +#include +#include /* ADXL372 registers definition */ #define ADXL372_DEVID 0x00 @@ -123,6 +130,9 @@ #define ADXL372_INT1_MAP_LOW_MSK BIT(7) #define ADXL372_INT1_MAP_LOW_MODE(x) (((x) & 0x1) << 7) +/* The ADXL372 includes a deep, 512 sample FIFO buffer */ +#define ADXL372_FIFO_SIZE 512 + /* * At +/- 200g with 12-bit resolution, scale is computed as: * (200 + 200) * 9.81 / (2^12 - 1) = 0.958241 @@ -170,6 +180,43 @@ static const unsigned int adxl372_th_reg_high_addr[3] = { [ADXL372_INACTIVITY] = ADXL372_X_THRESH_INACT_H, }; +enum adxl372_fifo_format { + ADXL372_XYZ_FIFO, + ADXL372_X_FIFO, + ADXL372_Y_FIFO, + ADXL372_XY_FIFO, + ADXL372_Z_FIFO, + ADXL372_XZ_FIFO, + ADXL372_YZ_FIFO, + ADXL372_XYZ_PEAK_FIFO, +}; + +enum adxl372_fifo_mode { + ADXL372_FIFO_BYPASSED, + ADXL372_FIFO_STREAMED, + ADXL372_FIFO_TRIGGERED, + ADXL372_FIFO_OLD_SAVED +}; + +static const int adxl372_samp_freq_tbl[5] = { + 400, 800, 1600, 3200, 6400, +}; + +struct adxl372_axis_lookup { + unsigned int bits; + enum adxl372_fifo_format fifo_format; +}; + +static const struct adxl372_axis_lookup adxl372_axis_lookup_table[] = { + { BIT(0), ADXL372_X_FIFO }, + { BIT(1), ADXL372_Y_FIFO }, + { BIT(2), ADXL372_Z_FIFO }, + { BIT(0) | BIT(1), ADXL372_XY_FIFO }, + { BIT(0) | BIT(2), ADXL372_XZ_FIFO }, + { BIT(1) | BIT(2), ADXL372_YZ_FIFO }, + { BIT(0) | BIT(1) | BIT(2), ADXL372_XYZ_FIFO }, +}; + #define ADXL372_ACCEL_CHANNEL(index, reg, axis) { \ .type = IIO_ACCEL, \ .address = reg, \ @@ -177,6 +224,13 @@ static const unsigned int adxl372_th_reg_high_addr[3] = { .channel2 = IIO_MOD_##axis, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 4, \ + }, \ } static const struct iio_chan_spec adxl372_channels[] = { @@ -188,12 +242,29 @@ static const struct iio_chan_spec adxl372_channels[] = { struct adxl372_state { struct spi_device *spi; struct regmap *regmap; + struct iio_trigger *dready_trig; + enum adxl372_fifo_mode fifo_mode; + enum adxl372_fifo_format fifo_format; enum adxl372_op_mode op_mode; enum adxl372_act_proc_mode act_proc_mode; enum adxl372_odr odr; enum adxl372_bandwidth bw; u32 act_time_ms; u32 inact_time_ms; + u8 fifo_set_size; + u8 int1_bitmask; + u8 int2_bitmask; + u16 watermark; + __be16 fifo_buf[ADXL372_FIFO_SIZE]; +}; + +static const unsigned long adxl372_channel_masks[] = { + BIT(0), BIT(1), BIT(2), + BIT(0) | BIT(1), + BIT(0) | BIT(2), + BIT(1) | BIT(2), + BIT(0) | BIT(1) | BIT(2), + 0 }; static int adxl372_read_axis(struct adxl372_state *st, u8 addr) @@ -359,6 +430,112 @@ static int adxl372_set_inactivity_time_ms(struct adxl372_state *st, return ret; } +static int adxl372_set_interrupts(struct adxl372_state *st, + unsigned char int1_bitmask, + unsigned char int2_bitmask) +{ + int ret; + + ret = regmap_write(st->regmap, ADXL372_INT1_MAP, int1_bitmask); + if (ret < 0) + return ret; + + return regmap_write(st->regmap, ADXL372_INT2_MAP, int2_bitmask); +} + +static int adxl372_configure_fifo(struct adxl372_state *st) +{ + unsigned int fifo_samples, fifo_ctl; + int ret; + + /* FIFO must be configured while in standby mode */ + ret = adxl372_set_op_mode(st, ADXL372_STANDBY); + if (ret < 0) + return ret; + + fifo_samples = st->watermark & 0xFF; + fifo_ctl = ADXL372_FIFO_CTL_FORMAT_MODE(st->fifo_format) | + ADXL372_FIFO_CTL_MODE_MODE(st->fifo_mode) | + ADXL372_FIFO_CTL_SAMPLES_MODE(st->watermark); + + ret = regmap_write(st->regmap, ADXL372_FIFO_SAMPLES, fifo_samples); + if (ret < 0) + return ret; + + ret = regmap_write(st->regmap, ADXL372_FIFO_CTL, fifo_ctl); + if (ret < 0) + return ret; + + return adxl372_set_op_mode(st, ADXL372_FULL_BW_MEASUREMENT); +} + +static int adxl372_get_status(struct adxl372_state *st, + u8 *status1, u8 *status2, + u16 *fifo_entries) +{ + __be32 buf; + u32 val; + int ret; + + /* STATUS1, STATUS2, FIFO_ENTRIES2 and FIFO_ENTRIES are adjacent regs */ + ret = regmap_bulk_read(st->regmap, ADXL372_STATUS_1, + &buf, sizeof(buf)); + if (ret < 0) + return ret; + + val = be32_to_cpu(buf); + + *status1 = (val >> 24) & 0x0F; + *status2 = (val >> 16) & 0x0F; + /* + * FIFO_ENTRIES contains the least significant byte, and FIFO_ENTRIES2 + * contains the two most significant bits + */ + *fifo_entries = val & 0x3FF; + + return ret; +} + +static irqreturn_t adxl372_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct adxl372_state *st = iio_priv(indio_dev); + u8 status1, status2; + u16 fifo_entries; + int i, ret; + + ret = adxl372_get_status(st, &status1, &status2, &fifo_entries); + if (ret < 0) + goto err; + + if (st->fifo_mode != ADXL372_FIFO_BYPASSED && + ADXL372_STATUS_1_FIFO_FULL(status1)) { + /* + * When reading data from multiple axes from the FIFO, + * to ensure that data is not overwritten and stored out + * of order at least one sample set must be left in the + * FIFO after every read. + */ + fifo_entries -= st->fifo_set_size; + + /* Read data from the FIFO */ + ret = regmap_noinc_read(st->regmap, ADXL372_FIFO_DATA, + st->fifo_buf, + fifo_entries * sizeof(u16)); + if (ret < 0) + goto err; + + /* Each sample is 2 bytes */ + for (i = 0; i < fifo_entries * sizeof(u16); + i += st->fifo_set_size * sizeof(u16)) + iio_push_to_buffers(indio_dev, &st->fifo_buf[i]); + } +err: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + static int adxl372_setup(struct adxl372_state *st) { unsigned int regval; @@ -438,7 +615,12 @@ static int adxl372_read_raw(struct iio_dev *indio_dev, switch (info) { case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + ret = adxl372_read_axis(st, chan->address); + iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; @@ -454,16 +636,153 @@ static int adxl372_read_raw(struct iio_dev *indio_dev, } } +static ssize_t adxl372_get_fifo_enabled(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct adxl372_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->fifo_mode); +} + +static ssize_t adxl372_get_fifo_watermark(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct adxl372_state *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->watermark); +} + +static IIO_CONST_ATTR(hwfifo_watermark_min, "1"); +static IIO_CONST_ATTR(hwfifo_watermark_max, + __stringify(ADXL372_FIFO_SIZE)); +static IIO_DEVICE_ATTR(hwfifo_watermark, 0444, + adxl372_get_fifo_watermark, NULL, 0); +static IIO_DEVICE_ATTR(hwfifo_enabled, 0444, + adxl372_get_fifo_enabled, NULL, 0); + +static const struct attribute *adxl372_fifo_attributes[] = { + &iio_const_attr_hwfifo_watermark_min.dev_attr.attr, + &iio_const_attr_hwfifo_watermark_max.dev_attr.attr, + &iio_dev_attr_hwfifo_watermark.dev_attr.attr, + &iio_dev_attr_hwfifo_enabled.dev_attr.attr, + NULL, +}; + +static int adxl372_set_watermark(struct iio_dev *indio_dev, unsigned int val) +{ + struct adxl372_state *st = iio_priv(indio_dev); + + if (val > ADXL372_FIFO_SIZE) + val = ADXL372_FIFO_SIZE; + + st->watermark = val; + + return 0; +} + +static int adxl372_buffer_postenable(struct iio_dev *indio_dev) +{ + struct adxl372_state *st = iio_priv(indio_dev); + unsigned int mask; + int i, ret; + + ret = adxl372_set_interrupts(st, ADXL372_INT1_MAP_FIFO_FULL_MSK, 0); + if (ret < 0) + return ret; + + mask = *indio_dev->active_scan_mask; + + for (i = 0; i < ARRAY_SIZE(adxl372_axis_lookup_table); i++) { + if (mask == adxl372_axis_lookup_table[i].bits) + break; + } + + if (i == ARRAY_SIZE(adxl372_axis_lookup_table)) + return -EINVAL; + + st->fifo_format = adxl372_axis_lookup_table[i].fifo_format; + st->fifo_set_size = bitmap_weight(indio_dev->active_scan_mask, + indio_dev->masklength); + /* + * The 512 FIFO samples can be allotted in several ways, such as: + * 170 sample sets of concurrent 3-axis data + * 256 sample sets of concurrent 2-axis data (user selectable) + * 512 sample sets of single-axis data + */ + if ((st->watermark * st->fifo_set_size) > ADXL372_FIFO_SIZE) + st->watermark = (ADXL372_FIFO_SIZE / st->fifo_set_size); + + st->fifo_mode = ADXL372_FIFO_STREAMED; + + ret = adxl372_configure_fifo(st); + if (ret < 0) { + st->fifo_mode = ADXL372_FIFO_BYPASSED; + adxl372_set_interrupts(st, 0, 0); + return ret; + } + + return iio_triggered_buffer_postenable(indio_dev); +} + +static int adxl372_buffer_predisable(struct iio_dev *indio_dev) +{ + struct adxl372_state *st = iio_priv(indio_dev); + int ret; + + ret = iio_triggered_buffer_predisable(indio_dev); + if (ret < 0) + return ret; + + adxl372_set_interrupts(st, 0, 0); + st->fifo_mode = ADXL372_FIFO_BYPASSED; + adxl372_configure_fifo(st); + + return 0; +} + +static const struct iio_buffer_setup_ops adxl372_buffer_ops = { + .postenable = adxl372_buffer_postenable, + .predisable = adxl372_buffer_predisable, +}; + +static int adxl372_dready_trig_set_state(struct iio_trigger *trig, + bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct adxl372_state *st = iio_priv(indio_dev); + unsigned long int mask = 0; + + if (state) + mask = ADXL372_INT1_MAP_FIFO_FULL_MSK; + + return adxl372_set_interrupts(st, mask, 0); +} + +static const struct iio_trigger_ops adxl372_trigger_ops = { + .set_trigger_state = adxl372_dready_trig_set_state, +}; + static const struct iio_info adxl372_info = { .read_raw = adxl372_read_raw, .debugfs_reg_access = &adxl372_reg_access, + .hwfifo_set_watermark = adxl372_set_watermark, }; +static bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg) +{ + return (reg == ADXL372_FIFO_DATA); +} + static const struct regmap_config adxl372_spi_regmap_config = { .reg_bits = 7, .pad_bits = 1, .val_bits = 8, .read_flag_mask = BIT(0), + .readable_noinc_reg = adxl372_readable_noinc_reg, }; static int adxl372_probe(struct spi_device *spi) @@ -490,10 +809,11 @@ static int adxl372_probe(struct spi_device *spi) indio_dev->channels = adxl372_channels; indio_dev->num_channels = ARRAY_SIZE(adxl372_channels); + indio_dev->available_scan_masks = adxl372_channel_masks; indio_dev->dev.parent = &spi->dev; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->info = &adxl372_info; - indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; ret = adxl372_setup(st); if (ret < 0) { @@ -501,6 +821,41 @@ static int adxl372_probe(struct spi_device *spi) return ret; } + ret = devm_iio_triggered_buffer_setup(&st->spi->dev, + indio_dev, NULL, + adxl372_trigger_handler, + &adxl372_buffer_ops); + if (ret < 0) + return ret; + + iio_buffer_set_attrs(indio_dev->buffer, adxl372_fifo_attributes); + + if (st->spi->irq) { + st->dready_trig = devm_iio_trigger_alloc(&st->spi->dev, + "%s-dev%d", + indio_dev->name, + indio_dev->id); + if (st->dready_trig == NULL) + return -ENOMEM; + + st->dready_trig->ops = &adxl372_trigger_ops; + st->dready_trig->dev.parent = &st->spi->dev; + iio_trigger_set_drvdata(st->dready_trig, indio_dev); + ret = devm_iio_trigger_register(&st->spi->dev, st->dready_trig); + if (ret < 0) + return ret; + + indio_dev->trig = iio_trigger_get(st->dready_trig); + + ret = devm_request_threaded_irq(&st->spi->dev, st->spi->irq, + iio_trigger_generic_data_rdy_poll, + NULL, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + indio_dev->name, st->dready_trig); + if (ret < 0) + return ret; + } + return devm_iio_device_register(&st->spi->dev, indio_dev); } From eb2e188817b13d2f13b61ea6b96b7753ccff48dd Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 9 Aug 2018 14:53:38 +0100 Subject: [PATCH 29/68] iio: max44000: remove unused variable max44000_alstim_shift Variable max44000_alstim_shift is defined but is never used hence it is redundant and can be removed. This variable has been like this since the driver was added back in 2016. Cleans up clang warning: warning: 'max44000_alstim_shift' defined but not used [-Wunused-const-variable=] Signed-off-by: Colin Ian King Signed-off-by: Jonathan Cameron --- drivers/iio/light/max44000.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c index bcdb0eb9e537..b9b0bd341be8 100644 --- a/drivers/iio/light/max44000.c +++ b/drivers/iio/light/max44000.c @@ -99,7 +99,6 @@ static const int max44000_alspga_shift[] = {0, 2, 4, 7}; * Handling this internally is also required for buffer support because the * channel's scan_type can't be modified dynamically. */ -static const int max44000_alstim_shift[] = {0, 2, 4, 6}; #define MAX44000_ALSTIM_SHIFT(alstim) (2 * (alstim)) /* Available integration times with pretty manual alignment: */ From 6b8471e4601db48ef76144fac6e706256c2aae2d Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Tue, 7 Aug 2018 17:06:05 +0300 Subject: [PATCH 30/68] iio: adxl345: move null check for i2c id at start of probe Fixes ef89f4b96a2 ("iio: adxl345: Add support for the ADXL375"). This was found via static checker. After looking into the code a bit, it's unlikely that there will be a NULL dereference if the `id` object in that specific code path. However, it's safe to add a NULL (paranoid) check just to make sure and remove any uncertainties. Reported-by: Dan Carpenter Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl345_i2c.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/iio/accel/adxl345_i2c.c b/drivers/iio/accel/adxl345_i2c.c index 785c89de91e7..f22f71315a0c 100644 --- a/drivers/iio/accel/adxl345_i2c.c +++ b/drivers/iio/accel/adxl345_i2c.c @@ -27,6 +27,9 @@ static int adxl345_i2c_probe(struct i2c_client *client, { struct regmap *regmap; + if (!id) + return -ENODEV; + regmap = devm_regmap_init_i2c(client, &adxl345_i2c_regmap_config); if (IS_ERR(regmap)) { dev_err(&client->dev, "Error initializing i2c regmap: %ld\n", @@ -35,7 +38,7 @@ static int adxl345_i2c_probe(struct i2c_client *client, } return adxl345_core_probe(&client->dev, regmap, id->driver_data, - id ? id->name : NULL); + id->name); } static int adxl345_i2c_remove(struct i2c_client *client) From 3a89b289df5df45e2343c2a8e837ad445f9776f6 Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Wed, 8 Aug 2018 10:09:15 +0200 Subject: [PATCH 31/68] iio: adc: add support for mcp3911 MCP3911 is a dual channel Analog Front End (AFE) containing two synchronous sampling delta-sigma Analog-to-Digital Converters (ADC). Co-developed-by: Kent Gustavsson Signed-off-by: Kent Gustavsson Signed-off-by: Marcus Folkesson Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 10 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/mcp3911.c | 363 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 374 insertions(+) create mode 100644 drivers/iio/adc/mcp3911.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 690fdb249565..a52fea8749a9 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -501,6 +501,16 @@ config MCP3422 This driver can also be built as a module. If so, the module will be called mcp3422. +config MCP3911 + tristate "Microchip Technology MCP3911 driver" + depends on SPI + help + Say yes here to build support for Microchip Technology's MCP3911 + analog to digital converter. + + This driver can also be built as a module. If so, the module will be + called mcp3911. + config MEDIATEK_MT6577_AUXADC tristate "MediaTek AUXADC driver" depends on ARCH_MEDIATEK || COMPILE_TEST diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index e478b3065ecd..a6e6a0b659e2 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_MAX1363) += max1363.o obj-$(CONFIG_MAX9611) += max9611.o obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o +obj-$(CONFIG_MCP3911) += mcp3911.o obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o obj-$(CONFIG_MESON_SARADC) += meson_saradc.o diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c new file mode 100644 index 000000000000..dd52f08ec82e --- /dev/null +++ b/drivers/iio/adc/mcp3911.c @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Microchip MCP3911, Two-channel Analog Front End + * + * Copyright (C) 2018 Marcus Folkesson + * Copyright (C) 2018 Kent Gustavsson + */ +#include +#include +#include +#include +#include +#include +#include + +#define MCP3911_REG_CHANNEL0 0x00 +#define MCP3911_REG_CHANNEL1 0x03 +#define MCP3911_REG_MOD 0x06 +#define MCP3911_REG_PHASE 0x07 +#define MCP3911_REG_GAIN 0x09 + +#define MCP3911_REG_STATUSCOM 0x0a +#define MCP3911_STATUSCOM_CH1_24WIDTH BIT(4) +#define MCP3911_STATUSCOM_CH0_24WIDTH BIT(3) +#define MCP3911_STATUSCOM_EN_OFFCAL BIT(2) +#define MCP3911_STATUSCOM_EN_GAINCAL BIT(1) + +#define MCP3911_REG_CONFIG 0x0c +#define MCP3911_CONFIG_CLKEXT BIT(1) +#define MCP3911_CONFIG_VREFEXT BIT(2) + +#define MCP3911_REG_OFFCAL_CH0 0x0e +#define MCP3911_REG_GAINCAL_CH0 0x11 +#define MCP3911_REG_OFFCAL_CH1 0x14 +#define MCP3911_REG_GAINCAL_CH1 0x17 +#define MCP3911_REG_VREFCAL 0x1a + +#define MCP3911_CHANNEL(x) (MCP3911_REG_CHANNEL0 + x * 3) +#define MCP3911_OFFCAL(x) (MCP3911_REG_OFFCAL_CH0 + x * 6) + +/* Internal voltage reference in uV */ +#define MCP3911_INT_VREF_UV 1200000 + +#define MCP3911_REG_READ(reg, id) ((((reg) << 1) | ((id) << 5) | (1 << 0)) & 0xff) +#define MCP3911_REG_WRITE(reg, id) ((((reg) << 1) | ((id) << 5) | (0 << 0)) & 0xff) + +#define MCP3911_NUM_CHANNELS 2 + +struct mcp3911 { + struct spi_device *spi; + struct mutex lock; + struct regulator *vref; + struct clk *clki; + u32 dev_addr; +}; + +static int mcp3911_read(struct mcp3911 *adc, u8 reg, u32 *val, u8 len) +{ + int ret; + + reg = MCP3911_REG_READ(reg, adc->dev_addr); + ret = spi_write_then_read(adc->spi, ®, 1, val, len); + if (ret < 0) + return ret; + + be32_to_cpus(val); + *val >>= ((4 - len) * 8); + dev_dbg(&adc->spi->dev, "reading 0x%x from register 0x%x\n", *val, + reg >> 1); + return ret; +} + +static int mcp3911_write(struct mcp3911 *adc, u8 reg, u32 val, u8 len) +{ + dev_dbg(&adc->spi->dev, "writing 0x%x to register 0x%x\n", val, reg); + + val <<= (3 - len) * 8; + cpu_to_be32s(&val); + val |= MCP3911_REG_WRITE(reg, adc->dev_addr); + + return spi_write(adc->spi, &val, len + 1); +} + +static int mcp3911_update(struct mcp3911 *adc, u8 reg, u32 mask, + u32 val, u8 len) +{ + u32 tmp; + int ret; + + ret = mcp3911_read(adc, reg, &tmp, len); + if (ret) + return ret; + + val &= mask; + val |= tmp & ~mask; + return mcp3911_write(adc, reg, val, len); +} + +static int mcp3911_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct mcp3911 *adc = iio_priv(indio_dev); + int ret = -EINVAL; + + mutex_lock(&adc->lock); + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = mcp3911_read(adc, + MCP3911_CHANNEL(channel->channel), val, 3); + if (ret) + goto out; + + ret = IIO_VAL_INT; + break; + + case IIO_CHAN_INFO_OFFSET: + ret = mcp3911_read(adc, + MCP3911_OFFCAL(channel->channel), val, 3); + if (ret) + goto out; + + ret = IIO_VAL_INT; + break; + + case IIO_CHAN_INFO_SCALE: + if (adc->vref) { + ret = regulator_get_voltage(adc->vref); + if (ret < 0) { + dev_err(indio_dev->dev.parent, + "failed to get vref voltage: %d\n", + ret); + goto out; + } + + *val = ret / 1000; + } else { + *val = MCP3911_INT_VREF_UV; + } + + *val2 = 24; + ret = IIO_VAL_FRACTIONAL_LOG2; + break; + } + +out: + mutex_unlock(&adc->lock); + return ret; +} + +static int mcp3911_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int val, + int val2, long mask) +{ + struct mcp3911 *adc = iio_priv(indio_dev); + int ret = -EINVAL; + + mutex_lock(&adc->lock); + switch (mask) { + case IIO_CHAN_INFO_OFFSET: + if (val2 != 0) { + ret = -EINVAL; + goto out; + } + + /* Write offset */ + ret = mcp3911_write(adc, MCP3911_OFFCAL(channel->channel), val, + 3); + if (ret) + goto out; + + /* Enable offset*/ + ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM, + MCP3911_STATUSCOM_EN_OFFCAL, + MCP3911_STATUSCOM_EN_OFFCAL, 2); + break; + } + +out: + mutex_unlock(&adc->lock); + return ret; +} + +#define MCP3911_CHAN(idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = idx, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET) | \ + BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec mcp3911_channels[] = { + MCP3911_CHAN(0), + MCP3911_CHAN(1), +}; + +static const struct iio_info mcp3911_info = { + .read_raw = mcp3911_read_raw, + .write_raw = mcp3911_write_raw, +}; + +static int mcp3911_config(struct mcp3911 *adc, struct device_node *of_node) +{ + u32 configreg; + int ret; + + of_property_read_u32(of_node, "device-addr", &adc->dev_addr); + if (adc->dev_addr > 3) { + dev_err(&adc->spi->dev, + "invalid device address (%i). Must be in range 0-3.\n", + adc->dev_addr); + return -EINVAL; + } + dev_dbg(&adc->spi->dev, "use device address %i\n", adc->dev_addr); + + ret = mcp3911_read(adc, MCP3911_REG_CONFIG, &configreg, 2); + if (ret) + return ret; + + if (adc->vref) { + dev_dbg(&adc->spi->dev, "use external voltage reference\n"); + configreg |= MCP3911_CONFIG_VREFEXT; + } else { + dev_dbg(&adc->spi->dev, + "use internal voltage reference (1.2V)\n"); + configreg &= ~MCP3911_CONFIG_VREFEXT; + } + + if (adc->clki) { + dev_dbg(&adc->spi->dev, "use external clock as clocksource\n"); + configreg |= MCP3911_CONFIG_CLKEXT; + } else { + dev_dbg(&adc->spi->dev, + "use crystal oscillator as clocksource\n"); + configreg &= ~MCP3911_CONFIG_CLKEXT; + } + + return mcp3911_write(adc, MCP3911_REG_CONFIG, configreg, 2); +} + +static int mcp3911_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct mcp3911 *adc; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + + adc->vref = devm_regulator_get_optional(&adc->spi->dev, "vref"); + if (IS_ERR(adc->vref)) { + if (PTR_ERR(adc->vref) == -ENODEV) { + adc->vref = NULL; + } else { + dev_err(&adc->spi->dev, + "failed to get regulator (%ld)\n", + PTR_ERR(adc->vref)); + return PTR_ERR(adc->vref); + } + + } else { + ret = regulator_enable(adc->vref); + if (ret) + return ret; + } + + adc->clki = devm_clk_get(&adc->spi->dev, NULL); + if (IS_ERR(adc->clki)) { + if (PTR_ERR(adc->clki) == -ENOENT) { + adc->clki = NULL; + } else { + dev_err(&adc->spi->dev, + "failed to get adc clk (%ld)\n", + PTR_ERR(adc->clki)); + ret = PTR_ERR(adc->clki); + goto reg_disable; + } + } else { + ret = clk_prepare_enable(adc->clki); + if (ret < 0) { + dev_err(&adc->spi->dev, + "Failed to enable clki: %d\n", ret); + goto reg_disable; + } + } + + ret = mcp3911_config(adc, spi->dev.of_node); + if (ret) + goto clk_disable; + + indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &mcp3911_info; + spi_set_drvdata(spi, indio_dev); + + indio_dev->channels = mcp3911_channels; + indio_dev->num_channels = ARRAY_SIZE(mcp3911_channels); + + mutex_init(&adc->lock); + + ret = iio_device_register(indio_dev); + if (ret) + goto clk_disable; + + return ret; + +clk_disable: + clk_disable_unprepare(adc->clki); +reg_disable: + if (adc->vref) + regulator_disable(adc->vref); + + return ret; +} + +static int mcp3911_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct mcp3911 *adc = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + clk_disable_unprepare(adc->clki); + if (adc->vref) + regulator_disable(adc->vref); + + return 0; +} + +static const struct of_device_id mcp3911_dt_ids[] = { + { .compatible = "microchip,mcp3911" }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp3911_dt_ids); + +static const struct spi_device_id mcp3911_id[] = { + { "mcp3911", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, mcp3911_id); + +static struct spi_driver mcp3911_driver = { + .driver = { + .name = "mcp3911", + .of_match_table = mcp3911_dt_ids, + }, + .probe = mcp3911_probe, + .remove = mcp3911_remove, + .id_table = mcp3911_id, +}; +module_spi_driver(mcp3911_driver); + +MODULE_AUTHOR("Marcus Folkesson "); +MODULE_AUTHOR("Kent Gustavsson "); +MODULE_DESCRIPTION("Microchip Technology MCP3911"); +MODULE_LICENSE("GPL v2"); From 1c4fbbea34087007b871d444bcbe7c24fad32bd3 Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Wed, 8 Aug 2018 10:09:16 +0200 Subject: [PATCH 32/68] dt-bindings: iio: adc: add bindings for mcp3911 MCP3911 is a dual channel Analog Front End (AFE) containing two synchronous sampling delta-sigma Analog-to-Digital Converters (ADC). Co-Developed-by: Kent Gustavsson Signed-off-by: Kent Gustavsson Signed-off-by: Marcus Folkesson Reviewed-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/mcp3911.txt | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/mcp3911.txt diff --git a/Documentation/devicetree/bindings/iio/adc/mcp3911.txt b/Documentation/devicetree/bindings/iio/adc/mcp3911.txt new file mode 100644 index 000000000000..3071f48fb30b --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/mcp3911.txt @@ -0,0 +1,30 @@ +* Microchip MCP3911 Dual channel analog front end (ADC) + +Required properties: + - compatible: Should be "microchip,mcp3911" + - reg: SPI chip select number for the device + +Recommended properties: + - spi-max-frequency: Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt. + Max frequency for this chip is 20MHz. + +Optional properties: + - clocks: Phandle and clock identifier for sampling clock + - interrupt-parent: Phandle to the parent interrupt controller + - interrupts: IRQ line for the ADC + - microchip,device-addr: Device address when multiple MCP3911 chips are present on the + same SPI bus. Valid values are 0-3. Defaults to 0. + - vref-supply: Phandle to the external reference voltage supply. + +Example: +adc@0 { + compatible = "microchip,mcp3911"; + reg = <0>; + interrupt-parent = <&gpio5>; + interrupts = <15 IRQ_TYPE_EDGE_RISING>; + spi-max-frequency = <20000000>; + microchip,device-addr = <0>; + vref-supply = <&vref_reg>; + clocks = <&xtal>; +}; From 010de20412fc40dabfbab926358ecd9c4486dd0d Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Wed, 8 Aug 2018 10:09:17 +0200 Subject: [PATCH 33/68] MAINTAINERS: Add entry for mcp3911 ADC driver Add an entry for mcp3911 ADC driver and add myself and Kent Gustavsson as maintainers of this driver. Co-Developed-by: Kent Gustavsson Signed-off-by: Kent Gustavsson Signed-off-by: Marcus Folkesson Signed-off-by: Jonathan Cameron --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 2605b733f603..2b7b24b145f0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9404,6 +9404,14 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/microchip/lan743x_* +MICROCHIP / ATMEL MCP3911 ADC DRIVER +M: Marcus Folkesson +M: Kent Gustavsson +L: linux-iio@vger.kernel.org +S: Supported +F: drivers/iio/adc/mcp3911.c +F: Documentation/devicetree/bindings/iio/adc/mcp3911.txt + MICROCHIP USB251XB DRIVER M: Richard Leitner L: linux-usb@vger.kernel.org From 892e62fab4983656d3ca5df4e83fdf2b496c06e4 Mon Sep 17 00:00:00 2001 From: Himanshu Jha Date: Sat, 11 Aug 2018 15:56:36 +0530 Subject: [PATCH 34/68] iio: chemical: bme680: Add check for val2 in the write_raw function val2 is responsible for the floating part of the number to be written to the device. We don't need the floating part while writing the oversampling ratio for BME680 since the available oversampling ratios are pure natural numbers. So, add a sanity check to make sure val2 is 0. Signed-off-by: Himanshu Jha Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/bme680_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c index 5ddeffb29fb0..87666ce8915f 100644 --- a/drivers/iio/chemical/bme680_core.c +++ b/drivers/iio/chemical/bme680_core.c @@ -800,6 +800,9 @@ static int bme680_write_raw(struct iio_dev *indio_dev, { struct bme680_data *data = iio_priv(indio_dev); + if (val2 != 0) + return -EINVAL; + switch (mask) { case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { From ce7c637a8368d0293649a4acc0b89930295cb62a Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 21 May 2018 11:49:10 +0200 Subject: [PATCH 35/68] iio: potentiometer: merge calls to of_match_device and of_device_get_match_data Drop call to of_match_device, which is subsumed by the subsequent call to of_device_get_match_data. The code becomes simpler, and a temporary variable can be dropped. The semantic match that makes this change is as follows: (http://coccinelle.lip6.fr/) // @r@ local idexpression match; identifier i; expression x, dev, e, e1; @@ - match@i = of_match_device(x, dev); - if (match) e = of_device_get_match_data(dev); - else e = e1; + e = of_device_get_match_data(dev); + if (!e) e = e1; @@ identifier r.i; @@ - const struct of_device_id *i; ... when != i // Signed-off-by: Julia Lawall Reviewed-by: Peter Rosin Signed-off-by: Jonathan Cameron --- drivers/iio/potentiometer/max5481.c | 7 ++----- drivers/iio/potentiometer/mcp4018.c | 7 ++----- drivers/iio/potentiometer/mcp4531.c | 7 ++----- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/drivers/iio/potentiometer/max5481.c b/drivers/iio/potentiometer/max5481.c index ffe2761333a2..6d2f13fa5662 100644 --- a/drivers/iio/potentiometer/max5481.c +++ b/drivers/iio/potentiometer/max5481.c @@ -137,7 +137,6 @@ static int max5481_probe(struct spi_device *spi) struct iio_dev *indio_dev; struct max5481_data *data; const struct spi_device_id *id = spi_get_device_id(spi); - const struct of_device_id *match; int ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); @@ -149,10 +148,8 @@ static int max5481_probe(struct spi_device *spi) data->spi = spi; - match = of_match_device(of_match_ptr(max5481_match), &spi->dev); - if (match) - data->cfg = of_device_get_match_data(&spi->dev); - else + data->cfg = of_device_get_match_data(&spi->dev); + if (!data->cfg) data->cfg = &max5481_cfg[id->driver_data]; indio_dev->name = id->name; diff --git a/drivers/iio/potentiometer/mcp4018.c b/drivers/iio/potentiometer/mcp4018.c index 320a7c929777..c051ee06709f 100644 --- a/drivers/iio/potentiometer/mcp4018.c +++ b/drivers/iio/potentiometer/mcp4018.c @@ -147,7 +147,6 @@ static int mcp4018_probe(struct i2c_client *client) struct device *dev = &client->dev; struct mcp4018_data *data; struct iio_dev *indio_dev; - const struct of_device_id *match; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { @@ -162,10 +161,8 @@ static int mcp4018_probe(struct i2c_client *client) i2c_set_clientdata(client, indio_dev); data->client = client; - match = of_match_device(of_match_ptr(mcp4018_of_match), dev); - if (match) - data->cfg = of_device_get_match_data(dev); - else + data->cfg = of_device_get_match_data(dev); + if (!data->cfg) data->cfg = &mcp4018_cfg[i2c_match_id(mcp4018_id, client)->driver_data]; indio_dev->dev.parent = dev; diff --git a/drivers/iio/potentiometer/mcp4531.c b/drivers/iio/potentiometer/mcp4531.c index df894af6cccb..d87ca85645db 100644 --- a/drivers/iio/potentiometer/mcp4531.c +++ b/drivers/iio/potentiometer/mcp4531.c @@ -360,7 +360,6 @@ static int mcp4531_probe(struct i2c_client *client) struct device *dev = &client->dev; struct mcp4531_data *data; struct iio_dev *indio_dev; - const struct of_device_id *match; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) { @@ -375,10 +374,8 @@ static int mcp4531_probe(struct i2c_client *client) i2c_set_clientdata(client, indio_dev); data->client = client; - match = of_match_device(of_match_ptr(mcp4531_of_match), dev); - if (match) - data->cfg = of_device_get_match_data(dev); - else + data->cfg = of_device_get_match_data(dev); + if (!data->cfg) data->cfg = &mcp4531_cfg[i2c_match_id(mcp4531_id, client)->driver_data]; indio_dev->dev.parent = dev; From b9ea8c31e36e8e244becf5250ec28d82b8faf4b0 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Mon, 20 Aug 2018 12:01:10 +0200 Subject: [PATCH 36/68] iio: potentiometer: mcp4018: use the correct MODULE_LICENSE The file is GPL v2 only. Signed-off-by: Peter Rosin Signed-off-by: Jonathan Cameron --- drivers/iio/potentiometer/mcp4018.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/potentiometer/mcp4018.c b/drivers/iio/potentiometer/mcp4018.c index c051ee06709f..62151b2a2b12 100644 --- a/drivers/iio/potentiometer/mcp4018.c +++ b/drivers/iio/potentiometer/mcp4018.c @@ -187,4 +187,4 @@ module_i2c_driver(mcp4018_driver); MODULE_AUTHOR("Peter Rosin "); MODULE_DESCRIPTION("MCP4018 digital potentiometer"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); From 0ba56985824e2232ac2d8dfe5e793385c36527d4 Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Mon, 20 Aug 2018 12:01:11 +0200 Subject: [PATCH 37/68] iio: potentiometer: mcp4531: switch to SPDX license identifier Drop the boilerplate license text and use the correct MODULE_LICENSE. Signed-off-by: Peter Rosin Signed-off-by: Jonathan Cameron --- drivers/iio/potentiometer/mcp4531.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/iio/potentiometer/mcp4531.c b/drivers/iio/potentiometer/mcp4531.c index d87ca85645db..d71a22d71a30 100644 --- a/drivers/iio/potentiometer/mcp4531.c +++ b/drivers/iio/potentiometer/mcp4531.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Industrial I/O driver for Microchip digital potentiometers * Copyright (c) 2015 Axentia Technologies AB @@ -22,10 +23,6 @@ * mcp4652 2 257 5, 10, 50, 100 01011xx * mcp4661 2 257 5, 10, 50, 100 0101xxx * mcp4662 2 257 5, 10, 50, 100 01011xx - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. */ #include @@ -400,4 +397,4 @@ module_i2c_driver(mcp4531_driver); MODULE_AUTHOR("Peter Rosin "); MODULE_DESCRIPTION("MCP4531 digital potentiometer"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); From 10d6e7955594dcb318c8a52ed6b85ce66fcd32bd Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Mon, 20 Aug 2018 12:01:12 +0200 Subject: [PATCH 38/68] iio: envelope-detector: switch to SPDX license identifier Drop the boilerplate license text. Signed-off-by: Peter Rosin Signed-off-by: Jonathan Cameron --- drivers/iio/adc/envelope-detector.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/iio/adc/envelope-detector.c b/drivers/iio/adc/envelope-detector.c index 4ebda8ab54fe..2f2b563c1162 100644 --- a/drivers/iio/adc/envelope-detector.c +++ b/drivers/iio/adc/envelope-detector.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for an envelope detector using a DAC and a comparator * * Copyright (C) 2016 Axentia Technologies AB * * Author: Peter Rosin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ /* From 3c4b0f4ddf462bd713ce53d1b95e782dcba5298e Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Mon, 20 Aug 2018 12:01:13 +0200 Subject: [PATCH 39/68] iio: dpot-dac: switch to SPDX license identifier Drop the boilerplate license text. Signed-off-by: Peter Rosin Signed-off-by: Jonathan Cameron --- drivers/iio/dac/dpot-dac.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/iio/dac/dpot-dac.c b/drivers/iio/dac/dpot-dac.c index aaa2103d7c2b..a791d0a09d3b 100644 --- a/drivers/iio/dac/dpot-dac.c +++ b/drivers/iio/dac/dpot-dac.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * IIO DAC emulation driver using a digital potentiometer * * Copyright (C) 2016 Axentia Technologies AB * * Author: Peter Rosin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ /* From 8109b2c94f586e120cc86449777818a2e1020e7c Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Mon, 20 Aug 2018 12:01:14 +0200 Subject: [PATCH 40/68] iio: multiplexer: switch to SPDX license identifier Drop the boilerplate license text. Signed-off-by: Peter Rosin Signed-off-by: Jonathan Cameron --- drivers/iio/multiplexer/iio-mux.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/iio/multiplexer/iio-mux.c b/drivers/iio/multiplexer/iio-mux.c index e1f44cecdef4..0422ef57914c 100644 --- a/drivers/iio/multiplexer/iio-mux.c +++ b/drivers/iio/multiplexer/iio-mux.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * IIO multiplexer driver * * Copyright (C) 2017 Axentia Technologies AB * * Author: Peter Rosin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include From 1691d4ca7081d3154771c2c4b18ec816936fef4f Mon Sep 17 00:00:00 2001 From: David Frey Date: Mon, 20 Aug 2018 10:39:59 -0700 Subject: [PATCH 41/68] iio: chemical: bme680: Remove field value defines Remove BME680_RUN_GAS_EN_BIT and BME680_NB_CONV_0_VAL field value definitions because the fields are simply boolean and integer respectively. Signed-off-by: David Frey Reviewed-by: Himanshu Jha Tested-by: Himanshu Jha Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/bme680.h | 2 -- drivers/iio/chemical/bme680_core.c | 5 +++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/iio/chemical/bme680.h b/drivers/iio/chemical/bme680.h index a9f2a9a6abc5..0ae89b87e2d6 100644 --- a/drivers/iio/chemical/bme680.h +++ b/drivers/iio/chemical/bme680.h @@ -54,8 +54,6 @@ #define BME680_REG_CTRL_GAS_1 0x71 #define BME680_RUN_GAS_MASK BIT(4) #define BME680_NB_CONV_MASK GENMASK(3, 0) -#define BME680_RUN_GAS_EN_BIT BIT(4) -#define BME680_NB_CONV_0_VAL 0 #define BME680_REG_MEAS_STAT_0 0x1D #define BME680_GAS_MEAS_BIT BIT(6) diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c index 87666ce8915f..70c1fe4366f4 100644 --- a/drivers/iio/chemical/bme680_core.c +++ b/drivers/iio/chemical/bme680_core.c @@ -572,10 +572,11 @@ static int bme680_gas_config(struct bme680_data *data) return ret; } - /* Selecting the runGas and NB conversion settings for the sensor */ + /* Enable the gas sensor and select heater profile set-point 0 */ ret = regmap_update_bits(data->regmap, BME680_REG_CTRL_GAS_1, BME680_RUN_GAS_MASK | BME680_NB_CONV_MASK, - BME680_RUN_GAS_EN_BIT | BME680_NB_CONV_0_VAL); + FIELD_PREP(BME680_RUN_GAS_MASK, 1) | + FIELD_PREP(BME680_NB_CONV_MASK, 0)); if (ret < 0) dev_err(dev, "failed to write ctrl_gas_1 register\n"); From 1c412a321590d4a01698b85710e29b1e575de1a7 Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Mon, 20 Aug 2018 17:53:43 +0300 Subject: [PATCH 42/68] iio: adxl372: Provide validate_trigger and validate_device callbacks This patch provides a validate_device callback for the trigger which makes sure that other devices are rejected. Signed-off-by: Stefan Popa Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl372.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index d2fdc7581fe0..5a039ba0b3eb 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -762,11 +762,24 @@ static int adxl372_dready_trig_set_state(struct iio_trigger *trig, return adxl372_set_interrupts(st, mask, 0); } +static int adxl372_validate_trigger(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + struct adxl372_state *st = iio_priv(indio_dev); + + if (st->dready_trig != trig) + return -EINVAL; + + return 0; +} + static const struct iio_trigger_ops adxl372_trigger_ops = { + .validate_device = &iio_trigger_validate_own_device, .set_trigger_state = adxl372_dready_trig_set_state, }; static const struct iio_info adxl372_info = { + .validate_trigger = &adxl372_validate_trigger, .read_raw = adxl372_read_raw, .debugfs_reg_access = &adxl372_reg_access, .hwfifo_set_watermark = adxl372_set_watermark, From 5e605a4df6f18dc4e2e529f5f2e01f76441cece5 Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Mon, 20 Aug 2018 17:54:27 +0300 Subject: [PATCH 43/68] iio:adxl372: Add sampling frequency support This patch adds the option for the user to select the sampling frequency. Also, the user can read the available frequencies and read the currently set frequency via the read_raw function. The frequency can be set via the write_raw function. When the frequency is set, the bandwidth is also checked and ensured that it is constrained to at most half of the sampling frequency. Also, the activity and inactivity timers have to be updated because they depend on the selected ODR. Signed-off-by: Stefan Popa Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl372.c | 74 ++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index 5a039ba0b3eb..6281e4a11632 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -223,7 +223,8 @@ static const struct adxl372_axis_lookup adxl372_axis_lookup_table[] = { .modified = 1, \ .channel2 = IIO_MOD_##axis, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ .scan_index = index, \ .scan_type = { \ .sign = 's', \ @@ -311,6 +312,19 @@ static int adxl372_set_odr(struct adxl372_state *st, return ret; } +static int adxl372_find_closest_match(const int *array, + unsigned int size, int val) +{ + int i; + + for (i = 0; i < size; i++) { + if (val <= array[i]) + return i; + } + + return size - 1; +} + static int adxl372_set_bandwidth(struct adxl372_state *st, enum adxl372_bandwidth bw) { @@ -631,6 +645,51 @@ static int adxl372_read_raw(struct iio_dev *indio_dev, *val = 0; *val2 = ADXL372_USCALE; return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = adxl372_samp_freq_tbl[st->odr]; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int adxl372_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct adxl372_state *st = iio_priv(indio_dev); + int odr_index, ret; + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + odr_index = adxl372_find_closest_match(adxl372_samp_freq_tbl, + ARRAY_SIZE(adxl372_samp_freq_tbl), + val); + ret = adxl372_set_odr(st, odr_index); + if (ret < 0) + return ret; + /* + * The timer period depends on the ODR selected. + * At 3200 Hz and below, it is 6.6 ms; at 6400 Hz, it is 3.3 ms + */ + ret = adxl372_set_activity_time_ms(st, st->act_time_ms); + if (ret < 0) + return ret; + /* + * The timer period depends on the ODR selected. + * At 3200 Hz and below, it is 26 ms; at 6400 Hz, it is 13 ms + */ + ret = adxl372_set_inactivity_time_ms(st, st->inact_time_ms); + if (ret < 0) + return ret; + /* + * The maximum bandwidth is constrained to at most half of + * the ODR to ensure that the Nyquist criteria is not violated + */ + if (st->bw > odr_index) + ret = adxl372_set_bandwidth(st, odr_index); + + return ret; default: return -EINVAL; } @@ -778,9 +837,22 @@ static const struct iio_trigger_ops adxl372_trigger_ops = { .set_trigger_state = adxl372_dready_trig_set_state, }; +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("400 800 1600 3200 6400"); + +static struct attribute *adxl372_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group adxl372_attrs_group = { + .attrs = adxl372_attributes, +}; + static const struct iio_info adxl372_info = { .validate_trigger = &adxl372_validate_trigger, + .attrs = &adxl372_attrs_group, .read_raw = adxl372_read_raw, + .write_raw = adxl372_write_raw, .debugfs_reg_access = &adxl372_reg_access, .hwfifo_set_watermark = adxl372_set_watermark, }; From 7ec040af6ce11285c8bd3ec59a1a570db742e9aa Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Mon, 20 Aug 2018 17:54:55 +0300 Subject: [PATCH 44/68] iio:adxl372: Add filter bandwidth support This patch adds the option for the user to select the filter bandwidth. The user can also read the available bandwidths which are always adjusted to be at most half of the sampling frequency. Furthermore, the currently selected bandwidth can be read via the read_raw function, while the write_raw sets a new bandwidth value. Signed-off-by: Stefan Popa Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl372.c | 38 +++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index 6281e4a11632..fdaaa58ce68f 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -202,6 +202,10 @@ static const int adxl372_samp_freq_tbl[5] = { 400, 800, 1600, 3200, 6400, }; +static const int adxl372_bw_freq_tbl[5] = { + 200, 400, 800, 1600, 3200, +}; + struct adxl372_axis_lookup { unsigned int bits; enum adxl372_fifo_format fifo_format; @@ -224,7 +228,8 @@ static const struct adxl372_axis_lookup adxl372_axis_lookup_table[] = { .channel2 = IIO_MOD_##axis, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ .scan_index = index, \ .scan_type = { \ .sign = 's', \ @@ -648,6 +653,9 @@ static int adxl372_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SAMP_FREQ: *val = adxl372_samp_freq_tbl[st->odr]; return IIO_VAL_INT; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + *val = adxl372_bw_freq_tbl[st->bw]; + return IIO_VAL_INT; } return -EINVAL; @@ -658,7 +666,7 @@ static int adxl372_write_raw(struct iio_dev *indio_dev, int val, int val2, long info) { struct adxl372_state *st = iio_priv(indio_dev); - int odr_index, ret; + int odr_index, bw_index, ret; switch (info) { case IIO_CHAN_INFO_SAMP_FREQ: @@ -690,11 +698,34 @@ static int adxl372_write_raw(struct iio_dev *indio_dev, ret = adxl372_set_bandwidth(st, odr_index); return ret; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + bw_index = adxl372_find_closest_match(adxl372_bw_freq_tbl, + ARRAY_SIZE(adxl372_bw_freq_tbl), + val); + return adxl372_set_bandwidth(st, bw_index); default: return -EINVAL; } } +static ssize_t adxl372_show_filter_freq_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct adxl372_state *st = iio_priv(indio_dev); + int i; + size_t len = 0; + + for (i = 0; i <= st->odr; i++) + len += scnprintf(buf + len, PAGE_SIZE - len, + "%d ", adxl372_bw_freq_tbl[i]); + + buf[len - 1] = '\n'; + + return len; +} + static ssize_t adxl372_get_fifo_enabled(struct device *dev, struct device_attribute *attr, char *buf) @@ -838,9 +869,12 @@ static const struct iio_trigger_ops adxl372_trigger_ops = { }; static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("400 800 1600 3200 6400"); +static IIO_DEVICE_ATTR(in_accel_filter_low_pass_3db_frequency_available, + 0444, adxl372_show_filter_freq_avail, NULL, 0); static struct attribute *adxl372_attributes[] = { &iio_const_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_accel_filter_low_pass_3db_frequency_available.dev_attr.attr, NULL, }; From 8316cebd1e59823872d10799ce91f67c7c06968e Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Tue, 21 Aug 2018 21:31:24 +0200 Subject: [PATCH 45/68] iio: dac: add support for ltc1660 LTC1665/LTC1660 is a 8/10-bit Digital-to-Analog Converter (DAC) with eight individual channels. Signed-off-by: Marcus Folkesson Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 10 ++ drivers/iio/dac/Makefile | 1 + drivers/iio/dac/ltc1660.c | 250 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 261 insertions(+) create mode 100644 drivers/iio/dac/ltc1660.c diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 80beb64e9e0c..bb2057fd1b6f 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -120,6 +120,16 @@ config AD5624R_SPI Say yes here to build support for Analog Devices AD5624R, AD5644R and AD5664R converters (DAC). This driver uses the common SPI interface. +config LTC1660 + tristate "Linear Technology LTC1660/LTC1665 DAC SPI driver" + depends on SPI + help + Say yes here to build support for Linear Technology + LTC1660 and LTC1665 Digital to Analog Converters. + + To compile this driver as a module, choose M here: the + module will be called ltc1660. + config LTC2632 tristate "Linear Technology LTC2632-12/10/8 DAC spi driver" depends on SPI diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index a1b37cf99441..2ac93cc4a389 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_CIO_DAC) += cio-dac.o obj-$(CONFIG_DPOT_DAC) += dpot-dac.o obj-$(CONFIG_DS4424) += ds4424.o obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o +obj-$(CONFIG_LTC1660) += ltc1660.o obj-$(CONFIG_LTC2632) += ltc2632.o obj-$(CONFIG_M62332) += m62332.o obj-$(CONFIG_MAX517) += max517.o diff --git a/drivers/iio/dac/ltc1660.c b/drivers/iio/dac/ltc1660.c new file mode 100644 index 000000000000..10866838c72a --- /dev/null +++ b/drivers/iio/dac/ltc1660.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Linear Technology LTC1665/LTC1660, 8 channels DAC + * + * Copyright (C) 2018 Marcus Folkesson + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define LTC1660_REG_WAKE 0x0 +#define LTC1660_REG_DAC_A 0x1 +#define LTC1660_REG_DAC_B 0x2 +#define LTC1660_REG_DAC_C 0x3 +#define LTC1660_REG_DAC_D 0x4 +#define LTC1660_REG_DAC_E 0x5 +#define LTC1660_REG_DAC_F 0x6 +#define LTC1660_REG_DAC_G 0x7 +#define LTC1660_REG_DAC_H 0x8 +#define LTC1660_REG_SLEEP 0xe + +#define LTC1660_NUM_CHANNELS 8 + +static const struct regmap_config ltc1660_regmap_config = { + .reg_bits = 4, + .val_bits = 12, +}; + +enum ltc1660_supported_device_ids { + ID_LTC1660, + ID_LTC1665, +}; + +struct ltc1660_priv { + struct spi_device *spi; + struct regmap *regmap; + struct regulator *vref_reg; + unsigned int value[LTC1660_NUM_CHANNELS]; + unsigned int vref_mv; +}; + +static int ltc1660_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct ltc1660_priv *priv = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *val = priv->value[chan->channel]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = regulator_get_voltage(priv->vref_reg); + if (*val < 0) { + dev_err(&priv->spi->dev, "failed to read vref regulator: %d\n", + *val); + return *val; + } + + /* Convert to mV */ + *val /= 1000; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static int ltc1660_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ltc1660_priv *priv = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val2 != 0) + return -EINVAL; + + if (val < 0 || val > GENMASK(chan->scan_type.realbits - 1, 0)) + return -EINVAL; + + ret = regmap_write(priv->regmap, chan->channel, + (val << chan->scan_type.shift)); + if (!ret) + priv->value[chan->channel] = val; + + return ret; + default: + return -EINVAL; + } +} + +#define LTC1660_CHAN(chan, bits) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (bits), \ + .storagebits = 16, \ + .shift = 12 - (bits), \ + }, \ +} + +#define LTC1660_OCTAL_CHANNELS(bits) { \ + LTC1660_CHAN(LTC1660_REG_DAC_A, bits), \ + LTC1660_CHAN(LTC1660_REG_DAC_B, bits), \ + LTC1660_CHAN(LTC1660_REG_DAC_C, bits), \ + LTC1660_CHAN(LTC1660_REG_DAC_D, bits), \ + LTC1660_CHAN(LTC1660_REG_DAC_E, bits), \ + LTC1660_CHAN(LTC1660_REG_DAC_F, bits), \ + LTC1660_CHAN(LTC1660_REG_DAC_G, bits), \ + LTC1660_CHAN(LTC1660_REG_DAC_H, bits), \ +} + +static const struct iio_chan_spec ltc1660_channels[][LTC1660_NUM_CHANNELS] = { + [ID_LTC1660] = LTC1660_OCTAL_CHANNELS(10), + [ID_LTC1665] = LTC1660_OCTAL_CHANNELS(8), +}; + +static const struct iio_info ltc1660_info = { + .read_raw = <c1660_read_raw, + .write_raw = <c1660_write_raw, +}; + +static int __maybe_unused ltc1660_suspend(struct device *dev) +{ + struct ltc1660_priv *priv = iio_priv(spi_get_drvdata( + to_spi_device(dev))); + return regmap_write(priv->regmap, LTC1660_REG_SLEEP, 0x00); +} + +static int __maybe_unused ltc1660_resume(struct device *dev) +{ + struct ltc1660_priv *priv = iio_priv(spi_get_drvdata( + to_spi_device(dev))); + return regmap_write(priv->regmap, LTC1660_REG_WAKE, 0x00); +} +static SIMPLE_DEV_PM_OPS(ltc1660_pm_ops, ltc1660_suspend, ltc1660_resume); + +static int ltc1660_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct ltc1660_priv *priv; + const struct spi_device_id *id = spi_get_device_id(spi); + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*priv)); + if (indio_dev == NULL) + return -ENOMEM; + + priv = iio_priv(indio_dev); + priv->regmap = devm_regmap_init_spi(spi, <c1660_regmap_config); + if (IS_ERR(priv->regmap)) { + dev_err(&spi->dev, "failed to register spi regmap %ld\n", + PTR_ERR(priv->regmap)); + return PTR_ERR(priv->regmap); + } + + priv->vref_reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(priv->vref_reg)) { + dev_err(&spi->dev, "vref regulator not specified\n"); + return PTR_ERR(priv->vref_reg); + } + + ret = regulator_enable(priv->vref_reg); + if (ret) { + dev_err(&spi->dev, "failed to enable vref regulator: %d\n", + ret); + return ret; + } + + priv->spi = spi; + spi_set_drvdata(spi, indio_dev); + indio_dev->dev.parent = &spi->dev; + indio_dev->info = <c1660_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ltc1660_channels[id->driver_data]; + indio_dev->num_channels = LTC1660_NUM_CHANNELS; + indio_dev->name = id->name; + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&spi->dev, "failed to register iio device: %d\n", + ret); + goto error_disable_reg; + } + + return 0; + +error_disable_reg: + regulator_disable(priv->vref_reg); + + return ret; +} + +static int ltc1660_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ltc1660_priv *priv = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + regulator_disable(priv->vref_reg); + + return 0; +} + +static const struct of_device_id ltc1660_dt_ids[] = { + { .compatible = "lltc,ltc1660", .data = (void *)ID_LTC1660 }, + { .compatible = "lltc,ltc1665", .data = (void *)ID_LTC1665 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ltc1660_dt_ids); + +static const struct spi_device_id ltc1660_id[] = { + {"ltc1660", ID_LTC1660}, + {"ltc1665", ID_LTC1665}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, ltc1660_id); + +static struct spi_driver ltc1660_driver = { + .driver = { + .name = "ltc1660", + .of_match_table = ltc1660_dt_ids, + .pm = <c1660_pm_ops, + }, + .probe = ltc1660_probe, + .remove = ltc1660_remove, + .id_table = ltc1660_id, +}; +module_spi_driver(ltc1660_driver); + +MODULE_AUTHOR("Marcus Folkesson "); +MODULE_DESCRIPTION("Linear Technology LTC1660/LTC1665 DAC"); +MODULE_LICENSE("GPL v2"); From d1071f726a7640ce0525d1868844bbb107e9e43a Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Tue, 21 Aug 2018 21:31:25 +0200 Subject: [PATCH 46/68] dt-bindings: iio: dac: add bindings for ltc1660 LTC1665/LTC1660 is a 8/10-bit Digital-to-Analog Converter (DAC) with eight individual channels. Signed-off-by: Marcus Folkesson Reviewed-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/dac/ltc1660.txt | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/dac/ltc1660.txt diff --git a/Documentation/devicetree/bindings/iio/dac/ltc1660.txt b/Documentation/devicetree/bindings/iio/dac/ltc1660.txt new file mode 100644 index 000000000000..c5b5f22d6c64 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/ltc1660.txt @@ -0,0 +1,21 @@ +* Linear Technology Micropower octal 8-Bit and 10-Bit DACs + +Required properties: + - compatible: Must be one of the following: + "lltc,ltc1660" + "lltc,ltc1665" + - reg: SPI chip select number for the device + - vref-supply: Phandle to the voltage reference supply + +Recommended properties: + - spi-max-frequency: Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt. + Max frequency for this chip is 5 MHz. + +Example: +dac@0 { + compatible = "lltc,ltc1660"; + reg = <0>; + spi-max-frequency = <5000000>; + vref-supply = <&vref_reg>; +}; From e2b01faf6cde01b2ed5a7fb8a63e5fa25c2257ea Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Tue, 21 Aug 2018 21:31:26 +0200 Subject: [PATCH 47/68] MAINTAINERS: add entry for ltc1660 DAC driver Add entry for ltc1660 DAC driver and add myself as maintainer of this driver. Signed-off-by: Marcus Folkesson Signed-off-by: Jonathan Cameron --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 2b7b24b145f0..2bfc9b02dca4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8490,6 +8490,13 @@ L: linux-scsi@vger.kernel.org S: Maintained F: drivers/scsi/sym53c8xx_2/ +LTC1660 DAC DRIVER +M: Marcus Folkesson +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/dac/ltc1660.txt +F: drivers/iio/dac/ltc1660.c + LTC4261 HARDWARE MONITOR DRIVER M: Guenter Roeck L: linux-hwmon@vger.kernel.org From 0833627fc3f757a0dca11e2a9c46c96335a900ee Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Fri, 24 Aug 2018 22:24:40 +0200 Subject: [PATCH 48/68] iio: dac: mcp4922: fix error handling in mcp4922_write_raw Do not try to write negative values and make sure that the write goes well. Signed-off-by: Marcus Folkesson Signed-off-by: Jonathan Cameron --- drivers/iio/dac/mcp4922.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/iio/dac/mcp4922.c b/drivers/iio/dac/mcp4922.c index bf9aa3fc0534..b5190d1dae8e 100644 --- a/drivers/iio/dac/mcp4922.c +++ b/drivers/iio/dac/mcp4922.c @@ -94,17 +94,22 @@ static int mcp4922_write_raw(struct iio_dev *indio_dev, long mask) { struct mcp4922_state *state = iio_priv(indio_dev); + int ret; if (val2 != 0) return -EINVAL; switch (mask) { case IIO_CHAN_INFO_RAW: - if (val > GENMASK(chan->scan_type.realbits-1, 0)) + if (val < 0 || val > GENMASK(chan->scan_type.realbits - 1, 0)) return -EINVAL; val <<= chan->scan_type.shift; - state->value[chan->channel] = val; - return mcp4922_spi_write(state, chan->channel, val); + + ret = mcp4922_spi_write(state, chan->channel, val); + if (!ret) + state->value[chan->channel] = val; + return ret; + default: return -EINVAL; } From 91d05d7639f388bbed90076299867e85e983f298 Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Fri, 24 Aug 2018 22:24:59 +0200 Subject: [PATCH 49/68] iio: dac: ti-dac5571: provide of_match_table to driver Use the created list of of_device_id's as a match table. Signed-off-by: Marcus Folkesson Signed-off-by: Jonathan Cameron --- drivers/iio/dac/ti-dac5571.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/dac/ti-dac5571.c b/drivers/iio/dac/ti-dac5571.c index e39d1e901353..f6dcd8bce2b0 100644 --- a/drivers/iio/dac/ti-dac5571.c +++ b/drivers/iio/dac/ti-dac5571.c @@ -421,6 +421,7 @@ MODULE_DEVICE_TABLE(i2c, dac5571_id); static struct i2c_driver dac5571_driver = { .driver = { .name = "ti-dac5571", + .of_match_table = of_match_ptr(dac5571_of_id), }, .probe = dac5571_probe, .remove = dac5571_remove, From fd2f53ebf98173d667fe6b9c2300fef8b4f72f30 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Wed, 29 Aug 2018 14:04:04 +0800 Subject: [PATCH 50/68] iio: adc: sc27xx: Add raw data support The headset device will use channel 20 of ADC controller to detect events, but it needs the raw ADC data to do conversion according to its own formula. Thus we should configure the channel mask separately and configure channel 20 as IIO_CHAN_INFO_RAW, as well as adding raw data read support. Signed-off-by: Baolin Wang Signed-off-by: Jonathan Cameron --- drivers/iio/adc/sc27xx_adc.c | 80 ++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c index 2b60efea0c39..153c31104d4e 100644 --- a/drivers/iio/adc/sc27xx_adc.c +++ b/drivers/iio/adc/sc27xx_adc.c @@ -273,6 +273,17 @@ static int sc27xx_adc_read_raw(struct iio_dev *indio_dev, int ret, tmp; switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + ret = sc27xx_adc_read(data, chan->channel, scale, &tmp); + mutex_unlock(&indio_dev->mlock); + + if (ret) + return ret; + + *val = tmp; + return IIO_VAL_INT; + case IIO_CHAN_INFO_PROCESSED: mutex_lock(&indio_dev->mlock); ret = sc27xx_adc_read_processed(data, chan->channel, scale, @@ -315,48 +326,47 @@ static const struct iio_info sc27xx_info = { .write_raw = &sc27xx_adc_write_raw, }; -#define SC27XX_ADC_CHANNEL(index) { \ +#define SC27XX_ADC_CHANNEL(index, mask) { \ .type = IIO_VOLTAGE, \ .channel = index, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | \ - BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_separate = mask | BIT(IIO_CHAN_INFO_SCALE), \ .datasheet_name = "CH##index", \ .indexed = 1, \ } static const struct iio_chan_spec sc27xx_channels[] = { - SC27XX_ADC_CHANNEL(0), - SC27XX_ADC_CHANNEL(1), - SC27XX_ADC_CHANNEL(2), - SC27XX_ADC_CHANNEL(3), - SC27XX_ADC_CHANNEL(4), - SC27XX_ADC_CHANNEL(5), - SC27XX_ADC_CHANNEL(6), - SC27XX_ADC_CHANNEL(7), - SC27XX_ADC_CHANNEL(8), - SC27XX_ADC_CHANNEL(9), - SC27XX_ADC_CHANNEL(10), - SC27XX_ADC_CHANNEL(11), - SC27XX_ADC_CHANNEL(12), - SC27XX_ADC_CHANNEL(13), - SC27XX_ADC_CHANNEL(14), - SC27XX_ADC_CHANNEL(15), - SC27XX_ADC_CHANNEL(16), - SC27XX_ADC_CHANNEL(17), - SC27XX_ADC_CHANNEL(18), - SC27XX_ADC_CHANNEL(19), - SC27XX_ADC_CHANNEL(20), - SC27XX_ADC_CHANNEL(21), - SC27XX_ADC_CHANNEL(22), - SC27XX_ADC_CHANNEL(23), - SC27XX_ADC_CHANNEL(24), - SC27XX_ADC_CHANNEL(25), - SC27XX_ADC_CHANNEL(26), - SC27XX_ADC_CHANNEL(27), - SC27XX_ADC_CHANNEL(28), - SC27XX_ADC_CHANNEL(29), - SC27XX_ADC_CHANNEL(30), - SC27XX_ADC_CHANNEL(31), + SC27XX_ADC_CHANNEL(0, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(1, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(2, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(3, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(4, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(5, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(6, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(7, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(8, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(9, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(10, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(11, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(12, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(13, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(14, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(15, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(16, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(17, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(18, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(19, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(20, BIT(IIO_CHAN_INFO_RAW)), + SC27XX_ADC_CHANNEL(21, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(22, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(23, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(24, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(25, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(26, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(27, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(28, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(29, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(30, BIT(IIO_CHAN_INFO_PROCESSED)), + SC27XX_ADC_CHANNEL(31, BIT(IIO_CHAN_INFO_PROCESSED)), }; static int sc27xx_adc_enable(struct sc27xx_adc_data *data) From 8ba0dbfd07a3587aedb952a95292e41d70dbf16e Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Wed, 29 Aug 2018 14:04:05 +0800 Subject: [PATCH 51/68] iio: adc: sc27xx: Add ADC scale calibration This patch adds support to read calibration values from the eFuse controller to calibrate the ADC channel scales, which can make ADC sample data more accurate. Signed-off-by: Baolin Wang Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/sprd,sc27xx-adc.txt | 4 + drivers/iio/adc/sc27xx_adc.c | 74 ++++++++++++++++++- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/sprd,sc27xx-adc.txt b/Documentation/devicetree/bindings/iio/adc/sprd,sc27xx-adc.txt index 8aad960de50b..b4daa15dcf15 100644 --- a/Documentation/devicetree/bindings/iio/adc/sprd,sc27xx-adc.txt +++ b/Documentation/devicetree/bindings/iio/adc/sprd,sc27xx-adc.txt @@ -12,6 +12,8 @@ Required properties: - interrupts: The interrupt number for the ADC device. - #io-channel-cells: Number of cells in an IIO specifier. - hwlocks: Reference to a phandle of a hwlock provider node. +- nvmem-cells: A phandle to the calibration cells provided by eFuse device. +- nvmem-cell-names: Should be "big_scale_calib", "small_scale_calib". Example: @@ -32,5 +34,7 @@ Example: interrupts = <0 IRQ_TYPE_LEVEL_HIGH>; #io-channel-cells = <1>; hwlocks = <&hwlock 4>; + nvmem-cells = <&adc_big_scale>, <&adc_small_scale>; + nvmem-cell-names = "big_scale_calib", "small_scale_calib"; }; }; diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c index 153c31104d4e..7940b23dcad9 100644 --- a/drivers/iio/adc/sc27xx_adc.c +++ b/drivers/iio/adc/sc27xx_adc.c @@ -5,10 +5,12 @@ #include #include #include +#include #include #include #include #include +#include /* PMIC global registers definition */ #define SC27XX_MODULE_EN 0xc08 @@ -87,16 +89,73 @@ struct sc27xx_adc_linear_graph { * should use the small-scale graph, and if more than 1.2v, we should use the * big-scale graph. */ -static const struct sc27xx_adc_linear_graph big_scale_graph = { +static struct sc27xx_adc_linear_graph big_scale_graph = { 4200, 3310, 3600, 2832, }; -static const struct sc27xx_adc_linear_graph small_scale_graph = { +static struct sc27xx_adc_linear_graph small_scale_graph = { 1000, 3413, 100, 341, }; +static const struct sc27xx_adc_linear_graph big_scale_graph_calib = { + 4200, 856, + 3600, 733, +}; + +static const struct sc27xx_adc_linear_graph small_scale_graph_calib = { + 1000, 833, + 100, 80, +}; + +static int sc27xx_adc_get_calib_data(u32 calib_data, int calib_adc) +{ + return ((calib_data & 0xff) + calib_adc - 128) * 4; +} + +static int sc27xx_adc_scale_calibration(struct sc27xx_adc_data *data, + bool big_scale) +{ + const struct sc27xx_adc_linear_graph *calib_graph; + struct sc27xx_adc_linear_graph *graph; + struct nvmem_cell *cell; + const char *cell_name; + u32 calib_data = 0; + void *buf; + size_t len; + + if (big_scale) { + calib_graph = &big_scale_graph_calib; + graph = &big_scale_graph; + cell_name = "big_scale_calib"; + } else { + calib_graph = &small_scale_graph_calib; + graph = &small_scale_graph; + cell_name = "small_scale_calib"; + } + + cell = nvmem_cell_get(data->dev, cell_name); + if (IS_ERR(cell)) + return PTR_ERR(cell); + + buf = nvmem_cell_read(cell, &len); + nvmem_cell_put(cell); + + if (IS_ERR(buf)) + return PTR_ERR(buf); + + memcpy(&calib_data, buf, min(len, sizeof(u32))); + + /* Only need to calibrate the adc values in the linear graph. */ + graph->adc0 = sc27xx_adc_get_calib_data(calib_data, calib_graph->adc0); + graph->adc1 = sc27xx_adc_get_calib_data(calib_data >> 8, + calib_graph->adc1); + + kfree(buf); + return 0; +} + static int sc27xx_adc_get_ratio(int channel, int scale) { switch (channel) { @@ -209,7 +268,7 @@ static void sc27xx_adc_volt_ratio(struct sc27xx_adc_data *data, *div_denominator = ratio & SC27XX_RATIO_DENOMINATOR_MASK; } -static int sc27xx_adc_to_volt(const struct sc27xx_adc_linear_graph *graph, +static int sc27xx_adc_to_volt(struct sc27xx_adc_linear_graph *graph, int raw_adc) { int tmp; @@ -390,6 +449,15 @@ static int sc27xx_adc_enable(struct sc27xx_adc_data *data) if (ret) goto disable_clk; + /* ADC channel scales' calibration from nvmem device */ + ret = sc27xx_adc_scale_calibration(data, true); + if (ret) + goto disable_clk; + + ret = sc27xx_adc_scale_calibration(data, false); + if (ret) + goto disable_clk; + return 0; disable_clk: From 24493cce03b689f9723244f9a7c2247296fc617e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 23 Aug 2018 23:24:35 +0200 Subject: [PATCH 52/68] iio: light: isl29501: Simplify code to kill compiler warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With gcc 4.1.2: drivers/iio/proximity/isl29501.c: In function ‘isl29501_register_write’: drivers/iio/proximity/isl29501.c:235: warning: ‘msb’ may be used uninitialized in this function While this is a false positive, it can easily be avoided by removing the "msb" intermediate variable. Remove the "lsb" intermediate variable for consistency. Signed-off-by: Geert Uytterhoeven Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/isl29501.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/iio/proximity/isl29501.c b/drivers/iio/proximity/isl29501.c index e5e94540f404..5ae549075b27 100644 --- a/drivers/iio/proximity/isl29501.c +++ b/drivers/iio/proximity/isl29501.c @@ -232,7 +232,6 @@ static u32 isl29501_register_write(struct isl29501_private *isl29501, u32 value) { const struct isl29501_register_desc *reg = &isl29501_registers[name]; - u8 msb, lsb; int ret; if (!reg->msb && value > U8_MAX) @@ -241,22 +240,15 @@ static u32 isl29501_register_write(struct isl29501_private *isl29501, if (value > U16_MAX) return -ERANGE; - if (!reg->msb) { - lsb = value & 0xFF; - } else { - msb = (value >> 8) & 0xFF; - lsb = value & 0xFF; - } - mutex_lock(&isl29501->lock); if (reg->msb) { ret = i2c_smbus_write_byte_data(isl29501->client, - reg->msb, msb); + reg->msb, value >> 8); if (ret < 0) goto err; } - ret = i2c_smbus_write_byte_data(isl29501->client, reg->lsb, lsb); + ret = i2c_smbus_write_byte_data(isl29501->client, reg->lsb, value); err: mutex_unlock(&isl29501->lock); From 21eab7861688aa4c69fcb88440cc0c4a422bdcd6 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Tue, 28 Aug 2018 13:30:34 +0300 Subject: [PATCH 53/68] iio: fix position relative kernel version Position relative channel type was added in 4.19 kernel version Fixes: "3055a6cfa04ba" ("iio: Add channel for Position Relative") Signed-off-by: Eugen Hristev Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index a5b4f223641d..8127a08e366d 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -199,7 +199,7 @@ Description: What: /sys/bus/iio/devices/iio:deviceX/in_positionrelative_x_raw What: /sys/bus/iio/devices/iio:deviceX/in_positionrelative_y_raw -KernelVersion: 4.18 +KernelVersion: 4.19 Contact: linux-iio@vger.kernel.org Description: Relative position in direction x or y on a pad (may be From 1e46774fec4b54adbd7cc15f1931e9df98664530 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Mon, 27 Aug 2018 14:14:53 -0700 Subject: [PATCH 54/68] iio: adc: qcom-spmi-adc5: Verify channel numbers from DT The driver only defines a subset of all possible ADC channels. Channel numbers read from the device tree are accepted as long as they don't exceed a max value, even when no channel definition exists. Add a check to abort initialization in this case. Signed-off-by: Matthias Kaehlcke Signed-off-by: Jonathan Cameron --- drivers/iio/adc/qcom-spmi-adc5.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c index 1f9298a5c83d..f9af6b082916 100644 --- a/drivers/iio/adc/qcom-spmi-adc5.c +++ b/drivers/iio/adc/qcom-spmi-adc5.c @@ -519,7 +519,8 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc, return ret; } - if (chan > ADC5_PARALLEL_ISENSE_VBAT_IDATA) { + if (chan > ADC5_PARALLEL_ISENSE_VBAT_IDATA || + !data->adc_chans[chan].datasheet_name) { dev_err(dev, "%s invalid channel number %d\n", name, chan); return -EINVAL; } From 1fc378fa9dab9d3036562ecd58b26ecae2dbd85d Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 27 Aug 2018 20:52:23 -0500 Subject: [PATCH 55/68] iio: Convert to using %pOFn instead of device_node.name In preparation to remove the node name pointer from struct device_node, convert printf users to use the %pOFn format specifier. Cc: Jonathan Cameron Cc: Hartmut Knaack Cc: Lars-Peter Clausen Cc: linux-iio@vger.kernel.org Signed-off-by: Rob Herring Signed-off-by: Jonathan Cameron --- drivers/iio/adc/qcom-pm8xxx-xoadc.c | 4 ++-- drivers/iio/adc/rcar-gyroadc.c | 12 ++++++------ drivers/iio/frequency/adf4350.c | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c index b093ecddf1a8..c30c002f1fef 100644 --- a/drivers/iio/adc/qcom-pm8xxx-xoadc.c +++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c @@ -708,8 +708,8 @@ static int pm8xxx_of_xlate(struct iio_dev *indio_dev, * mux. */ if (iiospec->args_count != 2) { - dev_err(&indio_dev->dev, "wrong number of arguments for %s need 2 got %d\n", - iiospec->np->name, + dev_err(&indio_dev->dev, "wrong number of arguments for %pOFn need 2 got %d\n", + iiospec->np, iiospec->args_count); return -EINVAL; } diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c index dcb50172186f..4e982b51bcda 100644 --- a/drivers/iio/adc/rcar-gyroadc.c +++ b/drivers/iio/adc/rcar-gyroadc.c @@ -343,8 +343,8 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev) for_each_child_of_node(np, child) { of_id = of_match_node(rcar_gyroadc_child_match, child); if (!of_id) { - dev_err(dev, "Ignoring unsupported ADC \"%s\".", - child->name); + dev_err(dev, "Ignoring unsupported ADC \"%pOFn\".", + child); continue; } @@ -381,16 +381,16 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev) ret = of_property_read_u32(child, "reg", ®); if (ret) { dev_err(dev, - "Failed to get child reg property of ADC \"%s\".\n", - child->name); + "Failed to get child reg property of ADC \"%pOFn\".\n", + child); return ret; } /* Channel number is too high. */ if (reg >= num_channels) { dev_err(dev, - "Only %i channels supported with %s, but reg = <%i>.\n", - num_channels, child->name, reg); + "Only %i channels supported with %pOFn, but reg = <%i>.\n", + num_channels, child, reg); return ret; } } diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index 6d768431d90e..f4748ff243f7 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -388,7 +388,7 @@ static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev) if (!pdata) return NULL; - strncpy(&pdata->name[0], np->name, SPI_NAME_SIZE - 1); + snprintf(&pdata->name[0], SPI_NAME_SIZE - 1, "%pOFn", np); tmp = 10000; of_property_read_u32(np, "adi,channel-spacing", &tmp); From ff5059302642d349596a7ebc3282a978a78144bd Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Wed, 29 Aug 2018 17:58:42 +0300 Subject: [PATCH 56/68] iio: dac: ad5758: Add support for hard reset The ad5758 has a hardware reset active low input pin. This patch adds a devicetree entry for a reset GPIO and a new ad5758_reset() function. During initialization, it is checked if the reset property is specified and the the GPIO is being asserted, therefore the device will become active. When the reset function is called, if the gpio_reset var is set, then the GPIO will be toggled, otherwise a software reset is performed. Signed-off-by: Stefan Popa Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/dac/ad5758.txt | 5 ++++ drivers/iio/dac/ad5758.c | 26 +++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/dac/ad5758.txt b/Documentation/devicetree/bindings/iio/dac/ad5758.txt index bba01a5cab1b..2f607f41f9d3 100644 --- a/Documentation/devicetree/bindings/iio/dac/ad5758.txt +++ b/Documentation/devicetree/bindings/iio/dac/ad5758.txt @@ -50,6 +50,9 @@ Required properties: Optional properties: + - reset-gpios : GPIO spec for the RESET pin. If specified, it will be + asserted during driver probe. + - adi,dc-dc-ilim-microamp: The dc-to-dc converter current limit The following values are currently supported [uA]: * 150000 @@ -71,6 +74,8 @@ AD5758 Example: spi-max-frequency = <1000000>; spi-cpha; + reset-gpios = <&gpio 22 0>; + adi,dc-dc-mode = <2>; adi,range-microvolt = <0 10000000>; adi,dc-dc-ilim-microamp = <200000>; diff --git a/drivers/iio/dac/ad5758.c b/drivers/iio/dac/ad5758.c index bd36333257af..ef41f12bf262 100644 --- a/drivers/iio/dac/ad5758.c +++ b/drivers/iio/dac/ad5758.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -108,6 +109,7 @@ struct ad5758_range { struct ad5758_state { struct spi_device *spi; struct mutex lock; + struct gpio_desc *gpio_reset; struct ad5758_range out_range; unsigned int dc_dc_mode; unsigned int dc_dc_ilim; @@ -474,6 +476,21 @@ static int ad5758_internal_buffers_en(struct ad5758_state *st, bool enable) AD5758_CAL_MEM_UNREFRESHED_MSK); } +static int ad5758_reset(struct ad5758_state *st) +{ + if (st->gpio_reset) { + gpiod_set_value(st->gpio_reset, 0); + usleep_range(100, 1000); + gpiod_set_value(st->gpio_reset, 1); + usleep_range(100, 1000); + + return 0; + } else { + /* Perform a software reset */ + return ad5758_soft_reset(st); + } +} + static int ad5758_reg_access(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, @@ -768,13 +785,18 @@ static int ad5758_init(struct ad5758_state *st) { int regval, ret; + st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(st->gpio_reset)) + return PTR_ERR(st->gpio_reset); + /* Disable CRC checks */ ret = ad5758_crc_disable(st); if (ret < 0) return ret; - /* Perform a software reset */ - ret = ad5758_soft_reset(st); + /* Perform a reset */ + ret = ad5758_reset(st); if (ret < 0) return ret; From 50ff457d1b321d6392ade6bcc67877b8b4aeef89 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 30 Aug 2018 22:52:55 +0200 Subject: [PATCH 57/68] iio: imu: st_lsm6dsx: add read_fifo callback to fifo_ops Remove static qualifier from st_lsm6dsx_read_fifo definition and introduce read_fifo function pointer in fifo_ops data structure in order to run the proper read_fifo routine since other compliant devices will use a different FIFO queueing scheme. Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 5 +++++ drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 6 +++--- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 3 +++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index edcd838037cd..a0a6c07aad62 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -41,13 +41,17 @@ struct st_lsm6dsx_reg { u8 mask; }; +struct st_lsm6dsx_hw; + /** * struct st_lsm6dsx_fifo_ops - ST IMU FIFO settings + * @read_fifo: Read FIFO callback. * @fifo_th: FIFO threshold register info (addr + mask). * @fifo_diff: FIFO diff status register info (addr + mask). * @th_wl: FIFO threshold word length. */ struct st_lsm6dsx_fifo_ops { + int (*read_fifo)(struct st_lsm6dsx_hw *hw); struct { u8 addr; u16 mask; @@ -175,5 +179,6 @@ int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw); int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw, enum st_lsm6dsx_fifo_mode fifo_mode); +int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw); #endif /* ST_LSM6DSX_H */ diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index 7589f2ad1dae..9a1dd47afd97 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -283,7 +283,7 @@ static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 *data, * * Return: Number of bytes read from the FIFO */ -static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) +int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) { u16 fifo_len, pattern_len = hw->sip * ST_LSM6DSX_SAMPLE_SIZE; u16 fifo_diff_mask = hw->settings->fifo_ops.fifo_diff.mask; @@ -407,7 +407,7 @@ int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw) mutex_lock(&hw->fifo_lock); - st_lsm6dsx_read_fifo(hw); + hw->settings->fifo_ops.read_fifo(hw); err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_BYPASS); mutex_unlock(&hw->fifo_lock); @@ -479,7 +479,7 @@ static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private) int count; mutex_lock(&hw->fifo_lock); - count = st_lsm6dsx_read_fifo(hw); + count = hw->settings->fifo_ops.read_fifo(hw); mutex_unlock(&hw->fifo_lock); return !count ? IRQ_NONE : IRQ_HANDLED; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index aebbe0ddd8d8..394bcd3719e0 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -171,6 +171,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { }, }, .fifo_ops = { + .read_fifo = st_lsm6dsx_read_fifo, .fifo_th = { .addr = 0x06, .mask = GENMASK(11, 0), @@ -217,6 +218,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { }, }, .fifo_ops = { + .read_fifo = st_lsm6dsx_read_fifo, .fifo_th = { .addr = 0x06, .mask = GENMASK(11, 0), @@ -265,6 +267,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { }, }, .fifo_ops = { + .read_fifo = st_lsm6dsx_read_fifo, .fifo_th = { .addr = 0x06, .mask = GENMASK(10, 0), From 54a6d0c6882df3608103042eed7f57e6188f95ca Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 30 Aug 2018 22:52:56 +0200 Subject: [PATCH 58/68] iio: imu: st_lsm6dsx: make st_lsm6dsx_check_odr() non-static Remove static qualifier from st_lsm6dsx_check_odr() definition in order to use it for the support of new devices Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 1 + drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index a0a6c07aad62..6cf806f10787 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -180,5 +180,6 @@ int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw); int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw, enum st_lsm6dsx_fifo_mode fifo_mode); int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw); +int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val); #endif /* ST_LSM6DSX_H */ diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 394bcd3719e0..5a43d8139950 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -398,8 +398,7 @@ static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor, return 0; } -static int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, - u8 *val) +int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val) { int i; From 5b3c87fd92d5ef462b9b3894f4d4bc452ad7eff4 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 30 Aug 2018 22:52:57 +0200 Subject: [PATCH 59/68] iio: imu: st_lsm6dsx: add addr/max_word_len to st_lsm6dsx_read_block() Add reg addr and max_word_len parameters to st_lsm6dsx_read_block since LSM6DSO will use a different register address to read samples from the FIFO and a different sample len Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index 9a1dd47afd97..ab0eec2bc70e 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -254,18 +254,18 @@ static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw) * Set max bulk read to ST_LSM6DSX_MAX_WORD_LEN in order to avoid * a kmalloc for each bus access */ -static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 *data, - unsigned int data_len) +static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 addr, + u8 *data, unsigned int data_len, + unsigned int max_word_len) { unsigned int word_len, read_len = 0; int err; while (read_len < data_len) { word_len = min_t(unsigned int, data_len - read_len, - ST_LSM6DSX_MAX_WORD_LEN); - err = regmap_bulk_read(hw->regmap, - ST_LSM6DSX_REG_FIFO_OUTL_ADDR, - data + read_len, word_len); + max_word_len); + err = regmap_bulk_read(hw->regmap, addr, data + read_len, + word_len); if (err < 0) return err; read_len += word_len; @@ -315,7 +315,9 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]); for (read_len = 0; read_len < fifo_len; read_len += pattern_len) { - err = st_lsm6dsx_read_block(hw, hw->buff, pattern_len); + err = st_lsm6dsx_read_block(hw, ST_LSM6DSX_REG_FIFO_OUTL_ADDR, + hw->buff, pattern_len, + ST_LSM6DSX_MAX_WORD_LEN); if (err < 0) { dev_err(hw->dev, "failed to read pattern from fifo (err=%d)\n", From 801a6e0af0c6cedca2e99155e343ad385a50f08e Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 30 Aug 2018 22:52:58 +0200 Subject: [PATCH 60/68] iio: imu: st_lsm6dsx: add support to LSM6DSO Add support to STM LSM6DSO 6-axis (acc + gyro) Mems sensor https://www.st.com/resource/en/datasheet/lsm6dso.pdf Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/Kconfig | 2 +- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 12 +- .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 144 +++++++++++++++++- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 45 ++++++ drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c | 5 + drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c | 5 + 6 files changed, 205 insertions(+), 8 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/Kconfig b/drivers/iio/imu/st_lsm6dsx/Kconfig index ccc817e17eb8..094fd006b63d 100644 --- a/drivers/iio/imu/st_lsm6dsx/Kconfig +++ b/drivers/iio/imu/st_lsm6dsx/Kconfig @@ -9,7 +9,7 @@ config IIO_ST_LSM6DSX help Say yes here to build support for STMicroelectronics LSM6DSx imu sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm, - ism330dlc + ism330dlc, lsm6dso To compile this driver as a module, choose M here: the module will be called st_lsm6dsx. diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index 6cf806f10787..ef73519a0fb6 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -19,6 +19,7 @@ #define ST_LSM6DSL_DEV_NAME "lsm6dsl" #define ST_LSM6DSM_DEV_NAME "lsm6dsm" #define ST_ISM330DLC_DEV_NAME "ism330dlc" +#define ST_LSM6DSO_DEV_NAME "lsm6dso" enum st_lsm6dsx_hw_id { ST_LSM6DS3_ID, @@ -26,14 +27,20 @@ enum st_lsm6dsx_hw_id { ST_LSM6DSL_ID, ST_LSM6DSM_ID, ST_ISM330DLC_ID, + ST_LSM6DSO_ID, ST_LSM6DSX_MAX_ID, }; -#define ST_LSM6DSX_BUFF_SIZE 400 +#define ST_LSM6DSX_BUFF_SIZE 512 #define ST_LSM6DSX_CHAN_SIZE 2 #define ST_LSM6DSX_SAMPLE_SIZE 6 +#define ST_LSM6DSX_TAG_SIZE 1 +#define ST_LSM6DSX_TAGGED_SAMPLE_SIZE (ST_LSM6DSX_SAMPLE_SIZE + \ + ST_LSM6DSX_TAG_SIZE) #define ST_LSM6DSX_MAX_WORD_LEN ((32 / ST_LSM6DSX_SAMPLE_SIZE) * \ ST_LSM6DSX_SAMPLE_SIZE) +#define ST_LSM6DSX_MAX_TAGGED_WORD_LEN ((32 / ST_LSM6DSX_TAGGED_SAMPLE_SIZE) \ + * ST_LSM6DSX_TAGGED_SAMPLE_SIZE) #define ST_LSM6DSX_SHIFT_VAL(val, mask) (((val) << __ffs(mask)) & (mask)) struct st_lsm6dsx_reg { @@ -83,6 +90,7 @@ struct st_lsm6dsx_hw_ts_settings { * @max_fifo_size: Sensor max fifo length in FIFO words. * @id: List of hw id supported by the driver configuration. * @decimator: List of decimator register info (addr + mask). + * @batch: List of FIFO batching register info (addr + mask). * @fifo_ops: Sensor hw FIFO parameters. * @ts_settings: Hw timer related settings. */ @@ -91,6 +99,7 @@ struct st_lsm6dsx_settings { u16 max_fifo_size; enum st_lsm6dsx_hw_id id[ST_LSM6DSX_MAX_ID]; struct st_lsm6dsx_reg decimator[ST_LSM6DSX_MAX_ID]; + struct st_lsm6dsx_reg batch[ST_LSM6DSX_MAX_ID]; struct st_lsm6dsx_fifo_ops fifo_ops; struct st_lsm6dsx_hw_ts_settings ts_settings; }; @@ -180,6 +189,7 @@ int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw); int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw, enum st_lsm6dsx_fifo_mode fifo_mode); int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw); +int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw); int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val); #endif /* ST_LSM6DSX_H */ diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index ab0eec2bc70e..61f2c5777295 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -12,6 +12,11 @@ * buffer contains the data of all the enabled FIFO data sets * (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated depending on the * value of the decimation factor and ODR set for each FIFO data set. + * + * LSM6DSO: The FIFO buffer can be configured to store data from gyroscope and + * accelerometer. Each sample is queued with a tag (1B) indicating data source + * (gyroscope, accelerometer, hw timer). + * * FIFO supported modes: * - BYPASS: FIFO disabled * - CONTINUOUS: FIFO enabled. When the buffer is full, the FIFO index @@ -46,6 +51,7 @@ #define ST_LSM6DSX_FIFO_ODR_MASK GENMASK(6, 3) #define ST_LSM6DSX_FIFO_EMPTY_MASK BIT(12) #define ST_LSM6DSX_REG_FIFO_OUTL_ADDR 0x3e +#define ST_LSM6DSX_REG_FIFO_OUT_TAG_ADDR 0x78 #define ST_LSM6DSX_REG_TS_RESET_ADDR 0x42 #define ST_LSM6DSX_MAX_FIFO_ODR_VAL 0x08 @@ -58,6 +64,12 @@ struct st_lsm6dsx_decimator_entry { u8 val; }; +enum st_lsm6dsx_fifo_tag { + ST_LSM6DSX_GYRO_TAG = 0x01, + ST_LSM6DSX_ACC_TAG = 0x02, + ST_LSM6DSX_TS_TAG = 0x04, +}; + static const struct st_lsm6dsx_decimator_entry st_lsm6dsx_decimator_table[] = { { 0, 0x0 }, @@ -177,12 +189,34 @@ static int st_lsm6dsx_set_fifo_odr(struct st_lsm6dsx_sensor *sensor, bool enable) { struct st_lsm6dsx_hw *hw = sensor->hw; + const struct st_lsm6dsx_reg *batch_reg; u8 data; - data = hw->enable_mask ? ST_LSM6DSX_MAX_FIFO_ODR_VAL : 0; - return regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_FIFO_MODE_ADDR, - ST_LSM6DSX_FIFO_ODR_MASK, - FIELD_PREP(ST_LSM6DSX_FIFO_ODR_MASK, data)); + batch_reg = &hw->settings->batch[sensor->id]; + if (batch_reg->addr) { + int val; + + if (enable) { + int err; + + err = st_lsm6dsx_check_odr(sensor, sensor->odr, + &data); + if (err < 0) + return err; + } else { + data = 0; + } + val = ST_LSM6DSX_SHIFT_VAL(data, batch_reg->mask); + return regmap_update_bits(hw->regmap, batch_reg->addr, + batch_reg->mask, val); + } else { + data = hw->enable_mask ? ST_LSM6DSX_MAX_FIFO_ODR_VAL : 0; + return regmap_update_bits(hw->regmap, + ST_LSM6DSX_REG_FIFO_MODE_ADDR, + ST_LSM6DSX_FIFO_ODR_MASK, + FIELD_PREP(ST_LSM6DSX_FIFO_ODR_MASK, + data)); + } } int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark) @@ -251,8 +285,8 @@ static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw) } /* - * Set max bulk read to ST_LSM6DSX_MAX_WORD_LEN in order to avoid - * a kmalloc for each bus access + * Set max bulk read to ST_LSM6DSX_MAX_WORD_LEN/ST_LSM6DSX_MAX_TAGGED_WORD_LEN + * in order to avoid a kmalloc for each bus access */ static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 addr, u8 *data, unsigned int data_len, @@ -403,6 +437,104 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) return read_len; } +/** + * st_lsm6dsx_read_tagged_fifo() - LSM6DSO read FIFO routine + * @hw: Pointer to instance of struct st_lsm6dsx_hw. + * + * Read samples from the hw FIFO and push them to IIO buffers. + * + * Return: Number of bytes read from the FIFO + */ +int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw) +{ + u16 pattern_len = hw->sip * ST_LSM6DSX_TAGGED_SAMPLE_SIZE; + u16 fifo_len, fifo_diff_mask; + struct st_lsm6dsx_sensor *acc_sensor, *gyro_sensor; + u8 iio_buff[ST_LSM6DSX_IIO_BUFF_SIZE], tag; + bool reset_ts = false; + int i, err, read_len; + __le16 fifo_status; + s64 ts = 0; + + err = regmap_bulk_read(hw->regmap, + hw->settings->fifo_ops.fifo_diff.addr, + &fifo_status, sizeof(fifo_status)); + if (err < 0) { + dev_err(hw->dev, "failed to read fifo status (err=%d)\n", + err); + return err; + } + + fifo_diff_mask = hw->settings->fifo_ops.fifo_diff.mask; + fifo_len = (le16_to_cpu(fifo_status) & fifo_diff_mask) * + ST_LSM6DSX_TAGGED_SAMPLE_SIZE; + if (!fifo_len) + return 0; + + acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); + gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]); + + for (read_len = 0; read_len < fifo_len; read_len += pattern_len) { + err = st_lsm6dsx_read_block(hw, + ST_LSM6DSX_REG_FIFO_OUT_TAG_ADDR, + hw->buff, pattern_len, + ST_LSM6DSX_MAX_TAGGED_WORD_LEN); + if (err < 0) { + dev_err(hw->dev, + "failed to read pattern from fifo (err=%d)\n", + err); + return err; + } + + for (i = 0; i < pattern_len; + i += ST_LSM6DSX_TAGGED_SAMPLE_SIZE) { + memcpy(iio_buff, &hw->buff[i + ST_LSM6DSX_TAG_SIZE], + ST_LSM6DSX_SAMPLE_SIZE); + + tag = hw->buff[i] >> 3; + switch (tag) { + case ST_LSM6DSX_TS_TAG: + /* + * hw timestamp is 4B long and it is stored + * in FIFO according to this schema: + * B0 = ts[7:0], B1 = ts[15:8], B2 = ts[23:16], + * B3 = ts[31:24] + */ + ts = le32_to_cpu(*((__le32 *)iio_buff)); + /* + * check if hw timestamp engine is going to + * reset (the sensor generates an interrupt + * to signal the hw timestamp will reset in + * 1.638s) + */ + if (!reset_ts && ts >= 0xffff0000) + reset_ts = true; + ts *= ST_LSM6DSX_TS_SENSITIVITY; + break; + case ST_LSM6DSX_GYRO_TAG: + iio_push_to_buffers_with_timestamp( + hw->iio_devs[ST_LSM6DSX_ID_GYRO], + iio_buff, gyro_sensor->ts_ref + ts); + break; + case ST_LSM6DSX_ACC_TAG: + iio_push_to_buffers_with_timestamp( + hw->iio_devs[ST_LSM6DSX_ID_ACC], + iio_buff, acc_sensor->ts_ref + ts); + break; + default: + break; + } + } + } + + if (unlikely(reset_ts)) { + err = st_lsm6dsx_reset_hw_ts(hw); + if (err < 0) + return err; + } + return read_len; +} + int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw) { int err; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 5a43d8139950..2ad3c610e4b6 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -23,6 +23,12 @@ * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 * - FIFO size: 4KB * + * - LSM6DSO + * - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416 + * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 + * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 + * - FIFO size: 3KB + * * Copyright 2016 STMicroelectronics Inc. * * Lorenzo Bianconi @@ -297,6 +303,45 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { }, }, }, + { + .wai = 0x6c, + .max_fifo_size = 512, + .id = { + [0] = ST_LSM6DSO_ID, + }, + .batch = { + [ST_LSM6DSX_ID_ACC] = { + .addr = 0x09, + .mask = GENMASK(3, 0), + }, + [ST_LSM6DSX_ID_GYRO] = { + .addr = 0x09, + .mask = GENMASK(7, 4), + }, + }, + .fifo_ops = { + .read_fifo = st_lsm6dsx_read_tagged_fifo, + .fifo_th = { + .addr = 0x07, + .mask = GENMASK(8, 0), + }, + .fifo_diff = { + .addr = 0x3a, + .mask = GENMASK(8, 0), + }, + .th_wl = 1, + }, + .ts_settings = { + .timer_en = { + .addr = 0x19, + .mask = BIT(5), + }, + .decimator = { + .addr = 0x0a, + .mask = GENMASK(7, 6), + }, + }, + }, }; #define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx) \ diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c index 377c4e9997da..448b7bc1e578 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c @@ -61,6 +61,10 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = { .compatible = "st,ism330dlc", .data = (void *)ST_ISM330DLC_ID, }, + { + .compatible = "st,lsm6dso", + .data = (void *)ST_LSM6DSO_ID, + }, {}, }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match); @@ -71,6 +75,7 @@ static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = { { ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID }, { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID }, { ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID }, + { ST_LSM6DSO_DEV_NAME, ST_LSM6DSO_ID }, {}, }; MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c index fec5c6ce7eb7..b1df8a6973e6 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c @@ -61,6 +61,10 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = { .compatible = "st,ism330dlc", .data = (void *)ST_ISM330DLC_ID, }, + { + .compatible = "st,lsm6dso", + .data = (void *)ST_LSM6DSO_ID, + }, {}, }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match); @@ -71,6 +75,7 @@ static const struct spi_device_id st_lsm6dsx_spi_id_table[] = { { ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID }, { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID }, { ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID }, + { ST_LSM6DSO_DEV_NAME, ST_LSM6DSO_ID }, {}, }; MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table); From 897be9c0a747d32ec1c74742f3362d5db660318d Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Thu, 30 Aug 2018 22:52:59 +0200 Subject: [PATCH 61/68] dt-bindings: iio: imu: st_lsm6dsx: add LSM6DSO device bindings Signed-off-by: Lorenzo Bianconi Reviewed-by: Rob Herring Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt b/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt index ef8a8566c63f..858a2898908a 100644 --- a/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt +++ b/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt @@ -7,6 +7,7 @@ Required properties: "st,lsm6dsl" "st,lsm6dsm" "st,ism330dlc" + "st,lsm6dso" - reg: i2c address of the sensor / spi cs line Optional properties: From 53d3ae20a68c2dc8ef6242ccf23dfcb7a86c8c97 Mon Sep 17 00:00:00 2001 From: Tomasz Duszynski Date: Sat, 1 Sep 2018 21:51:44 +0200 Subject: [PATCH 62/68] iio: light: bh1750: switch to SPDX identifier Drop boilerplate license text and use SPDX identifier instead. Signed-off-by: Tomasz Duszynski Signed-off-by: Jonathan Cameron --- drivers/iio/light/bh1750.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/iio/light/bh1750.c b/drivers/iio/light/bh1750.c index a814828e69f5..493ca7420602 100644 --- a/drivers/iio/light/bh1750.c +++ b/drivers/iio/light/bh1750.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ROHM BH1710/BH1715/BH1721/BH1750/BH1751 ambient light sensor driver * * Copyright (c) Tomasz Duszynski * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * * Data sheets: * http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1710fvc-e.pdf * http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1715fvc-e.pdf From 6cfdb150e3fca6cd84a79116451071d6f3759946 Mon Sep 17 00:00:00 2001 From: Tomasz Duszynski Date: Sat, 1 Sep 2018 21:52:14 +0200 Subject: [PATCH 63/68] iio: pressure: ms5611: switch to SPDX identifier Drop boilerplate license text and use SPDX identifier instead. Signed-off-by: Tomasz Duszynski Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/ms5611.h | 5 +---- drivers/iio/pressure/ms5611_core.c | 5 +---- drivers/iio/pressure/ms5611_i2c.c | 5 +---- drivers/iio/pressure/ms5611_spi.c | 5 +---- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h index ead9e9f85894..bc06271fa38b 100644 --- a/drivers/iio/pressure/ms5611.h +++ b/drivers/iio/pressure/ms5611.h @@ -1,12 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * MS5611 pressure and temperature sensor driver * * Copyright (c) Tomasz Duszynski * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * */ #ifndef _MS5611_H diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c index f950cfde5db9..2f598ad91621 100644 --- a/drivers/iio/pressure/ms5611_core.c +++ b/drivers/iio/pressure/ms5611_core.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MS5611 pressure and temperature sensor driver * * Copyright (c) Tomasz Duszynski * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * * Data sheet: * http://www.meas-spec.com/downloads/MS5611-01BA03.pdf * http://www.meas-spec.com/downloads/MS5607-02BA03.pdf diff --git a/drivers/iio/pressure/ms5611_i2c.c b/drivers/iio/pressure/ms5611_i2c.c index 0469c8ae1134..8089c59adce5 100644 --- a/drivers/iio/pressure/ms5611_i2c.c +++ b/drivers/iio/pressure/ms5611_i2c.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MS5611 pressure and temperature sensor driver (I2C bus) * * Copyright (c) Tomasz Duszynski * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * * 7-bit I2C slave addresses: * * 0x77 (CSB pin low) diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c index cd11d022208e..b463eaa799ab 100644 --- a/drivers/iio/pressure/ms5611_spi.c +++ b/drivers/iio/pressure/ms5611_spi.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MS5611 pressure and temperature sensor driver (SPI bus) * * Copyright (c) Tomasz Duszynski * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * */ #include From 7ac346823bbb284e9e66a3d4044c108b99c7149c Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Thu, 6 Sep 2018 14:04:52 -0700 Subject: [PATCH 64/68] dt-bindings: iio: vadc: Fix documentation of 'reg' The documentation of Qualcomm's SPMI PMIC voltage ADC claims that the 'reg' property consists of two values, the SPMI address and the length of the controller's registers. However the SPMI bus to which it is added specifies "#size-cells = <0>;". Remove the controller register length from the documentation of the field and the example. Signed-off-by: Matthias Kaehlcke Reviewed-by: Douglas Anderson Reviewed-by: Rob Herring Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt index 8498f11d06cd..b3c86f4ac7cd 100644 --- a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt +++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-vadc.txt @@ -17,7 +17,7 @@ VADC node: - reg: Usage: required Value type: - Definition: VADC base address and length in the SPMI PMIC register map. + Definition: VADC base address in the SPMI PMIC register map. - #address-cells: Usage: required @@ -143,7 +143,7 @@ Example: /* VADC node */ pmic_vadc: vadc@3100 { compatible = "qcom,spmi-vadc"; - reg = <0x3100 0x100>; + reg = <0x3100>; interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>; #address-cells = <1>; #size-cells = <0>; From d9e8fd0421c2047ac233141612a433490963d211 Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Tue, 4 Sep 2018 17:11:31 +0300 Subject: [PATCH 65/68] iio: adxl372: Refactor the driver This patch restructures the existing adxl372 driver by adding a module for SPI and a header file, while the baseline module deals with the chip-logic. This is a necessary step, as this driver should support in the future a similar device which differs only in the type of interface used (I2C instead of SPI). Signed-off-by: Stefan Popa Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + drivers/iio/accel/Kconfig | 11 +++-- drivers/iio/accel/Makefile | 1 + drivers/iio/accel/adxl372.c | 73 +++++++++++---------------------- drivers/iio/accel/adxl372.h | 15 +++++++ drivers/iio/accel/adxl372_spi.c | 52 +++++++++++++++++++++++ 6 files changed, 102 insertions(+), 51 deletions(-) create mode 100644 drivers/iio/accel/adxl372.h create mode 100644 drivers/iio/accel/adxl372_spi.c diff --git a/MAINTAINERS b/MAINTAINERS index 2bfc9b02dca4..293863299b50 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -548,6 +548,7 @@ M: Stefan Popa W: http://ez.analog.com/community/linux-device-drivers S: Supported F: drivers/iio/accel/adxl372.c +F: drivers/iio/accel/adxl372_spi.c F: Documentation/devicetree/bindings/iio/accel/adxl372.txt AF9013 MEDIA DRIVER diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index eae23d66ce53..bed5da875eac 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -61,15 +61,20 @@ config ADXL345_SPI for the core module. config ADXL372 - tristate "Analog Devices ADXL372 3-Axis Accelerometer Driver" - depends on SPI + tristate select IIO_BUFFER select IIO_TRIGGERED_BUFFER + +config ADXL372_SPI + tristate "Analog Devices ADXL372 3-Axis Accelerometer SPI Driver" + depends on SPI + select ADXL372 + select REGMAP_SPI help Say yes here to add support for the Analog Devices ADXL372 triaxial acceleration sensor. To compile this driver as a module, choose M here: the - module will be called adxl372. + module will be called adxl372_spi. config BMA180 tristate "Bosch BMA180/BMA250 3-Axis Accelerometer Driver" diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 5758ffc113a6..c9c5db9764bd 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_ADXL345) += adxl345_core.o obj-$(CONFIG_ADXL345_I2C) += adxl345_i2c.o obj-$(CONFIG_ADXL345_SPI) += adxl345_spi.o obj-$(CONFIG_ADXL372) += adxl372.o +obj-$(CONFIG_ADXL372_SPI) += adxl372_spi.o obj-$(CONFIG_BMA180) += bma180.o obj-$(CONFIG_BMA220) += bma220_spi.o obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index fdaaa58ce68f..f1df89d8c6fa 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * ADXL372 3-Axis Digital Accelerometer SPI driver + * ADXL372 3-Axis Digital Accelerometer core driver * * Copyright 2018 Analog Devices Inc. */ @@ -20,6 +20,8 @@ #include #include +#include "adxl372.h" + /* ADXL372 registers definition */ #define ADXL372_DEVID 0x00 #define ADXL372_DEVID_MST 0x01 @@ -246,7 +248,8 @@ static const struct iio_chan_spec adxl372_channels[] = { }; struct adxl372_state { - struct spi_device *spi; + int irq; + struct device *dev; struct regmap *regmap; struct iio_trigger *dready_trig; enum adxl372_fifo_mode fifo_mode; @@ -565,7 +568,7 @@ static int adxl372_setup(struct adxl372_state *st) return ret; if (regval != ADXL372_DEVID_VAL) { - dev_err(&st->spi->dev, "Invalid chip id %x\n", regval); + dev_err(st->dev, "Invalid chip id %x\n", regval); return -ENODEV; } @@ -891,56 +894,45 @@ static const struct iio_info adxl372_info = { .hwfifo_set_watermark = adxl372_set_watermark, }; -static bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg) +bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg) { return (reg == ADXL372_FIFO_DATA); } +EXPORT_SYMBOL_GPL(adxl372_readable_noinc_reg); -static const struct regmap_config adxl372_spi_regmap_config = { - .reg_bits = 7, - .pad_bits = 1, - .val_bits = 8, - .read_flag_mask = BIT(0), - .readable_noinc_reg = adxl372_readable_noinc_reg, -}; - -static int adxl372_probe(struct spi_device *spi) +int adxl372_probe(struct device *dev, struct regmap *regmap, + int irq, const char *name) { struct iio_dev *indio_dev; struct adxl372_state *st; - struct regmap *regmap; int ret; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); - - st->spi = spi; - - regmap = devm_regmap_init_spi(spi, &adxl372_spi_regmap_config); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); + dev_set_drvdata(dev, indio_dev); + st->dev = dev; st->regmap = regmap; + st->irq = irq; indio_dev->channels = adxl372_channels; indio_dev->num_channels = ARRAY_SIZE(adxl372_channels); indio_dev->available_scan_masks = adxl372_channel_masks; - indio_dev->dev.parent = &spi->dev; - indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->dev.parent = dev; + indio_dev->name = name; indio_dev->info = &adxl372_info; indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; ret = adxl372_setup(st); if (ret < 0) { - dev_err(&st->spi->dev, "ADXL372 setup failed\n"); + dev_err(dev, "ADXL372 setup failed\n"); return ret; } - ret = devm_iio_triggered_buffer_setup(&st->spi->dev, + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, adxl372_trigger_handler, &adxl372_buffer_ops); @@ -949,8 +941,8 @@ static int adxl372_probe(struct spi_device *spi) iio_buffer_set_attrs(indio_dev->buffer, adxl372_fifo_attributes); - if (st->spi->irq) { - st->dready_trig = devm_iio_trigger_alloc(&st->spi->dev, + if (st->irq) { + st->dready_trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, indio_dev->id); @@ -958,15 +950,15 @@ static int adxl372_probe(struct spi_device *spi) return -ENOMEM; st->dready_trig->ops = &adxl372_trigger_ops; - st->dready_trig->dev.parent = &st->spi->dev; + st->dready_trig->dev.parent = dev; iio_trigger_set_drvdata(st->dready_trig, indio_dev); - ret = devm_iio_trigger_register(&st->spi->dev, st->dready_trig); + ret = devm_iio_trigger_register(dev, st->dready_trig); if (ret < 0) return ret; indio_dev->trig = iio_trigger_get(st->dready_trig); - ret = devm_request_threaded_irq(&st->spi->dev, st->spi->irq, + ret = devm_request_threaded_irq(dev, st->irq, iio_trigger_generic_data_rdy_poll, NULL, IRQF_TRIGGER_RISING | IRQF_ONESHOT, @@ -975,24 +967,9 @@ static int adxl372_probe(struct spi_device *spi) return ret; } - return devm_iio_device_register(&st->spi->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } - -static const struct spi_device_id adxl372_id[] = { - { "adxl372", 0 }, - {} -}; -MODULE_DEVICE_TABLE(spi, adxl372_id); - -static struct spi_driver adxl372_driver = { - .driver = { - .name = KBUILD_MODNAME, - }, - .probe = adxl372_probe, - .id_table = adxl372_id, -}; - -module_spi_driver(adxl372_driver); +EXPORT_SYMBOL_GPL(adxl372_probe); MODULE_AUTHOR("Stefan Popa "); MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer driver"); diff --git a/drivers/iio/accel/adxl372.h b/drivers/iio/accel/adxl372.h new file mode 100644 index 000000000000..5da89b19c17e --- /dev/null +++ b/drivers/iio/accel/adxl372.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * ADXL372 3-Axis Digital Accelerometer + * + * Copyright 2018 Analog Devices Inc. + */ + +#ifndef _ADXL372_H_ +#define _ADXL372_H_ + +int adxl372_probe(struct device *dev, struct regmap *regmap, + int irq, const char *name); +bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg); + +#endif /* _ADXL372_H_ */ diff --git a/drivers/iio/accel/adxl372_spi.c b/drivers/iio/accel/adxl372_spi.c new file mode 100644 index 000000000000..e14e655ef165 --- /dev/null +++ b/drivers/iio/accel/adxl372_spi.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ADXL372 3-Axis Digital Accelerometer SPI driver + * + * Copyright 2018 Analog Devices Inc. + */ + +#include +#include +#include + +#include "adxl372.h" + +static const struct regmap_config adxl372_spi_regmap_config = { + .reg_bits = 7, + .pad_bits = 1, + .val_bits = 8, + .read_flag_mask = BIT(0), + .readable_noinc_reg = adxl372_readable_noinc_reg, +}; + +static int adxl372_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &adxl372_spi_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return adxl372_probe(&spi->dev, regmap, spi->irq, id->name); +} + +static const struct spi_device_id adxl372_spi_id[] = { + { "adxl372", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, adxl372_spi_id); + +static struct spi_driver adxl372_spi_driver = { + .driver = { + .name = "adxl372_spi", + }, + .probe = adxl372_spi_probe, + .id_table = adxl372_spi_id, +}; + +module_spi_driver(adxl372_spi_driver); + +MODULE_AUTHOR("Stefan Popa "); +MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer SPI driver"); +MODULE_LICENSE("GPL"); From 94dbb46c7a8f0ee27394bc81f34b666dc4a14b71 Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Tue, 4 Sep 2018 17:12:32 +0300 Subject: [PATCH 66/68] iio: adxl372: Add support for I2C communication The adxl372 is designed to communicate in either SPI or I2C protocol. It autodetects the format being used, requiring no configuration control to select the format. Signed-off-by: Stefan Popa Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + drivers/iio/accel/Kconfig | 11 ++++++ drivers/iio/accel/Makefile | 1 + drivers/iio/accel/adxl372.c | 1 - drivers/iio/accel/adxl372.h | 2 ++ drivers/iio/accel/adxl372_i2c.c | 61 +++++++++++++++++++++++++++++++++ 6 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 drivers/iio/accel/adxl372_i2c.c diff --git a/MAINTAINERS b/MAINTAINERS index 293863299b50..2b9a364b043d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -549,6 +549,7 @@ W: http://ez.analog.com/community/linux-device-drivers S: Supported F: drivers/iio/accel/adxl372.c F: drivers/iio/accel/adxl372_spi.c +F: drivers/iio/accel/adxl372_i2c.c F: Documentation/devicetree/bindings/iio/accel/adxl372.txt AF9013 MEDIA DRIVER diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index bed5da875eac..7993a67bd351 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -76,6 +76,17 @@ config ADXL372_SPI To compile this driver as a module, choose M here: the module will be called adxl372_spi. +config ADXL372_I2C + tristate "Analog Devices ADXL372 3-Axis Accelerometer I2C Driver" + depends on I2C + select ADXL372 + select REGMAP_I2C + help + Say yes here to add support for the Analog Devices ADXL372 triaxial + acceleration sensor. + To compile this driver as a module, choose M here: the + module will be called adxl372_i2c. + config BMA180 tristate "Bosch BMA180/BMA250 3-Axis Accelerometer Driver" depends on I2C diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index c9c5db9764bd..56bd0215e0d4 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_ADXL345) += adxl345_core.o obj-$(CONFIG_ADXL345_I2C) += adxl345_i2c.o obj-$(CONFIG_ADXL345_SPI) += adxl345_spi.o obj-$(CONFIG_ADXL372) += adxl372.o +obj-$(CONFIG_ADXL372_I2C) += adxl372_i2c.o obj-$(CONFIG_ADXL372_SPI) += adxl372_spi.o obj-$(CONFIG_BMA180) += bma180.o obj-$(CONFIG_BMA220) += bma220_spi.o diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index f1df89d8c6fa..3b84cb243a87 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -26,7 +26,6 @@ #define ADXL372_DEVID 0x00 #define ADXL372_DEVID_MST 0x01 #define ADXL372_PARTID 0x02 -#define ADXL372_REVID 0x03 #define ADXL372_STATUS_1 0x04 #define ADXL372_STATUS_2 0x05 #define ADXL372_FIFO_ENTRIES_2 0x06 diff --git a/drivers/iio/accel/adxl372.h b/drivers/iio/accel/adxl372.h index 5da89b19c17e..80a0aa9714fc 100644 --- a/drivers/iio/accel/adxl372.h +++ b/drivers/iio/accel/adxl372.h @@ -8,6 +8,8 @@ #ifndef _ADXL372_H_ #define _ADXL372_H_ +#define ADXL372_REVID 0x03 + int adxl372_probe(struct device *dev, struct regmap *regmap, int irq, const char *name); bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg); diff --git a/drivers/iio/accel/adxl372_i2c.c b/drivers/iio/accel/adxl372_i2c.c new file mode 100644 index 000000000000..e1affe480c77 --- /dev/null +++ b/drivers/iio/accel/adxl372_i2c.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ADXL372 3-Axis Digital Accelerometer I2C driver + * + * Copyright 2018 Analog Devices Inc. + */ + +#include +#include +#include + +#include "adxl372.h" + +static const struct regmap_config adxl372_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .readable_noinc_reg = adxl372_readable_noinc_reg, +}; + +static int adxl372_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + unsigned int regval; + int ret; + + regmap = devm_regmap_init_i2c(client, &adxl372_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + ret = regmap_read(regmap, ADXL372_REVID, ®val); + if (ret < 0) + return ret; + + /* Starting with the 3rd revision an I2C chip bug was fixed */ + if (regval < 3) + dev_warn(&client->dev, + "I2C might not work properly with other devices on the bus"); + + return adxl372_probe(&client->dev, regmap, client->irq, id->name); +} + +static const struct i2c_device_id adxl372_i2c_id[] = { + { "adxl372", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, adxl372_i2c_id); + +static struct i2c_driver adxl372_i2c_driver = { + .driver = { + .name = "adxl372_i2c", + }, + .probe = adxl372_i2c_probe, + .id_table = adxl372_i2c_id, +}; + +module_i2c_driver(adxl372_i2c_driver); + +MODULE_AUTHOR("Stefan Popa "); +MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer I2C driver"); +MODULE_LICENSE("GPL"); From c0f87b3335100108ce80cda3e09f11c3a82f081a Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Tue, 4 Sep 2018 17:13:14 +0300 Subject: [PATCH 67/68] dt-bindings: adxl372: Document the adxl372 I2C bindings The adxl372 is designed to communicate in either SPI or I2C protocol. This patch adds the documentation of device tree bindings for adxl372 I2C. Signed-off-by: Stefan Popa Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/accel/adxl372.txt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/accel/adxl372.txt b/Documentation/devicetree/bindings/iio/accel/adxl372.txt index 9409984719e9..a289964756a7 100644 --- a/Documentation/devicetree/bindings/iio/accel/adxl372.txt +++ b/Documentation/devicetree/bindings/iio/accel/adxl372.txt @@ -4,14 +4,25 @@ http://www.analog.com/media/en/technical-documentation/data-sheets/adxl372.pdf Required properties: - compatible : should be "adi,adxl372" - - reg: SPI chip select number for the device + - reg: the I2C address or SPI chip select number for the device + +Required properties for SPI bus usage: - spi-max-frequency: Max SPI frequency to use Optional properties: - interrupts: interrupt mapping for IRQ as documented in Documentation/devicetree/bindings/interrupt-controller/interrupts.txt -Example: +Example for a I2C device node: + + accelerometer@53 { + compatible = "adi,adxl372"; + reg = <0x53>; + interrupt-parent = <&gpio>; + interrupts = <25 IRQ_TYPE_EDGE_FALLING>; + }; + +Example for a SPI device node: accelerometer@0 { compatible = "adi,adxl372"; From daae7861a696b66acb3547c1cb9247cb3abaf0b3 Mon Sep 17 00:00:00 2001 From: Tomasz Duszynski Date: Sun, 2 Sep 2018 15:29:23 +0200 Subject: [PATCH 68/68] iio: light: bh1750: simplify setting PM ops Relying on CONFIG_PM_SLEEP to set PM ops is not necessary since core will handle everything internally. One have to only make sure that functions that can go unused are marked with __maybe_unused. Signed-off-by: Tomasz Duszynski Signed-off-by: Jonathan Cameron --- drivers/iio/light/bh1750.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/iio/light/bh1750.c b/drivers/iio/light/bh1750.c index 493ca7420602..c3a481452b67 100644 --- a/drivers/iio/light/bh1750.c +++ b/drivers/iio/light/bh1750.c @@ -278,8 +278,7 @@ static int bh1750_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP -static int bh1750_suspend(struct device *dev) +static int __maybe_unused bh1750_suspend(struct device *dev) { int ret; struct bh1750_data *data = @@ -297,10 +296,6 @@ static int bh1750_suspend(struct device *dev) } static SIMPLE_DEV_PM_OPS(bh1750_pm_ops, bh1750_suspend, NULL); -#define BH1750_PM_OPS (&bh1750_pm_ops) -#else -#define BH1750_PM_OPS NULL -#endif static const struct i2c_device_id bh1750_id[] = { { "bh1710", BH1710 }, @@ -315,7 +310,7 @@ MODULE_DEVICE_TABLE(i2c, bh1750_id); static struct i2c_driver bh1750_driver = { .driver = { .name = "bh1750", - .pm = BH1750_PM_OPS, + .pm = &bh1750_pm_ops, }, .probe = bh1750_probe, .remove = bh1750_remove,