From 81f517270da632c0ddf4c511a086d5aa7e5606ee Mon Sep 17 00:00:00 2001 From: Andrew F. Davis Date: Sun, 1 May 2016 15:36:54 -0500 Subject: iio: health/afe440x: Always use separate gain values Locking the two gain stages to the same setting adds no value for us, so initialize them as unlocked and remove the sysfs for unlocking them. This also allows us to greatly simplify showing and setting the gain registers. Signed-off-by: Andrew F. Davis Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio-health-afe440x | 9 --------- 1 file changed, 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x b/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x index 3740f253d406..b19053a86054 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x +++ b/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x @@ -8,15 +8,6 @@ Description: Transimpedance Amplifier. Y is 1 for Rf1 and Cf1, Y is 2 for Rf2 and Cf2 values. -What: /sys/bus/iio/devices/iio:deviceX/tia_separate_en -Date: December 2015 -KernelVersion: -Contact: Andrew F. Davis -Description: - Enable or disable separate settings for the TransImpedance - Amplifier above, when disabled both values are set by the - first channel. - What: /sys/bus/iio/devices/iio:deviceX/in_intensity_ledY_raw /sys/bus/iio/devices/iio:deviceX/in_intensity_ledY_ambient_raw Date: December 2015 -- cgit v1.2.3 From 24b9dea764bdf0de8434fb4567e7f62038ba869e Mon Sep 17 00:00:00 2001 From: Andrew F. Davis Date: Sun, 1 May 2016 15:36:58 -0500 Subject: iio: health/afe440x: Remove channel names These AFEs have 4 ADC mesuring stages (called LED2, ALED2, LED1, and ALED1 in the datasheet), we map these as channels, these stages can serve different purposes depending on the application. For instance the AFE4404 has an additional LED (LED3), this LED can be timed to be active during stage 2 (or anystage, but the datasheet describes this case and the name of the stage reflects this use). This ability is used further in upcoming parts that tie the front-end gain and the LED timings together. For these reasons we remove explicit naming the channels. Without channel names it is best that the index numbers are in order to match the stage number, reorder the channel numbers. Signed-off-by: Andrew F. Davis Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-iio-health-afe440x | 39 ++++++++++------------ drivers/iio/health/afe4403.c | 28 ++++++++-------- drivers/iio/health/afe4404.c | 30 ++++++++--------- drivers/iio/health/afe440x.h | 8 ++--- 4 files changed, 51 insertions(+), 54 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x b/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x index b19053a86054..a06707320d84 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x +++ b/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x @@ -8,38 +8,35 @@ Description: Transimpedance Amplifier. Y is 1 for Rf1 and Cf1, Y is 2 for Rf2 and Cf2 values. -What: /sys/bus/iio/devices/iio:deviceX/in_intensity_ledY_raw - /sys/bus/iio/devices/iio:deviceX/in_intensity_ledY_ambient_raw -Date: December 2015 +What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_raw +Date: May 2016 KernelVersion: Contact: Andrew F. Davis Description: Get measured values from the ADC for these stages. Y is the - specific LED number. The values are expressed in 24-bit twos - complement. - -What: /sys/bus/iio/devices/iio:deviceX/in_intensity_ledY-ledY_ambient_raw -Date: December 2015 -KernelVersion: -Contact: Andrew F. Davis -Description: - Get differential values from the ADC for these stages. Y is the - specific LED number. The values are expressed in 24-bit twos - complement for the specified LEDs. + specific stage number corresponding to datasheet stage names + as follows: + 1 -> LED2 + 2 -> ALED2/LED3 + 3 -> LED1 + 4 -> ALED1/LED4 + Note that channels 5 and 6 represent LED2-ALED2 and LED1-ALED1 + respectively which simply helper channels containing the + calculated difference in the value of stage 1 - 2 and 3 - 4. + The values are expressed in 24-bit twos complement. -What: /sys/bus/iio/devices/iio:deviceX/out_current_ledY_offset - /sys/bus/iio/devices/iio:deviceX/out_current_ledY_ambient_offset -Date: December 2015 +What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_offset +Date: May 2016 KernelVersion: Contact: Andrew F. Davis Description: Get and set the offset cancellation DAC setting for these stages. The values are expressed in 5-bit sign-magnitude. -What: /sys/bus/iio/devices/iio:deviceX/out_current_ledY_raw -Date: December 2015 +What: /sys/bus/iio/devices/iio:deviceX/out_currentY_raw +Date: May 2016 KernelVersion: Contact: Andrew F. Davis Description: - Get and set the LED current for the specified LED. Y is the - specific LED number. + Get and set the LED current for the specified LED active during + this stage. Y is the specific stage number. diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index cac60909a585..4a580646f10e 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -121,38 +121,38 @@ struct afe4403_data { }; enum afe4403_chan_id { + LED2 = 1, + ALED2, LED1, ALED1, - LED2, - ALED2, - LED1_ALED1, LED2_ALED2, + LED1_ALED1, ILED1, ILED2, }; static const struct afe440x_reg_info afe4403_reg_info[] = { - [LED1] = AFE440X_REG_INFO(AFE440X_LED1VAL, 0, NULL), - [ALED1] = AFE440X_REG_INFO(AFE440X_ALED1VAL, 0, NULL), [LED2] = AFE440X_REG_INFO(AFE440X_LED2VAL, 0, NULL), [ALED2] = AFE440X_REG_INFO(AFE440X_ALED2VAL, 0, NULL), - [LED1_ALED1] = AFE440X_REG_INFO(AFE440X_LED1_ALED1VAL, 0, NULL), + [LED1] = AFE440X_REG_INFO(AFE440X_LED1VAL, 0, NULL), + [ALED1] = AFE440X_REG_INFO(AFE440X_ALED1VAL, 0, NULL), [LED2_ALED2] = AFE440X_REG_INFO(AFE440X_LED2_ALED2VAL, 0, NULL), + [LED1_ALED1] = AFE440X_REG_INFO(AFE440X_LED1_ALED1VAL, 0, NULL), [ILED1] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE440X_LEDCNTRL_LED1), [ILED2] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE440X_LEDCNTRL_LED2), }; static const struct iio_chan_spec afe4403_channels[] = { /* ADC values */ - AFE440X_INTENSITY_CHAN(LED1, "led1", 0), - AFE440X_INTENSITY_CHAN(ALED1, "led1_ambient", 0), - AFE440X_INTENSITY_CHAN(LED2, "led2", 0), - AFE440X_INTENSITY_CHAN(ALED2, "led2_ambient", 0), - AFE440X_INTENSITY_CHAN(LED1_ALED1, "led1-led1_ambient", 0), - AFE440X_INTENSITY_CHAN(LED2_ALED2, "led2-led2_ambient", 0), + AFE440X_INTENSITY_CHAN(LED2, 0), + AFE440X_INTENSITY_CHAN(ALED2, 0), + AFE440X_INTENSITY_CHAN(LED1, 0), + AFE440X_INTENSITY_CHAN(ALED1, 0), + AFE440X_INTENSITY_CHAN(LED2_ALED2, 0), + AFE440X_INTENSITY_CHAN(LED1_ALED1, 0), /* LED current */ - AFE440X_CURRENT_CHAN(ILED1, "led1"), - AFE440X_CURRENT_CHAN(ILED2, "led2"), + AFE440X_CURRENT_CHAN(ILED1), + AFE440X_CURRENT_CHAN(ILED2), }; static const struct afe440x_val_table afe4403_res_table[] = { diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 2edb7d75ef38..7806a452edf8 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -122,24 +122,24 @@ struct afe4404_data { }; enum afe4404_chan_id { + LED2 = 1, + ALED2, LED1, ALED1, - LED2, - ALED2, - LED1_ALED1, LED2_ALED2, + LED1_ALED1, ILED1, ILED2, ILED3, }; static const struct afe440x_reg_info afe4404_reg_info[] = { - [LED1] = AFE440X_REG_INFO(AFE440X_LED1VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_LED1), - [ALED1] = AFE440X_REG_INFO(AFE440X_ALED1VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_ALED1), [LED2] = AFE440X_REG_INFO(AFE440X_LED2VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_LED2), [ALED2] = AFE440X_REG_INFO(AFE440X_ALED2VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_ALED2), - [LED1_ALED1] = AFE440X_REG_INFO(AFE440X_LED1_ALED1VAL, 0, NULL), + [LED1] = AFE440X_REG_INFO(AFE440X_LED1VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_LED1), + [ALED1] = AFE440X_REG_INFO(AFE440X_ALED1VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_ALED1), [LED2_ALED2] = AFE440X_REG_INFO(AFE440X_LED2_ALED2VAL, 0, NULL), + [LED1_ALED1] = AFE440X_REG_INFO(AFE440X_LED1_ALED1VAL, 0, NULL), [ILED1] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED1), [ILED2] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED2), [ILED3] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED3), @@ -147,16 +147,16 @@ static const struct afe440x_reg_info afe4404_reg_info[] = { static const struct iio_chan_spec afe4404_channels[] = { /* ADC values */ - AFE440X_INTENSITY_CHAN(LED1, "led1", BIT(IIO_CHAN_INFO_OFFSET)), - AFE440X_INTENSITY_CHAN(ALED1, "led1_ambient", BIT(IIO_CHAN_INFO_OFFSET)), - AFE440X_INTENSITY_CHAN(LED2, "led2", BIT(IIO_CHAN_INFO_OFFSET)), - AFE440X_INTENSITY_CHAN(ALED2, "led2_ambient", BIT(IIO_CHAN_INFO_OFFSET)), - AFE440X_INTENSITY_CHAN(LED1_ALED1, "led1-led1_ambient", 0), - AFE440X_INTENSITY_CHAN(LED2_ALED2, "led2-led2_ambient", 0), + AFE440X_INTENSITY_CHAN(LED2, BIT(IIO_CHAN_INFO_OFFSET)), + AFE440X_INTENSITY_CHAN(ALED2, BIT(IIO_CHAN_INFO_OFFSET)), + AFE440X_INTENSITY_CHAN(LED1, BIT(IIO_CHAN_INFO_OFFSET)), + AFE440X_INTENSITY_CHAN(ALED1, BIT(IIO_CHAN_INFO_OFFSET)), + AFE440X_INTENSITY_CHAN(LED2_ALED2, 0), + AFE440X_INTENSITY_CHAN(LED1_ALED1, 0), /* LED current */ - AFE440X_CURRENT_CHAN(ILED1, "led1"), - AFE440X_CURRENT_CHAN(ILED2, "led2"), - AFE440X_CURRENT_CHAN(ILED3, "led3"), + AFE440X_CURRENT_CHAN(ILED1), + AFE440X_CURRENT_CHAN(ILED2), + AFE440X_CURRENT_CHAN(ILED3), }; static const struct afe440x_val_table afe4404_res_table[] = { diff --git a/drivers/iio/health/afe440x.h b/drivers/iio/health/afe440x.h index 583d071af11d..713972fd4601 100644 --- a/drivers/iio/health/afe440x.h +++ b/drivers/iio/health/afe440x.h @@ -103,7 +103,7 @@ struct afe440x_reg_info { .mask = _sm ## _MASK, \ } -#define AFE440X_INTENSITY_CHAN(_index, _name, _mask) \ +#define AFE440X_INTENSITY_CHAN(_index, _mask) \ { \ .type = IIO_INTENSITY, \ .channel = _index, \ @@ -115,20 +115,20 @@ struct afe440x_reg_info { .storagebits = 32, \ .endianness = IIO_CPU, \ }, \ - .extend_name = _name, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ _mask, \ + .indexed = true, \ } -#define AFE440X_CURRENT_CHAN(_index, _name) \ +#define AFE440X_CURRENT_CHAN(_index) \ { \ .type = IIO_CURRENT, \ .channel = _index, \ .address = _index, \ .scan_index = -1, \ - .extend_name = _name, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = true, \ .output = true, \ } -- cgit v1.2.3 From 1276187c5261217aa9cc23ec153e0e903181c16b Mon Sep 17 00:00:00 2001 From: Andrew F. Davis Date: Sun, 1 May 2016 15:37:00 -0500 Subject: iio: health/afe440x: Make gain settings a modifier for the stages Currently the TIA gain settings are exported to userspace as sysfs entries that do not clearly represent their internal relation to the sampling stages. The gain settings are enabled on a per-stage basis, this can be seen in figure 24 of the current AFE4404 datasheet. These gain settings should therefore be tied to the channels that are read during these stages. Make this change here. Signed-off-by: Andrew F. Davis Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-iio-health-afe440x | 19 ++++++------ drivers/iio/health/afe4403.c | 34 ++++++++++++++-------- drivers/iio/health/afe4404.c | 34 ++++++++++++++-------- 3 files changed, 53 insertions(+), 34 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x b/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x index a06707320d84..6adba9058b22 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x +++ b/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x @@ -1,13 +1,3 @@ -What: /sys/bus/iio/devices/iio:deviceX/tia_resistanceY - /sys/bus/iio/devices/iio:deviceX/tia_capacitanceY -Date: December 2015 -KernelVersion: -Contact: Andrew F. Davis -Description: - Get and set the resistance and the capacitance settings for the - Transimpedance Amplifier. Y is 1 for Rf1 and Cf1, Y is 2 for - Rf2 and Cf2 values. - What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_raw Date: May 2016 KernelVersion: @@ -33,6 +23,15 @@ Description: Get and set the offset cancellation DAC setting for these stages. The values are expressed in 5-bit sign-magnitude. +What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_resistance +What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_capacitance +Date: May 2016 +KernelVersion: +Contact: Andrew F. Davis +Description: + Get and set the resistance and the capacitance settings for the + Transimpedance Amplifier during the associated stage. + What: /sys/bus/iio/devices/iio:deviceX/out_currentY_raw Date: May 2016 KernelVersion: diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index 19501550c6ff..610631b036ec 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -161,7 +161,7 @@ static const struct afe440x_val_table afe4403_res_table[] = { { 500000 }, { 250000 }, { 100000 }, { 50000 }, { 25000 }, { 10000 }, { 1000000 }, { 0 }, }; -AFE440X_TABLE_ATTR(tia_resistance_available, afe4403_res_table); +AFE440X_TABLE_ATTR(in_intensity_resistance_available, afe4403_res_table); static const struct afe440x_val_table afe4403_cap_table[] = { { 0, 5000 }, { 0, 10000 }, { 0, 20000 }, { 0, 25000 }, @@ -173,7 +173,7 @@ static const struct afe440x_val_table afe4403_cap_table[] = { { 0, 205000 }, { 0, 210000 }, { 0, 220000 }, { 0, 225000 }, { 0, 230000 }, { 0, 235000 }, { 0, 245000 }, { 0, 250000 }, }; -AFE440X_TABLE_ATTR(tia_capacitance_available, afe4403_cap_table); +AFE440X_TABLE_ATTR(in_intensity_capacitance_available, afe4403_cap_table); static ssize_t afe440x_show_register(struct device *dev, struct device_attribute *attr, @@ -226,19 +226,29 @@ static ssize_t afe440x_store_register(struct device *dev, return count; } -static AFE440X_ATTR(tia_resistance1, F_RF_LED1, afe4403_res_table); -static AFE440X_ATTR(tia_capacitance1, F_CF_LED1, afe4403_cap_table); +static AFE440X_ATTR(in_intensity1_resistance, F_RF_LED, afe4403_res_table); +static AFE440X_ATTR(in_intensity1_capacitance, F_CF_LED, afe4403_cap_table); -static AFE440X_ATTR(tia_resistance2, F_RF_LED, afe4403_res_table); -static AFE440X_ATTR(tia_capacitance2, F_CF_LED, afe4403_cap_table); +static AFE440X_ATTR(in_intensity2_resistance, F_RF_LED, afe4403_res_table); +static AFE440X_ATTR(in_intensity2_capacitance, F_CF_LED, afe4403_cap_table); + +static AFE440X_ATTR(in_intensity3_resistance, F_RF_LED1, afe4403_res_table); +static AFE440X_ATTR(in_intensity3_capacitance, F_CF_LED1, afe4403_cap_table); + +static AFE440X_ATTR(in_intensity4_resistance, F_RF_LED1, afe4403_res_table); +static AFE440X_ATTR(in_intensity4_capacitance, F_CF_LED1, afe4403_cap_table); static struct attribute *afe440x_attributes[] = { - &afe440x_attr_tia_resistance1.dev_attr.attr, - &afe440x_attr_tia_capacitance1.dev_attr.attr, - &afe440x_attr_tia_resistance2.dev_attr.attr, - &afe440x_attr_tia_capacitance2.dev_attr.attr, - &dev_attr_tia_resistance_available.attr, - &dev_attr_tia_capacitance_available.attr, + &dev_attr_in_intensity_resistance_available.attr, + &dev_attr_in_intensity_capacitance_available.attr, + &afe440x_attr_in_intensity1_resistance.dev_attr.attr, + &afe440x_attr_in_intensity1_capacitance.dev_attr.attr, + &afe440x_attr_in_intensity2_resistance.dev_attr.attr, + &afe440x_attr_in_intensity2_capacitance.dev_attr.attr, + &afe440x_attr_in_intensity3_resistance.dev_attr.attr, + &afe440x_attr_in_intensity3_capacitance.dev_attr.attr, + &afe440x_attr_in_intensity4_resistance.dev_attr.attr, + &afe440x_attr_in_intensity4_capacitance.dev_attr.attr, NULL }; diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 0d1af4a4a903..69116cdd9d92 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -179,7 +179,7 @@ static const struct afe440x_val_table afe4404_res_table[] = { { .integer = 1000000, .fract = 0 }, { .integer = 2000000, .fract = 0 }, }; -AFE440X_TABLE_ATTR(tia_resistance_available, afe4404_res_table); +AFE440X_TABLE_ATTR(in_intensity_resistance_available, afe4404_res_table); static const struct afe440x_val_table afe4404_cap_table[] = { { .integer = 0, .fract = 5000 }, @@ -191,7 +191,7 @@ static const struct afe440x_val_table afe4404_cap_table[] = { { .integer = 0, .fract = 25000 }, { .integer = 0, .fract = 22500 }, }; -AFE440X_TABLE_ATTR(tia_capacitance_available, afe4404_cap_table); +AFE440X_TABLE_ATTR(in_intensity_capacitance_available, afe4404_cap_table); static ssize_t afe440x_show_register(struct device *dev, struct device_attribute *attr, @@ -244,19 +244,29 @@ static ssize_t afe440x_store_register(struct device *dev, return count; } -static AFE440X_ATTR(tia_resistance1, F_TIA_GAIN, afe4404_res_table); -static AFE440X_ATTR(tia_capacitance1, TIA_CF, afe4404_cap_table); +static AFE440X_ATTR(in_intensity1_resistance, F_TIA_GAIN_SEP, afe4404_res_table); +static AFE440X_ATTR(in_intensity1_capacitance, F_TIA_CF_SEP, afe4404_cap_table); -static AFE440X_ATTR(tia_resistance2, F_TIA_GAIN_SEP, afe4404_res_table); -static AFE440X_ATTR(tia_capacitance2, F_TIA_CF_SEP, afe4404_cap_table); +static AFE440X_ATTR(in_intensity2_resistance, F_TIA_GAIN_SEP, afe4404_res_table); +static AFE440X_ATTR(in_intensity2_capacitance, F_TIA_CF_SEP, afe4404_cap_table); + +static AFE440X_ATTR(in_intensity3_resistance, F_TIA_GAIN, afe4404_res_table); +static AFE440X_ATTR(in_intensity3_capacitance, TIA_CF, afe4404_cap_table); + +static AFE440X_ATTR(in_intensity4_resistance, F_TIA_GAIN, afe4404_res_table); +static AFE440X_ATTR(in_intensity4_capacitance, TIA_CF, afe4404_cap_table); static struct attribute *afe440x_attributes[] = { - &afe440x_attr_tia_resistance1.dev_attr.attr, - &afe440x_attr_tia_capacitance1.dev_attr.attr, - &afe440x_attr_tia_resistance2.dev_attr.attr, - &afe440x_attr_tia_capacitance2.dev_attr.attr, - &dev_attr_tia_resistance_available.attr, - &dev_attr_tia_capacitance_available.attr, + &dev_attr_in_intensity_resistance_available.attr, + &dev_attr_in_intensity_capacitance_available.attr, + &afe440x_attr_in_intensity1_resistance.dev_attr.attr, + &afe440x_attr_in_intensity1_capacitance.dev_attr.attr, + &afe440x_attr_in_intensity2_resistance.dev_attr.attr, + &afe440x_attr_in_intensity2_capacitance.dev_attr.attr, + &afe440x_attr_in_intensity3_resistance.dev_attr.attr, + &afe440x_attr_in_intensity3_capacitance.dev_attr.attr, + &afe440x_attr_in_intensity4_resistance.dev_attr.attr, + &afe440x_attr_in_intensity4_capacitance.dev_attr.attr, NULL }; -- cgit v1.2.3 From 6994aea7842d824a8cc38c950ddcab3f8a75c278 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Mon, 25 Apr 2016 16:15:53 +0300 Subject: Documentation: iio: Add IIO software devices docs Signed-off-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/configfs-iio | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/configfs-iio b/Documentation/ABI/testing/configfs-iio index 2483756fccf5..aebda53ec0f7 100644 --- a/Documentation/ABI/testing/configfs-iio +++ b/Documentation/ABI/testing/configfs-iio @@ -19,3 +19,16 @@ KernelVersion: 4.4 Description: High resolution timers directory. Creating a directory here will result in creating a hrtimer trigger in the IIO subsystem. + +What: /config/iio/devices +Date: April 2016 +KernelVersion: 4.7 +Description: + Industrial IO software devices directory. + +What: /config/iio/devices/dummy +Date: April 2016 +KernelVersion: 4.7 +Description: + Dummy IIO devices directory. Creating a directory here will result + in creating a dummy IIO device in the IIO subystem. -- cgit v1.2.3 From e039e2f5b4dab9a90bb5441a154c01a051b1abfa Mon Sep 17 00:00:00 2001 From: Gregor Boirie Date: Tue, 19 Apr 2016 11:18:32 +0200 Subject: iio:st_pressure:initial lps22hb sensor support Initial support for ST LPS22HB pressure sensor. Datasheet: http://www2.st.com/resource/en/datasheet/lps22hb.pdf Features: * pressure data and timestamping channels * sampling frequency selection * interrupt based trigger * over I2C or SPI Signed-off-by: Gregor Boirie Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/st-sensors.txt | 1 + drivers/iio/pressure/Kconfig | 2 +- drivers/iio/pressure/st_pressure.h | 1 + drivers/iio/pressure/st_pressure_core.c | 93 +++++++++++++++++++++- drivers/iio/pressure/st_pressure_i2c.c | 4 + drivers/iio/pressure/st_pressure_spi.c | 1 + 6 files changed, 97 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/st-sensors.txt b/Documentation/devicetree/bindings/iio/st-sensors.txt index 5844cf72862d..e41fe340162b 100644 --- a/Documentation/devicetree/bindings/iio/st-sensors.txt +++ b/Documentation/devicetree/bindings/iio/st-sensors.txt @@ -64,3 +64,4 @@ Pressure sensors: - st,lps001wp-press - st,lps25h-press - st,lps331ap-press +- st,lps22hb-press diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index 9125a9382b3e..8d654f671b6b 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -131,7 +131,7 @@ config IIO_ST_PRESS select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) help Say yes here to build support for STMicroelectronics pressure - sensors: LPS001WP, LPS25H, LPS331AP. + sensors: LPS001WP, LPS25H, LPS331AP, LPS22HB. This driver can also be built as a module. If so, these modules will be created: diff --git a/drivers/iio/pressure/st_pressure.h b/drivers/iio/pressure/st_pressure.h index f5f41490060b..903a21e46874 100644 --- a/drivers/iio/pressure/st_pressure.h +++ b/drivers/iio/pressure/st_pressure.h @@ -17,6 +17,7 @@ #define LPS001WP_PRESS_DEV_NAME "lps001wp" #define LPS25H_PRESS_DEV_NAME "lps25h" #define LPS331AP_PRESS_DEV_NAME "lps331ap" +#define LPS22HB_PRESS_DEV_NAME "lps22hb" /** * struct st_sensors_platform_data - default press platform data diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index 9e9b72a8f18f..c0ff3bf071d8 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -113,6 +113,26 @@ #define ST_PRESS_LPS25H_OUT_XL_ADDR 0x28 #define ST_TEMP_LPS25H_OUT_L_ADDR 0x2b +/* CUSTOM VALUES FOR LPS22HB SENSOR */ +#define ST_PRESS_LPS22HB_WAI_EXP 0xb1 +#define ST_PRESS_LPS22HB_ODR_ADDR 0x10 +#define ST_PRESS_LPS22HB_ODR_MASK 0x70 +#define ST_PRESS_LPS22HB_ODR_AVL_1HZ_VAL 0x01 +#define ST_PRESS_LPS22HB_ODR_AVL_10HZ_VAL 0x02 +#define ST_PRESS_LPS22HB_ODR_AVL_25HZ_VAL 0x03 +#define ST_PRESS_LPS22HB_ODR_AVL_50HZ_VAL 0x04 +#define ST_PRESS_LPS22HB_ODR_AVL_75HZ_VAL 0x05 +#define ST_PRESS_LPS22HB_PW_ADDR 0x10 +#define ST_PRESS_LPS22HB_PW_MASK 0x70 +#define ST_PRESS_LPS22HB_BDU_ADDR 0x10 +#define ST_PRESS_LPS22HB_BDU_MASK 0x02 +#define ST_PRESS_LPS22HB_DRDY_IRQ_ADDR 0x12 +#define ST_PRESS_LPS22HB_DRDY_IRQ_INT1_MASK 0x04 +#define ST_PRESS_LPS22HB_DRDY_IRQ_INT2_MASK 0x08 +#define ST_PRESS_LPS22HB_IHL_IRQ_ADDR 0x12 +#define ST_PRESS_LPS22HB_IHL_IRQ_MASK 0x80 +#define ST_PRESS_LPS22HB_MULTIREAD_BIT true + static const struct iio_chan_spec st_press_1_channels[] = { { .type = IIO_PRESSURE, @@ -183,6 +203,27 @@ static const struct iio_chan_spec st_press_lps001wp_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(1) }; +static const struct iio_chan_spec st_press_lps22hb_channels[] = { + { + .type = IIO_PRESSURE, + .channel2 = IIO_NO_MOD, + .address = ST_PRESS_1_OUT_XL_ADDR, + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 24, + .endianness = IIO_LE, + }, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .modified = 0, + }, + IIO_CHAN_SOFT_TIMESTAMP(1) +}; + static const struct st_sensor_settings st_press_sensors_settings[] = { { .wai = ST_PRESS_LPS331AP_WAI_EXP, @@ -326,6 +367,51 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .multi_read_bit = ST_PRESS_LPS25H_MULTIREAD_BIT, .bootime = 2, }, + { + .wai = ST_PRESS_LPS22HB_WAI_EXP, + .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, + .sensors_supported = { + [0] = LPS22HB_PRESS_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_press_lps22hb_channels, + .num_ch = ARRAY_SIZE(st_press_lps22hb_channels), + .odr = { + .addr = ST_PRESS_LPS22HB_ODR_ADDR, + .mask = ST_PRESS_LPS22HB_ODR_MASK, + .odr_avl = { + { 1, ST_PRESS_LPS22HB_ODR_AVL_1HZ_VAL, }, + { 10, ST_PRESS_LPS22HB_ODR_AVL_10HZ_VAL, }, + { 25, ST_PRESS_LPS22HB_ODR_AVL_25HZ_VAL, }, + { 50, ST_PRESS_LPS22HB_ODR_AVL_50HZ_VAL, }, + { 75, ST_PRESS_LPS22HB_ODR_AVL_75HZ_VAL, }, + }, + }, + .pw = { + .addr = ST_PRESS_LPS22HB_PW_ADDR, + .mask = ST_PRESS_LPS22HB_PW_MASK, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .fs = { + .fs_avl = { + [0] = { + .num = ST_PRESS_FS_AVL_1260MB, + .gain = ST_PRESS_KPASCAL_NANO_SCALE, + }, + }, + }, + .bdu = { + .addr = ST_PRESS_LPS22HB_BDU_ADDR, + .mask = ST_PRESS_LPS22HB_BDU_MASK, + }, + .drdy_irq = { + .addr = ST_PRESS_LPS22HB_DRDY_IRQ_ADDR, + .mask_int1 = ST_PRESS_LPS22HB_DRDY_IRQ_INT1_MASK, + .mask_int2 = ST_PRESS_LPS22HB_DRDY_IRQ_INT2_MASK, + .addr_ihl = ST_PRESS_LPS22HB_IHL_IRQ_ADDR, + .mask_ihl = ST_PRESS_LPS22HB_IHL_IRQ_MASK, + }, + .multi_read_bit = ST_PRESS_LPS22HB_MULTIREAD_BIT, + }, }; static int st_press_write_raw(struct iio_dev *indio_dev, @@ -454,10 +540,9 @@ int st_press_common_probe(struct iio_dev *indio_dev) indio_dev->channels = press_data->sensor_settings->ch; indio_dev->num_channels = press_data->sensor_settings->num_ch; - if (press_data->sensor_settings->fs.addr != 0) - press_data->current_fullscale = - (struct st_sensor_fullscale_avl *) - &press_data->sensor_settings->fs.fs_avl[0]; + press_data->current_fullscale = + (struct st_sensor_fullscale_avl *) + &press_data->sensor_settings->fs.fs_avl[0]; press_data->odr = press_data->sensor_settings->odr.odr_avl[0].hz; diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index 8fcf9766eaec..ed18701c68c9 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -32,6 +32,10 @@ static const struct of_device_id st_press_of_match[] = { .compatible = "st,lps331ap-press", .data = LPS331AP_PRESS_DEV_NAME, }, + { + .compatible = "st,lps22hb-press", + .data = LPS22HB_PRESS_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_press_of_match); diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index 40c0692ff1de..550508025af1 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -50,6 +50,7 @@ static const struct spi_device_id st_press_id_table[] = { { LPS001WP_PRESS_DEV_NAME }, { LPS25H_PRESS_DEV_NAME }, { LPS331AP_PRESS_DEV_NAME }, + { LPS22HB_PRESS_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(spi, st_press_id_table); -- cgit v1.2.3 From 4b9d2090a444d44f16788b9ad60180011d133f97 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Tue, 24 May 2016 21:29:19 -0700 Subject: iio: electricalconductivity: add IIO_ELECTRICALCONDUCTIVITY type Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 7 +++++++ drivers/iio/industrialio-core.c | 1 + include/uapi/linux/iio/types.h | 1 + 3 files changed, 9 insertions(+) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index df44998e7506..e7f590c6ef8e 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1565,3 +1565,10 @@ Description: * X is in the plane of the propellers, perpendicular to Y axis, and positive towards the starboard side of the UAV ; * Z is perpendicular to propellers plane and positive upwards. + +What: /sys/bus/iio/devices/iio:deviceX/in_electricalconductivity_raw +KernelVersion: 4.8 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled no offset etc.) electric conductivity reading that + can be processed to siemens per meter. diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index e6319a9346b2..2a85bd888619 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -80,6 +80,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_RESISTANCE] = "resistance", [IIO_PH] = "ph", [IIO_UVINDEX] = "uvindex", + [IIO_ELECTRICALCONDUCTIVITY] = "electricalconductivity", }; static const char * const iio_modifier_names[] = { diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index b0916fc72cce..22e5e589a274 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -39,6 +39,7 @@ enum iio_chan_type { IIO_RESISTANCE, IIO_PH, IIO_UVINDEX, + IIO_ELECTRICALCONDUCTIVITY, }; enum iio_modifier { -- cgit v1.2.3 From e8dd92bfbff2516f3e76bf08f38131c522454edd Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Tue, 24 May 2016 21:29:20 -0700 Subject: iio: chemical: atlas-ph-sensor: add EC feature Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- .../bindings/iio/chemical/atlas,ec-sm.txt | 22 ++++ drivers/iio/chemical/Kconfig | 8 +- drivers/iio/chemical/atlas-ph-sensor.c | 122 ++++++++++++++++++++- 3 files changed, 147 insertions(+), 5 deletions(-) create mode 100644 Documentation/devicetree/bindings/iio/chemical/atlas,ec-sm.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/chemical/atlas,ec-sm.txt b/Documentation/devicetree/bindings/iio/chemical/atlas,ec-sm.txt new file mode 100644 index 000000000000..2962bd9a2b3d --- /dev/null +++ b/Documentation/devicetree/bindings/iio/chemical/atlas,ec-sm.txt @@ -0,0 +1,22 @@ +* Atlas Scientific EC-SM OEM sensor + +http://www.atlas-scientific.com/_files/_datasheets/_oem/EC_oem_datasheet.pdf + +Required properties: + + - compatible: must be "atlas,ec-sm" + - reg: the I2C address of the sensor + - interrupt-parent: should be the phandle for the interrupt controller + - interrupts: the sole interrupt generated by the device + + Refer to interrupt-controller/interrupts.txt for generic interrupt client + node bindings. + +Example: + +atlas@64 { + compatible = "atlas,ec-sm"; + reg = <0x64>; + interrupt-parent = <&gpio1>; + interrupts = <16 2>; +}; diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig index f73290f84c90..4bcc025e8c8a 100644 --- a/drivers/iio/chemical/Kconfig +++ b/drivers/iio/chemical/Kconfig @@ -5,15 +5,17 @@ menu "Chemical Sensors" config ATLAS_PH_SENSOR - tristate "Atlas Scientific OEM pH-SM sensor" + tristate "Atlas Scientific OEM SM sensors" depends on I2C select REGMAP_I2C select IIO_BUFFER select IIO_TRIGGERED_BUFFER select IRQ_WORK help - Say Y here to build I2C interface support for the Atlas - Scientific OEM pH-SM sensor. + Say Y here to build I2C interface support for the following + Atlas Scientific OEM SM sensors: + * pH SM sensor + * EC SM sensor To compile this driver as module, choose M here: the module will be called atlas-ph-sensor. diff --git a/drivers/iio/chemical/atlas-ph-sensor.c b/drivers/iio/chemical/atlas-ph-sensor.c index e85477cb16fd..02e85db5d31f 100644 --- a/drivers/iio/chemical/atlas-ph-sensor.c +++ b/drivers/iio/chemical/atlas-ph-sensor.c @@ -50,13 +50,28 @@ #define ATLAS_REG_PH_CALIB_STATUS_MID BIT(1) #define ATLAS_REG_PH_CALIB_STATUS_HIGH BIT(2) +#define ATLAS_REG_EC_CALIB_STATUS 0x0f +#define ATLAS_REG_EC_CALIB_STATUS_MASK 0x0f +#define ATLAS_REG_EC_CALIB_STATUS_DRY BIT(0) +#define ATLAS_REG_EC_CALIB_STATUS_SINGLE BIT(1) +#define ATLAS_REG_EC_CALIB_STATUS_LOW BIT(2) +#define ATLAS_REG_EC_CALIB_STATUS_HIGH BIT(3) + #define ATLAS_REG_PH_TEMP_DATA 0x0e #define ATLAS_REG_PH_DATA 0x16 +#define ATLAS_REG_EC_PROBE 0x08 +#define ATLAS_REG_EC_TEMP_DATA 0x10 +#define ATLAS_REG_EC_DATA 0x18 +#define ATLAS_REG_TDS_DATA 0x1c +#define ATLAS_REG_PSS_DATA 0x20 + #define ATLAS_PH_INT_TIME_IN_US 450000 +#define ATLAS_EC_INT_TIME_IN_US 650000 enum { ATLAS_PH_SM, + ATLAS_EC_SM, }; struct atlas_data { @@ -66,12 +81,13 @@ struct atlas_data { struct regmap *regmap; struct irq_work work; - __be32 buffer[4]; /* 32-bit pH data + 32-bit pad + 64-bit timestamp */ + __be32 buffer[6]; /* 96-bit data + 32-bit pad + 64-bit timestamp */ }; static const struct regmap_range atlas_volatile_ranges[] = { regmap_reg_range(ATLAS_REG_INT_CONTROL, ATLAS_REG_INT_CONTROL), regmap_reg_range(ATLAS_REG_PH_DATA, ATLAS_REG_PH_DATA + 4), + regmap_reg_range(ATLAS_REG_EC_DATA, ATLAS_REG_PSS_DATA + 4), }; static const struct regmap_access_table atlas_volatile_table = { @@ -86,7 +102,7 @@ static const struct regmap_config atlas_regmap_config = { .val_bits = 8, .volatile_table = &atlas_volatile_table, - .max_register = ATLAS_REG_PH_DATA + 4, + .max_register = ATLAS_REG_PSS_DATA + 4, .cache_type = REGCACHE_RBTREE, }; @@ -115,6 +131,50 @@ static const struct iio_chan_spec atlas_ph_channels[] = { }, }; +#define ATLAS_EC_CHANNEL(_idx, _addr) \ + {\ + .type = IIO_CONCENTRATION, \ + .indexed = 1, \ + .channel = _idx, \ + .address = _addr, \ + .info_mask_separate = \ + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = _idx + 1, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 32, \ + .storagebits = 32, \ + .endianness = IIO_BE, \ + }, \ + } + +static const struct iio_chan_spec atlas_ec_channels[] = { + { + .type = IIO_ELECTRICALCONDUCTIVITY, + .address = ATLAS_REG_EC_DATA, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_BE, + }, + }, + ATLAS_EC_CHANNEL(0, ATLAS_REG_TDS_DATA), + ATLAS_EC_CHANNEL(1, ATLAS_REG_PSS_DATA), + IIO_CHAN_SOFT_TIMESTAMP(3), + { + .type = IIO_TEMP, + .address = ATLAS_REG_EC_TEMP_DATA, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .output = 1, + .scan_index = -1 + }, +}; + static int atlas_check_ph_calibration(struct atlas_data *data) { struct device *dev = &data->client->dev; @@ -142,6 +202,44 @@ static int atlas_check_ph_calibration(struct atlas_data *data) return 0; } +static int atlas_check_ec_calibration(struct atlas_data *data) +{ + struct device *dev = &data->client->dev; + int ret; + unsigned int val; + + ret = regmap_bulk_read(data->regmap, ATLAS_REG_EC_PROBE, &val, 2); + if (ret) + return ret; + + dev_info(dev, "probe set to K = %d.%.2d", be16_to_cpu(val) / 100, + be16_to_cpu(val) % 100); + + ret = regmap_read(data->regmap, ATLAS_REG_EC_CALIB_STATUS, &val); + if (ret) + return ret; + + if (!(val & ATLAS_REG_EC_CALIB_STATUS_MASK)) { + dev_warn(dev, "device has not been calibrated\n"); + return 0; + } + + if (!(val & ATLAS_REG_EC_CALIB_STATUS_DRY)) + dev_warn(dev, "device missing dry point calibration\n"); + + if (val & ATLAS_REG_EC_CALIB_STATUS_SINGLE) { + dev_warn(dev, "device using single point calibration\n"); + } else { + if (!(val & ATLAS_REG_EC_CALIB_STATUS_LOW)) + dev_warn(dev, "device missing low point calibration\n"); + + if (!(val & ATLAS_REG_EC_CALIB_STATUS_HIGH)) + dev_warn(dev, "device missing high point calibration\n"); + } + + return 0; +} + struct atlas_device { const struct iio_chan_spec *channels; int num_channels; @@ -159,6 +257,14 @@ static struct atlas_device atlas_devices[] = { .calibration = &atlas_check_ph_calibration, .delay = ATLAS_PH_INT_TIME_IN_US, }, + [ATLAS_EC_SM] = { + .channels = atlas_ec_channels, + .num_channels = 5, + .data_reg = ATLAS_REG_EC_DATA, + .calibration = &atlas_check_ec_calibration, + .delay = ATLAS_EC_INT_TIME_IN_US, + }, + }; static int atlas_set_powermode(struct atlas_data *data, int on) @@ -294,6 +400,8 @@ static int atlas_read_raw(struct iio_dev *indio_dev, (u8 *) ®, sizeof(reg)); break; case IIO_PH: + case IIO_CONCENTRATION: + case IIO_ELECTRICALCONDUCTIVITY: mutex_lock(&indio_dev->mlock); if (iio_buffer_enabled(indio_dev)) @@ -324,6 +432,14 @@ static int atlas_read_raw(struct iio_dev *indio_dev, *val = 1; /* 0.001 */ *val2 = 1000; break; + case IIO_ELECTRICALCONDUCTIVITY: + *val = 1; /* 0.00001 */ + *val = 100000; + break; + case IIO_CONCENTRATION: + *val = 0; /* 0.000000001 */ + *val2 = 1000; + return IIO_VAL_INT_PLUS_NANO; default: return -EINVAL; } @@ -358,12 +474,14 @@ static const struct iio_info atlas_info = { static const struct i2c_device_id atlas_id[] = { { "atlas-ph-sm", ATLAS_PH_SM}, + { "atlas-ec-sm", ATLAS_EC_SM}, {} }; MODULE_DEVICE_TABLE(i2c, atlas_id); static const struct of_device_id atlas_dt_ids[] = { { .compatible = "atlas,ph-sm", .data = (void *)ATLAS_PH_SM, }, + { .compatible = "atlas,ec-sm", .data = (void *)ATLAS_EC_SM, }, { } }; MODULE_DEVICE_TABLE(of, atlas_dt_ids); -- cgit v1.2.3 From 7150bc9b4d43471fa37b26f5839892d4cf1fe09b Mon Sep 17 00:00:00 2001 From: Wenyou Yang Date: Wed, 8 Jun 2016 12:15:10 +0800 Subject: usb: ohci-at91: Forcibly suspend ports while USB suspend In order to the save power consumption, as a workaround, suspend forcibly the USB PORTA/B/C via set the SUSPEND_A/B/C bits of OHCI Interrupt Configuration Register in the SFRs while OHCI USB suspend. This suspend operation must be done before the USB clock is disabled, resume after the USB clock is enabled. Signed-off-by: Wenyou Yang Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/atmel-usb.txt | 6 +- drivers/usb/host/ohci-at91.c | 80 +++++++++++++++++++++- include/soc/at91/at91_sfr.h | 29 ++++++++ 3 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 include/soc/at91/at91_sfr.h (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/atmel-usb.txt b/Documentation/devicetree/bindings/usb/atmel-usb.txt index 5883b73ea1b5..888deaa8a4ca 100644 --- a/Documentation/devicetree/bindings/usb/atmel-usb.txt +++ b/Documentation/devicetree/bindings/usb/atmel-usb.txt @@ -3,8 +3,10 @@ Atmel SOC USB controllers OHCI Required properties: - - compatible: Should be "atmel,at91rm9200-ohci" for USB controllers - used in host mode. + - compatible: Should be one of the following + "atmel,at91rm9200-ohci" for USB controllers used in host mode. + "atmel,sama5d2-ohci" for USB controllers used in host mode + on SAMA5D2 which can force to suspend. - reg: Address and length of the register set for the device - interrupts: Should contain ehci interrupt - clocks: Should reference the peripheral, host and system clocks diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index d177372bb357..54e8feb8a964 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -21,8 +21,11 @@ #include #include #include +#include +#include #include #include +#include #include "ohci.h" @@ -45,12 +48,18 @@ struct at91_usbh_data { u8 overcurrent_changed[AT91_MAX_USBH_PORTS]; }; +struct ohci_at91_caps { + bool suspend_ctrl; +}; + struct ohci_at91_priv { struct clk *iclk; struct clk *fclk; struct clk *hclk; bool clocked; bool wakeup; /* Saved wake-up state for resume */ + const struct ohci_at91_caps *caps; + struct regmap *sfr_regmap; }; /* interface and function clocks; sometimes also an AHB clock */ @@ -132,6 +141,17 @@ static void at91_stop_hc(struct platform_device *pdev) /*-------------------------------------------------------------------------*/ +struct regmap *at91_dt_syscon_sfr(void) +{ + struct regmap *regmap; + + regmap = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr"); + if (IS_ERR(regmap)) + regmap = NULL; + + return regmap; +} + static void usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *); /* configure so an HC device and id are always provided */ @@ -197,6 +217,17 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, goto err; } + ohci_at91->caps = (const struct ohci_at91_caps *) + of_device_get_match_data(&pdev->dev); + if (!ohci_at91->caps) + return -ENODEV; + + if (ohci_at91->caps->suspend_ctrl) { + ohci_at91->sfr_regmap = at91_dt_syscon_sfr(); + if (!ohci_at91->sfr_regmap) + dev_warn(dev, "failed to find sfr node\n"); + } + board = hcd->self.controller->platform_data; ohci = hcd_to_ohci(hcd); ohci->num_ports = board->ports; @@ -440,8 +471,17 @@ static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data) return IRQ_HANDLED; } +static const struct ohci_at91_caps at91rm9200_caps = { + .suspend_ctrl = false, +}; + +static const struct ohci_at91_caps sama5d2_caps = { + .suspend_ctrl = true, +}; + static const struct of_device_id at91_ohci_dt_ids[] = { - { .compatible = "atmel,at91rm9200-ohci" }, + { .compatible = "atmel,at91rm9200-ohci", .data = &at91rm9200_caps }, + { .compatible = "atmel,sama5d2-ohci", .data = &sama5d2_caps }, { /* sentinel */ } }; @@ -581,6 +621,38 @@ static int ohci_hcd_at91_drv_remove(struct platform_device *pdev) return 0; } +static int ohci_at91_port_ctrl(struct regmap *regmap, bool enable) +{ + u32 regval; + int ret; + + if (!regmap) + return -EINVAL; + + ret = regmap_read(regmap, SFR_OHCIICR, ®val); + if (ret) + return ret; + + if (enable) + regval &= ~SFR_OHCIICR_USB_SUSPEND; + else + regval |= SFR_OHCIICR_USB_SUSPEND; + + regmap_write(regmap, SFR_OHCIICR, regval); + + return 0; +} + +static int ohci_at91_port_suspend(struct regmap *regmap) +{ + return ohci_at91_port_ctrl(regmap, false); +} + +static int ohci_at91_port_resume(struct regmap *regmap) +{ + return ohci_at91_port_ctrl(regmap, true); +} + static int __maybe_unused ohci_hcd_at91_drv_suspend(struct device *dev) { @@ -618,6 +690,9 @@ ohci_hcd_at91_drv_suspend(struct device *dev) ohci_writel(ohci, ohci->hc_control, &ohci->regs->control); ohci->rh_state = OHCI_RH_HALTED; + if (ohci_at91->caps->suspend_ctrl) + ohci_at91_port_suspend(ohci_at91->sfr_regmap); + /* flush the writes */ (void) ohci_readl (ohci, &ohci->regs->control); at91_stop_clock(ohci_at91); @@ -637,6 +712,9 @@ ohci_hcd_at91_drv_resume(struct device *dev) at91_start_clock(ohci_at91); + if (ohci_at91->caps->suspend_ctrl) + ohci_at91_port_resume(ohci_at91->sfr_regmap); + ohci_resume(hcd, false); return 0; } diff --git a/include/soc/at91/at91_sfr.h b/include/soc/at91/at91_sfr.h new file mode 100644 index 000000000000..04a3a1eee22e --- /dev/null +++ b/include/soc/at91/at91_sfr.h @@ -0,0 +1,29 @@ +/* + * Header file for the Atmel DDR/SDR SDRAM Controller + * + * Copyright (C) 2016 Atmel Corporation + * + * Author: Wenyou Yang + * + * 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 __AT91_SFR_H__ +#define __AT91_SFR_H__ + +#define SFR_DDRCFG 0x04 /* DDR Configuration Register */ +/* 0x08 ~ 0x0c: Reserved */ +#define SFR_OHCIICR 0x10 /* OHCI Interrupt Configuration Register */ +#define SFR_OHCIISR 0x14 /* OHCI Interrupt Status Register */ + +#define SFR_OHCIICR_SUSPEND_A BIT(8) +#define SFR_OHCIICR_SUSPEND_B BIT(9) +#define SFR_OHCIICR_SUSPEND_C BIT(10) + +#define SFR_OHCIICR_USB_SUSPEND (SFR_OHCIICR_SUSPEND_A | \ + SFR_OHCIICR_SUSPEND_B | \ + SFR_OHCIICR_SUSPEND_C) + +#endif -- cgit v1.2.3 From 62d9694a003dba585026df36c181e3ca930aeafc Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 2 Jun 2016 17:14:06 +0200 Subject: ohci-platform: Add support for controllers with multiple reset lines At least the EHCI/OHCI found on the Allwinnner H3 SoC needs multiple reset lines, the controller will not initialize while the reset for its companion is still asserted, which means we need to de-assert 2 resets for the controller to work. Signed-off-by: Hans de Goede Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/usb-ohci.txt | 2 +- drivers/usb/host/ohci-platform.c | 43 ++++++++++++---------- 2 files changed, 24 insertions(+), 21 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/usb-ohci.txt b/Documentation/devicetree/bindings/usb/usb-ohci.txt index 19233b7365e1..9df456968596 100644 --- a/Documentation/devicetree/bindings/usb/usb-ohci.txt +++ b/Documentation/devicetree/bindings/usb/usb-ohci.txt @@ -14,7 +14,7 @@ Optional properties: - clocks : a list of phandle + clock specifier pairs - phys : phandle + phy specifier pair - phy-names : "usb" -- resets : phandle + reset specifier pair +- resets : a list of phandle + reset specifier pairs Example: diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index ae1c988da146..898b74086c12 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -33,11 +33,12 @@ #define DRIVER_DESC "OHCI generic platform driver" #define OHCI_MAX_CLKS 3 +#define OHCI_MAX_RESETS 2 #define hcd_to_ohci_priv(h) ((struct ohci_platform_priv *)hcd_to_ohci(h)->priv) struct ohci_platform_priv { struct clk *clks[OHCI_MAX_CLKS]; - struct reset_control *rst; + struct reset_control *resets[OHCI_MAX_RESETS]; struct phy **phys; int num_phys; }; @@ -117,7 +118,7 @@ static int ohci_platform_probe(struct platform_device *dev) struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); struct ohci_platform_priv *priv; struct ohci_hcd *ohci; - int err, irq, phy_num, clk = 0; + int err, irq, phy_num, clk = 0, rst = 0; if (usb_disabled()) return -ENODEV; @@ -195,19 +196,21 @@ static int ohci_platform_probe(struct platform_device *dev) break; } } - - } - - priv->rst = devm_reset_control_get_optional(&dev->dev, NULL); - if (IS_ERR(priv->rst)) { - err = PTR_ERR(priv->rst); - if (err == -EPROBE_DEFER) - goto err_put_clks; - priv->rst = NULL; - } else { - err = reset_control_deassert(priv->rst); - if (err) - goto err_put_clks; + for (rst = 0; rst < OHCI_MAX_RESETS; rst++) { + priv->resets[rst] = + devm_reset_control_get_shared_by_index( + &dev->dev, rst); + if (IS_ERR(priv->resets[rst])) { + err = PTR_ERR(priv->resets[rst]); + if (err == -EPROBE_DEFER) + goto err_reset; + priv->resets[rst] = NULL; + break; + } + err = reset_control_deassert(priv->resets[rst]); + if (err) + goto err_reset; + } } if (pdata->big_endian_desc) @@ -265,8 +268,8 @@ err_power: if (pdata->power_off) pdata->power_off(dev); err_reset: - if (priv->rst) - reset_control_assert(priv->rst); + while (--rst >= 0) + reset_control_assert(priv->resets[rst]); err_put_clks: while (--clk >= 0) clk_put(priv->clks[clk]); @@ -284,15 +287,15 @@ static int ohci_platform_remove(struct platform_device *dev) struct usb_hcd *hcd = platform_get_drvdata(dev); struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); - int clk; + int clk, rst; usb_remove_hcd(hcd); if (pdata->power_off) pdata->power_off(dev); - if (priv->rst) - reset_control_assert(priv->rst); + for (rst = 0; rst < OHCI_MAX_RESETS && priv->resets[rst]; rst++) + reset_control_assert(priv->resets[rst]); for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++) clk_put(priv->clks[clk]); -- cgit v1.2.3 From 39f8f23d13666c4c5644e5add7d9598d9e798f22 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 7 Jun 2016 16:12:33 +0100 Subject: dt-bindings: arm-gic: Add documentation for Tegra210 AGIC The Tegra AGIC interrupt controller is compatible with the ARM GIC-400 interrupt controller. Add the compatible string and clock information for the AGIC to the GIC device-tree binding documentation. Signed-off-by: Jon Hunter Acked-by: Rob Herring Signed-off-by: Marc Zyngier --- Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt b/Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt index 793c20ff8fcc..5393e2a45a42 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt +++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic.txt @@ -21,6 +21,7 @@ Main node required properties: "arm,pl390" "arm,tc11mp-gic" "brcm,brahma-b15-gic" + "nvidia,tegra210-agic" "qcom,msm-8660-qgic" "qcom,msm-qgic2" - interrupt-controller : Identifies the node as an interrupt controller @@ -68,7 +69,7 @@ Optional "ic_clk" (for "arm,arm11mp-gic") "PERIPHCLKEN" (for "arm,cortex-a15-gic") "PERIPHCLK", "PERIPHCLKEN" (for "arm,cortex-a9-gic") - "clk" (for "arm,gic-400") + "clk" (for "arm,gic-400" and "nvidia,tegra210") "gclk" (for "arm,pl390") - power-domains : A phandle and PM domain specifier as defined by bindings of -- cgit v1.2.3 From 2921b12362babf5af7d45e7b933637cfdfdf6a2a Mon Sep 17 00:00:00 2001 From: Paul E. McKenney Date: Fri, 1 Apr 2016 05:09:53 -0700 Subject: documentation: Add reference to 2014 RCU API LWN article Reported-by: Jim Roskind Signed-off-by: Paul E. McKenney --- Documentation/RCU/whatisRCU.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'Documentation') diff --git a/Documentation/RCU/whatisRCU.txt b/Documentation/RCU/whatisRCU.txt index 111770ffa10e..13266cff42ff 100644 --- a/Documentation/RCU/whatisRCU.txt +++ b/Documentation/RCU/whatisRCU.txt @@ -5,6 +5,7 @@ to start learning about RCU: 2. What is RCU? Part 2: Usage http://lwn.net/Articles/263130/ 3. RCU part 3: the RCU API http://lwn.net/Articles/264090/ 4. The RCU API, 2010 Edition http://lwn.net/Articles/418853/ +5. The RCU API, 2014 Edition http://lwn.net/Articles/609904/ What is RCU? -- cgit v1.2.3 From db4855b5a88535746558fb756512090fc1ce5607 Mon Sep 17 00:00:00 2001 From: Paul E. McKenney Date: Fri, 1 Apr 2016 05:21:56 -0700 Subject: documentation: Add references to 2010 and 2014 Big API Tables Reported-by: Jim Roskind Signed-off-by: Paul E. McKenney --- Documentation/RCU/whatisRCU.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Documentation') diff --git a/Documentation/RCU/whatisRCU.txt b/Documentation/RCU/whatisRCU.txt index 13266cff42ff..204422719197 100644 --- a/Documentation/RCU/whatisRCU.txt +++ b/Documentation/RCU/whatisRCU.txt @@ -5,7 +5,9 @@ to start learning about RCU: 2. What is RCU? Part 2: Usage http://lwn.net/Articles/263130/ 3. RCU part 3: the RCU API http://lwn.net/Articles/264090/ 4. The RCU API, 2010 Edition http://lwn.net/Articles/418853/ + 2010 Big API Table http://lwn.net/Articles/419086/ 5. The RCU API, 2014 Edition http://lwn.net/Articles/609904/ + 2014 Big API Table http://lwn.net/Articles/609973/ What is RCU? -- cgit v1.2.3 From c79dac758dc59f7461a721aaf65301cde8a448bc Mon Sep 17 00:00:00 2001 From: Paul E. McKenney Date: Wed, 20 Apr 2016 09:22:54 -0700 Subject: documentation: Add RCU_NONIDLE() restrictions to requirements Signed-off-by: Paul E. McKenney --- .../RCU/Design/Requirements/Requirements.html | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'Documentation') diff --git a/Documentation/RCU/Design/Requirements/Requirements.html b/Documentation/RCU/Design/Requirements/Requirements.html index e7e24b3e86e2..ece410f40436 100644 --- a/Documentation/RCU/Design/Requirements/Requirements.html +++ b/Documentation/RCU/Design/Requirements/Requirements.html @@ -2391,6 +2391,41 @@ and RCU_NONIDLE() on the other while inspecting idle-loop code. Steven Rostedt supplied _rcuidle event tracing, which is used quite heavily in the idle loop. +However, there are some restrictions on the code placed within +RCU_NONIDLE(): + +
    +
  1. Blocking is prohibited. + In practice, this is not a serious restriction given that idle + tasks are prohibited from blocking to begin with. +
  2. Although nesting RCU_NONIDLE() is permited, they cannot + nest indefinitely deeply. + However, given that they can be nested on the order of a million + deep, even on 32-bit systems, this should not be a serious + restriction. + This nesting limit would probably be reached long after the + compiler OOMed or the stack overflowed. +
  3. Any code path that enters RCU_NONIDLE() must sequence + out of that same RCU_NONIDLE(). + For example, the following is grossly illegal: + +
    +
    + 1     RCU_NONIDLE({
    + 2       do_something();
    + 3       goto bad_idea;  /* BUG!!! */
    + 4       do_something_else();});
    + 5   bad_idea:
    +	
    +
    + +

    + It is just as illegal to transfer control into the middle of + RCU_NONIDLE()'s argument. + Yes, in theory, you could transfer in as long as you also + transferred out, but in practice you could also expect to get sharply + worded review comments. +

It is similarly socially unacceptable to interrupt an -- cgit v1.2.3 From 14ef05754f397d468926187d285b9fee55603e86 Mon Sep 17 00:00:00 2001 From: Eric Engestrom Date: Mon, 25 Apr 2016 07:36:59 +0100 Subject: Documentation: Fix spelling mistake Signed-off-by: Eric Engestrom Reviewed-by: Josh Triplett Signed-off-by: Paul E. McKenney --- Documentation/RCU/stallwarn.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/RCU/stallwarn.txt b/Documentation/RCU/stallwarn.txt index 0f7fb4298e7e..e93d04133fe7 100644 --- a/Documentation/RCU/stallwarn.txt +++ b/Documentation/RCU/stallwarn.txt @@ -49,7 +49,7 @@ rcupdate.rcu_task_stall_timeout This boot/sysfs parameter controls the RCU-tasks stall warning interval. A value of zero or less suppresses RCU-tasks stall warnings. A positive value sets the stall-warning interval - in jiffies. An RCU-tasks stall warning starts wtih the line: + in jiffies. An RCU-tasks stall warning starts with the line: INFO: rcu_tasks detected stalls on tasks: -- cgit v1.2.3 From 088e9d253d3a4ab7e058dd84bb532c32dadf1882 Mon Sep 17 00:00:00 2001 From: Daniel Bristot de Oliveira Date: Thu, 2 Jun 2016 13:51:41 -0300 Subject: rcu: sysctl: Panic on RCU Stall It is not always easy to determine the cause of an RCU stall just by analysing the RCU stall messages, mainly when the problem is caused by the indirect starvation of rcu threads. For example, when preempt_rcu is not awakened due to the starvation of a timer softirq. We have been hard coding panic() in the RCU stall functions for some time while testing the kernel-rt. But this is not possible in some scenarios, like when supporting customers. This patch implements the sysctl kernel.panic_on_rcu_stall. If set to 1, the system will panic() when an RCU stall takes place, enabling the capture of a vmcore. The vmcore provides a way to analyze all kernel/tasks states, helping out to point to the culprit and the solution for the stall. The kernel.panic_on_rcu_stall sysctl is disabled by default. Changes from v1: - Fixed a typo in the git log - The if(sysctl_panic_on_rcu_stall) panic() is in a static function - Fixed the CONFIG_TINY_RCU compilation issue - The var sysctl_panic_on_rcu_stall is now __read_mostly Cc: Jonathan Corbet Cc: "Paul E. McKenney" Cc: Josh Triplett Cc: Steven Rostedt Cc: Mathieu Desnoyers Cc: Lai Jiangshan Acked-by: Christian Borntraeger Reviewed-by: Josh Triplett Reviewed-by: Arnaldo Carvalho de Melo Tested-by: "Luis Claudio R. Goncalves" Signed-off-by: Daniel Bristot de Oliveira Signed-off-by: Paul E. McKenney --- Documentation/sysctl/kernel.txt | 12 ++++++++++++ include/linux/kernel.h | 1 + kernel/rcu/tree.c | 12 ++++++++++++ kernel/sysctl.c | 11 +++++++++++ 4 files changed, 36 insertions(+) (limited to 'Documentation') diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt index a3683ce2a2f3..33204604de6c 100644 --- a/Documentation/sysctl/kernel.txt +++ b/Documentation/sysctl/kernel.txt @@ -58,6 +58,7 @@ show up in /proc/sys/kernel: - panic_on_stackoverflow - panic_on_unrecovered_nmi - panic_on_warn +- panic_on_rcu_stall - perf_cpu_time_max_percent - perf_event_paranoid - perf_event_max_stack @@ -618,6 +619,17 @@ a kernel rebuild when attempting to kdump at the location of a WARN(). ============================================================== +panic_on_rcu_stall: + +When set to 1, calls panic() after RCU stall detection messages. This +is useful to define the root cause of RCU stalls using a vmcore. + +0: do not panic() when RCU stall takes place, default behavior. + +1: panic() after printing RCU stall messages. + +============================================================== + perf_cpu_time_max_percent: Hints to the kernel how much CPU time it should be allowed to diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 94aa10ffe156..c42082112ec8 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -451,6 +451,7 @@ extern int panic_on_oops; extern int panic_on_unrecovered_nmi; extern int panic_on_io_nmi; extern int panic_on_warn; +extern int sysctl_panic_on_rcu_stall; extern int sysctl_panic_on_stackoverflow; extern bool crash_kexec_post_notifiers; diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index c844b6142a86..e5ca15a461b9 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -125,6 +125,8 @@ int rcu_num_lvls __read_mostly = RCU_NUM_LVLS; /* Number of rcu_nodes at specified level. */ static int num_rcu_lvl[] = NUM_RCU_LVL_INIT; int rcu_num_nodes __read_mostly = NUM_RCU_NODES; /* Total # rcu_nodes in use. */ +/* panic() on RCU Stall sysctl. */ +int sysctl_panic_on_rcu_stall __read_mostly; /* * The rcu_scheduler_active variable transitions from zero to one just @@ -1312,6 +1314,12 @@ static void rcu_stall_kick_kthreads(struct rcu_state *rsp) } } +static inline void panic_on_rcu_stall(void) +{ + if (sysctl_panic_on_rcu_stall) + panic("RCU Stall\n"); +} + static void print_other_cpu_stall(struct rcu_state *rsp, unsigned long gpnum) { int cpu; @@ -1391,6 +1399,8 @@ static void print_other_cpu_stall(struct rcu_state *rsp, unsigned long gpnum) rcu_check_gp_kthread_starvation(rsp); + panic_on_rcu_stall(); + force_quiescent_state(rsp); /* Kick them all. */ } @@ -1431,6 +1441,8 @@ static void print_cpu_stall(struct rcu_state *rsp) jiffies + 3 * rcu_jiffies_till_stall_check() + 3); raw_spin_unlock_irqrestore_rcu_node(rnp, flags); + panic_on_rcu_stall(); + /* * Attempt to revive the RCU machinery by forcing a context switch. * diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 87b2fc38398b..35f0dcb1cb4f 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1204,6 +1204,17 @@ static struct ctl_table kern_table[] = { .extra1 = &one, .extra2 = &one, }, +#endif +#if defined(CONFIG_TREE_RCU) || defined(CONFIG_PREEMPT_RCU) + { + .procname = "panic_on_rcu_stall", + .data = &sysctl_panic_on_rcu_stall, + .maxlen = sizeof(sysctl_panic_on_rcu_stall), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one, + }, #endif { } }; -- cgit v1.2.3 From ebff09a6ff164aec2b33bf1f9a488c45ac108413 Mon Sep 17 00:00:00 2001 From: Paul E. McKenney Date: Wed, 15 Jun 2016 16:08:17 -0700 Subject: locking/Documentation: Clarify limited control-dependency scope Nothing in the control-dependencies section of memory-barriers.txt says that control dependencies don't extend beyond the end of the if-statement containing the control dependency. Worse yet, in many situations, they do extend beyond that if-statement. In particular, the compiler cannot destroy the control dependency given proper use of READ_ONCE() and WRITE_ONCE(). However, a weakly ordered system having a conditional-move instruction provides the control-dependency guarantee only to code within the scope of the if-statement itself. This commit therefore adds words and an example demonstrating this limitation of control dependencies. Reported-by: Will Deacon Signed-off-by: Paul E. McKenney Acked-by: Peter Zijlstra (Intel) Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: corbet@lwn.net Cc: linux-arch@vger.kernel.org Cc: linux-doc@vger.kernel.org Link: http://lkml.kernel.org/r/20160615230817.GA18039@linux.vnet.ibm.com Signed-off-by: Ingo Molnar --- Documentation/memory-barriers.txt | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'Documentation') diff --git a/Documentation/memory-barriers.txt b/Documentation/memory-barriers.txt index 147ae8ec836f..a4d0a99de04d 100644 --- a/Documentation/memory-barriers.txt +++ b/Documentation/memory-barriers.txt @@ -806,6 +806,41 @@ out-guess your code. More generally, although READ_ONCE() does force the compiler to actually emit code for a given load, it does not force the compiler to use the results. +In addition, control dependencies apply only to the then-clause and +else-clause of the if-statement in question. In particular, it does +not necessarily apply to code following the if-statement: + + q = READ_ONCE(a); + if (q) { + WRITE_ONCE(b, p); + } else { + WRITE_ONCE(b, r); + } + WRITE_ONCE(c, 1); /* BUG: No ordering against the read from "a". */ + +It is tempting to argue that there in fact is ordering because the +compiler cannot reorder volatile accesses and also cannot reorder +the writes to "b" with the condition. Unfortunately for this line +of reasoning, the compiler might compile the two writes to "b" as +conditional-move instructions, as in this fanciful pseudo-assembly +language: + + ld r1,a + ld r2,p + ld r3,r + cmp r1,$0 + cmov,ne r4,r2 + cmov,eq r4,r3 + st r4,b + st $1,c + +A weakly ordered CPU would have no dependency of any sort between the load +from "a" and the store to "c". The control dependencies would extend +only to the pair of cmov instructions and the store depending on them. +In short, control dependencies apply only to the stores in the then-clause +and else-clause of the if-statement in question (including functions +invoked by those two clauses), not to code following that if-statement. + Finally, control dependencies do -not- provide transitivity. This is demonstrated by two related examples, with the initial values of x and y both being zero: @@ -869,6 +904,12 @@ In summary: atomic{,64}_read() can help to preserve your control dependency. Please see the COMPILER BARRIER section for more information. + (*) Control dependencies apply only to the then-clause and else-clause + of the if-statement containing the control dependency, including + any functions that these two clauses call. Control dependencies + do -not- apply to code following the if-statement containing the + control dependency. + (*) Control dependencies pair normally with other types of barriers. (*) Control dependencies do -not- provide transitivity. If you -- cgit v1.2.3 From aa5e94a2e13377e374795de5400e346c978f46be Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 20 Jun 2016 07:42:07 -0700 Subject: Revert "usb: ohci-at91: Forcibly suspend ports while USB suspend" This reverts commit 7150bc9b4d43471fa37b26f5839892d4cf1fe09b. It is not correct, based on review from others. Reported-by: Wenyou Yang Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/atmel-usb.txt | 6 +- drivers/usb/host/ohci-at91.c | 80 +--------------------- include/soc/at91/at91_sfr.h | 29 -------- 3 files changed, 3 insertions(+), 112 deletions(-) delete mode 100644 include/soc/at91/at91_sfr.h (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/atmel-usb.txt b/Documentation/devicetree/bindings/usb/atmel-usb.txt index 888deaa8a4ca..5883b73ea1b5 100644 --- a/Documentation/devicetree/bindings/usb/atmel-usb.txt +++ b/Documentation/devicetree/bindings/usb/atmel-usb.txt @@ -3,10 +3,8 @@ Atmel SOC USB controllers OHCI Required properties: - - compatible: Should be one of the following - "atmel,at91rm9200-ohci" for USB controllers used in host mode. - "atmel,sama5d2-ohci" for USB controllers used in host mode - on SAMA5D2 which can force to suspend. + - compatible: Should be "atmel,at91rm9200-ohci" for USB controllers + used in host mode. - reg: Address and length of the register set for the device - interrupts: Should contain ehci interrupt - clocks: Should reference the peripheral, host and system clocks diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 54e8feb8a964..d177372bb357 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -21,11 +21,8 @@ #include #include #include -#include -#include #include #include -#include #include "ohci.h" @@ -48,18 +45,12 @@ struct at91_usbh_data { u8 overcurrent_changed[AT91_MAX_USBH_PORTS]; }; -struct ohci_at91_caps { - bool suspend_ctrl; -}; - struct ohci_at91_priv { struct clk *iclk; struct clk *fclk; struct clk *hclk; bool clocked; bool wakeup; /* Saved wake-up state for resume */ - const struct ohci_at91_caps *caps; - struct regmap *sfr_regmap; }; /* interface and function clocks; sometimes also an AHB clock */ @@ -141,17 +132,6 @@ static void at91_stop_hc(struct platform_device *pdev) /*-------------------------------------------------------------------------*/ -struct regmap *at91_dt_syscon_sfr(void) -{ - struct regmap *regmap; - - regmap = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr"); - if (IS_ERR(regmap)) - regmap = NULL; - - return regmap; -} - static void usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *); /* configure so an HC device and id are always provided */ @@ -217,17 +197,6 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, goto err; } - ohci_at91->caps = (const struct ohci_at91_caps *) - of_device_get_match_data(&pdev->dev); - if (!ohci_at91->caps) - return -ENODEV; - - if (ohci_at91->caps->suspend_ctrl) { - ohci_at91->sfr_regmap = at91_dt_syscon_sfr(); - if (!ohci_at91->sfr_regmap) - dev_warn(dev, "failed to find sfr node\n"); - } - board = hcd->self.controller->platform_data; ohci = hcd_to_ohci(hcd); ohci->num_ports = board->ports; @@ -471,17 +440,8 @@ static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data) return IRQ_HANDLED; } -static const struct ohci_at91_caps at91rm9200_caps = { - .suspend_ctrl = false, -}; - -static const struct ohci_at91_caps sama5d2_caps = { - .suspend_ctrl = true, -}; - static const struct of_device_id at91_ohci_dt_ids[] = { - { .compatible = "atmel,at91rm9200-ohci", .data = &at91rm9200_caps }, - { .compatible = "atmel,sama5d2-ohci", .data = &sama5d2_caps }, + { .compatible = "atmel,at91rm9200-ohci" }, { /* sentinel */ } }; @@ -621,38 +581,6 @@ static int ohci_hcd_at91_drv_remove(struct platform_device *pdev) return 0; } -static int ohci_at91_port_ctrl(struct regmap *regmap, bool enable) -{ - u32 regval; - int ret; - - if (!regmap) - return -EINVAL; - - ret = regmap_read(regmap, SFR_OHCIICR, ®val); - if (ret) - return ret; - - if (enable) - regval &= ~SFR_OHCIICR_USB_SUSPEND; - else - regval |= SFR_OHCIICR_USB_SUSPEND; - - regmap_write(regmap, SFR_OHCIICR, regval); - - return 0; -} - -static int ohci_at91_port_suspend(struct regmap *regmap) -{ - return ohci_at91_port_ctrl(regmap, false); -} - -static int ohci_at91_port_resume(struct regmap *regmap) -{ - return ohci_at91_port_ctrl(regmap, true); -} - static int __maybe_unused ohci_hcd_at91_drv_suspend(struct device *dev) { @@ -690,9 +618,6 @@ ohci_hcd_at91_drv_suspend(struct device *dev) ohci_writel(ohci, ohci->hc_control, &ohci->regs->control); ohci->rh_state = OHCI_RH_HALTED; - if (ohci_at91->caps->suspend_ctrl) - ohci_at91_port_suspend(ohci_at91->sfr_regmap); - /* flush the writes */ (void) ohci_readl (ohci, &ohci->regs->control); at91_stop_clock(ohci_at91); @@ -712,9 +637,6 @@ ohci_hcd_at91_drv_resume(struct device *dev) at91_start_clock(ohci_at91); - if (ohci_at91->caps->suspend_ctrl) - ohci_at91_port_resume(ohci_at91->sfr_regmap); - ohci_resume(hcd, false); return 0; } diff --git a/include/soc/at91/at91_sfr.h b/include/soc/at91/at91_sfr.h deleted file mode 100644 index 04a3a1eee22e..000000000000 --- a/include/soc/at91/at91_sfr.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Header file for the Atmel DDR/SDR SDRAM Controller - * - * Copyright (C) 2016 Atmel Corporation - * - * Author: Wenyou Yang - * - * 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 __AT91_SFR_H__ -#define __AT91_SFR_H__ - -#define SFR_DDRCFG 0x04 /* DDR Configuration Register */ -/* 0x08 ~ 0x0c: Reserved */ -#define SFR_OHCIICR 0x10 /* OHCI Interrupt Configuration Register */ -#define SFR_OHCIISR 0x14 /* OHCI Interrupt Status Register */ - -#define SFR_OHCIICR_SUSPEND_A BIT(8) -#define SFR_OHCIICR_SUSPEND_B BIT(9) -#define SFR_OHCIICR_SUSPEND_C BIT(10) - -#define SFR_OHCIICR_USB_SUSPEND (SFR_OHCIICR_SUSPEND_A | \ - SFR_OHCIICR_SUSPEND_B | \ - SFR_OHCIICR_SUSPEND_C) - -#endif -- cgit v1.2.3 From ebfad91c5da3b32d83aaad280d6918867aea501c Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Thu, 16 Jun 2016 16:13:18 +0800 Subject: dt-bindings: ci-hdrc-usb2: s/gadget-itc-setting/itc-setting in example What the code expect is "itc-setting" rather than "gadget-itc-setting", and this is also correctly described in the optional properties. Signed-off-by: Jisheng Zhang Signed-off-by: Peter Chen Acked-by: Rob Herring --- Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt index 1084e2bcbe1c..341dc67f3472 100644 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt +++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt @@ -93,7 +93,7 @@ Example: phys = <&usb_phy0>; phy-names = "usb-phy"; vbus-supply = <®_usb0_vbus>; - gadget-itc-setting = <0x4>; /* 4 micro-frames */ + itc-setting = <0x4>; /* 4 micro-frames */ /* Incremental burst of unspecified length */ ahb-burst-config = <0x0>; tx-burst-size-dword = <0x10>; /* 64 bytes */ -- cgit v1.2.3 From 846d9ce535309798ae6d8c8781b0207f3cb41e6e Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 1 Jun 2016 16:13:12 +0100 Subject: extcon: arizona: Update binding docs to mention new defines for GPSW Update the binding documentation to mention the defines added for the possible values of wlf,gpsw. Signed-off-by: Charles Keepax Signed-off-by: Chanwoo Choi --- Documentation/devicetree/bindings/extcon/extcon-arizona.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/extcon/extcon-arizona.txt b/Documentation/devicetree/bindings/extcon/extcon-arizona.txt index e27341f8a4c7..7f3d94ae81ff 100644 --- a/Documentation/devicetree/bindings/extcon/extcon-arizona.txt +++ b/Documentation/devicetree/bindings/extcon/extcon-arizona.txt @@ -46,7 +46,8 @@ Optional properties: The second cell represents the MICBIAS to be used. The third cell represents the value of the micd-pol-gpio pin. - - wlf,gpsw : Settings for the general purpose switch + - wlf,gpsw : Settings for the general purpose switch, set as one of the + ARIZONA_GPSW_XXX defines. Example: -- cgit v1.2.3 From 50ad77db41fe3a08aeeae04088869142a1d9d007 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Fri, 13 May 2016 00:13:12 +0930 Subject: doc/devicetree: Add Aspeed VIC bindings Signed-off-by: Joel Stanley Link: https://lkml.kernel.org/r/1463064193-2178-2-git-send-email-joel@jms.id.au Signed-off-by: Jason Cooper --- .../interrupt-controller/aspeed,ast2400-vic.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Documentation/devicetree/bindings/interrupt-controller/aspeed,ast2400-vic.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/interrupt-controller/aspeed,ast2400-vic.txt b/Documentation/devicetree/bindings/interrupt-controller/aspeed,ast2400-vic.txt new file mode 100644 index 000000000000..6c6e85324b9d --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/aspeed,ast2400-vic.txt @@ -0,0 +1,22 @@ +Aspeed Vectored Interrupt Controller + +These bindings are for the Aspeed AST2400 interrupt controller register layout. +The SoC has an legacy register layout, but this driver does not support that +mode of operation. + +Required properties: + +- compatible : should be "aspeed,ast2400-vic". + +- interrupt-controller : Identifies the node as an interrupt controller +- #interrupt-cells : Specifies the number of cells needed to encode an + interrupt source. The value shall be 1. + +Example: + + vic: interrupt-controller@1e6c0080 { + compatible = "aspeed,ast2400-vic"; + interrupt-controller; + #interrupt-cells = <1>; + reg = <0x1e6c0080 0x80>; + }; -- cgit v1.2.3 From 0c529b3fc6c66e618088aa3a998d760d1ad05272 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 3 Jun 2016 12:00:01 +0200 Subject: serial: sh-sci: Update DT binding documentation for GPIO modem lines Amend the DT bindings for the Renesas SCI driver to allow describing optional GPIO-controlled modem lines, which can be used where dedicated modem lines are not available. The property naming is dictated by the Generic Serial DT Bindings. Signed-off-by: Geert Uytterhoeven Acked-by: Rob Herring Cc: devicetree@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/renesas,sci-serial.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt index 528c3b90f23c..997baeaa869b 100644 --- a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt +++ b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt @@ -76,6 +76,8 @@ Optional properties: - dmas: Must contain a list of two references to DMA specifiers, one for transmission, and one for reception. - dma-names: Must contain a list of two DMA names, "tx" and "rx". + - {cts,dsr,dcd,rng,rts,dtr}-gpios: Specify GPIOs for modem lines, cfr. the + generic serial DT bindings in serial.txt. Example: aliases { -- cgit v1.2.3 From b0405dc998e425bbb1f64488b6fda781b627056b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 3 Jun 2016 12:00:02 +0200 Subject: serial: sh-sci: Update DT binding documentation for dedicated RTS/CTS Some Renesas SCIF UARTs have dedicated lines for RTS/CTS hardware flow control. Whether these lines exist depends on SoC and UART instance inside the SoC. Whether these lines can be used for hardware flow control depends on board wiring. Amend the DT bindings with an optional property to indicate that RTS/CTS hardware flow control lines exist, and can be used as such, according to the Generic Serial DT Bindings. Signed-off-by: Geert Uytterhoeven Acked-by: Rob Herring Cc: devicetree@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/renesas,sci-serial.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt index 997baeaa869b..7a0e15053db9 100644 --- a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt +++ b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt @@ -78,6 +78,8 @@ Optional properties: - dma-names: Must contain a list of two DMA names, "tx" and "rx". - {cts,dsr,dcd,rng,rts,dtr}-gpios: Specify GPIOs for modem lines, cfr. the generic serial DT bindings in serial.txt. + - uart-has-rtscts: Indicates dedicated lines for RTS/CTS hardware flow + control, cfr. the generic serial DT bindings in serial.txt. Example: aliases { -- cgit v1.2.3 From ac8305cd16ea2c35084ceb5cdf36f5a90c275c3a Mon Sep 17 00:00:00 2001 From: Hiromitsu Yamasaki Date: Thu, 12 May 2016 15:33:58 +0900 Subject: serial: sh-sci: Document SoC specific bindings for r8a7796 Signed-off-by: Hiromitsu Yamasaki Signed-off-by: Simon Horman Acked-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/renesas,sci-serial.txt | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt index 7a0e15053db9..1e4000d83aee 100644 --- a/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt +++ b/Documentation/devicetree/bindings/serial/renesas,sci-serial.txt @@ -31,6 +31,8 @@ Required properties: - "renesas,hscif-r8a7794" for R8A7794 (R-Car E2) HSCIF compatible UART. - "renesas,scif-r8a7795" for R8A7795 (R-Car H3) SCIF compatible UART. - "renesas,hscif-r8a7795" for R8A7795 (R-Car H3) HSCIF compatible UART. + - "renesas,scif-r8a7796" for R8A7796 (R-Car M3-W) SCIF compatible UART. + - "renesas,hscif-r8a7796" for R8A7796 (R-Car M3-W) HSCIF compatible UART. - "renesas,scifa-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFA compatible UART. - "renesas,scifb-sh73a0" for SH73A0 (SH-Mobile AG5) SCIFB compatible UART. - "renesas,rcar-gen1-scif" for R-Car Gen1 SCIF compatible UART, -- cgit v1.2.3 From 4ef03d328769eddbfeca1f1c958fdb181a69c341 Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Tue, 31 May 2016 10:59:18 +0200 Subject: tty/serial/8250: use mctrl_gpio helpers This patch permits the usage for GPIOs to control the CTS/RTS/DTR/DSR/DCD/RI signals. Signed-off-by: Yegor Yefremov Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/serial/8250.txt | 19 ++++++++++++ drivers/tty/serial/8250/8250.h | 35 ++++++++++++++++++++++- drivers/tty/serial/8250/8250_core.c | 9 ++++++ drivers/tty/serial/8250/8250_omap.c | 31 +++++++++++--------- drivers/tty/serial/8250/8250_port.c | 7 ++++- drivers/tty/serial/8250/Kconfig | 1 + include/linux/serial_8250.h | 1 + 7 files changed, 88 insertions(+), 15 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/serial/8250.txt b/Documentation/devicetree/bindings/serial/8250.txt index 936ab5b87324..f5561ac7e17e 100644 --- a/Documentation/devicetree/bindings/serial/8250.txt +++ b/Documentation/devicetree/bindings/serial/8250.txt @@ -42,6 +42,9 @@ Optional properties: - auto-flow-control: one way to enable automatic flow control support. The driver is allowed to detect support for the capability even without this property. +- {rts,cts,dtr,dsr,rng,dcd}-gpios: specify a GPIO for RTS/CTS/DTR/DSR/RI/DCD + line respectively. It will use specified GPIO instead of the peripheral + function pin for the UART feature. If unsure, don't specify this property. Note: * fsl,ns16550: @@ -63,3 +66,19 @@ Example: interrupts = <10>; reg-shift = <2>; }; + +Example for OMAP UART using GPIO-based modem control signals: + + uart4: serial@49042000 { + compatible = "ti,omap3-uart"; + reg = <0x49042000 0x400>; + interrupts = <80>; + ti,hwmods = "uart4"; + clock-frequency = <48000000>; + cts-gpios = <&gpio3 5 GPIO_ACTIVE_LOW>; + rts-gpios = <&gpio3 6 GPIO_ACTIVE_LOW>; + dtr-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>; + dsr-gpios = <&gpio1 13 GPIO_ACTIVE_LOW>; + dcd-gpios = <&gpio1 14 GPIO_ACTIVE_LOW>; + rng-gpios = <&gpio1 15 GPIO_ACTIVE_LOW>; + }; diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 1a16feac9a36..122e0e4029fe 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -15,6 +15,8 @@ #include #include +#include "../serial_mctrl_gpio.h" + struct uart_8250_dma { int (*tx_dma)(struct uart_8250_port *p); int (*rx_dma)(struct uart_8250_port *p); @@ -131,12 +133,43 @@ void serial8250_em485_destroy(struct uart_8250_port *p); static inline void serial8250_out_MCR(struct uart_8250_port *up, int value) { + int mctrl_gpio = 0; + serial_out(up, UART_MCR, value); + + if (value & UART_MCR_RTS) + mctrl_gpio |= TIOCM_RTS; + if (value & UART_MCR_DTR) + mctrl_gpio |= TIOCM_DTR; + + mctrl_gpio_set(up->gpios, mctrl_gpio); } static inline int serial8250_in_MCR(struct uart_8250_port *up) { - return serial_in(up, UART_MCR); + int mctrl, mctrl_gpio = 0; + + mctrl = serial_in(up, UART_MCR); + + /* save current MCR values */ + if (mctrl & UART_MCR_RTS) + mctrl_gpio |= TIOCM_RTS; + if (mctrl & UART_MCR_DTR) + mctrl_gpio |= TIOCM_DTR; + + mctrl_gpio = mctrl_gpio_get_outputs(up->gpios, &mctrl_gpio); + + if (mctrl_gpio & TIOCM_RTS) + mctrl |= UART_MCR_RTS; + else + mctrl &= ~UART_MCR_RTS; + + if (mctrl_gpio & TIOCM_DTR) + mctrl |= UART_MCR_DTR; + else + mctrl &= ~UART_MCR_DTR; + + return mctrl; } #if defined(__alpha__) && !defined(CONFIG_PCI) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index dcf43f66404f..13ad5c3d2e68 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -974,6 +974,8 @@ int serial8250_register_8250_port(struct uart_8250_port *up) uart = serial8250_find_match_or_unused(&up->port); if (uart && uart->port.type != PORT_8250_CIR) { + struct mctrl_gpios *gpios; + if (uart->port.dev) uart_remove_one_port(&serial8250_reg, &uart->port); @@ -1011,6 +1013,13 @@ int serial8250_register_8250_port(struct uart_8250_port *up) if (up->port.flags & UPF_FIXED_TYPE) uart->port.type = up->port.type; + gpios = mctrl_gpio_init(&uart->port, 0); + if (IS_ERR(gpios)) { + if (PTR_ERR(gpios) != -ENOSYS) + return PTR_ERR(gpios); + } else + uart->gpios = gpios; + serial8250_set_defaults(uart); /* Possibly override default I/O functions. */ diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 61ad6c3b20a0..e14982f36a04 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -134,18 +134,21 @@ static void omap8250_set_mctrl(struct uart_port *port, unsigned int mctrl) serial8250_do_set_mctrl(port, mctrl); - /* - * Turn off autoRTS if RTS is lowered and restore autoRTS setting - * if RTS is raised - */ - lcr = serial_in(up, UART_LCR); - serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); - if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS)) - priv->efr |= UART_EFR_RTS; - else - priv->efr &= ~UART_EFR_RTS; - serial_out(up, UART_EFR, priv->efr); - serial_out(up, UART_LCR, lcr); + if (IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(up->gpios, + UART_GPIO_RTS))) { + /* + * Turn off autoRTS if RTS is lowered and restore autoRTS + * setting if RTS is raised + */ + lcr = serial_in(up, UART_LCR); + serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); + if ((mctrl & TIOCM_RTS) && (port->status & UPSTAT_AUTORTS)) + priv->efr |= UART_EFR_RTS; + else + priv->efr &= ~UART_EFR_RTS; + serial_out(up, UART_EFR, priv->efr); + serial_out(up, UART_LCR, lcr); + } } /* @@ -446,7 +449,9 @@ static void omap_8250_set_termios(struct uart_port *port, priv->efr = 0; up->port.status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS | UPSTAT_AUTOXOFF); - if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) { + if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW + && IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(up->gpios, + UART_GPIO_RTS))) { /* Enable AUTOCTS (autoRTS is enabled when RTS is raised) */ up->port.status |= UPSTAT_AUTOCTS | UPSTAT_AUTORTS; priv->efr |= UART_EFR_CTS; diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index bdfa659b9606..7481b95c6d84 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1618,6 +1618,8 @@ static void serial8250_disable_ms(struct uart_port *port) if (up->bugs & UART_BUG_NOMSR) return; + mctrl_gpio_disable_ms(up->gpios); + up->ier &= ~UART_IER_MSI; serial_port_out(port, UART_IER, up->ier); } @@ -1630,6 +1632,8 @@ static void serial8250_enable_ms(struct uart_port *port) if (up->bugs & UART_BUG_NOMSR) return; + mctrl_gpio_enable_ms(up->gpios); + up->ier |= UART_IER_MSI; serial8250_rpm_get(up); @@ -1913,7 +1917,8 @@ unsigned int serial8250_do_get_mctrl(struct uart_port *port) ret |= TIOCM_DSR; if (status & UART_MSR_CTS) ret |= TIOCM_CTS; - return ret; + + return mctrl_gpio_get(up->gpios, &ret); } EXPORT_SYMBOL_GPL(serial8250_do_get_mctrl); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index b5a0f2e76f08..b1c73dec7eda 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -6,6 +6,7 @@ config SERIAL_8250 tristate "8250/16550 and compatible serial support" select SERIAL_CORE + select SERIAL_MCTRL_GPIO if GPIOLIB ---help--- This selects whether you want to include the driver for the standard serial ports. The standard answer is Y. People who might say N diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 48ec7651989b..923266cd294a 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -111,6 +111,7 @@ struct uart_8250_port { * if no_console_suspend */ unsigned char probe; + struct mctrl_gpios *gpios; #define UART_PROBE_RSA (1 << 0) /* -- cgit v1.2.3 From 65fe935dd2387a4faf15314c73f5e6d31ef0217e Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 13 Jun 2016 15:10:02 -0700 Subject: x86/KASLR, x86/power: Remove x86 hibernation restrictions With the following fix: 70595b479ce1 ("x86/power/64: Fix crash whan the hibernation code passes control to the image kernel") ... there is no longer a problem with hibernation resuming a KASLR-booted kernel image, so remove the restriction. Signed-off-by: Kees Cook Cc: Andy Lutomirski Cc: Baoquan He Cc: Borislav Petkov Cc: Brian Gerst Cc: Denys Vlasenko Cc: H. Peter Anvin Cc: Jonathan Corbet Cc: Len Brown Cc: Linus Torvalds Cc: Linux PM list Cc: Logan Gunthorpe Cc: Pavel Machek Cc: Peter Zijlstra Cc: Stephen Smalley Cc: Thomas Gleixner Cc: Yinghai Lu Cc: linux-doc@vger.kernel.org Link: http://lkml.kernel.org/r/20160613221002.GA29719@www.outflux.net Signed-off-by: Ingo Molnar --- Documentation/kernel-parameters.txt | 10 ++++------ arch/x86/boot/compressed/kaslr.c | 7 ------- kernel/power/hibernate.c | 6 ------ 3 files changed, 4 insertions(+), 19 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 82b42c958d1c..fa8c6d470ad2 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1803,12 +1803,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. js= [HW,JOY] Analog joystick See Documentation/input/joystick.txt. - kaslr/nokaslr [X86] - Enable/disable kernel and module base offset ASLR - (Address Space Layout Randomization) if built into - the kernel. When CONFIG_HIBERNATION is selected, - kASLR is disabled by default. When kASLR is enabled, - hibernation will be disabled. + nokaslr [KNL] + When CONFIG_RANDOMIZE_BASE is set, this disables + kernel and module base offset ASLR (Address Space + Layout Randomization). keepinitrd [HW,ARM] diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c index cfeb0259ed81..dff42177cb0c 100644 --- a/arch/x86/boot/compressed/kaslr.c +++ b/arch/x86/boot/compressed/kaslr.c @@ -471,17 +471,10 @@ unsigned char *choose_random_location(unsigned long input, unsigned long choice = output; unsigned long random_addr; -#ifdef CONFIG_HIBERNATION - if (!cmdline_find_option_bool("kaslr")) { - warn("KASLR disabled: 'kaslr' not on cmdline (hibernation selected)."); - goto out; - } -#else if (cmdline_find_option_bool("nokaslr")) { warn("KASLR disabled: 'nokaslr' on cmdline."); goto out; } -#endif boot_params->hdr.loadflags |= KASLR_FLAG; diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index fca9254280ee..9021387c6ff4 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -1154,11 +1154,6 @@ static int __init nohibernate_setup(char *str) return 1; } -static int __init kaslr_nohibernate_setup(char *str) -{ - return nohibernate_setup(str); -} - static int __init page_poison_nohibernate_setup(char *str) { #ifdef CONFIG_PAGE_POISONING_ZERO @@ -1182,5 +1177,4 @@ __setup("hibernate=", hibernate_setup); __setup("resumewait", resumewait_setup); __setup("resumedelay=", resumedelay_setup); __setup("nohibernate", nohibernate_setup); -__setup("kaslr", kaslr_nohibernate_setup); __setup("page_poison=", page_poison_nohibernate_setup); -- cgit v1.2.3 From 67626cc1a0f7bd6700b6f2f547244382247f5bb3 Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Mon, 27 Jun 2016 10:27:18 +0200 Subject: iio: ad5755: Add DT binding documentation Signed-off-by: Sean Nyekjaer Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/dac/ad5755.txt | 124 +++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/dac/ad5755.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/dac/ad5755.txt b/Documentation/devicetree/bindings/iio/dac/ad5755.txt new file mode 100644 index 000000000000..f0bbd7e1029b --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/ad5755.txt @@ -0,0 +1,124 @@ +* Analog Device AD5755 IIO Multi-Channel DAC Linux Driver + +Required properties: + - compatible: Has to contain one of the following: + adi,ad5755 + adi,ad5755-1 + adi,ad5757 + adi,ad5735 + adi,ad5737 + + - reg: spi chip select number for the device + - spi-cpha or spi-cpol: is the only modes that is supported + +Recommended properties: + - spi-max-frequency: Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt + +Optional properties: +See include/dt-bindings/iio/ad5755.h + - adi,ext-dc-dc-compenstation-resistor: boolean set if the hardware have an + external resistor and thereby bypasses + the internal compensation resistor. + - adi,dc-dc-phase: + Valid values for DC DC Phase control is: + 0: All dc-to-dc converters clock on the same edge. + 1: Channel A and Channel B clock on the same edge, + Channel C and Channel D clock on opposite edges. + 2: Channel A and Channel C clock on the same edge, + Channel B and Channel D clock on opposite edges. + 3: Channel A, Channel B, Channel C, and Channel D + clock 90 degrees out of phase from each other. + - adi,dc-dc-freq-hz: + Valid values for DC DC frequency is [Hz]: + 250000 + 410000 + 650000 + - adi,dc-dc-max-microvolt: + Valid values for the maximum allowed Vboost voltage supplied by + the dc-to-dc converter is: + 23000000 + 24500000 + 27000000 + 29500000 + +Optional for every channel: + - adi,mode: + Valid values for DAC modes is: + 0: 0 V to 5 V voltage range. + 1: 0 V to 10 V voltage range. + 2: Plus minus 5 V voltage range. + 3: Plus minus 10 V voltage range. + 4: 4 mA to 20 mA current range. + 5: 0 mA to 20 mA current range. + 6: 0 mA to 24 mA current range. + - adi,ext-current-sense-resistor: boolean set if the hardware a external + current sense resistor. + - adi,enable-voltage-overrange: boolean enable voltage overrange + - adi,slew: Array of slewrate settings should contain 3 fields: + 1: Should be either 0 or 1 in order to enable or disable slewrate. + 2: Slew rate settings: + Valid values for the slew rate update frequency: + 64000 + 32000 + 16000 + 8000 + 4000 + 2000 + 1000 + 500 + 250 + 125 + 64 + 32 + 16 + 8 + 4 + 0 + 3: Slew step size: + Valid values for the step size LSBs: + 1 + 2 + 4 + 16 + 32 + 64 + 128 + 256 + +Example: +dac@0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,ad5755"; + reg = <0>; + spi-max-frequency = <1000000>; + spi-cpha; + adi,dc-dc-phase = <0>; + adi,dc-dc-freq-hz = <410000>; + adi,dc-dc-max-microvolt = <23000000>; + channel@0 { + reg = <0>; + adi,mode = <4>; + adi,ext-current-sense-resistor; + adi,slew = <0 64000 1>; + }; + channel@1 { + reg = <1>; + adi,mode = <4>; + adi,ext-current-sense-resistor; + adi,slew = <0 64000 1>; + }; + channel@2 { + reg = <2>; + adi,mode = <4>; + adi,ext-current-sense-resistor; + adi,slew = <0 64000 1>; + }; + channel@3 { + reg = <3>; + adi,mode = <4>; + adi,ext-current-sense-resistor; + adi,slew = <0 64000 1>; + }; +}; -- cgit v1.2.3 From b6757042200f8c876be99213712f4a101334e203 Mon Sep 17 00:00:00 2001 From: Andrea Gelmini Date: Sat, 21 May 2016 13:41:00 +0200 Subject: hwmon: (abituguru) Fix typos in documentation Signed-off-by: Andrea Gelmini [groeck: Updated subject] Signed-off-by: Guenter Roeck --- Documentation/hwmon/abituguru | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/abituguru b/Documentation/hwmon/abituguru index f1d4fe4c366c..44013d23b3f0 100644 --- a/Documentation/hwmon/abituguru +++ b/Documentation/hwmon/abituguru @@ -24,7 +24,7 @@ Supported chips: AW9D-MAX) (2) 1) For revisions 2 and 3 uGuru's the driver can autodetect the sensortype (Volt or Temp) for bank1 sensors, for revision 1 uGuru's - this doesnot always work. For these uGuru's the autodection can + this does not always work. For these uGuru's the autodetection can be overridden with the bank1_types module param. For all 3 known revison 1 motherboards the correct use of this param is: bank1_types=1,1,0,0,0,0,0,2,0,0,0,0,2,0,0,1 -- cgit v1.2.3 From 76b5808e8cb9d8be214a1bbf613e19b6e3ebb3fb Mon Sep 17 00:00:00 2001 From: Andrea Gelmini Date: Sat, 21 May 2016 13:41:07 +0200 Subject: hwmon: (max1668) Fix typo in documentation Signed-off-by: Andrea Gelmini [groeck: Updated subject] Signed-off-by: Guenter Roeck --- Documentation/hwmon/max1668 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/max1668 b/Documentation/hwmon/max1668 index 0616ed9758df..8f9d570dbfec 100644 --- a/Documentation/hwmon/max1668 +++ b/Documentation/hwmon/max1668 @@ -17,7 +17,7 @@ This driver implements support for the Maxim MAX1668, MAX1805 and MAX1989 chips. The three devices are very similar, but the MAX1805 has a reduced feature -set; only two remote temperature inputs vs the four avaible on the other +set; only two remote temperature inputs vs the four available on the other two ICs. The driver is able to distinguish between the devices and creates sysfs -- cgit v1.2.3 From 7c84f7f80d6fcea36246b793d06c3555ca53ddcd Mon Sep 17 00:00:00 2001 From: David Frey Date: Thu, 2 Jun 2016 09:59:11 +0200 Subject: hwmon: add support for Sensirion SHT3x sensors This driver implements support for the Sensirion SHT3x-DIS chip, a humidity and temperature sensor. Temperature is measured in degrees celsius, relative humidity is expressed as a percentage. In the sysfs interface, all values are scaled by 1000, i.e. the value for 31.5 degrees celsius is 31500. Signed-off-by: Pascal Sachs [groeck: Fixed 'Variable length array is used' gcc warning] Signed-off-by: Guenter Roeck --- Documentation/hwmon/sht3x | 72 ++++ drivers/hwmon/Kconfig | 11 + drivers/hwmon/Makefile | 1 + drivers/hwmon/sht3x.c | 725 ++++++++++++++++++++++++++++++++++++ include/linux/platform_data/sht3x.h | 25 ++ 5 files changed, 834 insertions(+) create mode 100644 Documentation/hwmon/sht3x create mode 100644 drivers/hwmon/sht3x.c create mode 100644 include/linux/platform_data/sht3x.h (limited to 'Documentation') diff --git a/Documentation/hwmon/sht3x b/Documentation/hwmon/sht3x new file mode 100644 index 000000000000..f5aa633ed383 --- /dev/null +++ b/Documentation/hwmon/sht3x @@ -0,0 +1,72 @@ +Kernel driver sht3x +=================== + +Supported chips: + * Sensirion SHT3x-DIS + Prefix: 'sht3x' + Addresses scanned: none + Datasheet: http://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/Humidity/Sensirion_Humidity_Datasheet_SHT3x_DIS.pdf + +Author: + David Frey + Pascal Sachs + +Description +----------- + +This driver implements support for the Sensirion SHT3x-DIS chip, a humidity +and temperature sensor. Temperature is measured in degrees celsius, relative +humidity is expressed as a percentage. In the sysfs interface, all values are +scaled by 1000, i.e. the value for 31.5 degrees celsius is 31500. + +The device communicates with the I2C protocol. Sensors can have the I2C +addresses 0x44 or 0x45, depending on the wiring. See +Documentation/i2c/instantiating-devices for methods to instantiate the device. + +There are two options configurable by means of sht3x_platform_data: +1. blocking (pull the I2C clock line down while performing the measurement) or + non-blocking mode. Blocking mode will guarantee the fastest result but + the I2C bus will be busy during that time. By default, non-blocking mode + is used. Make sure clock-stretching works properly on your device if you + want to use blocking mode. +2. high or low accuracy. High accuracy is used by default and using it is + strongly recommended. + +The sht3x sensor supports a single shot mode as well as 5 periodic measure +modes, which can be controlled with the update_interval sysfs interface. +The allowed update_interval in milliseconds are as follows: + * 0 single shot mode + * 2000 0.5 Hz periodic measurement + * 1000 1 Hz periodic measurement + * 500 2 Hz periodic measurement + * 250 4 Hz periodic measurement + * 100 10 Hz periodic measurement + +In the periodic measure mode, the sensor automatically triggers a measurement +with the configured update interval on the chip. When a temperature or humidity +reading exceeds the configured limits, the alert attribute is set to 1 and +the alert pin on the sensor is set to high. +When the temperature and humidity readings move back between the hysteresis +values, the alert bit is set to 0 and the alert pin on the sensor is set to +low. + +sysfs-Interface +--------------- + +temp1_input: temperature input +humidity1_input: humidity input +temp1_max: temperature max value +temp1_max_hyst: temperature hysteresis value for max limit +humidity1_max: humidity max value +humidity1_max_hyst: humidity hysteresis value for max limit +temp1_min: temperature min value +temp1_min_hyst: temperature hysteresis value for min limit +humidity1_min: humidity min value +humidity1_min_hyst: humidity hysteresis value for min limit +temp1_alarm: alarm flag is set to 1 if the temperature is outside the + configured limits. Alarm only works in periodic measure mode +humidity1_alarm: alarm flag is set to 1 if the humidity is outside the + configured limits. Alarm only works in periodic measure mode +update_interval: update interval, 0 for single shot, interval in msec + for periodic measurement. If the interval is not supported + by the sensor, the next faster interval is chosen diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index ff940075bb90..e0be99a4615c 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1265,6 +1265,17 @@ config SENSORS_SHT21 This driver can also be built as a module. If so, the module will be called sht21. +config SENSORS_SHT3x + tristate "Sensiron humidity and temperature sensors. SHT3x and compat." + depends on I2C + select CRC8 + help + If you say yes here you get support for the Sensiron SHT30 and SHT31 + humidity and temperature sensors. + + This driver can also be built as a module. If so, the module + will be called sht3x. + config SENSORS_SHTC1 tristate "Sensiron humidity and temperature sensors. SHTC1 and compat." depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 2ef5b7c4c54f..406b18b995b3 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -138,6 +138,7 @@ obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o obj-$(CONFIG_SENSORS_SHT15) += sht15.o obj-$(CONFIG_SENSORS_SHT21) += sht21.o +obj-$(CONFIG_SENSORS_SHT3x) += sht3x.o obj-$(CONFIG_SENSORS_SHTC1) += shtc1.o obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o obj-$(CONFIG_SENSORS_SMM665) += smm665.o diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c new file mode 100644 index 000000000000..707a3f85385b --- /dev/null +++ b/drivers/hwmon/sht3x.c @@ -0,0 +1,725 @@ +/* Sensirion SHT3x-DIS humidity and temperature sensor driver. + * The SHT3x comes in many different versions, this driver is for the + * I2C version only. + * + * Copyright (C) 2016 Sensirion AG, Switzerland + * Author: David Frey + * Author: Pascal Sachs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* commands (high precision mode) */ +static const unsigned char sht3x_cmd_measure_blocking_hpm[] = { 0x2c, 0x06 }; +static const unsigned char sht3x_cmd_measure_nonblocking_hpm[] = { 0x24, 0x00 }; + +/* commands (low power mode) */ +static const unsigned char sht3x_cmd_measure_blocking_lpm[] = { 0x2c, 0x10 }; +static const unsigned char sht3x_cmd_measure_nonblocking_lpm[] = { 0x24, 0x16 }; + +/* commands for periodic mode */ +static const unsigned char sht3x_cmd_measure_periodic_mode[] = { 0xe0, 0x00 }; +static const unsigned char sht3x_cmd_break[] = { 0x30, 0x93 }; + +/* other commands */ +static const unsigned char sht3x_cmd_read_status_reg[] = { 0xf3, 0x2d }; +static const unsigned char sht3x_cmd_clear_status_reg[] = { 0x30, 0x41 }; + +/* delays for non-blocking i2c commands, both in us */ +#define SHT3X_NONBLOCKING_WAIT_TIME_HPM 15000 +#define SHT3X_NONBLOCKING_WAIT_TIME_LPM 4000 + +#define SHT3X_WORD_LEN 2 +#define SHT3X_CMD_LENGTH 2 +#define SHT3X_CRC8_LEN 1 +#define SHT3X_RESPONSE_LENGTH 6 +#define SHT3X_CRC8_POLYNOMIAL 0x31 +#define SHT3X_CRC8_INIT 0xFF +#define SHT3X_MIN_TEMPERATURE -45000 +#define SHT3X_MAX_TEMPERATURE 130000 +#define SHT3X_MIN_HUMIDITY 0 +#define SHT3X_MAX_HUMIDITY 100000 + +enum sht3x_chips { + sht3x, + sts3x, +}; + +enum sht3x_limits { + limit_max = 0, + limit_max_hyst, + limit_min, + limit_min_hyst, +}; + +DECLARE_CRC8_TABLE(sht3x_crc8_table); + +/* periodic measure commands (high precision mode) */ +static const char periodic_measure_commands_hpm[][SHT3X_CMD_LENGTH] = { + /* 0.5 measurements per second */ + {0x20, 0x32}, + /* 1 measurements per second */ + {0x21, 0x30}, + /* 2 measurements per second */ + {0x22, 0x36}, + /* 4 measurements per second */ + {0x23, 0x34}, + /* 10 measurements per second */ + {0x27, 0x37}, +}; + +/* periodic measure commands (low power mode) */ +static const char periodic_measure_commands_lpm[][SHT3X_CMD_LENGTH] = { + /* 0.5 measurements per second */ + {0x20, 0x2f}, + /* 1 measurements per second */ + {0x21, 0x2d}, + /* 2 measurements per second */ + {0x22, 0x2b}, + /* 4 measurements per second */ + {0x23, 0x29}, + /* 10 measurements per second */ + {0x27, 0x2a}, +}; + +struct sht3x_limit_commands { + const char read_command[SHT3X_CMD_LENGTH]; + const char write_command[SHT3X_CMD_LENGTH]; +}; + +static const struct sht3x_limit_commands limit_commands[] = { + /* temp1_max, humidity1_max */ + [limit_max] = { {0xe1, 0x1f}, {0x61, 0x1d} }, + /* temp_1_max_hyst, humidity1_max_hyst */ + [limit_max_hyst] = { {0xe1, 0x14}, {0x61, 0x16} }, + /* temp1_min, humidity1_min */ + [limit_min] = { {0xe1, 0x02}, {0x61, 0x00} }, + /* temp_1_min_hyst, humidity1_min_hyst */ + [limit_min_hyst] = { {0xe1, 0x09}, {0x61, 0x0B} }, +}; + +#define SHT3X_NUM_LIMIT_CMD ARRAY_SIZE(limit_commands) + +static const u16 mode_to_update_interval[] = { + 0, + 2000, + 1000, + 500, + 250, + 100, +}; + +struct sht3x_data { + struct i2c_client *client; + struct mutex i2c_lock; /* lock for sending i2c commands */ + struct mutex data_lock; /* lock for updating driver data */ + + u8 mode; + const unsigned char *command; + u32 wait_time; /* in us*/ + unsigned long last_update; /* last update in periodic mode*/ + + struct sht3x_platform_data setup; + + /* + * cached values for temperature and humidity and limits + * the limits arrays have the following order: + * max, max_hyst, min, min_hyst + */ + int temperature; + int temperature_limits[SHT3X_NUM_LIMIT_CMD]; + u32 humidity; + u32 humidity_limits[SHT3X_NUM_LIMIT_CMD]; +}; + +static u8 get_mode_from_update_interval(u16 value) +{ + size_t index; + u8 number_of_modes = ARRAY_SIZE(mode_to_update_interval); + + if (value == 0) + return 0; + + /* find next faster update interval */ + for (index = 1; index < number_of_modes; index++) { + if (mode_to_update_interval[index] <= value) + return index; + } + + return number_of_modes - 1; +} + +static int sht3x_read_from_command(struct i2c_client *client, + struct sht3x_data *data, + const char *command, + char *buf, int length, u32 wait_time) +{ + int ret; + + mutex_lock(&data->i2c_lock); + ret = i2c_master_send(client, command, SHT3X_CMD_LENGTH); + + if (ret != SHT3X_CMD_LENGTH) { + ret = ret < 0 ? ret : -EIO; + goto out; + } + + if (wait_time) + usleep_range(wait_time, wait_time + 1000); + + ret = i2c_master_recv(client, buf, length); + if (ret != length) { + ret = ret < 0 ? ret : -EIO; + goto out; + } + + ret = 0; +out: + mutex_unlock(&data->i2c_lock); + return ret; +} + +static int sht3x_extract_temperature(u16 raw) +{ + /* + * From datasheet: + * T = -45 + 175 * ST / 2^16 + * Adapted for integer fixed point (3 digit) arithmetic. + */ + return ((21875 * (int)raw) >> 13) - 45000; +} + +static u32 sht3x_extract_humidity(u16 raw) +{ + /* + * From datasheet: + * RH = 100 * SRH / 2^16 + * Adapted for integer fixed point (3 digit) arithmetic. + */ + return (12500 * (u32)raw) >> 13; +} + +static struct sht3x_data *sht3x_update_client(struct device *dev) +{ + struct sht3x_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + u16 interval_ms = mode_to_update_interval[data->mode]; + unsigned long interval_jiffies = msecs_to_jiffies(interval_ms); + unsigned char buf[SHT3X_RESPONSE_LENGTH]; + u16 val; + int ret = 0; + + mutex_lock(&data->data_lock); + /* + * Only update cached readings once per update interval in periodic + * mode. In single shot mode the sensor measures values on demand, so + * every time the sysfs interface is called, a measurement is triggered. + * In periodic mode however, the measurement process is handled + * internally by the sensor and reading out sensor values only makes + * sense if a new reading is available. + */ + if (time_after(jiffies, data->last_update + interval_jiffies)) { + ret = sht3x_read_from_command(client, data, data->command, buf, + sizeof(buf), data->wait_time); + if (ret) + goto out; + + val = be16_to_cpup((__be16 *)buf); + data->temperature = sht3x_extract_temperature(val); + val = be16_to_cpup((__be16 *)(buf + 3)); + data->humidity = sht3x_extract_humidity(val); + data->last_update = jiffies; + } + +out: + mutex_unlock(&data->data_lock); + if (ret) + return ERR_PTR(ret); + + return data; +} + +/* sysfs attributes */ +static ssize_t temp1_input_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sht3x_data *data = sht3x_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%d\n", data->temperature); +} + +static ssize_t humidity1_input_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sht3x_data *data = sht3x_update_client(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + return sprintf(buf, "%u\n", data->humidity); +} + +/* + * limits_update must only be called from probe or with data_lock held + */ +static int limits_update(struct sht3x_data *data) +{ + int ret; + u8 index; + int temperature; + u32 humidity; + u16 raw; + char buffer[SHT3X_RESPONSE_LENGTH]; + const struct sht3x_limit_commands *commands; + struct i2c_client *client = data->client; + + for (index = 0; index < SHT3X_NUM_LIMIT_CMD; index++) { + commands = &limit_commands[index]; + ret = sht3x_read_from_command(client, data, + commands->read_command, buffer, + SHT3X_RESPONSE_LENGTH, 0); + + if (ret) + return ret; + + raw = be16_to_cpup((__be16 *)buffer); + temperature = sht3x_extract_temperature((raw & 0x01ff) << 7); + humidity = sht3x_extract_humidity(raw & 0xfe00); + data->temperature_limits[index] = temperature; + data->humidity_limits[index] = humidity; + } + + return ret; +} + +static ssize_t temp1_limit_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sht3x_data *data = dev_get_drvdata(dev); + u8 index = to_sensor_dev_attr(attr)->index; + int temperature_limit = data->temperature_limits[index]; + + return scnprintf(buf, PAGE_SIZE, "%d\n", temperature_limit); +} + +static ssize_t humidity1_limit_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sht3x_data *data = dev_get_drvdata(dev); + u8 index = to_sensor_dev_attr(attr)->index; + u32 humidity_limit = data->humidity_limits[index]; + + return scnprintf(buf, PAGE_SIZE, "%u\n", humidity_limit); +} + +/* + * limit_store must only be called with data_lock held + */ +static size_t limit_store(struct device *dev, + size_t count, + u8 index, + int temperature, + u32 humidity) +{ + char buffer[SHT3X_CMD_LENGTH + SHT3X_WORD_LEN + SHT3X_CRC8_LEN]; + char *position = buffer; + int ret; + u16 raw; + struct sht3x_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + const struct sht3x_limit_commands *commands; + + commands = &limit_commands[index]; + + memcpy(position, commands->write_command, SHT3X_CMD_LENGTH); + position += SHT3X_CMD_LENGTH; + /* + * ST = (T + 45) / 175 * 2^16 + * SRH = RH / 100 * 2^16 + * adapted for fixed point arithmetic and packed the same as + * in limit_show() + */ + raw = ((u32)(temperature + 45000) * 24543) >> (16 + 7); + raw |= ((humidity * 42950) >> 16) & 0xfe00; + + *((__be16 *)position) = cpu_to_be16(raw); + position += SHT3X_WORD_LEN; + *position = crc8(sht3x_crc8_table, + position - SHT3X_WORD_LEN, + SHT3X_WORD_LEN, + SHT3X_CRC8_INIT); + + mutex_lock(&data->i2c_lock); + ret = i2c_master_send(client, buffer, sizeof(buffer)); + mutex_unlock(&data->i2c_lock); + + if (ret != sizeof(buffer)) + return ret < 0 ? ret : -EIO; + + data->temperature_limits[index] = temperature; + data->humidity_limits[index] = humidity; + return count; +} + +static ssize_t temp1_limit_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int temperature; + int ret; + struct sht3x_data *data = dev_get_drvdata(dev); + u8 index = to_sensor_dev_attr(attr)->index; + + ret = kstrtoint(buf, 0, &temperature); + if (ret) + return ret; + + temperature = clamp_val(temperature, SHT3X_MIN_TEMPERATURE, + SHT3X_MAX_TEMPERATURE); + mutex_lock(&data->data_lock); + ret = limit_store(dev, count, index, temperature, + data->humidity_limits[index]); + mutex_unlock(&data->data_lock); + + return ret; +} + +static ssize_t humidity1_limit_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + u32 humidity; + int ret; + struct sht3x_data *data = dev_get_drvdata(dev); + u8 index = to_sensor_dev_attr(attr)->index; + + ret = kstrtou32(buf, 0, &humidity); + if (ret) + return ret; + + humidity = clamp_val(humidity, SHT3X_MIN_HUMIDITY, SHT3X_MAX_HUMIDITY); + mutex_lock(&data->data_lock); + ret = limit_store(dev, count, index, data->temperature_limits[index], + humidity); + mutex_unlock(&data->data_lock); + + return ret; +} + +static void sht3x_select_command(struct sht3x_data *data) +{ + /* + * In blocking mode (clock stretching mode) the I2C bus + * is blocked for other traffic, thus the call to i2c_master_recv() + * will wait until the data is ready. For non blocking mode, we + * have to wait ourselves. + */ + if (data->mode > 0) { + data->command = sht3x_cmd_measure_periodic_mode; + data->wait_time = 0; + } else if (data->setup.blocking_io) { + data->command = data->setup.high_precision ? + sht3x_cmd_measure_blocking_hpm : + sht3x_cmd_measure_blocking_lpm; + data->wait_time = 0; + } else { + if (data->setup.high_precision) { + data->command = sht3x_cmd_measure_nonblocking_hpm; + data->wait_time = SHT3X_NONBLOCKING_WAIT_TIME_HPM; + } else { + data->command = sht3x_cmd_measure_nonblocking_lpm; + data->wait_time = SHT3X_NONBLOCKING_WAIT_TIME_LPM; + } + } +} + +static int status_register_read(struct device *dev, + struct device_attribute *attr, + char *buffer, int length) +{ + int ret; + struct sht3x_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + ret = sht3x_read_from_command(client, data, sht3x_cmd_read_status_reg, + buffer, length, 0); + + return ret; +} + +static ssize_t temp1_alarm_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + char buffer[SHT3X_WORD_LEN + SHT3X_CRC8_LEN]; + int ret; + + ret = status_register_read(dev, attr, buffer, + SHT3X_WORD_LEN + SHT3X_CRC8_LEN); + if (ret) + return ret; + + return scnprintf(buf, PAGE_SIZE, "%d\n", !!(buffer[0] & 0x04)); +} + +static ssize_t humidity1_alarm_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + char buffer[SHT3X_WORD_LEN + SHT3X_CRC8_LEN]; + int ret; + + ret = status_register_read(dev, attr, buffer, + SHT3X_WORD_LEN + SHT3X_CRC8_LEN); + if (ret) + return ret; + + return scnprintf(buf, PAGE_SIZE, "%d\n", !!(buffer[0] & 0x08)); +} + +static ssize_t update_interval_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sht3x_data *data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%u\n", + mode_to_update_interval[data->mode]); +} + +static ssize_t update_interval_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + u16 update_interval; + u8 mode; + int ret; + const char *command; + struct sht3x_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + ret = kstrtou16(buf, 0, &update_interval); + if (ret) + return ret; + + mode = get_mode_from_update_interval(update_interval); + + mutex_lock(&data->data_lock); + /* mode did not change */ + if (mode == data->mode) { + mutex_unlock(&data->data_lock); + return count; + } + + mutex_lock(&data->i2c_lock); + /* + * Abort periodic measure mode. + * To do any changes to the configuration while in periodic mode, we + * have to send a break command to the sensor, which then falls back + * to single shot (mode = 0). + */ + if (data->mode > 0) { + ret = i2c_master_send(client, sht3x_cmd_break, + SHT3X_CMD_LENGTH); + if (ret != SHT3X_CMD_LENGTH) + goto out; + data->mode = 0; + } + + if (mode > 0) { + if (data->setup.high_precision) + command = periodic_measure_commands_hpm[mode - 1]; + else + command = periodic_measure_commands_lpm[mode - 1]; + + /* select mode */ + ret = i2c_master_send(client, command, SHT3X_CMD_LENGTH); + if (ret != SHT3X_CMD_LENGTH) + goto out; + } + + /* select mode and command */ + data->mode = mode; + sht3x_select_command(data); + +out: + mutex_unlock(&data->i2c_lock); + mutex_unlock(&data->data_lock); + if (ret != SHT3X_CMD_LENGTH) + return ret < 0 ? ret : -EIO; + + return count; +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, temp1_input_show, NULL, 0); +static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO, humidity1_input_show, + NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, + temp1_limit_show, temp1_limit_store, + limit_max); +static SENSOR_DEVICE_ATTR(humidity1_max, S_IRUGO | S_IWUSR, + humidity1_limit_show, humidity1_limit_store, + limit_max); +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, + temp1_limit_show, temp1_limit_store, + limit_max_hyst); +static SENSOR_DEVICE_ATTR(humidity1_max_hyst, S_IRUGO | S_IWUSR, + humidity1_limit_show, humidity1_limit_store, + limit_max_hyst); +static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR, + temp1_limit_show, temp1_limit_store, + limit_min); +static SENSOR_DEVICE_ATTR(humidity1_min, S_IRUGO | S_IWUSR, + humidity1_limit_show, humidity1_limit_store, + limit_min); +static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO | S_IWUSR, + temp1_limit_show, temp1_limit_store, + limit_min_hyst); +static SENSOR_DEVICE_ATTR(humidity1_min_hyst, S_IRUGO | S_IWUSR, + humidity1_limit_show, humidity1_limit_store, + limit_min_hyst); +static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, temp1_alarm_show, NULL, 0); +static SENSOR_DEVICE_ATTR(humidity1_alarm, S_IRUGO, humidity1_alarm_show, + NULL, 0); +static SENSOR_DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, + update_interval_show, update_interval_store, 0); + +static struct attribute *sht3x_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_humidity1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_humidity1_max.dev_attr.attr, + &sensor_dev_attr_humidity1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_min_hyst.dev_attr.attr, + &sensor_dev_attr_humidity1_min.dev_attr.attr, + &sensor_dev_attr_humidity1_min_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_alarm.dev_attr.attr, + &sensor_dev_attr_humidity1_alarm.dev_attr.attr, + &sensor_dev_attr_update_interval.dev_attr.attr, + NULL +}; + +static struct attribute *sts3x_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL +}; + +ATTRIBUTE_GROUPS(sht3x); +ATTRIBUTE_GROUPS(sts3x); + +static int sht3x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct sht3x_data *data; + struct device *hwmon_dev; + struct i2c_adapter *adap = client->adapter; + struct device *dev = &client->dev; + const struct attribute_group **attribute_groups; + + /* + * we require full i2c support since the sht3x uses multi-byte read and + * writes as well as multi-byte commands which are not supported by + * the smbus protocol + */ + if (!i2c_check_functionality(adap, I2C_FUNC_I2C)) + return -ENODEV; + + ret = i2c_master_send(client, sht3x_cmd_clear_status_reg, + SHT3X_CMD_LENGTH); + if (ret != SHT3X_CMD_LENGTH) + return ret < 0 ? ret : -ENODEV; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->setup.blocking_io = false; + data->setup.high_precision = true; + data->mode = 0; + data->last_update = 0; + data->client = client; + crc8_populate_msb(sht3x_crc8_table, SHT3X_CRC8_POLYNOMIAL); + + if (client->dev.platform_data) + data->setup = *(struct sht3x_platform_data *)dev->platform_data; + + sht3x_select_command(data); + + mutex_init(&data->i2c_lock); + mutex_init(&data->data_lock); + + ret = limits_update(data); + if (ret) + return ret; + + if (id->driver_data == sts3x) + attribute_groups = sts3x_groups; + else + attribute_groups = sht3x_groups; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, + client->name, + data, + attribute_groups); + + if (IS_ERR(hwmon_dev)) + dev_dbg(dev, "unable to register hwmon device\n"); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +/* device ID table */ +static const struct i2c_device_id sht3x_ids[] = { + {"sht3x", sht3x}, + {"sts3x", sts3x}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, sht3x_ids); + +static struct i2c_driver sht3x_i2c_driver = { + .driver.name = "sht3x", + .probe = sht3x_probe, + .id_table = sht3x_ids, +}; + +module_i2c_driver(sht3x_i2c_driver); + +MODULE_AUTHOR("David Frey "); +MODULE_AUTHOR("Pascal Sachs "); +MODULE_DESCRIPTION("Sensirion SHT3x humidity and temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/sht3x.h b/include/linux/platform_data/sht3x.h new file mode 100644 index 000000000000..2e5eea358194 --- /dev/null +++ b/include/linux/platform_data/sht3x.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2016 Sensirion AG, Switzerland + * Author: David Frey + * Author: Pascal Sachs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 __SHT3X_H_ +#define __SHT3X_H_ + +struct sht3x_platform_data { + bool blocking_io; + bool high_precision; +}; +#endif /* __SHT3X_H_ */ -- cgit v1.2.3 From c0a68601804dcb4ee8a141e42e1e6893b6b0610c Mon Sep 17 00:00:00 2001 From: Andrew F. Davis Date: Wed, 8 Jun 2016 12:00:54 -0500 Subject: hwmon: (tmp401) Add support for TI TMP461 Signed-off-by: Andrew F. Davis Signed-off-by: Guenter Roeck --- Documentation/hwmon/tmp401 | 14 ++++++++++++-- drivers/hwmon/Kconfig | 2 +- drivers/hwmon/tmp401.c | 35 +++++++++++++++++++++++++++++------ 3 files changed, 42 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/tmp401 b/Documentation/hwmon/tmp401 index 711f75e189eb..2d9ca42213cf 100644 --- a/Documentation/hwmon/tmp401 +++ b/Documentation/hwmon/tmp401 @@ -22,6 +22,9 @@ Supported chips: Prefix: 'tmp435' Addresses scanned: I2C 0x48 - 0x4f Datasheet: http://focus.ti.com/docs/prod/folders/print/tmp435.html + * Texas Instruments TMP461 + Prefix: 'tmp461' + Datasheet: http://www.ti.com/product/tmp461 Authors: Hans de Goede @@ -31,8 +34,8 @@ Description ----------- This driver implements support for Texas Instruments TMP401, TMP411, -TMP431, TMP432 and TMP435 chips. These chips implement one or two remote -and one local temperature sensors. Temperature is measured in degrees +TMP431, TMP432, TMP435, and TMP461 chips. These chips implement one or two +remote and one local temperature sensors. Temperature is measured in degrees Celsius. Resolution of the remote sensor is 0.0625 degree. Local sensor resolution can be set to 0.5, 0.25, 0.125 or 0.0625 degree (not supported by the driver so far, so using the default resolution of 0.5 @@ -55,3 +58,10 @@ some additional features. TMP432 is compatible with TMP401 and TMP431. It supports two external temperature sensors. + +TMP461 is compatible with TMP401. It supports offset correction +that is applied to the remote sensor. + +* Sensor offset values are temperature values + + Exported via sysfs attribute tempX_offset diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index e0be99a4615c..be63b140a976 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1572,7 +1572,7 @@ config SENSORS_TMP401 depends on I2C help If you say yes here you get support for Texas Instruments TMP401, - TMP411, TMP431, TMP432 and TMP435 temperature sensor chips. + TMP411, TMP431, TMP432, TMP435, and TMP461 temperature sensor chips. This driver can also be built as a module. If so, the module will be called tmp401. diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index ccf4cffe0ee1..eeeed2c7d081 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -47,7 +47,7 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; -enum chips { tmp401, tmp411, tmp431, tmp432, tmp435 }; +enum chips { tmp401, tmp411, tmp431, tmp432, tmp435, tmp461 }; /* * The TMP401 registers, note some registers have different addresses for @@ -62,31 +62,34 @@ enum chips { tmp401, tmp411, tmp431, tmp432, tmp435 }; #define TMP401_MANUFACTURER_ID_REG 0xFE #define TMP401_DEVICE_ID_REG 0xFF -static const u8 TMP401_TEMP_MSB_READ[6][2] = { +static const u8 TMP401_TEMP_MSB_READ[7][2] = { { 0x00, 0x01 }, /* temp */ { 0x06, 0x08 }, /* low limit */ { 0x05, 0x07 }, /* high limit */ { 0x20, 0x19 }, /* therm (crit) limit */ { 0x30, 0x34 }, /* lowest */ { 0x32, 0x36 }, /* highest */ + { 0, 0x11 }, /* offset */ }; -static const u8 TMP401_TEMP_MSB_WRITE[6][2] = { +static const u8 TMP401_TEMP_MSB_WRITE[7][2] = { { 0, 0 }, /* temp (unused) */ { 0x0C, 0x0E }, /* low limit */ { 0x0B, 0x0D }, /* high limit */ { 0x20, 0x19 }, /* therm (crit) limit */ { 0x30, 0x34 }, /* lowest */ { 0x32, 0x36 }, /* highest */ + { 0, 0x11 }, /* offset */ }; -static const u8 TMP401_TEMP_LSB[6][2] = { +static const u8 TMP401_TEMP_LSB[7][2] = { { 0x15, 0x10 }, /* temp */ { 0x17, 0x14 }, /* low limit */ { 0x16, 0x13 }, /* high limit */ { 0, 0 }, /* therm (crit) limit (unused) */ { 0x31, 0x35 }, /* lowest */ { 0x33, 0x37 }, /* highest */ + { 0, 0x12 }, /* offset */ }; static const u8 TMP432_TEMP_MSB_READ[4][3] = { @@ -149,6 +152,7 @@ static const struct i2c_device_id tmp401_id[] = { { "tmp431", tmp431 }, { "tmp432", tmp432 }, { "tmp435", tmp435 }, + { "tmp461", tmp461 }, { } }; MODULE_DEVICE_TABLE(i2c, tmp401_id); @@ -170,7 +174,7 @@ struct tmp401_data { /* register values */ u8 status[4]; u8 config; - u16 temp[6][3]; + u16 temp[7][3]; u8 temp_crit_hyst; }; @@ -612,6 +616,22 @@ static const struct attribute_group tmp432_group = { .attrs = tmp432_attributes, }; +/* + * Additional features of the TMP461 chip. + * The TMP461 temperature offset for the remote channel. + */ +static SENSOR_DEVICE_ATTR_2(temp2_offset, S_IWUSR | S_IRUGO, show_temp, + store_temp, 6, 1); + +static struct attribute *tmp461_attributes[] = { + &sensor_dev_attr_temp2_offset.dev_attr.attr, + NULL +}; + +static const struct attribute_group tmp461_group = { + .attrs = tmp461_attributes, +}; + /* * Begin non sysfs callback code (aka Real code) */ @@ -714,7 +734,7 @@ static int tmp401_probe(struct i2c_client *client, const struct i2c_device_id *id) { static const char * const names[] = { - "TMP401", "TMP411", "TMP431", "TMP432", "TMP435" + "TMP401", "TMP411", "TMP431", "TMP432", "TMP435", "TMP461" }; struct device *dev = &client->dev; struct device *hwmon_dev; @@ -745,6 +765,9 @@ static int tmp401_probe(struct i2c_client *client, if (data->kind == tmp432) data->groups[groups++] = &tmp432_group; + if (data->kind == tmp461) + data->groups[groups++] = &tmp461_group; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, data, data->groups); if (IS_ERR(hwmon_dev)) -- cgit v1.2.3 From 7cb6dcff1956ec9e338abfa2f298d2971cfbab79 Mon Sep 17 00:00:00 2001 From: Andrew F. Davis Date: Fri, 10 Jun 2016 10:32:33 -0500 Subject: hwmon: Add support for INA3221 Triple Current/Voltage Monitors Add support for the the INA3221 26v capable, Triple channel, Bi-Directional, Zero-Drift, Low-/High-Side, Current/Voltage Monitor with I2C interface. Signed-off-by: Andrew F. Davis Signed-off-by: Guenter Roeck --- Documentation/hwmon/ina3221 | 35 ++++ drivers/hwmon/Kconfig | 11 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/ina3221.c | 446 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 493 insertions(+) create mode 100644 Documentation/hwmon/ina3221 create mode 100644 drivers/hwmon/ina3221.c (limited to 'Documentation') diff --git a/Documentation/hwmon/ina3221 b/Documentation/hwmon/ina3221 new file mode 100644 index 000000000000..0ff74854cb2e --- /dev/null +++ b/Documentation/hwmon/ina3221 @@ -0,0 +1,35 @@ +Kernel driver ina3221 +===================== + +Supported chips: + * Texas Instruments INA3221 + Prefix: 'ina3221' + Addresses: I2C 0x40 - 0x43 + Datasheet: Publicly available at the Texas Instruments website + http://www.ti.com/ + +Author: Andrew F. Davis + +Description +----------- + +The Texas Instruments INA3221 monitors voltage, current, and power on the high +side of up to three D.C. power supplies. The INA3221 monitors both shunt drop +and supply voltage, with programmable conversion times and averaging, current +and power are calculated host-side from these. + +Sysfs entries +------------- + +in[123]_input Bus voltage(mV) channels +curr[123]_input Current(mA) measurement channels +shunt[123]_resistor Shunt resistance(uOhm) channels +curr[123]_crit Critical alert current(mA) setting, activates the + corresponding alarm when the respective current + is above this value +curr[123]_crit_alarm Critical alert current limit exceeded +curr[123]_max Warning alert current(mA) setting, activates the + corresponding alarm when the respective current + average is above this value. +curr[123]_max_alarm Warning alert current limit exceeded +in[456]_input Shunt voltage(uV) for channels 1, 2, and 3 respectively diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index be63b140a976..b2aa13bd89dc 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1525,6 +1525,17 @@ config SENSORS_INA2XX This driver can also be built as a module. If so, the module will be called ina2xx. +config SENSORS_INA3221 + tristate "Texas Instruments INA3221 Triple Power Monitor" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for the TI INA3221 Triple Power + Monitor. + + This driver can also be built as a module. If so, the module + will be called ina3221. + config SENSORS_TC74 tristate "Microchip TC74" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 406b18b995b3..446a4e74c7cf 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -77,6 +77,7 @@ obj-$(CONFIG_SENSORS_IBMPOWERNV)+= ibmpowernv.o obj-$(CONFIG_SENSORS_IIO_HWMON) += iio_hwmon.o obj-$(CONFIG_SENSORS_INA209) += ina209.o obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o +obj-$(CONFIG_SENSORS_INA3221) += ina3221.o obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_JC42) += jc42.o obj-$(CONFIG_SENSORS_JZ4740) += jz4740-hwmon.o diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c new file mode 100644 index 000000000000..d055b6a2266b --- /dev/null +++ b/drivers/hwmon/ina3221.c @@ -0,0 +1,446 @@ +/* + * INA3221 Triple Current/Voltage Monitor + * + * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/ + * Andrew F. Davis + * + * 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. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#define INA3221_DRIVER_NAME "ina3221" + +#define INA3221_CONFIG 0x00 +#define INA3221_SHUNT1 0x01 +#define INA3221_BUS1 0x02 +#define INA3221_SHUNT2 0x03 +#define INA3221_BUS2 0x04 +#define INA3221_SHUNT3 0x05 +#define INA3221_BUS3 0x06 +#define INA3221_CRIT1 0x07 +#define INA3221_WARN1 0x08 +#define INA3221_CRIT2 0x09 +#define INA3221_WARN2 0x0a +#define INA3221_CRIT3 0x0b +#define INA3221_WARN3 0x0c +#define INA3221_MASK_ENABLE 0x0f + +#define INA3221_CONFIG_MODE_SHUNT BIT(1) +#define INA3221_CONFIG_MODE_BUS BIT(2) +#define INA3221_CONFIG_MODE_CONTINUOUS BIT(3) + +#define INA3221_RSHUNT_DEFAULT 10000 + +enum ina3221_fields { + /* Configuration */ + F_RST, + + /* Alert Flags */ + F_WF3, F_WF2, F_WF1, + F_CF3, F_CF2, F_CF1, + + /* sentinel */ + F_MAX_FIELDS +}; + +static const struct reg_field ina3221_reg_fields[] = { + [F_RST] = REG_FIELD(INA3221_CONFIG, 15, 15), + + [F_WF3] = REG_FIELD(INA3221_MASK_ENABLE, 3, 3), + [F_WF2] = REG_FIELD(INA3221_MASK_ENABLE, 4, 4), + [F_WF1] = REG_FIELD(INA3221_MASK_ENABLE, 5, 5), + [F_CF3] = REG_FIELD(INA3221_MASK_ENABLE, 7, 7), + [F_CF2] = REG_FIELD(INA3221_MASK_ENABLE, 8, 8), + [F_CF1] = REG_FIELD(INA3221_MASK_ENABLE, 9, 9), +}; + +enum ina3221_channels { + INA3221_CHANNEL1, + INA3221_CHANNEL2, + INA3221_CHANNEL3, + INA3221_NUM_CHANNELS +}; + +static const unsigned int register_channel[] = { + [INA3221_SHUNT1] = INA3221_CHANNEL1, + [INA3221_SHUNT2] = INA3221_CHANNEL2, + [INA3221_SHUNT3] = INA3221_CHANNEL3, + [INA3221_CRIT1] = INA3221_CHANNEL1, + [INA3221_CRIT2] = INA3221_CHANNEL2, + [INA3221_CRIT3] = INA3221_CHANNEL3, + [INA3221_WARN1] = INA3221_CHANNEL1, + [INA3221_WARN2] = INA3221_CHANNEL2, + [INA3221_WARN3] = INA3221_CHANNEL3, +}; + +/** + * struct ina3221_data - device specific information + * @regmap: Register map of the device + * @fields: Register fields of the device + * @shunt_resistors: Array of resistor values per channel + */ +struct ina3221_data { + struct regmap *regmap; + struct regmap_field *fields[F_MAX_FIELDS]; + unsigned int shunt_resistors[INA3221_NUM_CHANNELS]; +}; + +static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg, + int *val) +{ + unsigned int regval; + int ret; + + ret = regmap_read(ina->regmap, reg, ®val); + if (ret) + return ret; + + *val = sign_extend32(regval >> 3, 12); + + return 0; +} + +static ssize_t ina3221_show_bus_voltage(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); + struct ina3221_data *ina = dev_get_drvdata(dev); + unsigned int reg = sd_attr->index; + int val, voltage_mv, ret; + + ret = ina3221_read_value(ina, reg, &val); + if (ret) + return ret; + + voltage_mv = val * 8; + + return snprintf(buf, PAGE_SIZE, "%d\n", voltage_mv); +} + +static ssize_t ina3221_show_shunt_voltage(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); + struct ina3221_data *ina = dev_get_drvdata(dev); + unsigned int reg = sd_attr->index; + int val, voltage_uv, ret; + + ret = ina3221_read_value(ina, reg, &val); + if (ret) + return ret; + voltage_uv = val * 40; + + return snprintf(buf, PAGE_SIZE, "%d\n", voltage_uv); +} + +static ssize_t ina3221_show_current(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); + struct ina3221_data *ina = dev_get_drvdata(dev); + unsigned int reg = sd_attr->index; + unsigned int channel = register_channel[reg]; + unsigned int resistance_uo = ina->shunt_resistors[channel]; + int val, current_ma, voltage_nv, ret; + + ret = ina3221_read_value(ina, reg, &val); + if (ret) + return ret; + voltage_nv = val * 40000; + + current_ma = DIV_ROUND_CLOSEST(voltage_nv, resistance_uo); + + return snprintf(buf, PAGE_SIZE, "%d\n", current_ma); +} + +static ssize_t ina3221_set_current(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); + struct ina3221_data *ina = dev_get_drvdata(dev); + unsigned int reg = sd_attr->index; + unsigned int channel = register_channel[reg]; + unsigned int resistance_uo = ina->shunt_resistors[channel]; + int val, current_ma, voltage_uv, ret; + + ret = kstrtoint(buf, 0, ¤t_ma); + if (ret) + return ret; + + /* clamp current */ + current_ma = clamp_val(current_ma, + INT_MIN / resistance_uo, + INT_MAX / resistance_uo); + + voltage_uv = DIV_ROUND_CLOSEST(current_ma * resistance_uo, 1000); + + /* clamp voltage */ + voltage_uv = clamp_val(voltage_uv, -163800, 163800); + + /* 1 / 40uV(scale) << 3(register shift) = 5 */ + val = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8; + + ret = regmap_write(ina->regmap, reg, val); + if (ret) + return ret; + + return count; +} + +static ssize_t ina3221_show_shunt(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); + struct ina3221_data *ina = dev_get_drvdata(dev); + unsigned int channel = sd_attr->index; + unsigned int resistance_uo; + + resistance_uo = ina->shunt_resistors[channel]; + + return snprintf(buf, PAGE_SIZE, "%d\n", resistance_uo); +} + +static ssize_t ina3221_set_shunt(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); + struct ina3221_data *ina = dev_get_drvdata(dev); + unsigned int channel = sd_attr->index; + unsigned int val; + int ret; + + ret = kstrtouint(buf, 0, &val); + if (ret) + return ret; + + if (val == 0) + return -EINVAL; + + ina->shunt_resistors[channel] = val; + + return count; +} + +static ssize_t ina3221_show_alert(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); + struct ina3221_data *ina = dev_get_drvdata(dev); + unsigned int field = sd_attr->index; + unsigned int regval; + int ret; + + ret = regmap_field_read(ina->fields[field], ®val); + if (ret) + return ret; + + return snprintf(buf, PAGE_SIZE, "%d\n", regval); +} + +/* bus voltage */ +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, + ina3221_show_bus_voltage, NULL, INA3221_BUS1); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, + ina3221_show_bus_voltage, NULL, INA3221_BUS2); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, + ina3221_show_bus_voltage, NULL, INA3221_BUS3); + +/* calculated current */ +static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, + ina3221_show_current, NULL, INA3221_SHUNT1); +static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, + ina3221_show_current, NULL, INA3221_SHUNT2); +static SENSOR_DEVICE_ATTR(curr3_input, S_IRUGO, + ina3221_show_current, NULL, INA3221_SHUNT3); + +/* shunt resistance */ +static SENSOR_DEVICE_ATTR(shunt1_resistor, S_IRUGO | S_IWUSR, + ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL1); +static SENSOR_DEVICE_ATTR(shunt2_resistor, S_IRUGO | S_IWUSR, + ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL2); +static SENSOR_DEVICE_ATTR(shunt3_resistor, S_IRUGO | S_IWUSR, + ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL3); + +/* critical current */ +static SENSOR_DEVICE_ATTR(curr1_crit, S_IRUGO | S_IWUSR, + ina3221_show_current, ina3221_set_current, INA3221_CRIT1); +static SENSOR_DEVICE_ATTR(curr2_crit, S_IRUGO | S_IWUSR, + ina3221_show_current, ina3221_set_current, INA3221_CRIT2); +static SENSOR_DEVICE_ATTR(curr3_crit, S_IRUGO | S_IWUSR, + ina3221_show_current, ina3221_set_current, INA3221_CRIT3); + +/* critical current alert */ +static SENSOR_DEVICE_ATTR(curr1_crit_alarm, S_IRUGO, + ina3221_show_alert, NULL, F_CF1); +static SENSOR_DEVICE_ATTR(curr2_crit_alarm, S_IRUGO, + ina3221_show_alert, NULL, F_CF2); +static SENSOR_DEVICE_ATTR(curr3_crit_alarm, S_IRUGO, + ina3221_show_alert, NULL, F_CF3); + +/* warning current */ +static SENSOR_DEVICE_ATTR(curr1_max, S_IRUGO | S_IWUSR, + ina3221_show_current, ina3221_set_current, INA3221_WARN1); +static SENSOR_DEVICE_ATTR(curr2_max, S_IRUGO | S_IWUSR, + ina3221_show_current, ina3221_set_current, INA3221_WARN2); +static SENSOR_DEVICE_ATTR(curr3_max, S_IRUGO | S_IWUSR, + ina3221_show_current, ina3221_set_current, INA3221_WARN3); + +/* warning current alert */ +static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, + ina3221_show_alert, NULL, F_WF1); +static SENSOR_DEVICE_ATTR(curr2_max_alarm, S_IRUGO, + ina3221_show_alert, NULL, F_WF2); +static SENSOR_DEVICE_ATTR(curr3_max_alarm, S_IRUGO, + ina3221_show_alert, NULL, F_WF3); + +/* shunt voltage */ +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, + ina3221_show_shunt_voltage, NULL, INA3221_SHUNT1); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, + ina3221_show_shunt_voltage, NULL, INA3221_SHUNT2); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, + ina3221_show_shunt_voltage, NULL, INA3221_SHUNT3); + +static struct attribute *ina3221_attrs[] = { + /* channel 1 */ + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_shunt1_resistor.dev_attr.attr, + &sensor_dev_attr_curr1_crit.dev_attr.attr, + &sensor_dev_attr_curr1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_curr1_max.dev_attr.attr, + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + + /* channel 2 */ + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_curr2_input.dev_attr.attr, + &sensor_dev_attr_shunt2_resistor.dev_attr.attr, + &sensor_dev_attr_curr2_crit.dev_attr.attr, + &sensor_dev_attr_curr2_crit_alarm.dev_attr.attr, + &sensor_dev_attr_curr2_max.dev_attr.attr, + &sensor_dev_attr_curr2_max_alarm.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + + /* channel 3 */ + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_curr3_input.dev_attr.attr, + &sensor_dev_attr_shunt3_resistor.dev_attr.attr, + &sensor_dev_attr_curr3_crit.dev_attr.attr, + &sensor_dev_attr_curr3_crit_alarm.dev_attr.attr, + &sensor_dev_attr_curr3_max.dev_attr.attr, + &sensor_dev_attr_curr3_max_alarm.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + + NULL, +}; +ATTRIBUTE_GROUPS(ina3221); + +static const struct regmap_range ina3221_yes_ranges[] = { + regmap_reg_range(INA3221_SHUNT1, INA3221_BUS3), + regmap_reg_range(INA3221_MASK_ENABLE, INA3221_MASK_ENABLE), +}; + +static const struct regmap_access_table ina3221_volatile_table = { + .yes_ranges = ina3221_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(ina3221_yes_ranges), +}; + +static const struct regmap_config ina3221_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + + .cache_type = REGCACHE_RBTREE, + .volatile_table = &ina3221_volatile_table, +}; + +static int ina3221_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct ina3221_data *ina; + struct device *hwmon_dev; + int i, ret; + + ina = devm_kzalloc(dev, sizeof(*ina), GFP_KERNEL); + if (!ina) + return -ENOMEM; + + ina->regmap = devm_regmap_init_i2c(client, &ina3221_regmap_config); + if (IS_ERR(ina->regmap)) { + dev_err(dev, "Unable to allocate register map\n"); + return PTR_ERR(ina->regmap); + } + + for (i = 0; i < F_MAX_FIELDS; i++) { + ina->fields[i] = devm_regmap_field_alloc(dev, + ina->regmap, + ina3221_reg_fields[i]); + if (IS_ERR(ina->fields[i])) { + dev_err(dev, "Unable to allocate regmap fields\n"); + return PTR_ERR(ina->fields[i]); + } + } + + for (i = 0; i < INA3221_NUM_CHANNELS; i++) + ina->shunt_resistors[i] = INA3221_RSHUNT_DEFAULT; + + ret = regmap_field_write(ina->fields[F_RST], true); + if (ret) { + dev_err(dev, "Unable to reset device\n"); + return ret; + } + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, + client->name, + ina, ina3221_groups); + if (IS_ERR(hwmon_dev)) { + dev_err(dev, "Unable to register hwmon device\n"); + return PTR_ERR(hwmon_dev); + } + + return 0; +} + +static const struct of_device_id ina3221_of_match_table[] = { + { .compatible = "ti,ina3221", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ina3221_of_match_table); + +static const struct i2c_device_id ina3221_ids[] = { + { "ina3221", 0 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, ina3221_ids); + +static struct i2c_driver ina3221_i2c_driver = { + .probe = ina3221_probe, + .driver = { + .name = INA3221_DRIVER_NAME, + .of_match_table = ina3221_of_match_table, + }, + .id_table = ina3221_ids, +}; +module_i2c_driver(ina3221_i2c_driver); + +MODULE_AUTHOR("Andrew F. Davis "); +MODULE_DESCRIPTION("Texas Instruments INA3221 HWMon Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 165720d9ff3e33f626802da6cef3118ebfc6940c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 10 Jun 2016 21:01:28 -0700 Subject: hwmon: Update guildelines for submitting patches Add more details to the guidelines for submitting patches. Signed-off-by: Guenter Roeck --- Documentation/hwmon/submitting-patches | 40 +++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 6 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/submitting-patches b/Documentation/hwmon/submitting-patches index d201828d202f..57f60307accc 100644 --- a/Documentation/hwmon/submitting-patches +++ b/Documentation/hwmon/submitting-patches @@ -15,10 +15,15 @@ increase the chances of your change being accepted. Documentation/SubmittingPatches Documentation/CodingStyle -* If your patch generates checkpatch warnings, please refrain from explanations - such as "I don't like that coding style". Keep in mind that each unnecessary - warning helps hiding a real problem. If you don't like the kernel coding - style, don't write kernel drivers. +* Please run your patch through 'checkpatch --strict'. There should be no + errors, no warnings, and few if any check messages. If there are any + messages, please be prepared to explain. + +* If your patch generates checkpatch errors, warnings, or check messages, + please refrain from explanations such as "I prefer that coding style". + Keep in mind that each unnecessary message helps hiding a real problem, + and a consistent coding style makes it easier for others to understand + and review the code. * Please test your patch thoroughly. We are not your test group. Sometimes a patch can not or not completely be tested because of missing @@ -61,15 +66,30 @@ increase the chances of your change being accepted. * Make sure that all dependencies are listed in Kconfig. +* Please list include files in alphabetic order. + +* Please align continuation lines with '(' on the previous line. + * Avoid forward declarations if you can. Rearrange the code if necessary. +* Avoid macros to generate groups of sensor attributes. It not only confuses + checkpatch, but also makes it more difficult to review the code. + * Avoid calculations in macros and macro-generated functions. While such macros may save a line or so in the source, it obfuscates the code and makes code review more difficult. It may also result in code which is more complicated than necessary. Use inline functions or just regular functions instead. +* Limit the number of kernel log messages. In general, your driver should not + generate an error message just because a runtime operation failed. Report + errors to user space instead, using an appropriate error code. Keep in mind + that kernel error log messages not only fill up the kernel log, but also are + printed synchronously, most likely with interrupt disabled, often to a serial + console. Excessive logging can seriously affect system performance. + * Use devres functions whenever possible to allocate resources. For rationale and supported functions, please see Documentation/driver-model/devres.txt. + If a function is not supported by devres, consider using devm_add_action(). * If the driver has a detect function, make sure it is silent. Debug messages and messages printed after a successful detection are acceptable, but it @@ -96,8 +116,16 @@ increase the chances of your change being accepted. writing to it might cause a bad misconfiguration. * Make sure there are no race conditions in the probe function. Specifically, - completely initialize your chip first, then create sysfs entries and register - with the hwmon subsystem. + completely initialize your chip and your driver first, then register with + the hwmon subsystem. + +* Use devm_hwmon_device_register_with_groups() or, if your driver needs a remove + function, hwmon_device_register_with_groups() to register your driver with the + hwmon subsystem. Try using devm_add_action() instead of a remove function if + possible. Do not use hwmon_device_register(). + +* Your driver should be buildable as module. If not, please be prepared to + explain why it has to be built into the kernel. * Do not provide support for deprecated sysfs attributes. -- cgit v1.2.3 From a31887dc9be1a65cde2562ed10e7fbf5c82581a2 Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Mon, 27 Jun 2016 17:23:27 -0700 Subject: hwmon: (jc42) Add support for Microchip MCP9808 temperature sensor MCP9808 is not officially compliant to JC-42, similar to MCP9804, but its registers are compatible to JC-42. Signed-off-by: Alison Schofield Cc: Daniel Baluta Signed-off-by: Guenter Roeck --- Documentation/hwmon/jc42 | 3 ++- drivers/hwmon/Kconfig | 4 ++-- drivers/hwmon/jc42.c | 4 ++++ 3 files changed, 8 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/jc42 b/Documentation/hwmon/jc42 index f7f1830a2566..b4b671f22453 100644 --- a/Documentation/hwmon/jc42 +++ b/Documentation/hwmon/jc42 @@ -18,10 +18,11 @@ Supported chips: * Maxim MAX6604 Datasheets: http://datasheets.maxim-ic.com/en/ds/MAX6604.pdf - * Microchip MCP9804, MCP9805, MCP98242, MCP98243, MCP98244, MCP9843 + * Microchip MCP9804, MCP9805, MCP9808, MCP98242, MCP98243, MCP98244, MCP9843 Datasheets: http://ww1.microchip.com/downloads/en/DeviceDoc/22203C.pdf http://ww1.microchip.com/downloads/en/DeviceDoc/21977b.pdf + http://ww1.microchip.com/downloads/en/DeviceDoc/25095A.pdf http://ww1.microchip.com/downloads/en/DeviceDoc/21996a.pdf http://ww1.microchip.com/downloads/en/DeviceDoc/22153c.pdf http://ww1.microchip.com/downloads/en/DeviceDoc/22327A.pdf diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index b2aa13bd89dc..91f145e0cd45 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -645,8 +645,8 @@ config SENSORS_JC42 temperature sensors, which are used on many DDR3 memory modules for mobile devices and servers. Support will include, but not be limited to, ADT7408, AT30TS00, CAT34TS02, CAT6095, MAX6604, MCP9804, MCP9805, - MCP98242, MCP98243, MCP98244, MCP9843, SE97, SE98, STTS424(E), - STTS2002, STTS3000, TSE2002, TSE2004, TS3000, and TS3001. + MCP9808, MCP98242, MCP98243, MCP98244, MCP9843, SE97, SE98, + STTS424(E), STTS2002, STTS3000, TSE2002, TSE2004, TS3000, and TS3001. This driver can also be built as a module. If so, the module will be called jc42. diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 9887d3224a86..f67c1bbe2c51 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -104,6 +104,9 @@ static const unsigned short normal_i2c[] = { #define MCP9804_DEVID 0x0200 #define MCP9804_DEVID_MASK 0xfffc +#define MCP9808_DEVID 0x0400 +#define MCP9808_DEVID_MASK 0xfffc + #define MCP98242_DEVID 0x2000 #define MCP98242_DEVID_MASK 0xfffc @@ -160,6 +163,7 @@ static struct jc42_chips jc42_chips[] = { { IDT_MANID, TS3001_DEVID, TS3001_DEVID_MASK }, { MAX_MANID, MAX6604_DEVID, MAX6604_DEVID_MASK }, { MCP_MANID, MCP9804_DEVID, MCP9804_DEVID_MASK }, + { MCP_MANID, MCP9808_DEVID, MCP9808_DEVID_MASK }, { MCP_MANID, MCP98242_DEVID, MCP98242_DEVID_MASK }, { MCP_MANID, MCP98243_DEVID, MCP98243_DEVID_MASK }, { MCP_MANID, MCP98244_DEVID, MCP98244_DEVID_MASK }, -- cgit v1.2.3 From 85889876ca37d67c20d14e444b111c0b81744b3e Mon Sep 17 00:00:00 2001 From: Huang Tao Date: Thu, 16 Jun 2016 15:54:27 +0200 Subject: dt-bindings: Document rk3399 rk-timer bindings Add a compatible string for rk3399 SoC because the timer is slightly different from the older SoCs. So rename the file name from rockchip,rk3288-timer.txt to rockchip,rk-timer.txt and clarify rockchip,rk3288-timer supported SoCs. Signed-off-by: Huang Tao Cc: Rob Herring Cc: Daniel Lezcano Cc: Thomas Gleixner Cc: Heiko Stuebner Signed-off-by: Caesar Wang Acked-by: Rob Herring Signed-off-by: Daniel Lezcano --- .../devicetree/bindings/timer/rockchip,rk-timer.txt | 20 ++++++++++++++++++++ .../bindings/timer/rockchip,rk3288-timer.txt | 18 ------------------ 2 files changed, 20 insertions(+), 18 deletions(-) create mode 100644 Documentation/devicetree/bindings/timer/rockchip,rk-timer.txt delete mode 100644 Documentation/devicetree/bindings/timer/rockchip,rk3288-timer.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/timer/rockchip,rk-timer.txt b/Documentation/devicetree/bindings/timer/rockchip,rk-timer.txt new file mode 100644 index 000000000000..a41b184d5538 --- /dev/null +++ b/Documentation/devicetree/bindings/timer/rockchip,rk-timer.txt @@ -0,0 +1,20 @@ +Rockchip rk timer + +Required properties: +- compatible: shall be one of: + "rockchip,rk3288-timer" - for rk3066, rk3036, rk3188, rk322x, rk3288, rk3368 + "rockchip,rk3399-timer" - for rk3399 +- reg: base address of the timer register starting with TIMERS CONTROL register +- interrupts: should contain the interrupts for Timer0 +- clocks : must contain an entry for each entry in clock-names +- clock-names : must include the following entries: + "timer", "pclk" + +Example: + timer: timer@ff810000 { + compatible = "rockchip,rk3288-timer"; + reg = <0xff810000 0x20>; + interrupts = ; + clocks = <&xin24m>, <&cru PCLK_TIMER>; + clock-names = "timer", "pclk"; + }; diff --git a/Documentation/devicetree/bindings/timer/rockchip,rk3288-timer.txt b/Documentation/devicetree/bindings/timer/rockchip,rk3288-timer.txt deleted file mode 100644 index 87f0b0042bae..000000000000 --- a/Documentation/devicetree/bindings/timer/rockchip,rk3288-timer.txt +++ /dev/null @@ -1,18 +0,0 @@ -Rockchip rk3288 timer - -Required properties: -- compatible: shall be "rockchip,rk3288-timer" -- reg: base address of the timer register starting with TIMERS CONTROL register -- interrupts: should contain the interrupts for Timer0 -- clocks : must contain an entry for each entry in clock-names -- clock-names : must include the following entries: - "timer", "pclk" - -Example: - timer: timer@ff810000 { - compatible = "rockchip,rk3288-timer"; - reg = <0xff810000 0x20>; - interrupts = ; - clocks = <&xin24m>, <&cru PCLK_TIMER>; - clock-names = "timer", "pclk"; - }; -- cgit v1.2.3 From 43e88c8a73f5896b964183d503d9bf892a551e25 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 15 Jun 2016 12:13:27 +0200 Subject: dt-bindings: clocksource: Add Oxford Semiconductor RPS Timer bindings Add DT bindings for the Oxford Semiconductor RPS dual Timer. Signed-off-by: Neil Armstrong Signed-off-by: Daniel Lezcano Acked-by: Rob Herring --- .../devicetree/bindings/timer/oxsemi,rps-timer.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Documentation/devicetree/bindings/timer/oxsemi,rps-timer.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/timer/oxsemi,rps-timer.txt b/Documentation/devicetree/bindings/timer/oxsemi,rps-timer.txt new file mode 100644 index 000000000000..3ca89cd1caef --- /dev/null +++ b/Documentation/devicetree/bindings/timer/oxsemi,rps-timer.txt @@ -0,0 +1,17 @@ +Oxford Semiconductor OXNAS SoCs Family RPS Timer +================================================ + +Required properties: +- compatible: Should be "oxsemi,ox810se-rps-timer" +- reg : Specifies base physical address and size of the registers. +- interrupts : The interrupts of the two timers +- clocks : The phandle of the timer clock source + +example: + +timer0: timer@200 { + compatible = "oxsemi,ox810se-rps-timer"; + reg = <0x200 0x40>; + clocks = <&rpsclk>; + interrupts = <4 5>; +}; -- cgit v1.2.3 From 46fd5c6b3059462131caa4d52691c9c5666c3223 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Mon, 27 Jun 2016 17:30:13 +0100 Subject: clocksource/drivers/arm_arch_timer: Control the evtstrm via the cmdline Disabling the eventstream can be useful for both remotely debugging a deployed production system and development of code using WFE-based polling loops. Whilst this can currently be controlled via a Kconfig option (CONFIG_ARM_ARCH_TIMER_EVTSTREAM), it's often desirable to toggle the feature on the command line, so this patch adds a new command-line option ("clocksource.arm_arch_timer.evtstrm") to do just that. The default behaviour is determined based on CONFIG_ARM_ARCH_TIMER_EVTSTREAM. Cc: Marc Zyngier Cc: Mark Rutland Signed-off-by: Will Deacon Signed-off-by: Daniel Lezcano --- Documentation/kernel-parameters.txt | 8 ++++++++ drivers/clocksource/Kconfig | 12 +++++++----- drivers/clocksource/arm_arch_timer.c | 10 +++++++++- 3 files changed, 24 insertions(+), 6 deletions(-) (limited to 'Documentation') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 82b42c958d1c..2c92f1df7c69 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -687,6 +687,14 @@ bytes respectively. Such letter suffixes can also be entirely omitted. [SPARC64] tick [X86-64] hpet,tsc + clocksource.arm_arch_timer.evtstrm= + [ARM,ARM64] + Format: + Enable/disable the eventstream feature of the ARM + architected timer so that code using WFE-based polling + loops can be debugged more effectively on production + systems. + clearcpuid=BITNUM [X86] Disable CPUID feature X for the kernel. See arch/x86/include/asm/cpufeatures.h for the valid bit diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 7acdf3d735ac..567788664723 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -288,14 +288,16 @@ config ARM_ARCH_TIMER select CLKSRC_ACPI if ACPI config ARM_ARCH_TIMER_EVTSTREAM - bool "Support for ARM architected timer event stream generation" + bool "Enable ARM architected timer event stream generation by default" default y if ARM_ARCH_TIMER depends on ARM_ARCH_TIMER help - This option enables support for event stream generation based on - the ARM architected timer. It is used for waking up CPUs executing - the wfe instruction at a frequency represented as a power-of-2 - divisor of the clock rate. + This option enables support by default for event stream generation + based on the ARM architected timer. It is used for waking up CPUs + executing the wfe instruction at a frequency represented as a + power-of-2 divisor of the clock rate. The behaviour can also be + overridden on the command line using the + clocksource.arm_arch_timer.evtstream parameter. The main use of the event stream is wfe-based timeouts of userspace locking implementations. It might also be useful for imposing timeout on wfe to safeguard against any programming errors in case an expected diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 9e33309ad2ea..5effd3027319 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -79,6 +79,14 @@ static enum ppi_nr arch_timer_uses_ppi = VIRT_PPI; static bool arch_timer_c3stop; static bool arch_timer_mem_use_virtual; +static bool evtstrm_enable = IS_ENABLED(CONFIG_ARM_ARCH_TIMER_EVTSTREAM); + +static int __init early_evtstrm_cfg(char *buf) +{ + return strtobool(buf, &evtstrm_enable); +} +early_param("clocksource.arm_arch_timer.evtstrm", early_evtstrm_cfg); + /* * Architected system timer support. */ @@ -372,7 +380,7 @@ static int arch_timer_setup(struct clock_event_device *clk) enable_percpu_irq(arch_timer_ppi[PHYS_NONSECURE_PPI], 0); arch_counter_set_user_access(); - if (IS_ENABLED(CONFIG_ARM_ARCH_TIMER_EVTSTREAM)) + if (evtstrm_enable) arch_timer_configure_evtstream(); return 0; -- cgit v1.2.3 From bc2b7dab629a51e8beb5fda4222c62a23b729f26 Mon Sep 17 00:00:00 2001 From: Gregor Boirie Date: Wed, 9 Mar 2016 19:05:49 +0100 Subject: iio:core: timestamping clock selection support Adds a new per-device sysfs attribute "current_timestamp_clock" to allow userspace to select a particular POSIX clock for buffered samples and events timestamping. Following clocks, as listed in clock_gettime(2), are supported: CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_MONOTONIC_RAW, CLOCK_REALTIME_COARSE, CLOCK_MONOTONIC_COARSE, CLOCK_BOOTTIME and CLOCK_TAI. Signed-off-by: Gregor Boirie Acked-by: Sanchayan Maity Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 7 + Documentation/DocBook/iio.tmpl | 2 +- drivers/iio/accel/bma180.c | 2 +- drivers/iio/accel/bmc150-accel-core.c | 4 +- drivers/iio/accel/kxcjk-1013.c | 2 +- drivers/iio/accel/mma7455_core.c | 3 +- drivers/iio/accel/mma8452.c | 4 +- drivers/iio/accel/mma9551.c | 2 +- drivers/iio/accel/mma9553.c | 2 +- drivers/iio/adc/ad7291.c | 2 +- drivers/iio/adc/ad7298.c | 2 +- drivers/iio/adc/ad7476.c | 2 +- drivers/iio/adc/ad7887.c | 2 +- drivers/iio/adc/ad7923.c | 2 +- drivers/iio/adc/ad799x.c | 4 +- drivers/iio/adc/cc10001_adc.c | 2 +- drivers/iio/adc/hi8435.c | 2 +- drivers/iio/adc/ina2xx-adc.c | 6 +- drivers/iio/adc/max1363.c | 5 +- drivers/iio/adc/ti-adc081c.c | 3 +- drivers/iio/adc/ti-ads1015.c | 3 +- drivers/iio/adc/vf610_adc.c | 3 +- drivers/iio/adc/xilinx-xadc-events.c | 4 +- drivers/iio/chemical/atlas-ph-sensor.c | 2 +- drivers/iio/common/st_sensors/st_sensors_buffer.c | 2 +- drivers/iio/common/st_sensors/st_sensors_trigger.c | 2 +- drivers/iio/dac/ad5421.c | 6 +- drivers/iio/dac/ad5504.c | 2 +- drivers/iio/dummy/iio_simple_dummy_buffer.c | 3 +- drivers/iio/dummy/iio_simple_dummy_events.c | 2 +- drivers/iio/gyro/bmg160_core.c | 30 ++-- drivers/iio/iio_core.h | 3 + drivers/iio/imu/bmi160/bmi160_core.c | 3 +- drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c | 2 +- drivers/iio/industrialio-core.c | 179 ++++++++++++++++++++- drivers/iio/industrialio-event.c | 19 ++- drivers/iio/industrialio-trigger.c | 2 +- drivers/iio/light/acpi-als.c | 2 +- drivers/iio/light/adjd_s311.c | 2 +- drivers/iio/light/apds9300.c | 2 +- drivers/iio/light/apds9960.c | 4 +- drivers/iio/light/cm36651.c | 2 +- drivers/iio/light/gp2ap020a00f.c | 8 +- drivers/iio/light/isl29125.c | 2 +- drivers/iio/light/lm3533-als.c | 2 +- drivers/iio/light/ltr501.c | 7 +- drivers/iio/light/max44000.c | 3 +- drivers/iio/light/opt3001.c | 4 +- drivers/iio/light/stk3310.c | 2 +- drivers/iio/light/tcs3414.c | 2 +- drivers/iio/light/tcs3472.c | 2 +- drivers/iio/light/tsl2563.c | 2 +- drivers/iio/light/us5182d.c | 2 +- drivers/iio/magnetometer/ak8975.c | 3 +- drivers/iio/magnetometer/hmc5843_core.c | 2 +- drivers/iio/magnetometer/mag3110.c | 2 +- drivers/iio/pressure/mpl3115.c | 2 +- drivers/iio/pressure/ms5611_core.c | 3 +- drivers/iio/proximity/pulsedlight-lidar-lite-v2.c | 2 +- drivers/iio/proximity/sx9500.c | 4 +- drivers/staging/iio/accel/lis3l02dq_core.c | 2 +- drivers/staging/iio/accel/sca3000_core.c | 2 +- drivers/staging/iio/adc/ad7280a.c | 8 +- drivers/staging/iio/adc/ad7606_ring.c | 3 +- drivers/staging/iio/adc/ad7816.c | 3 +- drivers/staging/iio/addac/adt7316.c | 4 +- drivers/staging/iio/cdc/ad7150.c | 2 +- drivers/staging/iio/light/tsl2x7x_core.c | 2 +- include/linux/iio/iio.h | 22 ++- 69 files changed, 327 insertions(+), 114 deletions(-) (limited to 'Documentation') diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index e7f590c6ef8e..fee35c00cc4e 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -32,6 +32,13 @@ Description: Description of the physical chip / device for device X. Typically a part number. +What: /sys/bus/iio/devices/iio:deviceX/timestamp_clock +KernelVersion: 4.5 +Contact: linux-iio@vger.kernel.org +Description: + String identifying current posix clock used to timestamp + buffered samples and events for device X. + What: /sys/bus/iio/devices/iio:deviceX/sampling_frequency What: /sys/bus/iio/devices/iio:deviceX/buffer/sampling_frequency What: /sys/bus/iio/devices/triggerX/sampling_frequency diff --git a/Documentation/DocBook/iio.tmpl b/Documentation/DocBook/iio.tmpl index f525bf56d1dd..e2ab6a1f223e 100644 --- a/Documentation/DocBook/iio.tmpl +++ b/Documentation/DocBook/iio.tmpl @@ -594,7 +594,7 @@ irqreturn_t sensor_iio_pollfunc(int irq, void *p) { - pf->timestamp = iio_get_time_ns(); + pf->timestamp = iio_get_time_ns((struct indio_dev *)p); return IRQ_WAKE_THREAD; } diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index f04b88406995..e3f88ba5faf3 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -654,7 +654,7 @@ static irqreturn_t bma180_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct bma180_data *data = iio_priv(indio_dev); - int64_t time_ns = iio_get_time_ns(); + s64 time_ns = iio_get_time_ns(indio_dev); int bit, ret, i = 0; mutex_lock(&data->mutex); diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 197e693e7e7b..bf17aae66145 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -901,7 +901,7 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev, */ if (!irq) { data->old_timestamp = data->timestamp; - data->timestamp = iio_get_time_ns(); + data->timestamp = iio_get_time_ns(indio_dev); } /* @@ -1303,7 +1303,7 @@ static irqreturn_t bmc150_accel_irq_handler(int irq, void *private) int i; data->old_timestamp = data->timestamp; - data->timestamp = iio_get_time_ns(); + data->timestamp = iio_get_time_ns(indio_dev); for (i = 0; i < BMC150_ACCEL_TRIGGERS; i++) { if (data->triggers[i].enabled) { diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index bfe219a8bea2..765a72362dc6 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -1129,7 +1129,7 @@ static irqreturn_t kxcjk1013_data_rdy_trig_poll(int irq, void *private) struct iio_dev *indio_dev = private; struct kxcjk1013_data *data = iio_priv(indio_dev); - data->timestamp = iio_get_time_ns(); + data->timestamp = iio_get_time_ns(indio_dev); if (data->dready_trigger_on) iio_trigger_poll(data->dready_trig); diff --git a/drivers/iio/accel/mma7455_core.c b/drivers/iio/accel/mma7455_core.c index c902f54c23f5..6551085bedd7 100644 --- a/drivers/iio/accel/mma7455_core.c +++ b/drivers/iio/accel/mma7455_core.c @@ -97,7 +97,8 @@ static irqreturn_t mma7455_trigger_handler(int irq, void *p) if (ret) goto done; - iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, buf, + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 799fe64fc286..c0df28328368 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -917,7 +917,7 @@ static int mma8452_write_event_config(struct iio_dev *indio_dev, static void mma8452_transient_interrupt(struct iio_dev *indio_dev) { struct mma8452_data *data = iio_priv(indio_dev); - s64 ts = iio_get_time_ns(); + s64 ts = iio_get_time_ns(indio_dev); int src; src = i2c_smbus_read_byte_data(data->client, data->chip_info->ev_src); @@ -997,7 +997,7 @@ static irqreturn_t mma8452_trigger_handler(int irq, void *p) goto done; iio_push_to_buffers_with_timestamp(indio_dev, buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c index d899a4d4307f..bf2704435629 100644 --- a/drivers/iio/accel/mma9551.c +++ b/drivers/iio/accel/mma9551.c @@ -391,7 +391,7 @@ static irqreturn_t mma9551_event_handler(int irq, void *private) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_INCLI, 0, (mma_axis + 1), IIO_EV_TYPE_ROC, IIO_EV_DIR_RISING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); out: mutex_unlock(&data->mutex); diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c index bb05f3efddca..36bf19733be0 100644 --- a/drivers/iio/accel/mma9553.c +++ b/drivers/iio/accel/mma9553.c @@ -1001,7 +1001,7 @@ static irqreturn_t mma9553_irq_handler(int irq, void *private) struct iio_dev *indio_dev = private; struct mma9553_data *data = iio_priv(indio_dev); - data->timestamp = iio_get_time_ns(); + data->timestamp = iio_get_time_ns(indio_dev); /* * Since we only configure the interrupt pin when an * event is enabled, we are sure we have at least diff --git a/drivers/iio/adc/ad7291.c b/drivers/iio/adc/ad7291.c index c0eabf156702..037630932749 100644 --- a/drivers/iio/adc/ad7291.c +++ b/drivers/iio/adc/ad7291.c @@ -115,7 +115,7 @@ static irqreturn_t ad7291_event_handler(int irq, void *private) u16 t_status, v_status; u16 command; int i; - s64 timestamp = iio_get_time_ns(); + s64 timestamp = iio_get_time_ns(indio_dev); if (ad7291_i2c_read(chip, AD7291_T_ALERT_STATUS, &t_status)) return IRQ_HANDLED; diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c index 62bb8f7ce4a0..5dd0742da75a 100644 --- a/drivers/iio/adc/ad7298.c +++ b/drivers/iio/adc/ad7298.c @@ -163,7 +163,7 @@ static irqreturn_t ad7298_trigger_handler(int irq, void *p) goto done; iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index 810c9a9fa62f..a819be288577 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -70,7 +70,7 @@ static irqreturn_t ad7476_trigger_handler(int irq, void *p) goto done; iio_push_to_buffers_with_timestamp(indio_dev, st->data, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c index ee2ccc19fab6..1bc363b0a4c1 100644 --- a/drivers/iio/adc/ad7887.c +++ b/drivers/iio/adc/ad7887.c @@ -122,7 +122,7 @@ static irqreturn_t ad7887_trigger_handler(int irq, void *p) goto done; iio_push_to_buffers_with_timestamp(indio_dev, st->data, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index ff444c19d749..133711680e55 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -181,7 +181,7 @@ static irqreturn_t ad7923_trigger_handler(int irq, void *p) goto done; iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index ec0200dd52cb..54284766d2ef 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -212,7 +212,7 @@ static irqreturn_t ad799x_trigger_handler(int irq, void *p) goto out; iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); out: iio_trigger_notify_done(indio_dev->trig); @@ -502,7 +502,7 @@ static irqreturn_t ad799x_event_handler(int irq, void *private) (i >> 1), IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } done: diff --git a/drivers/iio/adc/cc10001_adc.c b/drivers/iio/adc/cc10001_adc.c index 8254f529b2a9..91636c0ba5b5 100644 --- a/drivers/iio/adc/cc10001_adc.c +++ b/drivers/iio/adc/cc10001_adc.c @@ -186,7 +186,7 @@ done: if (!sample_invalid) iio_push_to_buffers_with_timestamp(indio_dev, data, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c index c73c6c62a6ac..c62bdb0a809e 100644 --- a/drivers/iio/adc/hi8435.c +++ b/drivers/iio/adc/hi8435.c @@ -400,7 +400,7 @@ static void hi8435_iio_push_event(struct iio_dev *idev, unsigned int val) iio_push_event(idev, IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, IIO_EV_TYPE_THRESH, dir), - iio_get_time_ns()); + iio_get_time_ns(idev)); } } diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 502f2fbe8aef..b95ce5d7ee2c 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -465,7 +465,7 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev) s64 time_a, time_b; unsigned int alert; - time_a = iio_get_time_ns(); + time_a = iio_get_time_ns(indio_dev); /* * Because the timer thread and the chip conversion clock @@ -504,7 +504,7 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev) data[i++] = val; } - time_b = iio_get_time_ns(); + time_b = iio_get_time_ns(indio_dev); iio_push_to_buffers_with_timestamp(indio_dev, (unsigned int *)data, time_a); @@ -554,7 +554,7 @@ static int ina2xx_buffer_enable(struct iio_dev *indio_dev) dev_dbg(&indio_dev->dev, "Async readout mode: %d\n", chip->allow_async_readout); - chip->prev_ns = iio_get_time_ns(); + chip->prev_ns = iio_get_time_ns(indio_dev); chip->task = kthread_run(ina2xx_capture_thread, (void *)indio_dev, "%s:%d-%uus", indio_dev->name, indio_dev->id, diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index 998dc3caad4c..73af30684657 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -788,7 +788,7 @@ static irqreturn_t max1363_event_handler(int irq, void *private) { struct iio_dev *indio_dev = private; struct max1363_state *st = iio_priv(indio_dev); - s64 timestamp = iio_get_time_ns(); + s64 timestamp = iio_get_time_ns(indio_dev); unsigned long mask, loc; u8 rx; u8 tx[2] = { st->setupbyte, @@ -1506,7 +1506,8 @@ static irqreturn_t max1363_trigger_handler(int irq, void *p) if (b_sent < 0) goto done_free; - iio_push_to_buffers_with_timestamp(indio_dev, rxbuf, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, rxbuf, + iio_get_time_ns(indio_dev)); done_free: kfree(rxbuf); diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c index f8807adc08c4..283d2a12b6da 100644 --- a/drivers/iio/adc/ti-adc081c.c +++ b/drivers/iio/adc/ti-adc081c.c @@ -139,7 +139,8 @@ static irqreturn_t adc081c_trigger_handler(int irq, void *p) if (ret < 0) goto out; buf[0] = ret; - iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, buf, + iio_get_time_ns(indio_dev)); out: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index 8be192a84893..c9574af09882 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -288,7 +288,8 @@ static irqreturn_t ads1015_trigger_handler(int irq, void *p) buf[0] = res; mutex_unlock(&data->lock); - iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, buf, + iio_get_time_ns(indio_dev)); err: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c index 653bf1379d2e..228a003adeed 100644 --- a/drivers/iio/adc/vf610_adc.c +++ b/drivers/iio/adc/vf610_adc.c @@ -594,7 +594,8 @@ static irqreturn_t vf610_adc_isr(int irq, void *dev_id) if (iio_buffer_enabled(indio_dev)) { info->buffer[0] = info->value; iio_push_to_buffers_with_timestamp(indio_dev, - info->buffer, iio_get_time_ns()); + info->buffer, + iio_get_time_ns(indio_dev)); iio_trigger_notify_done(indio_dev->trig); } else complete(&info->completion); diff --git a/drivers/iio/adc/xilinx-xadc-events.c b/drivers/iio/adc/xilinx-xadc-events.c index edcf3aabd70d..6d5c2a6f4e6e 100644 --- a/drivers/iio/adc/xilinx-xadc-events.c +++ b/drivers/iio/adc/xilinx-xadc-events.c @@ -46,7 +46,7 @@ static void xadc_handle_event(struct iio_dev *indio_dev, unsigned int event) iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } else { /* * For other channels we don't know whether it is a upper or @@ -56,7 +56,7 @@ static void xadc_handle_event(struct iio_dev *indio_dev, unsigned int event) iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } } diff --git a/drivers/iio/chemical/atlas-ph-sensor.c b/drivers/iio/chemical/atlas-ph-sensor.c index 02e85db5d31f..ae038a59d256 100644 --- a/drivers/iio/chemical/atlas-ph-sensor.c +++ b/drivers/iio/chemical/atlas-ph-sensor.c @@ -343,7 +343,7 @@ static irqreturn_t atlas_trigger_handler(int irq, void *private) if (!ret) iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c index 50afc0f8f16b..7c84e90d8ce8 100644 --- a/drivers/iio/common/st_sensors/st_sensors_buffer.c +++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c @@ -62,7 +62,7 @@ irqreturn_t st_sensors_trigger_handler(int irq, void *p) if (sdata->hw_irq_trigger) timestamp = sdata->hw_timestamp; else - timestamp = iio_get_time_ns(); + timestamp = iio_get_time_ns(indio_dev); len = st_sensors_get_buffer_element(indio_dev, sdata->buffer_data); if (len < 0) diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index 296e4ff19ae8..fab494d71951 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -29,7 +29,7 @@ irqreturn_t st_sensors_irq_handler(int irq, void *p) struct st_sensor_data *sdata = iio_priv(indio_dev); /* Get the time stamp as close in time as possible */ - sdata->hw_timestamp = iio_get_time_ns(); + sdata->hw_timestamp = iio_get_time_ns(indio_dev); return IRQ_WAKE_THREAD; } diff --git a/drivers/iio/dac/ad5421.c b/drivers/iio/dac/ad5421.c index 968712be967f..559061ab1982 100644 --- a/drivers/iio/dac/ad5421.c +++ b/drivers/iio/dac/ad5421.c @@ -242,7 +242,7 @@ static irqreturn_t ad5421_fault_handler(int irq, void *data) 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } if (events & AD5421_FAULT_UNDER_CURRENT) { @@ -251,7 +251,7 @@ static irqreturn_t ad5421_fault_handler(int irq, void *data) 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } if (events & AD5421_FAULT_TEMP_OVER_140) { @@ -260,7 +260,7 @@ static irqreturn_t ad5421_fault_handler(int irq, void *data) 0, IIO_EV_TYPE_MAG, IIO_EV_DIR_RISING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } old_fault = fault; diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index 4e4c20d6d8b5..788b3d6fd1cc 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -223,7 +223,7 @@ static irqreturn_t ad5504_event_handler(int irq, void *private) 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), - iio_get_time_ns()); + iio_get_time_ns((struct iio_dev *)private)); return IRQ_HANDLED; } diff --git a/drivers/iio/dummy/iio_simple_dummy_buffer.c b/drivers/iio/dummy/iio_simple_dummy_buffer.c index cf44a6f79431..b383892a5193 100644 --- a/drivers/iio/dummy/iio_simple_dummy_buffer.c +++ b/drivers/iio/dummy/iio_simple_dummy_buffer.c @@ -85,7 +85,8 @@ static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p) } } - iio_push_to_buffers_with_timestamp(indio_dev, data, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, data, + iio_get_time_ns(indio_dev)); kfree(data); diff --git a/drivers/iio/dummy/iio_simple_dummy_events.c b/drivers/iio/dummy/iio_simple_dummy_events.c index 6eb600ff7056..ed63ffd849f8 100644 --- a/drivers/iio/dummy/iio_simple_dummy_events.c +++ b/drivers/iio/dummy/iio_simple_dummy_events.c @@ -158,7 +158,7 @@ static irqreturn_t iio_simple_dummy_get_timestamp(int irq, void *private) struct iio_dev *indio_dev = private; struct iio_dummy_state *st = iio_priv(indio_dev); - st->event_timestamp = iio_get_time_ns(); + st->event_timestamp = iio_get_time_ns(indio_dev); return IRQ_WAKE_THREAD; } diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c index 7ccc044063f6..8155251d9984 100644 --- a/drivers/iio/gyro/bmg160_core.c +++ b/drivers/iio/gyro/bmg160_core.c @@ -885,25 +885,25 @@ static irqreturn_t bmg160_event_handler(int irq, void *private) if (val & BMG160_ANY_MOTION_BIT_X) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, - 0, - IIO_MOD_X, - IIO_EV_TYPE_ROC, - dir), - iio_get_time_ns()); + 0, + IIO_MOD_X, + IIO_EV_TYPE_ROC, + dir), + iio_get_time_ns(indio_dev)); if (val & BMG160_ANY_MOTION_BIT_Y) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, - 0, - IIO_MOD_Y, - IIO_EV_TYPE_ROC, - dir), - iio_get_time_ns()); + 0, + IIO_MOD_Y, + IIO_EV_TYPE_ROC, + dir), + iio_get_time_ns(indio_dev)); if (val & BMG160_ANY_MOTION_BIT_Z) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, - 0, - IIO_MOD_Z, - IIO_EV_TYPE_ROC, - dir), - iio_get_time_ns()); + 0, + IIO_MOD_Z, + IIO_EV_TYPE_ROC, + dir), + iio_get_time_ns(indio_dev)); ack_intr_status: if (!data->dready_trigger_on) { diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index 359883525ab7..4c45488e3a7f 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -79,4 +79,7 @@ void iio_device_unregister_eventset(struct iio_dev *indio_dev); void iio_device_wakeup_eventset(struct iio_dev *indio_dev); int iio_event_getfd(struct iio_dev *indio_dev); +struct iio_event_interface; +bool iio_event_enabled(const struct iio_event_interface *ev_int); + #endif diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c index 97928d558a15..e0251b8c1a52 100644 --- a/drivers/iio/imu/bmi160/bmi160_core.c +++ b/drivers/iio/imu/bmi160/bmi160_core.c @@ -411,7 +411,8 @@ static irqreturn_t bmi160_trigger_handler(int irq, void *p) buf[j++] = sample; } - iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, buf, + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c index d0700628ee6d..3a9f3eac91ab 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c @@ -107,7 +107,7 @@ irqreturn_t inv_mpu6050_irq_handler(int irq, void *p) struct inv_mpu6050_state *st = iio_priv(indio_dev); s64 timestamp; - timestamp = iio_get_time_ns(); + timestamp = iio_get_time_ns(indio_dev); kfifo_in_spinlocked(&st->timestamps, ×tamp, 1, &st->time_stamp_lock); diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 2a85bd888619..f914d5d140e4 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -178,6 +178,86 @@ ssize_t iio_read_const_attr(struct device *dev, } EXPORT_SYMBOL(iio_read_const_attr); +static int iio_device_set_clock(struct iio_dev *indio_dev, clockid_t clock_id) +{ + int ret; + const struct iio_event_interface *ev_int = indio_dev->event_interface; + + ret = mutex_lock_interruptible(&indio_dev->mlock); + if (ret) + return ret; + if ((ev_int && iio_event_enabled(ev_int)) || + iio_buffer_enabled(indio_dev)) { + mutex_unlock(&indio_dev->mlock); + return -EBUSY; + } + indio_dev->clock_id = clock_id; + mutex_unlock(&indio_dev->mlock); + + return 0; +} + +/** + * iio_get_time_ns() - utility function to get a time stamp for events etc + * @indio_dev: device + */ +s64 iio_get_time_ns(const struct iio_dev *indio_dev) +{ + struct timespec tp; + + switch (iio_device_get_clock(indio_dev)) { + case CLOCK_REALTIME: + ktime_get_real_ts(&tp); + break; + case CLOCK_MONOTONIC: + ktime_get_ts(&tp); + break; + case CLOCK_MONOTONIC_RAW: + getrawmonotonic(&tp); + break; + case CLOCK_REALTIME_COARSE: + tp = current_kernel_time(); + break; + case CLOCK_MONOTONIC_COARSE: + tp = get_monotonic_coarse(); + break; + case CLOCK_BOOTTIME: + get_monotonic_boottime(&tp); + break; + case CLOCK_TAI: + timekeeping_clocktai(&tp); + break; + default: + BUG(); + } + + return timespec_to_ns(&tp); +} +EXPORT_SYMBOL(iio_get_time_ns); + +/** + * iio_get_time_res() - utility function to get time stamp clock resolution in + * nano seconds. + * @indio_dev: device + */ +unsigned int iio_get_time_res(const struct iio_dev *indio_dev) +{ + switch (iio_device_get_clock(indio_dev)) { + case CLOCK_REALTIME: + case CLOCK_MONOTONIC: + case CLOCK_MONOTONIC_RAW: + case CLOCK_BOOTTIME: + case CLOCK_TAI: + return hrtimer_resolution; + case CLOCK_REALTIME_COARSE: + case CLOCK_MONOTONIC_COARSE: + return LOW_RES_NSEC; + default: + BUG(); + } +} +EXPORT_SYMBOL(iio_get_time_res); + static int __init iio_init(void) { int ret; @@ -990,11 +1070,91 @@ static ssize_t iio_show_dev_name(struct device *dev, static DEVICE_ATTR(name, S_IRUGO, iio_show_dev_name, NULL); +static ssize_t iio_show_timestamp_clock(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + const struct iio_dev *indio_dev = dev_to_iio_dev(dev); + const clockid_t clk = iio_device_get_clock(indio_dev); + const char *name; + ssize_t sz; + + switch (clk) { + case CLOCK_REALTIME: + name = "realtime\n"; + sz = sizeof("realtime\n"); + break; + case CLOCK_MONOTONIC: + name = "monotonic\n"; + sz = sizeof("monotonic\n"); + break; + case CLOCK_MONOTONIC_RAW: + name = "monotonic_raw\n"; + sz = sizeof("monotonic_raw\n"); + break; + case CLOCK_REALTIME_COARSE: + name = "realtime_coarse\n"; + sz = sizeof("realtime_coarse\n"); + break; + case CLOCK_MONOTONIC_COARSE: + name = "monotonic_coarse\n"; + sz = sizeof("monotonic_coarse\n"); + break; + case CLOCK_BOOTTIME: + name = "boottime\n"; + sz = sizeof("boottime\n"); + break; + case CLOCK_TAI: + name = "tai\n"; + sz = sizeof("tai\n"); + break; + default: + BUG(); + } + + memcpy(buf, name, sz); + return sz; +} + +static ssize_t iio_store_timestamp_clock(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + clockid_t clk; + int ret; + + if (sysfs_streq(buf, "realtime")) + clk = CLOCK_REALTIME; + else if (sysfs_streq(buf, "monotonic")) + clk = CLOCK_MONOTONIC; + else if (sysfs_streq(buf, "monotonic_raw")) + clk = CLOCK_MONOTONIC_RAW; + else if (sysfs_streq(buf, "realtime_coarse")) + clk = CLOCK_REALTIME_COARSE; + else if (sysfs_streq(buf, "monotonic_coarse")) + clk = CLOCK_MONOTONIC_COARSE; + else if (sysfs_streq(buf, "boottime")) + clk = CLOCK_BOOTTIME; + else if (sysfs_streq(buf, "tai")) + clk = CLOCK_TAI; + else + return -EINVAL; + + ret = iio_device_set_clock(dev_to_iio_dev(dev), clk); + if (ret) + return ret; + + return len; +} + +static DEVICE_ATTR(current_timestamp_clock, S_IRUGO | S_IWUSR, + iio_show_timestamp_clock, iio_store_timestamp_clock); + static int iio_device_register_sysfs(struct iio_dev *indio_dev) { int i, ret = 0, attrcount, attrn, attrcount_orig = 0; struct iio_dev_attr *p; - struct attribute **attr; + struct attribute **attr, *clk = NULL; /* First count elements in any existing group */ if (indio_dev->info->attrs) { @@ -1009,16 +1169,25 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev) */ if (indio_dev->channels) for (i = 0; i < indio_dev->num_channels; i++) { - ret = iio_device_add_channel_sysfs(indio_dev, - &indio_dev - ->channels[i]); + const struct iio_chan_spec *chan = + &indio_dev->channels[i]; + + if (chan->type == IIO_TIMESTAMP) + clk = &dev_attr_current_timestamp_clock.attr; + + ret = iio_device_add_channel_sysfs(indio_dev, chan); if (ret < 0) goto error_clear_attrs; attrcount += ret; } + if (indio_dev->event_interface) + clk = &dev_attr_current_timestamp_clock.attr; + if (indio_dev->name) attrcount++; + if (clk) + attrcount++; indio_dev->chan_attr_group.attrs = kcalloc(attrcount + 1, sizeof(indio_dev->chan_attr_group.attrs[0]), @@ -1039,6 +1208,8 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev) indio_dev->chan_attr_group.attrs[attrn++] = &p->dev_attr.attr; if (indio_dev->name) indio_dev->chan_attr_group.attrs[attrn++] = &dev_attr_name.attr; + if (clk) + indio_dev->chan_attr_group.attrs[attrn++] = clk; indio_dev->groups[indio_dev->groupcounter++] = &indio_dev->chan_attr_group; diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index cae332b1d7ea..0ebfc923a997 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -44,6 +44,11 @@ struct iio_event_interface { struct mutex read_lock; }; +bool iio_event_enabled(const struct iio_event_interface *ev_int) +{ + return !!test_bit(IIO_BUSY_BIT_POS, &ev_int->flags); +} + /** * iio_push_event() - try to add event to the list for userspace reading * @indio_dev: IIO device structure @@ -60,7 +65,7 @@ int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp) int copied; /* Does anyone care? */ - if (test_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) { + if (iio_event_enabled(ev_int)) { ev.id = ev_code; ev.timestamp = timestamp; @@ -180,8 +185,14 @@ int iio_event_getfd(struct iio_dev *indio_dev) if (ev_int == NULL) return -ENODEV; - if (test_and_set_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) - return -EBUSY; + fd = mutex_lock_interruptible(&indio_dev->mlock); + if (fd) + return fd; + + if (test_and_set_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) { + fd = -EBUSY; + goto unlock; + } iio_device_get(indio_dev); @@ -194,6 +205,8 @@ int iio_event_getfd(struct iio_dev *indio_dev) kfifo_reset_out(&ev_int->det_events); } +unlock: + mutex_unlock(&indio_dev->mlock); return fd; } diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index 98457f044aa5..7ad82fdd3e5b 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -289,7 +289,7 @@ static int iio_trigger_detach_poll_func(struct iio_trigger *trig, irqreturn_t iio_pollfunc_store_time(int irq, void *p) { struct iio_poll_func *pf = p; - pf->timestamp = iio_get_time_ns(); + pf->timestamp = iio_get_time_ns(pf->indio_dev); return IRQ_WAKE_THREAD; } EXPORT_SYMBOL(iio_pollfunc_store_time); diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c index 53201d99a16c..f0b47c501f4e 100644 --- a/drivers/iio/light/acpi-als.c +++ b/drivers/iio/light/acpi-als.c @@ -118,7 +118,7 @@ static void acpi_als_notify(struct acpi_device *device, u32 event) struct iio_dev *indio_dev = acpi_driver_data(device); struct acpi_als *als = iio_priv(indio_dev); s32 *buffer = als->evt_buffer; - s64 time_ns = iio_get_time_ns(); + s64 time_ns = iio_get_time_ns(indio_dev); s32 val; int ret; diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c index 09ad5f1ce539..0113fc843a81 100644 --- a/drivers/iio/light/adjd_s311.c +++ b/drivers/iio/light/adjd_s311.c @@ -118,7 +118,7 @@ static irqreturn_t adjd_s311_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct adjd_s311_data *data = iio_priv(indio_dev); - s64 time_ns = iio_get_time_ns(); + s64 time_ns = iio_get_time_ns(indio_dev); int i, j = 0; int ret = adjd_s311_req_data(indio_dev); diff --git a/drivers/iio/light/apds9300.c b/drivers/iio/light/apds9300.c index e1b9fa5a7e91..649b26f67813 100644 --- a/drivers/iio/light/apds9300.c +++ b/drivers/iio/light/apds9300.c @@ -396,7 +396,7 @@ static irqreturn_t apds9300_interrupt_handler(int irq, void *private) IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), - iio_get_time_ns()); + iio_get_time_ns(dev_info)); apds9300_clear_intr(data); diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c index 651d57b8abbf..a4304edc3e0f 100644 --- a/drivers/iio/light/apds9960.c +++ b/drivers/iio/light/apds9960.c @@ -807,7 +807,7 @@ static irqreturn_t apds9960_interrupt_handler(int irq, void *private) IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); regmap_write(data->regmap, APDS9960_REG_CICLEAR, 1); } @@ -816,7 +816,7 @@ static irqreturn_t apds9960_interrupt_handler(int irq, void *private) IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); regmap_write(data->regmap, APDS9960_REG_PICLEAR, 1); } diff --git a/drivers/iio/light/cm36651.c b/drivers/iio/light/cm36651.c index c8d7b5ea7e78..9d66e89c57ef 100644 --- a/drivers/iio/light/cm36651.c +++ b/drivers/iio/light/cm36651.c @@ -268,7 +268,7 @@ static irqreturn_t cm36651_irq_handler(int irq, void *data) CM36651_CMD_READ_RAW_PROXIMITY, IIO_EV_TYPE_THRESH, ev_dir); - iio_push_event(indio_dev, ev_code, iio_get_time_ns()); + iio_push_event(indio_dev, ev_code, iio_get_time_ns(indio_dev)); return IRQ_HANDLED; } diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index af73af3586a9..6ada9149f142 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -851,7 +851,7 @@ static irqreturn_t gp2ap020a00f_prox_sensing_handler(int irq, void *data) GP2AP020A00F_SCAN_MODE_PROXIMITY, IIO_EV_TYPE_ROC, IIO_EV_DIR_RISING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } else { iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE( @@ -859,7 +859,7 @@ static irqreturn_t gp2ap020a00f_prox_sensing_handler(int irq, void *data) GP2AP020A00F_SCAN_MODE_PROXIMITY, IIO_EV_TYPE_ROC, IIO_EV_DIR_FALLING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } } @@ -925,7 +925,7 @@ static irqreturn_t gp2ap020a00f_thresh_event_handler(int irq, void *data) IIO_MOD_LIGHT_CLEAR, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } if (test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &priv->flags)) { @@ -939,7 +939,7 @@ static irqreturn_t gp2ap020a00f_thresh_event_handler(int irq, void *data) IIO_MOD_LIGHT_CLEAR, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } } diff --git a/drivers/iio/light/isl29125.c b/drivers/iio/light/isl29125.c index a6b9d66233d5..f48b9e5bdb0e 100644 --- a/drivers/iio/light/isl29125.c +++ b/drivers/iio/light/isl29125.c @@ -188,7 +188,7 @@ static irqreturn_t isl29125_trigger_handler(int irq, void *p) } iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c index e56937c40a18..f409c2047c05 100644 --- a/drivers/iio/light/lm3533-als.c +++ b/drivers/iio/light/lm3533-als.c @@ -267,7 +267,7 @@ static irqreturn_t lm3533_als_isr(int irq, void *dev_id) 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); out: return IRQ_HANDLED; } diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 6bf89d8f3741..3afc53a3d0b6 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -1256,7 +1256,8 @@ static irqreturn_t ltr501_trigger_handler(int irq, void *p) buf[j++] = psdata & LTR501_PS_DATA_MASK; } - iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, buf, + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); @@ -1282,14 +1283,14 @@ static irqreturn_t ltr501_interrupt_handler(int irq, void *private) IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); if (status & LTR501_STATUS_PS_INTR) iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); return IRQ_HANDLED; } diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c index f17cb2ea18f5..6511b20a2a29 100644 --- a/drivers/iio/light/max44000.c +++ b/drivers/iio/light/max44000.c @@ -511,7 +511,8 @@ static irqreturn_t max44000_trigger_handler(int irq, void *p) } mutex_unlock(&data->lock); - iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, buf, + iio_get_time_ns(indio_dev)); iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index b776c8ed4387..78c9b3a6453a 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -713,13 +713,13 @@ static irqreturn_t opt3001_irq(int irq, void *_iio) IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), - iio_get_time_ns()); + iio_get_time_ns(iio)); if (ret & OPT3001_CONFIGURATION_FL) iio_push_event(iio, IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), - iio_get_time_ns()); + iio_get_time_ns(iio)); } else if (ret & OPT3001_CONFIGURATION_CRF) { ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_RESULT); if (ret < 0) { diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index 9e847f8f4f0c..45cf8b0a4363 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -528,7 +528,7 @@ static irqreturn_t stk3310_irq_handler(int irq, void *private) struct iio_dev *indio_dev = private; struct stk3310_data *data = iio_priv(indio_dev); - data->timestamp = iio_get_time_ns(); + data->timestamp = iio_get_time_ns(indio_dev); return IRQ_WAKE_THREAD; } diff --git a/drivers/iio/light/tcs3414.c b/drivers/iio/light/tcs3414.c index 8a15fb541dc3..a795afb7667b 100644 --- a/drivers/iio/light/tcs3414.c +++ b/drivers/iio/light/tcs3414.c @@ -216,7 +216,7 @@ static irqreturn_t tcs3414_trigger_handler(int irq, void *p) } iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index b29312f99077..3aa71e34ae28 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -202,7 +202,7 @@ static irqreturn_t tcs3472_trigger_handler(int irq, void *p) } iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index 57b108c30e98..04598ae993d4 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -630,7 +630,7 @@ static irqreturn_t tsl2563_event_handler(int irq, void *private) 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), - iio_get_time_ns()); + iio_get_time_ns(dev_info)); /* clear the interrupt and push the event */ i2c_smbus_write_byte(chip->client, TSL2563_CMD | TSL2563_CLEARINT); diff --git a/drivers/iio/light/us5182d.c b/drivers/iio/light/us5182d.c index 45bc2f742f46..20c40f780964 100644 --- a/drivers/iio/light/us5182d.c +++ b/drivers/iio/light/us5182d.c @@ -833,7 +833,7 @@ static irqreturn_t us5182d_irq_thread_handler(int irq, void *private) dir = ret & US5182D_CFG0_PROX ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING; ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 1, IIO_EV_TYPE_THRESH, dir); - iio_push_event(indio_dev, ev, iio_get_time_ns()); + iio_push_event(indio_dev, ev, iio_get_time_ns(indio_dev)); ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG0, ret & ~US5182D_CFG0_PX_IRQ); diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 57d3654d7caf..2121b50ab9b3 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -829,7 +829,8 @@ static void ak8975_fill_buffer(struct iio_dev *indio_dev) buff[1] = clamp_t(s16, le16_to_cpu(buff[1]), -def->range, def->range); buff[2] = clamp_t(s16, le16_to_cpu(buff[2]), -def->range, def->range); - iio_push_to_buffers_with_timestamp(indio_dev, buff, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, buff, + iio_get_time_ns(indio_dev)); return; unlock: diff --git a/drivers/iio/magnetometer/hmc5843_core.c b/drivers/iio/magnetometer/hmc5843_core.c index 77882b466e0f..ba3e2a374ee5 100644 --- a/drivers/iio/magnetometer/hmc5843_core.c +++ b/drivers/iio/magnetometer/hmc5843_core.c @@ -451,7 +451,7 @@ static irqreturn_t hmc5843_trigger_handler(int irq, void *p) goto done; iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c index 261d517428e4..f2be4a049056 100644 --- a/drivers/iio/magnetometer/mag3110.c +++ b/drivers/iio/magnetometer/mag3110.c @@ -261,7 +261,7 @@ static irqreturn_t mag3110_trigger_handler(int irq, void *p) } iio_push_to_buffers_with_timestamp(indio_dev, buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/pressure/mpl3115.c b/drivers/iio/pressure/mpl3115.c index 01b2e0b18878..6392d7b62841 100644 --- a/drivers/iio/pressure/mpl3115.c +++ b/drivers/iio/pressure/mpl3115.c @@ -171,7 +171,7 @@ static irqreturn_t mpl3115_trigger_handler(int irq, void *p) mutex_unlock(&data->lock); iio_push_to_buffers_with_timestamp(indio_dev, buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); done: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c index 76578b07bb6e..feb41f82c64a 100644 --- a/drivers/iio/pressure/ms5611_core.c +++ b/drivers/iio/pressure/ms5611_core.c @@ -224,7 +224,8 @@ static irqreturn_t ms5611_trigger_handler(int irq, void *p) if (ret < 0) goto err; - iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, buf, + iio_get_time_ns(indio_dev)); err: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c index c0b0e82abf94..3141c3c161bb 100644 --- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c +++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c @@ -238,7 +238,7 @@ static irqreturn_t lidar_trigger_handler(int irq, void *private) ret = lidar_get_measurement(data, data->buffer); if (!ret) { iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } else if (ret != -EINVAL) { dev_err(&data->client->dev, "cannot read LIDAR measurement"); } diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 66cd09a18786..1d74b3aafeed 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -492,7 +492,7 @@ static void sx9500_push_events(struct iio_dev *indio_dev) dir = new_prox ? IIO_EV_DIR_FALLING : IIO_EV_DIR_RISING; ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, chan, IIO_EV_TYPE_THRESH, dir); - iio_push_event(indio_dev, ev, iio_get_time_ns()); + iio_push_event(indio_dev, ev, iio_get_time_ns(indio_dev)); data->prox_stat[chan] = new_prox; } } @@ -669,7 +669,7 @@ static irqreturn_t sx9500_trigger_handler(int irq, void *private) } iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); out: mutex_unlock(&data->mutex); diff --git a/drivers/staging/iio/accel/lis3l02dq_core.c b/drivers/staging/iio/accel/lis3l02dq_core.c index 7a6fed3f2d3f..5c3410a74fdb 100644 --- a/drivers/staging/iio/accel/lis3l02dq_core.c +++ b/drivers/staging/iio/accel/lis3l02dq_core.c @@ -451,7 +451,7 @@ static irqreturn_t lis3l02dq_event_handler(int irq, void *private) struct iio_dev *indio_dev = private; u8 t; - s64 timestamp = iio_get_time_ns(); + s64 timestamp = iio_get_time_ns(indio_dev); lis3l02dq_spi_read_reg_8(indio_dev, LIS3L02DQ_REG_WAKE_UP_SRC_ADDR, diff --git a/drivers/staging/iio/accel/sca3000_core.c b/drivers/staging/iio/accel/sca3000_core.c index 53c5425d02b3..aacf643e87d3 100644 --- a/drivers/staging/iio/accel/sca3000_core.c +++ b/drivers/staging/iio/accel/sca3000_core.c @@ -774,7 +774,7 @@ static irqreturn_t sca3000_event_handler(int irq, void *private) struct iio_dev *indio_dev = private; struct sca3000_state *st = iio_priv(indio_dev); int ret, val; - s64 last_timestamp = iio_get_time_ns(); + s64 last_timestamp = iio_get_time_ns(indio_dev); /* * Could lead if badly timed to an extra read of status reg, diff --git a/drivers/staging/iio/adc/ad7280a.c b/drivers/staging/iio/adc/ad7280a.c index a06b46cb81ca..2177f1dd2b5d 100644 --- a/drivers/staging/iio/adc/ad7280a.c +++ b/drivers/staging/iio/adc/ad7280a.c @@ -705,7 +705,7 @@ static irqreturn_t ad7280_event_handler(int irq, void *private) IIO_EV_DIR_RISING, IIO_EV_TYPE_THRESH, 0, 0, 0), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); else if (((channels[i] >> 11) & 0xFFF) <= st->cell_threshlow) iio_push_event(indio_dev, @@ -715,7 +715,7 @@ static irqreturn_t ad7280_event_handler(int irq, void *private) IIO_EV_DIR_FALLING, IIO_EV_TYPE_THRESH, 0, 0, 0), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } else { if (((channels[i] >> 11) & 0xFFF) >= st->aux_threshhigh) iio_push_event(indio_dev, @@ -724,7 +724,7 @@ static irqreturn_t ad7280_event_handler(int irq, void *private) 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); else if (((channels[i] >> 11) & 0xFFF) <= st->aux_threshlow) iio_push_event(indio_dev, @@ -733,7 +733,7 @@ static irqreturn_t ad7280_event_handler(int irq, void *private) 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } } diff --git a/drivers/staging/iio/adc/ad7606_ring.c b/drivers/staging/iio/adc/ad7606_ring.c index a6f8eb11242c..0572df9aad85 100644 --- a/drivers/staging/iio/adc/ad7606_ring.c +++ b/drivers/staging/iio/adc/ad7606_ring.c @@ -77,7 +77,8 @@ static void ad7606_poll_bh_to_ring(struct work_struct *work_s) goto done; } - iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); + iio_push_to_buffers_with_timestamp(indio_dev, buf, + iio_get_time_ns(indio_dev)); done: gpio_set_value(st->pdata->gpio_convst, 0); iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/staging/iio/adc/ad7816.c b/drivers/staging/iio/adc/ad7816.c index ac3735c7f4a9..5e8115b01011 100644 --- a/drivers/staging/iio/adc/ad7816.c +++ b/drivers/staging/iio/adc/ad7816.c @@ -253,7 +253,8 @@ static const struct attribute_group ad7816_attribute_group = { static irqreturn_t ad7816_event_handler(int irq, void *private) { - iio_push_event(private, IIO_EVENT_CODE_AD7816_OTI, iio_get_time_ns()); + iio_push_event(private, IIO_EVENT_CODE_AD7816_OTI, + iio_get_time_ns((struct iio_dev *)private)); return IRQ_HANDLED; } diff --git a/drivers/staging/iio/addac/adt7316.c b/drivers/staging/iio/addac/adt7316.c index a10e7d8e6002..3faffe59c933 100644 --- a/drivers/staging/iio/addac/adt7316.c +++ b/drivers/staging/iio/addac/adt7316.c @@ -1752,7 +1752,7 @@ static irqreturn_t adt7316_event_handler(int irq, void *private) if ((chip->id & ID_FAMILY_MASK) != ID_ADT75XX) stat1 &= 0x1F; - time = iio_get_time_ns(); + time = iio_get_time_ns(indio_dev); if (stat1 & BIT(0)) iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0, @@ -1804,7 +1804,7 @@ static irqreturn_t adt7316_event_handler(int irq, void *private) 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), - iio_get_time_ns()); + iio_get_time_ns(indio_dev)); } return IRQ_HANDLED; diff --git a/drivers/staging/iio/cdc/ad7150.c b/drivers/staging/iio/cdc/ad7150.c index f6b9a10326ea..5578a077fcfb 100644 --- a/drivers/staging/iio/cdc/ad7150.c +++ b/drivers/staging/iio/cdc/ad7150.c @@ -493,7 +493,7 @@ static irqreturn_t ad7150_event_handler(int irq, void *private) struct iio_dev *indio_dev = private; struct ad7150_chip_info *chip = iio_priv(indio_dev); u8 int_status; - s64 timestamp = iio_get_time_ns(); + s64 timestamp = iio_get_time_ns(indio_dev); int ret; ret = i2c_smbus_read_byte_data(chip->client, AD7150_STATUS); diff --git a/drivers/staging/iio/light/tsl2x7x_core.c b/drivers/staging/iio/light/tsl2x7x_core.c index d553c8e18fcc..ea15bc1c300c 100644 --- a/drivers/staging/iio/light/tsl2x7x_core.c +++ b/drivers/staging/iio/light/tsl2x7x_core.c @@ -1554,7 +1554,7 @@ static irqreturn_t tsl2x7x_event_handler(int irq, void *private) { struct iio_dev *indio_dev = private; struct tsl2X7X_chip *chip = iio_priv(indio_dev); - s64 timestamp = iio_get_time_ns(); + s64 timestamp = iio_get_time_ns(indio_dev); int ret; u8 value; diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 7c29cb0124ae..854e2dad1e0d 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -312,13 +312,8 @@ static inline bool iio_channel_has_info(const struct iio_chan_spec *chan, }, \ } -/** - * iio_get_time_ns() - utility function to get a time stamp for events etc - **/ -static inline s64 iio_get_time_ns(void) -{ - return ktime_get_real_ns(); -} +s64 iio_get_time_ns(const struct iio_dev *indio_dev); +unsigned int iio_get_time_res(const struct iio_dev *indio_dev); /* Device operating modes */ #define INDIO_DIRECT_MODE 0x01 @@ -497,6 +492,7 @@ struct iio_buffer_setup_ops { * @chan_attr_group: [INTERN] group for all attrs in base directory * @name: [DRIVER] name of the device. * @info: [DRIVER] callbacks and constant info from driver + * @clock_id: [INTERN] timestamping clock posix identifier * @info_exist_lock: [INTERN] lock to prevent use during removal * @setup_ops: [DRIVER] callbacks to call before and after buffer * enable/disable @@ -537,6 +533,7 @@ struct iio_dev { struct attribute_group chan_attr_group; const char *name; const struct iio_info *info; + clockid_t clock_id; struct mutex info_exist_lock; const struct iio_buffer_setup_ops *setup_ops; struct cdev chrdev; @@ -565,7 +562,7 @@ extern struct bus_type iio_bus_type; /** * iio_device_put() - reference counted deallocation of struct device - * @indio_dev: IIO device structure containing the device + * @indio_dev: IIO device structure containing the device **/ static inline void iio_device_put(struct iio_dev *indio_dev) { @@ -573,6 +570,15 @@ static inline void iio_device_put(struct iio_dev *indio_dev) put_device(&indio_dev->dev); } +/** + * iio_device_get_clock() - Retrieve current timestamping clock for the device + * @indio_dev: IIO device structure containing the device + */ +static inline clockid_t iio_device_get_clock(const struct iio_dev *indio_dev) +{ + return indio_dev->clock_id; +} + /** * dev_to_iio_dev() - Get IIO device struct from a device struct * @dev: The device embedded in the IIO device -- cgit v1.2.3 From 8d2c7ef80bc8a66b8c6d657a857403f93952604f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 30 Jun 2016 03:48:45 +0200 Subject: iio: pressure: bmp280: augment DT bindings This adds standard device tree bindings for a reset GPIO line, and the VDDD and VDDA power regulators. Cc: devicetree@vger.kernel.org Acked-by: Rob Herring Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/pressure/bmp085.txt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/pressure/bmp085.txt b/Documentation/devicetree/bindings/iio/pressure/bmp085.txt index d7a6deb6b21e..c7198a03c906 100644 --- a/Documentation/devicetree/bindings/iio/pressure/bmp085.txt +++ b/Documentation/devicetree/bindings/iio/pressure/bmp085.txt @@ -1,7 +1,11 @@ -BMP085/BMP18x digital pressure sensors +BMP085/BMP18x/BMP28x digital pressure sensors Required properties: -- compatible: bosch,bmp085 +- compatible: must be one of: + "bosch,bmp085" + "bosch,bmp180" + "bosch,bmp280" + "bosch,bme280" Optional properties: - chip-id: configurable chip id for non-default chip revisions @@ -10,6 +14,10 @@ Optional properties: value range is 0-3 with rising sensitivity. - interrupt-parent: should be the phandle for the interrupt controller - interrupts: interrupt mapping for IRQ +- reset-gpios: a GPIO line handling reset of the sensor: as the line is + active low, it should be marked GPIO_ACTIVE_LOW (see gpio/gpio.txt) +- vddd-supply: digital voltage regulator (see regulator/regulator.txt) +- vdda-supply: analog voltage regulator (see regulator/regulator.txt) Example: @@ -21,4 +29,7 @@ pressure@77 { default-oversampling = <2>; interrupt-parent = <&gpio0>; interrupts = <25 IRQ_TYPE_EDGE_RISING>; + reset-gpios = <&gpio0 26 GPIO_ACTIVE_LOW>; + vddd-supply = <&foo>; + vdda-supply = <&bar>; }; -- cgit v1.2.3 From c76a093dc1415d364020b8b33f1e194ef4d26fd0 Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Fri, 1 Jul 2016 12:46:01 +0900 Subject: x86/Documentation: Fix various typos in Documentation/x86/ files Signed-off-by: Masanari Iida Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: corbet@lwn.net Cc: linux-doc@vger.kernel.org Link: http://lkml.kernel.org/r/20160701034601.30308-1-standby24x7@gmail.com Signed-off-by: Ingo Molnar --- Documentation/x86/intel_mpx.txt | 6 +++--- Documentation/x86/tlb.txt | 4 ++-- Documentation/x86/x86_64/machinecheck | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'Documentation') diff --git a/Documentation/x86/intel_mpx.txt b/Documentation/x86/intel_mpx.txt index 1a5a12184a35..85d0549ad846 100644 --- a/Documentation/x86/intel_mpx.txt +++ b/Documentation/x86/intel_mpx.txt @@ -45,7 +45,7 @@ is how we expect the compiler, application and kernel to work together. MPX-instrumented. 3) The kernel detects that the CPU has MPX, allows the new prctl() to succeed, and notes the location of the bounds directory. Userspace is - expected to keep the bounds directory at that locationWe note it + expected to keep the bounds directory at that location. We note it instead of reading it each time because the 'xsave' operation needed to access the bounds directory register is an expensive operation. 4) If the application needs to spill bounds out of the 4 registers, it @@ -167,7 +167,7 @@ If a #BR is generated due to a bounds violation caused by MPX. We need to decode MPX instructions to get violation address and set this address into extended struct siginfo. -The _sigfault feild of struct siginfo is extended as follow: +The _sigfault field of struct siginfo is extended as follow: 87 /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ 88 struct { @@ -240,5 +240,5 @@ them at the same bounds table. This is allowed architecturally. See more information "Intel(R) Architecture Instruction Set Extensions Programming Reference" (9.3.4). -However, if users did this, the kernel might be fooled in to unmaping an +However, if users did this, the kernel might be fooled in to unmapping an in-use bounds table since it does not recognize sharing. diff --git a/Documentation/x86/tlb.txt b/Documentation/x86/tlb.txt index 39d172326703..6a0607b99ed8 100644 --- a/Documentation/x86/tlb.txt +++ b/Documentation/x86/tlb.txt @@ -5,7 +5,7 @@ memory, it has two choices: from areas other than the one we are trying to flush will be destroyed and must be refilled later, at some cost. 2. Use the invlpg instruction to invalidate a single page at a - time. This could potentialy cost many more instructions, but + time. This could potentially cost many more instructions, but it is a much more precise operation, causing no collateral damage to other TLB entries. @@ -19,7 +19,7 @@ Which method to do depends on a few things: work. 3. The size of the TLB. The larger the TLB, the more collateral damage we do with a full flush. So, the larger the TLB, the - more attrative an individual flush looks. Data and + more attractive an individual flush looks. Data and instructions have separate TLBs, as do different page sizes. 4. The microarchitecture. The TLB has become a multi-level cache on modern CPUs, and the global flushes have become more diff --git a/Documentation/x86/x86_64/machinecheck b/Documentation/x86/x86_64/machinecheck index b1fb30273286..d0648a74fceb 100644 --- a/Documentation/x86/x86_64/machinecheck +++ b/Documentation/x86/x86_64/machinecheck @@ -36,7 +36,7 @@ between all CPUs. check_interval How often to poll for corrected machine check errors, in seconds - (Note output is hexademical). Default 5 minutes. When the poller + (Note output is hexadecimal). Default 5 minutes. When the poller finds MCEs it triggers an exponential speedup (poll more often) on the polling interval. When the poller stops finding MCEs, it triggers an exponential backoff (poll less often) on the polling -- cgit v1.2.3 From d3c4d0ab3b4e7cdfe5a209ba4d58bf5787e7c47f Mon Sep 17 00:00:00 2001 From: Raveendra Padasalagi Date: Tue, 28 Jun 2016 13:10:34 +0530 Subject: Documentation: DT: Add iproc-static-adc binding The patch adds devicetree binding document for broadcom's iproc-static-adc controller driver. Signed-off-by: Raveendra Padasalagi Reviewed-by: Ray Jui Reviewed-by: Scott Branden Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/brcm,iproc-static-adc.txt | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/brcm,iproc-static-adc.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/adc/brcm,iproc-static-adc.txt b/Documentation/devicetree/bindings/iio/adc/brcm,iproc-static-adc.txt new file mode 100644 index 000000000000..caaaed765ce4 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/brcm,iproc-static-adc.txt @@ -0,0 +1,41 @@ +* Broadcom's IPROC Static ADC controller + +Broadcom iProc ADC controller has 8 channels 10bit ADC. +Allows user to convert analog input voltage values to digital. + +Required properties: + +- compatible: Must be "brcm,iproc-static-adc" + +- adc-syscon: Handler of syscon node defining physical base address of the + controller and length of memory mapped region. + +- #io-channel-cells = <1>; As ADC has multiple outputs + refer to Documentation/devicetree/bindings/iio/iio-bindings.txt for details. + +- io-channel-ranges: + refer to Documentation/devicetree/bindings/iio/iio-bindings.txt for details. + +- clocks: Clock used for this block. + +- clock-names: Clock name should be given as tsc_clk. + +- interrupts: interrupt line number. + +For example: + + ts_adc_syscon: ts_adc_syscon@180a6000 { + compatible = "brcm,iproc-ts-adc-syscon","syscon"; + reg = <0x180a6000 0xc30>; + }; + + adc: adc@180a6000 { + compatible = "brcm,iproc-static-adc"; + adc-syscon = <&ts_adc_syscon>; + #io-channel-cells = <1>; + io-channel-ranges; + clocks = <&asiu_clks BCM_CYGNUS_ASIU_ADC_CLK>; + clock-names = "tsc_clk"; + interrupts = ; + status = "disabled"; + }; -- cgit v1.2.3 From 55389e8bc5dce8931d9c46d83c534c4997565e97 Mon Sep 17 00:00:00 2001 From: Florian Vaussard Date: Thu, 30 Jun 2016 09:43:05 +0200 Subject: iio: adc: max1363: Add device tree binding documentation Add the device tree documentation for all the supported parts. Mandatory binding is the compatible string and the slave I2C address. Optional properties can be used to specify the Vcc / Vref regulators, as well as the IRQ line if available. Acked-by: Rob Herring Signed-off-by: Florian Vaussard Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/max1363.txt | 63 ++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/max1363.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/iio/adc/max1363.txt b/Documentation/devicetree/bindings/iio/adc/max1363.txt new file mode 100644 index 000000000000..94a9011dd860 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/max1363.txt @@ -0,0 +1,63 @@ +* Maxim 1x3x/136x/116xx Analog to Digital Converter (ADC) + +The node for this driver must be a child node of a I2C controller, hence +all mandatory properties for your controller must be specified. See directory: + + Documentation/devicetree/bindings/i2c + +for more details. + +Required properties: + - compatible: Should be one of + "maxim,max1361" + "maxim,max1362" + "maxim,max1363" + "maxim,max1364" + "maxim,max1036" + "maxim,max1037" + "maxim,max1038" + "maxim,max1039" + "maxim,max1136" + "maxim,max1137" + "maxim,max1138" + "maxim,max1139" + "maxim,max1236" + "maxim,max1237" + "maxim,max1238" + "maxim,max1239" + "maxim,max11600" + "maxim,max11601" + "maxim,max11602" + "maxim,max11603" + "maxim,max11604" + "maxim,max11605" + "maxim,max11606" + "maxim,max11607" + "maxim,max11608" + "maxim,max11609" + "maxim,max11610" + "maxim,max11611" + "maxim,max11612" + "maxim,max11613" + "maxim,max11614" + "maxim,max11615" + "maxim,max11616" + "maxim,max11617" + "maxim,max11644" + "maxim,max11645" + "maxim,max11646" + "maxim,max11647" + - reg: Should contain the ADC I2C address + +Optional properties: + - vcc-supply: phandle to the regulator that provides power to the ADC. + - vref-supply: phandle to the regulator for ADC reference voltage. + - interrupts: IRQ line for the ADC. If not used the driver will use + polling. + +Example: +adc: max11644@36 { + compatible = "maxim,max11644"; + reg = <0x36>; + vref-supply = <&adc_vref>; +}; -- cgit v1.2.3 From deec2fadff6aa6a26e96cc12c7c8bacdc145b5af Mon Sep 17 00:00:00 2001 From: Florian Vaussard Date: Thu, 30 Jun 2016 09:34:49 +0200 Subject: iio: potentiometer: mcp4531: Add device tree binding documentation Add the device tree documentation for all the supported parts. Apart the compatible string and standard I2C binding, no other binding is currently needed. Signed-off-by: Florian Vaussard Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/i2c/trivial-devices.txt | 64 ++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt index 539874490492..acc5cd64711c 100644 --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt @@ -56,6 +56,70 @@ maxim,ds1050 5 Bit Programmable, Pulse-Width Modulator maxim,max1237 Low-Power, 4-/12-Channel, 2-Wire Serial, 12-Bit ADCs maxim,max6625 9-Bit/12-Bit Temperature Sensors with I²C-Compatible Serial Interface mc,rv3029c2 Real Time Clock Module with I2C-Bus +microchip,mcp4531-502 Microchip 7-bit Single I2C Digital Potentiometer (5k) +microchip,mcp4531-103 Microchip 7-bit Single I2C Digital Potentiometer (10k) +microchip,mcp4531-503 Microchip 7-bit Single I2C Digital Potentiometer (50k) +microchip,mcp4531-104 Microchip 7-bit Single I2C Digital Potentiometer (100k) +microchip,mcp4532-502 Microchip 7-bit Single I2C Digital Potentiometer (5k) +microchip,mcp4532-103 Microchip 7-bit Single I2C Digital Potentiometer (10k) +microchip,mcp4532-503 Microchip 7-bit Single I2C Digital Potentiometer (50k) +microchip,mcp4532-104 Microchip 7-bit Single I2C Digital Potentiometer (100k) +microchip,mcp4541-502 Microchip 7-bit Single I2C Digital Potentiometer with NV Memory (5k) +microchip,mcp4541-103 Microchip 7-bit Single I2C Digital Potentiometer with NV Memory (10k) +microchip,mcp4541-503 Microchip 7-bit Single I2C Digital Potentiometer with NV Memory (50k) +microchip,mcp4541-104 Microchip 7-bit Single I2C Digital Potentiometer with NV Memory (100k) +microchip,mcp4542-502 Microchip 7-bit Single I2C Digital Potentiometer with NV Memory (5k) +microchip,mcp4542-103 Microchip 7-bit Single I2C Digital Potentiometer with NV Memory (10k) +microchip,mcp4542-503 Microchip 7-bit Single I2C Digital Potentiometer with NV Memory (50k) +microchip,mcp4542-104 Microchip 7-bit Single I2C Digital Potentiometer with NV Memory (100k) +microchip,mcp4551-502 Microchip 8-bit Single I2C Digital Potentiometer (5k) +microchip,mcp4551-103 Microchip 8-bit Single I2C Digital Potentiometer (10k) +microchip,mcp4551-503 Microchip 8-bit Single I2C Digital Potentiometer (50k) +microchip,mcp4551-104 Microchip 8-bit Single I2C Digital Potentiometer (100k) +microchip,mcp4552-502 Microchip 8-bit Single I2C Digital Potentiometer (5k) +microchip,mcp4552-103 Microchip 8-bit Single I2C Digital Potentiometer (10k) +microchip,mcp4552-503 Microchip 8-bit Single I2C Digital Potentiometer (50k) +microchip,mcp4552-104 Microchip 8-bit Single I2C Digital Potentiometer (100k) +microchip,mcp4561-502 Microchip 8-bit Single I2C Digital Potentiometer with NV Memory (5k) +microchip,mcp4561-103 Microchip 8-bit Single I2C Digital Potentiometer with NV Memory (10k) +microchip,mcp4561-503 Microchip 8-bit Single I2C Digital Potentiometer with NV Memory (50k) +microchip,mcp4561-104 Microchip 8-bit Single I2C Digital Potentiometer with NV Memory (100k) +microchip,mcp4562-502 Microchip 8-bit Single I2C Digital Potentiometer with NV Memory (5k) +microchip,mcp4562-103 Microchip 8-bit Single I2C Digital Potentiometer with NV Memory (10k) +microchip,mcp4562-503 Microchip 8-bit Single I2C Digital Potentiometer with NV Memory (50k) +microchip,mcp4562-104 Microchip 8-bit Single I2C Digital Potentiometer with NV Memory (100k) +microchip,mcp4631-502 Microchip 7-bit Dual I2C Digital Potentiometer (5k) +microchip,mcp4631-103 Microchip 7-bit Dual I2C Digital Potentiometer (10k) +microchip,mcp4631-503 Microchip 7-bit Dual I2C Digital Potentiometer (50k) +microchip,mcp4631-104 Microchip 7-bit Dual I2C Digital Potentiometer (100k) +microchip,mcp4632-502 Microchip 7-bit Dual I2C Digital Potentiometer (5k) +microchip,mcp4632-103 Microchip 7-bit Dual I2C Digital Potentiometer (10k) +microchip,mcp4632-503 Microchip 7-bit Dual I2C Digital Potentiometer (50k) +microchip,mcp4632-104 Microchip 7-bit Dual I2C Digital Potentiometer (100k) +microchip,mcp4641-502 Microchip 7-bit Dual I2C Digital Potentiometer with NV Memory (5k) +microchip,mcp4641-103 Microchip 7-bit Dual I2C Digital Potentiometer with NV Memory (10k) +microchip,mcp4641-503 Microchip 7-bit Dual I2C Digital Potentiometer with NV Memory (50k) +microchip,mcp4641-104 Microchip 7-bit Dual I2C Digital Potentiometer with NV Memory (100k) +microchip,mcp4642-502 Microchip 7-bit Dual I2C Digital Potentiometer with NV Memory (5k) +microchip,mcp4642-103 Microchip 7-bit Dual I2C Digital Potentiometer with NV Memory (10k) +microchip,mcp4642-503 Microchip 7-bit Dual I2C Digital Potentiometer with NV Memory (50k) +microchip,mcp4642-104 Microchip 7-bit Dual I2C Digital Potentiometer with NV Memory (100k) +microchip,mcp4651-502 Microchip 8-bit Dual I2C Digital Potentiometer (5k) +microchip,mcp4651-103 Microchip 8-bit Dual I2C Digital Potentiometer (10k) +microchip,mcp4651-503 Microchip 8-bit Dual I2C Digital Potentiometer (50k) +microchip,mcp4651-104 Microchip 8-bit Dual I2C Digital Potentiometer (100k) +microchip,mcp4652-502 Microchip 8-bit Dual I2C Digital Potentiometer (5k) +microchip,mcp4652-103 Microchip 8-bit Dual I2C Digital Potentiometer (10k) +microchip,mcp4652-503 Microchip 8-bit Dual I2C Digital Potentiometer (50k) +microchip,mcp4652-104 Microchip 8-bit Dual I2C Digital Potentiometer (100k) +microchip,mcp4661-502 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (5k) +microchip,mcp4661-103 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (10k) +microchip,mcp4661-503 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (50k) +microchip,mcp4661-104 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (100k) +microchip,mcp4662-502 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (5k) +microchip,mcp4662-103 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (10k) +microchip,mcp4662-503 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (50k) +microchip,mcp4662-104 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (100k) national,lm63 Temperature sensor with integrated fan control national,lm75 I2C TEMP SENSOR national,lm80 Serial Interface ACPI-Compatible Microprocessor System Hardware Monitor -- cgit v1.2.3 From 5358aa3ea1d1f1d1b79c7f9e7c41449f584228fa Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 9 May 2016 18:39:58 -0500 Subject: dt-bindings: Add bindings for phy-da8xx-usb Device tree binding for new phy-da8xx-usb driver. Signed-off-by: David Lechner Acked-by: Rob Herring Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/phy-da8xx-usb.txt | 40 ++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/phy-da8xx-usb.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/phy/phy-da8xx-usb.txt b/Documentation/devicetree/bindings/phy/phy-da8xx-usb.txt new file mode 100644 index 000000000000..c26478be391b --- /dev/null +++ b/Documentation/devicetree/bindings/phy/phy-da8xx-usb.txt @@ -0,0 +1,40 @@ +TI DA8xx/OMAP-L1xx/AM18xx USB PHY + +Required properties: + - compatible: must be "ti,da830-usb-phy". + - #phy-cells: must be 1. + +This device controls the PHY for both the USB 1.1 OHCI and USB 2.0 OTG +controllers on DA8xx SoCs. Consumers of this device should use index 0 for +the USB 2.0 phy device and index 1 for the USB 1.1 phy device. + +It also requires a "syscon" node with compatible = "ti,da830-cfgchip", "syscon" +to access the CFGCHIP2 register. + +Example: + + cfgchip: cfgchip@1417c { + compatible = "ti,da830-cfgchip", "syscon"; + reg = <0x1417c 0x14>; + }; + + usb_phy: usb-phy { + compatible = "ti,da830-usb-phy"; + #phy-cells = <1>; + }; + + usb20: usb@200000 { + compatible = "ti,da830-musb"; + reg = <0x200000 0x1000>; + interrupts = <58>; + phys = <&usb_phy 0>; + phy-names = "usb-phy"; + }; + + usb11: usb@225000 { + compatible = "ti,da830-ohci"; + reg = <0x225000 0x1000>; + interrupts = <59>; + phys = <&usb_phy 1>; + phy-names = "usb-phy"; + }; -- cgit v1.2.3 From a0da445aabe49c31093ecf3930f531e3c63e0b83 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Wed, 29 Jun 2016 00:12:58 +0200 Subject: phy: rockchip-usb: should be a child device of the GRF The usb-phy is fully enclosed in the general register files (GRF). Therefore as seen from the device-tree it shouldn't be a separate platform-device but instead a sub-device of the GRF - using the simply-mfd mechanism. As the usb-phy is part of the kernel for some releases now, we keep the old (and now deprecated) binding for compatibility purposes. Signed-off-by: Heiko Stuebner Signed-off-by: Kishon Vijay Abraham I --- .../devicetree/bindings/phy/rockchip-usb-phy.txt | 27 ++++++++++++++-------- drivers/phy/phy-rockchip-usb.c | 15 +++++++++--- 2 files changed, 29 insertions(+), 13 deletions(-) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt b/Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt index 68498d560354..cc6be9680a6d 100644 --- a/Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt +++ b/Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt @@ -5,11 +5,13 @@ Required properties: "rockchip,rk3066a-usb-phy" "rockchip,rk3188-usb-phy" "rockchip,rk3288-usb-phy" - - rockchip,grf : phandle to the syscon managing the "general - register files" - #address-cells: should be 1 - #size-cells: should be 0 +Deprecated properties: + - rockchip,grf : phandle to the syscon managing the "general + register files" - phy should be a child of the GRF instead + Sub-nodes: Each PHY should be represented as a sub-node. @@ -28,14 +30,19 @@ Optional Properties: Example: -usbphy: phy { - compatible = "rockchip,rk3288-usb-phy"; - rockchip,grf = <&grf>; - #address-cells = <1>; - #size-cells = <0>; +grf: syscon@ff770000 { + compatible = "rockchip,rk3288-grf", "syscon", "simple-mfd"; + +... + + usbphy: phy { + compatible = "rockchip,rk3288-usb-phy"; + #address-cells = <1>; + #size-cells = <0>; - usbphy0: usb-phy0 { - #phy-cells = <0>; - reg = <0x320>; + usbphy0: usb-phy0 { + #phy-cells = <0>; + reg = <0x320>; + }; }; }; diff --git a/drivers/phy/phy-rockchip-usb.c b/drivers/phy/phy-rockchip-usb.c index d60b149cff0f..e66b5bf3605f 100644 --- a/drivers/phy/phy-rockchip-usb.c +++ b/drivers/phy/phy-rockchip-usb.c @@ -397,8 +397,13 @@ static int rockchip_usb_phy_probe(struct platform_device *pdev) phy_base->pdata = match->data; phy_base->dev = dev; - phy_base->reg_base = syscon_regmap_lookup_by_phandle(dev->of_node, - "rockchip,grf"); + phy_base->reg_base = ERR_PTR(-ENODEV); + if (dev->parent && dev->parent->of_node) + phy_base->reg_base = syscon_node_to_regmap( + dev->parent->of_node); + if (IS_ERR(phy_base->reg_base)) + phy_base->reg_base = syscon_regmap_lookup_by_phandle( + dev->of_node, "rockchip,grf"); if (IS_ERR(phy_base->reg_base)) { dev_err(&pdev->dev, "Missing rockchip,grf property\n"); return PTR_ERR(phy_base->reg_base); @@ -463,7 +468,11 @@ static int __init rockchip_init_usb_uart(void) return -ENOTSUPP; } - grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + grf = ERR_PTR(-ENODEV); + if (np->parent) + grf = syscon_node_to_regmap(np->parent); + if (IS_ERR(grf)) + grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); if (IS_ERR(grf)) { pr_err("%s: Missing rockchip,grf property, %lu\n", __func__, PTR_ERR(grf)); -- cgit v1.2.3 From 54b880caf15034644b564e378abf67b7f9eaf4dc Mon Sep 17 00:00:00 2001 From: James Hogan Date: Fri, 24 Jun 2016 23:42:23 +0100 Subject: kbuild, x86: Track generated headers with generated-y Track generated header files which aren't already in genhdr-y, alongside generic-y wrappers in the */include/generated/[uapi/]asm/ directories. Currently only x86 generates extra headers in these directories, for the purposes of enumerating system calls for different ABIs, and xen hypercalls. This will allow the asm-generic wrapper handling code to remove stale wrappers when files are removed from generic-y, without also removing these headers which are generated separately. Reported-by: kbuild test robot Signed-off-by: James Hogan Acked-by: Arnd Bergmann Cc: Jonathan Corbet Cc: linux-kbuild@vger.kernel.org Cc: linux-doc@vger.kernel.org Cc: Michal Marek Link: http://lkml.kernel.org/r/1466808144-23209-2-git-send-email-james.hogan@imgtec.com Signed-off-by: Thomas Gleixner --- Documentation/kbuild/makefiles.txt | 14 ++++++++++++++ arch/x86/include/asm/Kbuild | 6 ++++++ 2 files changed, 20 insertions(+) (limited to 'Documentation') diff --git a/Documentation/kbuild/makefiles.txt b/Documentation/kbuild/makefiles.txt index 13f888a02a3d..385a5ef41c17 100644 --- a/Documentation/kbuild/makefiles.txt +++ b/Documentation/kbuild/makefiles.txt @@ -47,6 +47,7 @@ This document describes the Linux kernel Makefiles. --- 7.2 genhdr-y --- 7.3 destination-y --- 7.4 generic-y + --- 7.5 generated-y === 8 Kbuild Variables === 9 Makefile language @@ -1319,6 +1320,19 @@ See subsequent chapter for the syntax of the Kbuild file. Example: termios.h #include + --- 7.5 generated-y + + If an architecture generates other header files alongside generic-y + wrappers, and not included in genhdr-y, then generated-y specifies + them. + + This prevents them being treated as stale asm-generic wrappers and + removed. + + Example: + #arch/x86/include/asm/Kbuild + generated-y += syscalls_32.h + === 8 Kbuild Variables The top Makefile exports the following variables: diff --git a/arch/x86/include/asm/Kbuild b/arch/x86/include/asm/Kbuild index aeac434c9feb..2cfed174e3c9 100644 --- a/arch/x86/include/asm/Kbuild +++ b/arch/x86/include/asm/Kbuild @@ -1,5 +1,11 @@ +generated-y += syscalls_32.h +generated-y += syscalls_64.h +generated-y += unistd_32_ia32.h +generated-y += unistd_64_x32.h +generated-y += xen-hypercalls.h + genhdr-y += unistd_32.h genhdr-y += unistd_64.h genhdr-y += unistd_x32.h -- cgit v1.2.3 From 0483e1fa6e09d4948272680f691dccb1edb9677f Mon Sep 17 00:00:00 2001 From: Thomas Garnier Date: Tue, 21 Jun 2016 17:47:02 -0700 Subject: x86/mm: Implement ASLR for kernel memory regions Randomizes the virtual address space of kernel memory regions for x86_64. This first patch adds the infrastructure and does not randomize any region. The following patches will randomize the physical memory mapping, vmalloc and vmemmap regions. This security feature mitigates exploits relying on predictable kernel addresses. These addresses can be used to disclose the kernel modules base addresses or corrupt specific structures to elevate privileges bypassing the current implementation of KASLR. This feature can be enabled with the CONFIG_RANDOMIZE_MEMORY option. The order of each memory region is not changed. The feature looks at the available space for the regions based on different configuration options and randomizes the base and space between each. The size of the physical memory mapping is the available physical memory. No performance impact was detected while testing the feature. Entropy is generated using the KASLR early boot functions now shared in the lib directory (originally written by Kees Cook). Randomization is done on PGD & PUD page table levels to increase possible addresses. The physical memory mapping code was adapted to support PUD level virtual addresses. This implementation on the best configuration provides 30,000 possible virtual addresses in average for each memory region. An additional low memory page is used to ensure each CPU can start with a PGD aligned virtual address (for realmode). x86/dump_pagetable was updated to correctly display each region. Updated documentation on x86_64 memory layout accordingly. Performance data, after all patches in the series: Kernbench shows almost no difference (-+ less than 1%): Before: Average Optimal load -j 12 Run (std deviation): Elapsed Time 102.63 (1.2695) User Time 1034.89 (1.18115) System Time 87.056 (0.456416) Percent CPU 1092.9 (13.892) Context Switches 199805 (3455.33) Sleeps 97907.8 (900.636) After: Average Optimal load -j 12 Run (std deviation): Elapsed Time 102.489 (1.10636) User Time 1034.86 (1.36053) System Time 87.764 (0.49345) Percent CPU 1095 (12.7715) Context Switches 199036 (4298.1) Sleeps 97681.6 (1031.11) Hackbench shows 0% difference on average (hackbench 90 repeated 10 times): attemp,before,after 1,0.076,0.069 2,0.072,0.069 3,0.066,0.066 4,0.066,0.068 5,0.066,0.067 6,0.066,0.069 7,0.067,0.066 8,0.063,0.067 9,0.067,0.065 10,0.068,0.071 average,0.0677,0.0677 Signed-off-by: Thomas Garnier Signed-off-by: Kees Cook Cc: Alexander Kuleshov Cc: Alexander Popov Cc: Andrew Morton Cc: Andy Lutomirski Cc: Aneesh Kumar K.V Cc: Baoquan He Cc: Boris Ostrovsky Cc: Borislav Petkov Cc: Borislav Petkov Cc: Brian Gerst Cc: Christian Borntraeger Cc: Dan Williams Cc: Dave Hansen Cc: Dave Young Cc: Denys Vlasenko Cc: Dmitry Vyukov Cc: H. Peter Anvin Cc: Jan Beulich Cc: Joerg Roedel Cc: Jonathan Corbet Cc: Josh Poimboeuf Cc: Juergen Gross Cc: Kirill A. Shutemov Cc: Linus Torvalds Cc: Lv Zheng Cc: Mark Salter Cc: Martin Schwidefsky Cc: Matt Fleming Cc: Peter Zijlstra Cc: Stephen Smalley Cc: Thomas Gleixner Cc: Toshi Kani Cc: Xiao Guangrong Cc: Yinghai Lu Cc: kernel-hardening@lists.openwall.com Cc: linux-doc@vger.kernel.org Link: http://lkml.kernel.org/r/1466556426-32664-6-git-send-email-keescook@chromium.org Signed-off-by: Ingo Molnar --- Documentation/x86/x86_64/mm.txt | 4 ++ arch/x86/Kconfig | 17 +++++ arch/x86/include/asm/kaslr.h | 6 ++ arch/x86/include/asm/pgtable.h | 7 +- arch/x86/kernel/setup.c | 3 + arch/x86/mm/Makefile | 1 + arch/x86/mm/dump_pagetables.c | 16 +++-- arch/x86/mm/init.c | 1 + arch/x86/mm/kaslr.c | 152 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 202 insertions(+), 5 deletions(-) create mode 100644 arch/x86/mm/kaslr.c (limited to 'Documentation') diff --git a/Documentation/x86/x86_64/mm.txt b/Documentation/x86/x86_64/mm.txt index 5aa738346062..8c7dd5957ae1 100644 --- a/Documentation/x86/x86_64/mm.txt +++ b/Documentation/x86/x86_64/mm.txt @@ -39,4 +39,8 @@ memory window (this size is arbitrary, it can be raised later if needed). The mappings are not part of any other kernel PGD and are only available during EFI runtime calls. +Note that if CONFIG_RANDOMIZE_MEMORY is enabled, the direct mapping of all +physical memory, vmalloc/ioremap space and virtual memory map are randomized. +Their order is preserved but their base will be offset early at boot time. + -Andi Kleen, Jul 2004 diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 930fe88095d3..9719b8eb38d3 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1993,6 +1993,23 @@ config PHYSICAL_ALIGN Don't change this unless you know what you are doing. +config RANDOMIZE_MEMORY + bool "Randomize the kernel memory sections" + depends on X86_64 + depends on RANDOMIZE_BASE + default RANDOMIZE_BASE + ---help--- + Randomizes the base virtual address of kernel memory sections + (physical memory mapping, vmalloc & vmemmap). This security feature + makes exploits relying on predictable memory locations less reliable. + + The order of allocations remains unchanged. Entropy is generated in + the same way as RANDOMIZE_BASE. Current implementation in the optimal + configuration have in average 30,000 different possible virtual + addresses for each memory section. + + If unsure, say N. + config HOTPLUG_CPU bool "Support for hot-pluggable CPUs" depends on SMP diff --git a/arch/x86/include/asm/kaslr.h b/arch/x86/include/asm/kaslr.h index 5547438db5ea..683c9d736314 100644 --- a/arch/x86/include/asm/kaslr.h +++ b/arch/x86/include/asm/kaslr.h @@ -3,4 +3,10 @@ unsigned long kaslr_get_random_long(const char *purpose); +#ifdef CONFIG_RANDOMIZE_MEMORY +void kernel_randomize_memory(void); +#else +static inline void kernel_randomize_memory(void) { } +#endif /* CONFIG_RANDOMIZE_MEMORY */ + #endif diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h index d455bef39e9c..5472682a307f 100644 --- a/arch/x86/include/asm/pgtable.h +++ b/arch/x86/include/asm/pgtable.h @@ -732,11 +732,16 @@ void early_alloc_pgt_buf(void); #ifdef CONFIG_X86_64 /* Realmode trampoline initialization. */ extern pgd_t trampoline_pgd_entry; -static inline void __meminit init_trampoline(void) +static inline void __meminit init_trampoline_default(void) { /* Default trampoline pgd value */ trampoline_pgd_entry = init_level4_pgt[pgd_index(__PAGE_OFFSET)]; } +# ifdef CONFIG_RANDOMIZE_MEMORY +void __meminit init_trampoline(void); +# else +# define init_trampoline init_trampoline_default +# endif #else static inline void init_trampoline(void) { } #endif diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index c4e7b3991b60..a2616584b6e9 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -113,6 +113,7 @@ #include #include #include +#include /* * max_low_pfn_mapped: highest direct mapped pfn under 4GB @@ -942,6 +943,8 @@ void __init setup_arch(char **cmdline_p) x86_init.oem.arch_setup(); + kernel_randomize_memory(); + iomem_resource.end = (1ULL << boot_cpu_data.x86_phys_bits) - 1; setup_memory_map(); parse_setup_data(); diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile index 62c0043a5fd5..96d2b847e09e 100644 --- a/arch/x86/mm/Makefile +++ b/arch/x86/mm/Makefile @@ -37,4 +37,5 @@ obj-$(CONFIG_NUMA_EMU) += numa_emulation.o obj-$(CONFIG_X86_INTEL_MPX) += mpx.o obj-$(CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS) += pkeys.o +obj-$(CONFIG_RANDOMIZE_MEMORY) += kaslr.o diff --git a/arch/x86/mm/dump_pagetables.c b/arch/x86/mm/dump_pagetables.c index 99bfb192803f..9a17250bcbe0 100644 --- a/arch/x86/mm/dump_pagetables.c +++ b/arch/x86/mm/dump_pagetables.c @@ -72,9 +72,9 @@ static struct addr_marker address_markers[] = { { 0, "User Space" }, #ifdef CONFIG_X86_64 { 0x8000000000000000UL, "Kernel Space" }, - { PAGE_OFFSET, "Low Kernel Mapping" }, - { VMALLOC_START, "vmalloc() Area" }, - { VMEMMAP_START, "Vmemmap" }, + { 0/* PAGE_OFFSET */, "Low Kernel Mapping" }, + { 0/* VMALLOC_START */, "vmalloc() Area" }, + { 0/* VMEMMAP_START */, "Vmemmap" }, # ifdef CONFIG_X86_ESPFIX64 { ESPFIX_BASE_ADDR, "ESPfix Area", 16 }, # endif @@ -434,8 +434,16 @@ void ptdump_walk_pgd_level_checkwx(void) static int __init pt_dump_init(void) { + /* + * Various markers are not compile-time constants, so assign them + * here. + */ +#ifdef CONFIG_X86_64 + address_markers[LOW_KERNEL_NR].start_address = PAGE_OFFSET; + address_markers[VMALLOC_START_NR].start_address = VMALLOC_START; + address_markers[VMEMMAP_START_NR].start_address = VMEMMAP_START; +#endif #ifdef CONFIG_X86_32 - /* Not a compile-time constant on x86-32 */ address_markers[VMALLOC_START_NR].start_address = VMALLOC_START; address_markers[VMALLOC_END_NR].start_address = VMALLOC_END; # ifdef CONFIG_HIGHMEM diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 4252acdfcbbd..cc82830bc8c4 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -17,6 +17,7 @@ #include #include /* for MAX_DMA_PFN */ #include +#include /* * We need to define the tracepoints somewhere, and tlb.c diff --git a/arch/x86/mm/kaslr.c b/arch/x86/mm/kaslr.c new file mode 100644 index 000000000000..d5380a48e8fb --- /dev/null +++ b/arch/x86/mm/kaslr.c @@ -0,0 +1,152 @@ +/* + * This file implements KASLR memory randomization for x86_64. It randomizes + * the virtual address space of kernel memory regions (physical memory + * mapping, vmalloc & vmemmap) for x86_64. This security feature mitigates + * exploits relying on predictable kernel addresses. + * + * Entropy is generated using the KASLR early boot functions now shared in + * the lib directory (originally written by Kees Cook). Randomization is + * done on PGD & PUD page table levels to increase possible addresses. The + * physical memory mapping code was adapted to support PUD level virtual + * addresses. This implementation on the best configuration provides 30,000 + * possible virtual addresses in average for each memory region. An additional + * low memory page is used to ensure each CPU can start with a PGD aligned + * virtual address (for realmode). + * + * The order of each memory region is not changed. The feature looks at + * the available space for the regions based on different configuration + * options and randomizes the base and space between each. The size of the + * physical memory mapping is the available physical memory. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "mm_internal.h" + +#define TB_SHIFT 40 + +/* + * Virtual address start and end range for randomization. The end changes base + * on configuration to have the highest amount of space for randomization. + * It increases the possible random position for each randomized region. + * + * You need to add an if/def entry if you introduce a new memory region + * compatible with KASLR. Your entry must be in logical order with memory + * layout. For example, ESPFIX is before EFI because its virtual address is + * before. You also need to add a BUILD_BUG_ON in kernel_randomize_memory to + * ensure that this order is correct and won't be changed. + */ +static const unsigned long vaddr_start; +static const unsigned long vaddr_end; + +/* + * Memory regions randomized by KASLR (except modules that use a separate logic + * earlier during boot). The list is ordered based on virtual addresses. This + * order is kept after randomization. + */ +static __initdata struct kaslr_memory_region { + unsigned long *base; + unsigned long size_tb; +} kaslr_regions[] = { +}; + +/* Get size in bytes used by the memory region */ +static inline unsigned long get_padding(struct kaslr_memory_region *region) +{ + return (region->size_tb << TB_SHIFT); +} + +/* + * Apply no randomization if KASLR was disabled at boot or if KASAN + * is enabled. KASAN shadow mappings rely on regions being PGD aligned. + */ +static inline bool kaslr_memory_enabled(void) +{ + return kaslr_enabled() && !config_enabled(CONFIG_KASAN); +} + +/* Initialize base and padding for each memory region randomized with KASLR */ +void __init kernel_randomize_memory(void) +{ + size_t i; + unsigned long vaddr = vaddr_start; + unsigned long rand; + struct rnd_state rand_state; + unsigned long remain_entropy; + + if (!kaslr_memory_enabled()) + return; + + /* Calculate entropy available between regions */ + remain_entropy = vaddr_end - vaddr_start; + for (i = 0; i < ARRAY_SIZE(kaslr_regions); i++) + remain_entropy -= get_padding(&kaslr_regions[i]); + + prandom_seed_state(&rand_state, kaslr_get_random_long("Memory")); + + for (i = 0; i < ARRAY_SIZE(kaslr_regions); i++) { + unsigned long entropy; + + /* + * Select a random virtual address using the extra entropy + * available. + */ + entropy = remain_entropy / (ARRAY_SIZE(kaslr_regions) - i); + prandom_bytes_state(&rand_state, &rand, sizeof(rand)); + entropy = (rand % (entropy + 1)) & PUD_MASK; + vaddr += entropy; + *kaslr_regions[i].base = vaddr; + + /* + * Jump the region and add a minimum padding based on + * randomization alignment. + */ + vaddr += get_padding(&kaslr_regions[i]); + vaddr = round_up(vaddr + 1, PUD_SIZE); + remain_entropy -= entropy; + } +} + +/* + * Create PGD aligned trampoline table to allow real mode initialization + * of additional CPUs. Consume only 1 low memory page. + */ +void __meminit init_trampoline(void) +{ + unsigned long paddr, paddr_next; + pgd_t *pgd; + pud_t *pud_page, *pud_page_tramp; + int i; + + if (!kaslr_memory_enabled()) { + init_trampoline_default(); + return; + } + + pud_page_tramp = alloc_low_page(); + + paddr = 0; + pgd = pgd_offset_k((unsigned long)__va(paddr)); + pud_page = (pud_t *) pgd_page_vaddr(*pgd); + + for (i = pud_index(paddr); i < PTRS_PER_PUD; i++, paddr = paddr_next) { + pud_t *pud, *pud_tramp; + unsigned long vaddr = (unsigned long)__va(paddr); + + pud_tramp = pud_page_tramp + pud_index(paddr); + pud = pud_page + pud_index(vaddr); + paddr_next = (paddr & PUD_MASK) + PUD_SIZE; + + *pud_tramp = *pud; + } + + set_pgd(&trampoline_pgd_entry, + __pgd(_KERNPG_TABLE | __pa(pud_page_tramp))); +} -- cgit v1.2.3 From 99e9d958726c04cec3e36902d8583fdd8cb5b1bb Mon Sep 17 00:00:00 2001 From: John Kacur Date: Fri, 17 Jun 2016 15:05:15 +0200 Subject: irq/Documentation: Correct result of echnoing 5 to smp_affinity This command: echo 5 > /proc/irq/10/smp_affinity means only the first and third (not fourth) CPUs can handle irqs That is, CPU0 is the first CPU and CPU2 is the third cpu Signed-off-by: John Kacur Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/1466168715-8410-1-git-send-email-jkacur@redhat.com Signed-off-by: Ingo Molnar --- Documentation/filesystems/proc.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index e8d00759bfa5..5b61eeae3f6e 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -725,7 +725,7 @@ IRQ, you can set it by doing: > echo 1 > /proc/irq/10/smp_affinity This means that only the first CPU will handle the IRQ, but you can also echo -5 which means that only the first and fourth CPU can handle the IRQ. +5 which means that only the first and third CPU can handle the IRQ. The contents of each smp_affinity file is the same by default: -- cgit v1.2.3 From e066f89d0608236a72d8e9e13cef05005e8d1aea Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 2 Jul 2016 09:02:43 -0700 Subject: dt/bindings: Add bindings for JC-42.4 compatible temperature sensors Provide generic bindings for all Jedec JC-42.4 compatible temperature sensor chips. Acked-by: Rob Herring Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/jc42.txt | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/jc42.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/hwmon/jc42.txt b/Documentation/devicetree/bindings/hwmon/jc42.txt new file mode 100644 index 000000000000..07a250498fbb --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/jc42.txt @@ -0,0 +1,42 @@ +Properties for Jedec JC-42.4 compatible temperature sensors + +Required properties: +- compatible: May include a device-specific string consisting of the + manufacturer and the name of the chip. A list of supported + chip names follows. + Must include "jedec,jc-42.4-temp" for any Jedec JC-42.4 + compatible temperature sensor. + + Supported chip names: + adi,adt7408 + atmel,at30ts00 + atmel,at30tse004 + onnn,cat6095 + onnn,cat34ts02 + maxim,max6604 + microchip,mcp9804 + microchip,mcp9805 + microchip,mcp9808 + microchip,mcp98243 + microchip,mcp98244 + microchip,mcp9843 + nxp,se97 + nxp,se98 + st,stts2002 + st,stts2004 + st,stts3000 + st,stts424 + st,stts424e + idt,tse2002 + idt,tse2004 + idt,ts3000 + idt,ts3001 + +- reg: I2C address + +Example: + +temp-sensor@1a { + compatible = "jedec,jc-42.4-temp"; + reg = <0x1a>; +}; -- cgit v1.2.3 From ef722fd4a7592ddbfa42bcb3ad8a5caa598589b3 Mon Sep 17 00:00:00 2001 From: Kieran Bingham Date: Thu, 14 Jul 2016 12:07:09 -0700 Subject: Revert "scripts/gdb: add documentation example for radix tree" This reverts commit 9b5580359a84 ("scripts/gdb: add documentation example for radix tree") The python implementation of radix tree was merged at the same time as a refactoring of the radix tree implementation and doesn't work. The feature is being reverted, thus we revert the documentation as well. Link: http://lkml.kernel.org/r/1467127337-11135-7-git-send-email-kieran@bingham.xyz Signed-off-by: Kieran Bingham Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/gdb-kernel-debugging.txt | 21 --------------------- 1 file changed, 21 deletions(-) (limited to 'Documentation') diff --git a/Documentation/gdb-kernel-debugging.txt b/Documentation/gdb-kernel-debugging.txt index 4ab7d43d0754..7050ce8794b9 100644 --- a/Documentation/gdb-kernel-debugging.txt +++ b/Documentation/gdb-kernel-debugging.txt @@ -139,27 +139,6 @@ Examples of using the Linux-provided gdb helpers start_comm = "swapper/2\000\000\000\000\000\000" } - o Dig into a radix tree data structure, such as the IRQ descriptors: - (gdb) print (struct irq_desc)$lx_radix_tree_lookup(irq_desc_tree, 18) - $6 = { - irq_common_data = { - state_use_accessors = 67584, - handler_data = 0x0 <__vectors_start>, - msi_desc = 0x0 <__vectors_start>, - affinity = {{ - bits = {65535} - }} - }, - irq_data = { - mask = 0, - irq = 18, - hwirq = 27, - common = 0xee803d80, - chip = 0xc0eb0854 , - domain = 0xee808000, - parent_data = 0x0 <__vectors_start>, - chip_data = 0xc0eb0854 - } <... trimmed ...> List of commands and functions ------------------------------ -- cgit v1.2.3 From 50c22cd154e5f7d13072d98ef3e2baba3c074734 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Sat, 9 Jul 2016 23:49:18 -0700 Subject: hwmon: (sht3x) add humidity heater element control The enables control of the SHT31 sensors heating element that can turned on to remove excess humidity. Cc: Guenter Roeck Cc: David Frey Signed-off-by: Matt Ranostay Signed-off-by: Guenter Roeck --- Documentation/hwmon/sht3x | 4 ++++ drivers/hwmon/sht3x.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) (limited to 'Documentation') diff --git a/Documentation/hwmon/sht3x b/Documentation/hwmon/sht3x index f5aa633ed383..b0d88184f48e 100644 --- a/Documentation/hwmon/sht3x +++ b/Documentation/hwmon/sht3x @@ -67,6 +67,10 @@ temp1_alarm: alarm flag is set to 1 if the temperature is outside the configured limits. Alarm only works in periodic measure mode humidity1_alarm: alarm flag is set to 1 if the humidity is outside the configured limits. Alarm only works in periodic measure mode +heater_enable: heater enable, heating element removes excess humidity from + sensor + 0: turned off + 1: turned on update_interval: update interval, 0 for single shot, interval in msec for periodic measurement. If the interval is not supported by the sensor, the next faster interval is chosen diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c index 707a3f85385b..b73a48832732 100644 --- a/drivers/hwmon/sht3x.c +++ b/drivers/hwmon/sht3x.c @@ -44,6 +44,10 @@ static const unsigned char sht3x_cmd_measure_nonblocking_lpm[] = { 0x24, 0x16 }; static const unsigned char sht3x_cmd_measure_periodic_mode[] = { 0xe0, 0x00 }; static const unsigned char sht3x_cmd_break[] = { 0x30, 0x93 }; +/* commands for heater control */ +static const unsigned char sht3x_cmd_heater_on[] = { 0x30, 0x6d }; +static const unsigned char sht3x_cmd_heater_off[] = { 0x30, 0x66 }; + /* other commands */ static const unsigned char sht3x_cmd_read_status_reg[] = { 0xf3, 0x2d }; static const unsigned char sht3x_cmd_clear_status_reg[] = { 0x30, 0x41 }; @@ -507,6 +511,49 @@ static ssize_t humidity1_alarm_show(struct device *dev, return scnprintf(buf, PAGE_SIZE, "%d\n", !!(buffer[0] & 0x08)); } +static ssize_t heater_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + char buffer[SHT3X_WORD_LEN + SHT3X_CRC8_LEN]; + int ret; + + ret = status_register_read(dev, attr, buffer, + SHT3X_WORD_LEN + SHT3X_CRC8_LEN); + if (ret) + return ret; + + return scnprintf(buf, PAGE_SIZE, "%d\n", !!(buffer[0] & 0x20)); +} + +static ssize_t heater_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct sht3x_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int ret; + bool status; + + ret = kstrtobool(buf, &status); + if (ret) + return ret; + + mutex_lock(&data->i2c_lock); + + if (status) + ret = i2c_master_send(client, (char *)&sht3x_cmd_heater_on, + SHT3X_CMD_LENGTH); + else + ret = i2c_master_send(client, (char *)&sht3x_cmd_heater_off, + SHT3X_CMD_LENGTH); + + mutex_unlock(&data->i2c_lock); + + return ret; +} + static ssize_t update_interval_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -612,6 +659,8 @@ static SENSOR_DEVICE_ATTR(humidity1_min_hyst, S_IRUGO | S_IWUSR, static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, temp1_alarm_show, NULL, 0); static SENSOR_DEVICE_ATTR(humidity1_alarm, S_IRUGO, humidity1_alarm_show, NULL, 0); +static SENSOR_DEVICE_ATTR(heater_enable, S_IRUGO | S_IWUSR, + heater_enable_show, heater_enable_store, 0); static SENSOR_DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, update_interval_show, update_interval_store, 0); @@ -628,6 +677,7 @@ static struct attribute *sht3x_attrs[] = { &sensor_dev_attr_humidity1_min_hyst.dev_attr.attr, &sensor_dev_attr_temp1_alarm.dev_attr.attr, &sensor_dev_attr_humidity1_alarm.dev_attr.attr, + &sensor_dev_attr_heater_enable.dev_attr.attr, &sensor_dev_attr_update_interval.dev_attr.attr, NULL }; -- cgit v1.2.3 From 08426eda58e07af44aac7c9900ec8a6a62e16b2f Mon Sep 17 00:00:00 2001 From: Thilo Cestonaro Date: Mon, 18 Jul 2016 13:51:29 +0200 Subject: hwmon: Add driver for FTS BMC chip "Teutates" This driver implements hardware monitoring and watchdog support for the FTS BMC Chip "Teutates". Signed-off-by: Thilo Cestonaro [groeck: Updated subject and description; fixed dependencies] Signed-off-by: Guenter Roeck --- Documentation/hwmon/ftsteutates | 23 ++ drivers/hwmon/Kconfig | 12 + drivers/hwmon/Makefile | 1 + drivers/hwmon/ftsteutates.c | 820 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 856 insertions(+) create mode 100644 Documentation/hwmon/ftsteutates create mode 100644 drivers/hwmon/ftsteutates.c (limited to 'Documentation') diff --git a/Documentation/hwmon/ftsteutates b/Documentation/hwmon/ftsteutates new file mode 100644 index 000000000000..2a1bf69c6a26 --- /dev/null +++ b/Documentation/hwmon/ftsteutates @@ -0,0 +1,23 @@ +Kernel driver ftsteutates +===================== + +Supported chips: + * FTS Teutates + Prefix: 'ftsteutates' + Addresses scanned: I2C 0x73 (7-Bit) + +Author: Thilo Cestonaro + + +Description +----------- +The BMC Teutates is the Eleventh generation of Superior System +monitoring and thermal management solution. It is builds on the basic +functionality of the BMC Theseus and contains several new features and +enhancements. It can monitor up to 4 voltages, 16 temperatures and +8 fans. It also contains an integrated watchdog which is currently +implemented in this driver. + +Specification of the chip can be found here: +ftp:///pub/Mainboard-OEM-Sales/Services/Software&Tools/Linux_SystemMonitoring&Watchdog&GPIO/BMC-Teutates_Specification_V1.21.pdf +ftp:///pub/Mainboard-OEM-Sales/Services/Software&Tools/Linux_SystemMonitoring&Watchdog&GPIO/Fujitsu_mainboards-1-Sensors_HowTo-en-US.pdf diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index e72cd3d33d4e..eaf2f916d48c 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -486,6 +486,18 @@ config SENSORS_FSCHMD This driver can also be built as a module. If so, the module will be called fschmd. +config SENSORS_FTSTEUTATES + tristate "Fujitsu Technology Solutions sensor chip Teutates" + depends on I2C && WATCHDOG + select WATCHDOG_CORE + help + If you say yes here you get support for the Fujitsu Technology + Solutions (FTS) sensor chip "Teutates" including support for + the integrated watchdog. + + This driver can also be built as a module. If so, the module + will be called ftsteutates. + config SENSORS_GL518SM tristate "Genesys Logic GL518SM" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 446a4e74c7cf..fe87d2895a97 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o obj-$(CONFIG_SENSORS_F75375S) += f75375s.o obj-$(CONFIG_SENSORS_FAM15H_POWER) += fam15h_power.o obj-$(CONFIG_SENSORS_FSCHMD) += fschmd.o +obj-$(CONFIG_SENSORS_FTSTEUTATES) += ftsteutates.o obj-$(CONFIG_SENSORS_G760A) += g760a.o obj-$(CONFIG_SENSORS_G762) += g762.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c new file mode 100644 index 000000000000..aaef5b570da7 --- /dev/null +++ b/drivers/hwmon/ftsteutates.c @@ -0,0 +1,820 @@ +/* + * Support for the FTS Systemmonitoring Chip "Teutates" + * + * Copyright (C) 2016 Fujitsu Technology Solutions GmbH, + * Thilo Cestonaro + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FTS_DEVICE_ID_REG 0x0000 +#define FTS_DEVICE_REVISION_REG 0x0001 +#define FTS_DEVICE_STATUS_REG 0x0004 +#define FTS_SATELLITE_STATUS_REG 0x0005 +#define FTS_EVENT_STATUS_REG 0x0006 +#define FTS_GLOBAL_CONTROL_REG 0x0007 + +#define FTS_SENSOR_EVENT_REG 0x0010 + +#define FTS_FAN_EVENT_REG 0x0014 +#define FTS_FAN_PRESENT_REG 0x0015 + +#define FTS_POWER_ON_TIME_COUNTER_A 0x007A +#define FTS_POWER_ON_TIME_COUNTER_B 0x007B +#define FTS_POWER_ON_TIME_COUNTER_C 0x007C + +#define FTS_PAGE_SELECT_REG 0x007F + +#define FTS_WATCHDOG_TIME_PRESET 0x000B +#define FTS_WATCHDOG_CONTROL 0x5081 + +#define FTS_NO_FAN_SENSORS 0x08 +#define FTS_NO_TEMP_SENSORS 0x10 +#define FTS_NO_VOLT_SENSORS 0x04 + +static struct i2c_device_id fts_id[] = { + { "ftsteutates", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, fts_id); + +enum WATCHDOG_RESOLUTION { + seconds = 1, + minutes = 60 +}; + +struct fts_data { + struct i2c_client *client; + /* update sensor data lock */ + struct mutex update_lock; + /* read/write register lock */ + struct mutex access_lock; + unsigned long last_updated; /* in jiffies */ + struct watchdog_device wdd; + enum WATCHDOG_RESOLUTION resolution; + bool valid; /* false until following fields are valid */ + + u8 volt[FTS_NO_VOLT_SENSORS]; + + u8 temp_input[FTS_NO_TEMP_SENSORS]; + u8 temp_alarm; + + u8 fan_present; + u8 fan_input[FTS_NO_FAN_SENSORS]; /* in rps */ + u8 fan_source[FTS_NO_FAN_SENSORS]; + u8 fan_alarm; +}; + +#define FTS_REG_FAN_INPUT(idx) ((idx) + 0x20) +#define FTS_REG_FAN_SOURCE(idx) ((idx) + 0x30) +#define FTS_REG_FAN_CONTROL(idx) (((idx) << 16) + 0x4881) + +#define FTS_REG_TEMP_INPUT(idx) ((idx) + 0x40) +#define FTS_REG_TEMP_CONTROL(idx) (((idx) << 16) + 0x0681) + +#define FTS_REG_VOLT(idx) ((idx) + 0x18) + +/*****************************************************************************/ +/* I2C Helper functions */ +/*****************************************************************************/ +static int fts_read_byte(struct i2c_client *client, unsigned short reg) +{ + int ret; + unsigned char page = reg >> 8; + struct fts_data *data = dev_get_drvdata(&client->dev); + + mutex_lock(&data->access_lock); + + dev_dbg(&client->dev, "page select - page: 0x%.02x\n", page); + ret = i2c_smbus_write_byte_data(client, FTS_PAGE_SELECT_REG, page); + if (ret < 0) + goto error; + + reg &= 0xFF; + ret = i2c_smbus_read_byte_data(client, reg); + dev_dbg(&client->dev, "read - reg: 0x%.02x: val: 0x%.02x\n", reg, ret); + +error: + mutex_unlock(&data->access_lock); + return ret; +} + +static int fts_write_byte(struct i2c_client *client, unsigned short reg, + unsigned char value) +{ + int ret; + unsigned char page = reg >> 8; + struct fts_data *data = dev_get_drvdata(&client->dev); + + mutex_lock(&data->access_lock); + + dev_dbg(&client->dev, "page select - page: 0x%.02x\n", page); + ret = i2c_smbus_write_byte_data(client, FTS_PAGE_SELECT_REG, page); + if (ret < 0) + goto error; + + reg &= 0xFF; + dev_dbg(&client->dev, + "write - reg: 0x%.02x: val: 0x%.02x\n", reg, value); + ret = i2c_smbus_write_byte_data(client, reg, value); + +error: + mutex_unlock(&data->access_lock); + return ret; +} + +/*****************************************************************************/ +/* Data Updater Helper function */ +/*****************************************************************************/ +static int fts_update_device(struct fts_data *data) +{ + int i; + int err = 0; + + mutex_lock(&data->update_lock); + if (!time_after(jiffies, data->last_updated + 2 * HZ) && data->valid) + goto exit; + + err = fts_read_byte(data->client, FTS_DEVICE_STATUS_REG); + if (err < 0) + goto exit; + + data->valid = !!(err & 0x02); /* Data not ready yet */ + if (unlikely(!data->valid)) { + err = -EAGAIN; + goto exit; + } + + err = fts_read_byte(data->client, FTS_FAN_PRESENT_REG); + if (err < 0) + goto exit; + data->fan_present = err; + + err = fts_read_byte(data->client, FTS_FAN_EVENT_REG); + if (err < 0) + goto exit; + data->fan_alarm = err; + + for (i = 0; i < FTS_NO_FAN_SENSORS; i++) { + if (data->fan_present & BIT(i)) { + err = fts_read_byte(data->client, FTS_REG_FAN_INPUT(i)); + if (err < 0) + goto exit; + data->fan_input[i] = err; + + err = fts_read_byte(data->client, + FTS_REG_FAN_SOURCE(i)); + if (err < 0) + goto exit; + data->fan_source[i] = err; + } else { + data->fan_input[i] = 0; + data->fan_source[i] = 0; + } + } + + err = fts_read_byte(data->client, FTS_SENSOR_EVENT_REG); + if (err < 0) + goto exit; + data->temp_alarm = err; + + for (i = 0; i < FTS_NO_TEMP_SENSORS; i++) { + err = fts_read_byte(data->client, FTS_REG_TEMP_INPUT(i)); + if (err < 0) + goto exit; + data->temp_input[i] = err; + } + + for (i = 0; i < FTS_NO_VOLT_SENSORS; i++) { + err = fts_read_byte(data->client, FTS_REG_VOLT(i)); + if (err < 0) + goto exit; + data->volt[i] = err; + } + data->last_updated = jiffies; + err = 0; +exit: + mutex_unlock(&data->update_lock); + return err; +} + +/*****************************************************************************/ +/* Watchdog functions */ +/*****************************************************************************/ +static int fts_wd_set_resolution(struct fts_data *data, + enum WATCHDOG_RESOLUTION resolution) +{ + int ret; + + if (data->resolution == resolution) + return 0; + + ret = fts_read_byte(data->client, FTS_WATCHDOG_CONTROL); + if (ret < 0) + return ret; + + if ((resolution == seconds && ret & BIT(1)) || + (resolution == minutes && (ret & BIT(1)) == 0)) { + data->resolution = resolution; + return 0; + } + + if (resolution == seconds) + set_bit(1, (unsigned long *)&ret); + else + ret &= ~BIT(1); + + ret = fts_write_byte(data->client, FTS_WATCHDOG_CONTROL, ret); + if (ret < 0) + return ret; + + data->resolution = resolution; + return ret; +} + +static int fts_wd_set_timeout(struct watchdog_device *wdd, unsigned int timeout) +{ + struct fts_data *data; + enum WATCHDOG_RESOLUTION resolution = seconds; + int ret; + + data = watchdog_get_drvdata(wdd); + /* switch watchdog resolution to minutes if timeout does not fit + * into a byte + */ + if (timeout > 0xFF) { + timeout = DIV_ROUND_UP(timeout, 60) * 60; + resolution = minutes; + } + + ret = fts_wd_set_resolution(data, resolution); + if (ret < 0) + return ret; + + wdd->timeout = timeout; + return 0; +} + +static int fts_wd_start(struct watchdog_device *wdd) +{ + struct fts_data *data = watchdog_get_drvdata(wdd); + + return fts_write_byte(data->client, FTS_WATCHDOG_TIME_PRESET, + wdd->timeout / (u8)data->resolution); +} + +static int fts_wd_stop(struct watchdog_device *wdd) +{ + struct fts_data *data; + + data = watchdog_get_drvdata(wdd); + return fts_write_byte(data->client, FTS_WATCHDOG_TIME_PRESET, 0); +} + +static const struct watchdog_info fts_wd_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "FTS Teutates Hardware Watchdog", +}; + +static const struct watchdog_ops fts_wd_ops = { + .owner = THIS_MODULE, + .start = fts_wd_start, + .stop = fts_wd_stop, + .set_timeout = fts_wd_set_timeout, +}; + +static int fts_watchdog_init(struct fts_data *data) +{ + int timeout, ret; + + watchdog_set_drvdata(&data->wdd, data); + + timeout = fts_read_byte(data->client, FTS_WATCHDOG_TIME_PRESET); + if (timeout < 0) + return timeout; + + /* watchdog not running, set timeout to a default of 60 sec. */ + if (timeout == 0) { + ret = fts_wd_set_resolution(data, seconds); + if (ret < 0) + return ret; + data->wdd.timeout = 60; + } else { + ret = fts_read_byte(data->client, FTS_WATCHDOG_CONTROL); + if (ret < 0) + return ret; + + data->resolution = ret & BIT(1) ? seconds : minutes; + data->wdd.timeout = timeout * (u8)data->resolution; + set_bit(WDOG_HW_RUNNING, &data->wdd.status); + } + + /* Register our watchdog part */ + data->wdd.info = &fts_wd_info; + data->wdd.ops = &fts_wd_ops; + data->wdd.parent = &data->client->dev; + data->wdd.min_timeout = 1; + + /* max timeout 255 minutes. */ + data->wdd.max_hw_heartbeat_ms = 0xFF * 60 * MSEC_PER_SEC; + + return watchdog_register_device(&data->wdd); +} + +/*****************************************************************************/ +/* SysFS handler functions */ +/*****************************************************************************/ +static ssize_t show_in_value(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct fts_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(devattr)->index; + int err; + + err = fts_update_device(data); + if (err < 0) + return err; + + return sprintf(buf, "%u\n", data->volt[index]); +} + +static ssize_t show_temp_value(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct fts_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(devattr)->index; + int err; + + err = fts_update_device(data); + if (err < 0) + return err; + + return sprintf(buf, "%u\n", data->temp_input[index]); +} + +static ssize_t show_temp_fault(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct fts_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(devattr)->index; + int err; + + err = fts_update_device(data); + if (err < 0) + return err; + + /* 00h Temperature = Sensor Error */ + return sprintf(buf, "%d\n", data->temp_input[index] == 0); +} + +static ssize_t show_temp_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct fts_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(devattr)->index; + int err; + + err = fts_update_device(data); + if (err < 0) + return err; + + return sprintf(buf, "%u\n", !!(data->temp_alarm & BIT(index))); +} + +static ssize_t +clear_temp_alarm(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct fts_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(devattr)->index; + long ret; + + ret = fts_update_device(data); + if (ret < 0) + return ret; + + if (kstrtoul(buf, 10, &ret) || ret != 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + ret = fts_read_byte(data->client, FTS_REG_TEMP_CONTROL(index)); + if (ret < 0) + goto error; + + ret = fts_write_byte(data->client, FTS_REG_TEMP_CONTROL(index), + ret | 0x1); + if (ret < 0) + goto error; + + data->valid = false; +error: + mutex_unlock(&data->update_lock); + return ret; +} + +static ssize_t show_fan_value(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct fts_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(devattr)->index; + int err; + + err = fts_update_device(data); + if (err < 0) + return err; + + return sprintf(buf, "%u\n", data->fan_input[index]); +} + +static ssize_t show_fan_source(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct fts_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(devattr)->index; + int err; + + err = fts_update_device(data); + if (err < 0) + return err; + + return sprintf(buf, "%u\n", data->fan_source[index]); +} + +static ssize_t show_fan_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct fts_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(devattr)->index; + int err; + + err = fts_update_device(data); + if (err < 0) + return err; + + return sprintf(buf, "%d\n", !!(data->fan_alarm & BIT(index))); +} + +static ssize_t +clear_fan_alarm(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct fts_data *data = dev_get_drvdata(dev); + int index = to_sensor_dev_attr(devattr)->index; + long ret; + + ret = fts_update_device(data); + if (ret < 0) + return ret; + + if (kstrtoul(buf, 10, &ret) || ret != 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + ret = fts_read_byte(data->client, FTS_REG_FAN_CONTROL(index)); + if (ret < 0) + goto error; + + ret = fts_write_byte(data->client, FTS_REG_FAN_CONTROL(index), + ret | 0x1); + if (ret < 0) + goto error; + + data->valid = false; +error: + mutex_unlock(&data->update_lock); + return ret; +} + +/*****************************************************************************/ +/* SysFS structs */ +/*****************************************************************************/ + +/* Temprature sensors */ +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_value, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_value, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp_value, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp_value, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp_value, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_temp_value, NULL, 5); +static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, show_temp_value, NULL, 6); +static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, show_temp_value, NULL, 7); +static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO, show_temp_value, NULL, 8); +static SENSOR_DEVICE_ATTR(temp10_input, S_IRUGO, show_temp_value, NULL, 9); +static SENSOR_DEVICE_ATTR(temp11_input, S_IRUGO, show_temp_value, NULL, 10); +static SENSOR_DEVICE_ATTR(temp12_input, S_IRUGO, show_temp_value, NULL, 11); +static SENSOR_DEVICE_ATTR(temp13_input, S_IRUGO, show_temp_value, NULL, 12); +static SENSOR_DEVICE_ATTR(temp14_input, S_IRUGO, show_temp_value, NULL, 13); +static SENSOR_DEVICE_ATTR(temp15_input, S_IRUGO, show_temp_value, NULL, 14); +static SENSOR_DEVICE_ATTR(temp16_input, S_IRUGO, show_temp_value, NULL, 15); + +static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_temp_fault, NULL, 3); +static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_temp_fault, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_fault, S_IRUGO, show_temp_fault, NULL, 5); +static SENSOR_DEVICE_ATTR(temp7_fault, S_IRUGO, show_temp_fault, NULL, 6); +static SENSOR_DEVICE_ATTR(temp8_fault, S_IRUGO, show_temp_fault, NULL, 7); +static SENSOR_DEVICE_ATTR(temp9_fault, S_IRUGO, show_temp_fault, NULL, 8); +static SENSOR_DEVICE_ATTR(temp10_fault, S_IRUGO, show_temp_fault, NULL, 9); +static SENSOR_DEVICE_ATTR(temp11_fault, S_IRUGO, show_temp_fault, NULL, 10); +static SENSOR_DEVICE_ATTR(temp12_fault, S_IRUGO, show_temp_fault, NULL, 11); +static SENSOR_DEVICE_ATTR(temp13_fault, S_IRUGO, show_temp_fault, NULL, 12); +static SENSOR_DEVICE_ATTR(temp14_fault, S_IRUGO, show_temp_fault, NULL, 13); +static SENSOR_DEVICE_ATTR(temp15_fault, S_IRUGO, show_temp_fault, NULL, 14); +static SENSOR_DEVICE_ATTR(temp16_fault, S_IRUGO, show_temp_fault, NULL, 15); + +static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO | S_IWUSR, show_temp_alarm, + clear_temp_alarm, 0); +static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO | S_IWUSR, show_temp_alarm, + clear_temp_alarm, 1); +static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO | S_IWUSR, show_temp_alarm, + clear_temp_alarm, 2); +static SENSOR_DEVICE_ATTR(temp4_alarm, S_IRUGO | S_IWUSR, show_temp_alarm, + clear_temp_alarm, 3); +static SENSOR_DEVICE_ATTR(temp5_alarm, S_IRUGO | S_IWUSR, show_temp_alarm, + clear_temp_alarm, 4); +static SENSOR_DEVICE_ATTR(temp6_alarm, S_IRUGO | S_IWUSR, show_temp_alarm, + clear_temp_alarm, 5); +static SENSOR_DEVICE_ATTR(temp7_alarm, S_IRUGO | S_IWUSR, show_temp_alarm, + clear_temp_alarm, 6); +static SENSOR_DEVICE_ATTR(temp8_alarm, S_IRUGO | S_IWUSR, show_temp_alarm, + clear_temp_alarm, 7); +static SENSOR_DEVICE_ATTR(temp9_alarm, S_IRUGO | S_IWUSR, show_temp_alarm, + clear_temp_alarm, 8); +static SENSOR_DEVICE_ATTR(temp10_alarm, S_IRUGO | S_IWUSR, show_temp_alarm, + clear_temp_alarm, 9); +static SENSOR_DEVICE_ATTR(temp11_alarm, S_IRUGO | S_IWUSR, show_temp_alarm, + clear_temp_alarm, 10); +static SENSOR_DEVICE_ATTR(temp12_alarm, S_IRUGO | S_IWUSR, show_temp_alarm, + clear_temp_alarm, 11); +static SENSOR_DEVICE_ATTR(temp13_alarm, S_IRUGO | S_IWUSR, show_temp_alarm, + clear_temp_alarm, 12); +static SENSOR_DEVICE_ATTR(temp14_alarm, S_IRUGO | S_IWUSR, show_temp_alarm, + clear_temp_alarm, 13); +static SENSOR_DEVICE_ATTR(temp15_alarm, S_IRUGO | S_IWUSR, show_temp_alarm, + clear_temp_alarm, 14); +static SENSOR_DEVICE_ATTR(temp16_alarm, S_IRUGO | S_IWUSR, show_temp_alarm, + clear_temp_alarm, 15); + +static struct attribute *fts_temp_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp6_input.dev_attr.attr, + &sensor_dev_attr_temp7_input.dev_attr.attr, + &sensor_dev_attr_temp8_input.dev_attr.attr, + &sensor_dev_attr_temp9_input.dev_attr.attr, + &sensor_dev_attr_temp10_input.dev_attr.attr, + &sensor_dev_attr_temp11_input.dev_attr.attr, + &sensor_dev_attr_temp12_input.dev_attr.attr, + &sensor_dev_attr_temp13_input.dev_attr.attr, + &sensor_dev_attr_temp14_input.dev_attr.attr, + &sensor_dev_attr_temp15_input.dev_attr.attr, + &sensor_dev_attr_temp16_input.dev_attr.attr, + + &sensor_dev_attr_temp1_fault.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &sensor_dev_attr_temp3_fault.dev_attr.attr, + &sensor_dev_attr_temp4_fault.dev_attr.attr, + &sensor_dev_attr_temp5_fault.dev_attr.attr, + &sensor_dev_attr_temp6_fault.dev_attr.attr, + &sensor_dev_attr_temp7_fault.dev_attr.attr, + &sensor_dev_attr_temp8_fault.dev_attr.attr, + &sensor_dev_attr_temp9_fault.dev_attr.attr, + &sensor_dev_attr_temp10_fault.dev_attr.attr, + &sensor_dev_attr_temp11_fault.dev_attr.attr, + &sensor_dev_attr_temp12_fault.dev_attr.attr, + &sensor_dev_attr_temp13_fault.dev_attr.attr, + &sensor_dev_attr_temp14_fault.dev_attr.attr, + &sensor_dev_attr_temp15_fault.dev_attr.attr, + &sensor_dev_attr_temp16_fault.dev_attr.attr, + + &sensor_dev_attr_temp1_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_alarm.dev_attr.attr, + &sensor_dev_attr_temp6_alarm.dev_attr.attr, + &sensor_dev_attr_temp7_alarm.dev_attr.attr, + &sensor_dev_attr_temp8_alarm.dev_attr.attr, + &sensor_dev_attr_temp9_alarm.dev_attr.attr, + &sensor_dev_attr_temp10_alarm.dev_attr.attr, + &sensor_dev_attr_temp11_alarm.dev_attr.attr, + &sensor_dev_attr_temp12_alarm.dev_attr.attr, + &sensor_dev_attr_temp13_alarm.dev_attr.attr, + &sensor_dev_attr_temp14_alarm.dev_attr.attr, + &sensor_dev_attr_temp15_alarm.dev_attr.attr, + &sensor_dev_attr_temp16_alarm.dev_attr.attr, + NULL +}; + +/* Fans */ +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_value, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_value, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan_value, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan_value, NULL, 3); +static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan_value, NULL, 4); +static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan_value, NULL, 5); +static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan_value, NULL, 6); +static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, show_fan_value, NULL, 7); + +static SENSOR_DEVICE_ATTR(fan1_source, S_IRUGO, show_fan_source, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_source, S_IRUGO, show_fan_source, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_source, S_IRUGO, show_fan_source, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_source, S_IRUGO, show_fan_source, NULL, 3); +static SENSOR_DEVICE_ATTR(fan5_source, S_IRUGO, show_fan_source, NULL, 4); +static SENSOR_DEVICE_ATTR(fan6_source, S_IRUGO, show_fan_source, NULL, 5); +static SENSOR_DEVICE_ATTR(fan7_source, S_IRUGO, show_fan_source, NULL, 6); +static SENSOR_DEVICE_ATTR(fan8_source, S_IRUGO, show_fan_source, NULL, 7); + +static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO | S_IWUSR, + show_fan_alarm, clear_fan_alarm, 0); +static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO | S_IWUSR, + show_fan_alarm, clear_fan_alarm, 1); +static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO | S_IWUSR, + show_fan_alarm, clear_fan_alarm, 2); +static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO | S_IWUSR, + show_fan_alarm, clear_fan_alarm, 3); +static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO | S_IWUSR, + show_fan_alarm, clear_fan_alarm, 4); +static SENSOR_DEVICE_ATTR(fan6_alarm, S_IRUGO | S_IWUSR, + show_fan_alarm, clear_fan_alarm, 5); +static SENSOR_DEVICE_ATTR(fan7_alarm, S_IRUGO | S_IWUSR, + show_fan_alarm, clear_fan_alarm, 6); +static SENSOR_DEVICE_ATTR(fan8_alarm, S_IRUGO | S_IWUSR, + show_fan_alarm, clear_fan_alarm, 7); + +static struct attribute *fts_fan_attrs[] = { + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_fan5_input.dev_attr.attr, + &sensor_dev_attr_fan6_input.dev_attr.attr, + &sensor_dev_attr_fan7_input.dev_attr.attr, + &sensor_dev_attr_fan8_input.dev_attr.attr, + + &sensor_dev_attr_fan1_source.dev_attr.attr, + &sensor_dev_attr_fan2_source.dev_attr.attr, + &sensor_dev_attr_fan3_source.dev_attr.attr, + &sensor_dev_attr_fan4_source.dev_attr.attr, + &sensor_dev_attr_fan5_source.dev_attr.attr, + &sensor_dev_attr_fan6_source.dev_attr.attr, + &sensor_dev_attr_fan7_source.dev_attr.attr, + &sensor_dev_attr_fan8_source.dev_attr.attr, + + &sensor_dev_attr_fan1_alarm.dev_attr.attr, + &sensor_dev_attr_fan2_alarm.dev_attr.attr, + &sensor_dev_attr_fan3_alarm.dev_attr.attr, + &sensor_dev_attr_fan4_alarm.dev_attr.attr, + &sensor_dev_attr_fan5_alarm.dev_attr.attr, + &sensor_dev_attr_fan6_alarm.dev_attr.attr, + &sensor_dev_attr_fan7_alarm.dev_attr.attr, + &sensor_dev_attr_fan8_alarm.dev_attr.attr, + NULL +}; + +/* Voltages */ +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in_value, NULL, 0); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in_value, NULL, 1); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in_value, NULL, 2); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_in_value, NULL, 3); +static struct attribute *fts_voltage_attrs[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group fts_voltage_attr_group = { + .attrs = fts_voltage_attrs +}; + +static const struct attribute_group fts_temp_attr_group = { + .attrs = fts_temp_attrs +}; + +static const struct attribute_group fts_fan_attr_group = { + .attrs = fts_fan_attrs +}; + +static const struct attribute_group *fts_attr_groups[] = { + &fts_voltage_attr_group, + &fts_temp_attr_group, + &fts_fan_attr_group, + NULL +}; + +/*****************************************************************************/ +/* Module initialization / remove functions */ +/*****************************************************************************/ +static int fts_remove(struct i2c_client *client) +{ + struct fts_data *data = dev_get_drvdata(&client->dev); + + watchdog_unregister_device(&data->wdd); + return 0; +} + +static int fts_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + u8 revision; + struct fts_data *data; + int err; + s8 deviceid; + struct device *hwmon_dev; + + if (client->addr != 0x73) + return -ENODEV; + + /* Baseboard Management Controller check */ + deviceid = i2c_smbus_read_byte_data(client, FTS_DEVICE_ID_REG); + if (deviceid > 0 && (deviceid & 0xF0) == 0x10) { + switch (deviceid & 0x0F) { + case 0x01: + break; + default: + dev_dbg(&client->dev, + "No Baseboard Management Controller\n"); + return -ENODEV; + } + } else { + dev_dbg(&client->dev, "No fujitsu board\n"); + return -ENODEV; + } + + data = devm_kzalloc(&client->dev, sizeof(struct fts_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + mutex_init(&data->update_lock); + mutex_init(&data->access_lock); + data->client = client; + dev_set_drvdata(&client->dev, data); + + err = i2c_smbus_read_byte_data(client, FTS_DEVICE_REVISION_REG); + if (err < 0) + return err; + revision = err; + + hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, + "ftsteutates", + data, + fts_attr_groups); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + err = fts_watchdog_init(data); + if (err) + return err; + + dev_info(&client->dev, "Detected FTS Teutates chip, revision: %d.%d\n", + (revision & 0xF0) >> 4, revision & 0x0F); + return 0; +} + +/*****************************************************************************/ +/* Module Details */ +/*****************************************************************************/ +static struct i2c_driver fts_driver = { + .driver = { + .name = "ftsteutates", + }, + .id_table = fts_id, + .probe = fts_probe, + .remove = fts_remove, +}; + +module_i2c_driver(fts_driver); + +MODULE_AUTHOR("Thilo Cestonaro "); +MODULE_DESCRIPTION("FTS Teutates driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 1d3dd4ce210f347dd214913544e22fd9a8122901 Mon Sep 17 00:00:00 2001 From: hotran Date: Thu, 21 Jul 2016 13:55:55 -0700 Subject: Documentation: dtb: xgene: Add hwmon dts binding documentation This patch adds the APM X-Gene hwmon device tree node documentation. Signed-off-by: Hoan Tran Acked-by: Rob Herring Signed-off-by: Guenter Roeck --- .../devicetree/bindings/hwmon/apm-xgene-hwmon.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/apm-xgene-hwmon.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/hwmon/apm-xgene-hwmon.txt b/Documentation/devicetree/bindings/hwmon/apm-xgene-hwmon.txt new file mode 100644 index 000000000000..59b38557f1bb --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/apm-xgene-hwmon.txt @@ -0,0 +1,14 @@ +APM X-Gene hwmon driver + +APM X-Gene SOC sensors are accessed over the "SLIMpro" mailbox. + +Required properties : + - compatible : should be "apm,xgene-slimpro-hwmon" + - mboxes : use the label reference for the mailbox as the first parameter. + The second parameter is the channel number. + +Example : + hwmonslimpro { + compatible = "apm,xgene-slimpro-hwmon"; + mboxes = <&mailbox 7>; + }; -- cgit v1.2.3