diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-09-03 18:43:20 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-09-03 18:43:20 -0700 |
commit | fe91f28138e730790db014812623cfaadd318fa6 (patch) | |
tree | ce8fb5cb94539235db9b7c4be564c2b7b7659317 | |
parent | aa9d4648c2fbb455df7750ade1b73dd9ad9b3690 (diff) | |
parent | 7074d0a92758603369655ef5d4f49e6caaae0b4e (diff) |
Merge tag 'hwmon-for-linus-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck:
- new drivers:
- Lantiq CPU temperature sensor
- IBM CFF power supply
- TPS53679 PMBus driver
- new support:
- LM5066I (lm25066 PMBus driver)
- Intel VID protocol VR13 (PMBus drivers)
- CAT34TS02C, GT30TS00, GT34TS02, and CAT34TS04 (jc42 driver)
- cleanup and minor improvements in several drivers
* tag 'hwmon-for-linus-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (36 commits)
hwmon: (ltq-cputemp) add cpu temp sensor driver
hwmon: (ltq-cputemp) add devicetree bindings documentation
hwmon: (pmbus) Add support for Texas Instruments tps53679 device
hwmon: (asc7621) make several arrays static const
hwmon: (pmbus/lm25066) Add support for TI LM5066I
hwmon: (pmbus/lm25066) Offset coefficient depends on CL
hwmon: (pmbus) Add support for Intel VID protocol VR13
Documentation: hwmon: Document the IBM CFF power supply
hwmon: (pmbus) Add IBM Common Form Factor (CFF) power supply driver
dt-bindings: hwmon: Document the IBM CCF power supply version 1
hwmon: (ftsteutates) constify i2c_device_id
hwmon: da9052: Add support for TSI channel
mfd: da9052: Make touchscreen registration optional
hwmon: da9052: Replace S_IRUGO with 0444
mfd: da9052: Add register details for TSI
hwmon: (aspeed-pwm) add THERMAL dependency
hwmon: (pmbus) Add debugfs for status registers
hwmon: (aspeed-pwm-tacho) cooling device support.
Documentation: dt-bindings: aspeed-pwm-tacho cooling device.
hwmon: (pmbus): Add generic alarm bit for iin and pin
...
33 files changed, 1505 insertions, 138 deletions
diff --git a/Documentation/devicetree/bindings/hwmon/aspeed-pwm-tacho.txt b/Documentation/devicetree/bindings/hwmon/aspeed-pwm-tacho.txt index cf4460564adb..367c8203213b 100644 --- a/Documentation/devicetree/bindings/hwmon/aspeed-pwm-tacho.txt +++ b/Documentation/devicetree/bindings/hwmon/aspeed-pwm-tacho.txt @@ -11,6 +11,8 @@ Required properties for pwm-tacho node: - #size-cells : should be 1. +- #cooling-cells: should be 2. + - reg : address and length of the register set for the device. - pinctrl-names : a pinctrl state named "default" must be defined. @@ -28,12 +30,17 @@ fan subnode format: Under fan subnode there can upto 8 child nodes, with each child node representing a fan. If there are 8 fans each fan can have one PWM port and one/two Fan tach inputs. +For PWM port can be configured cooling-levels to create cooling device. +Cooling device could be bound to a thermal zone for the thermal control. Required properties for each child node: - reg : should specify PWM source port. integer value in the range 0 to 7 with 0 indicating PWM port A and 7 indicating PWM port H. +- cooling-levels: PWM duty cycle values in a range from 0 to 255 + which correspond to thermal cooling states. + - aspeed,fan-tach-ch : should specify the Fan tach input channel. integer value in the range 0 through 15, with 0 indicating Fan tach channel 0 and 15 indicating Fan tach channel 15. @@ -50,6 +57,7 @@ pwm_tacho_fixed_clk: fixedclk { pwm_tacho: pwmtachocontroller@1e786000 { #address-cells = <1>; #size-cells = <1>; + #cooling-cells = <2>; reg = <0x1E786000 0x1000>; compatible = "aspeed,ast2500-pwm-tacho"; clocks = <&pwm_tacho_fixed_clk>; @@ -58,6 +66,7 @@ pwm_tacho: pwmtachocontroller@1e786000 { fan@0 { reg = <0x00>; + cooling-levels = /bits/ 8 <125 151 177 203 229 255>; aspeed,fan-tach-ch = /bits/ 8 <0x00>; }; diff --git a/Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt b/Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt new file mode 100644 index 000000000000..f68a0a68fc52 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt @@ -0,0 +1,21 @@ +Device-tree bindings for IBM Common Form Factor Power Supply Version 1 +---------------------------------------------------------------------- + +Required properties: + - compatible = "ibm,cffps1"; + - reg = < I2C bus address >; : Address of the power supply on the + I2C bus. + +Example: + + i2c-bus@100 { + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <1>; + < more properties > + + power-supply@68 { + compatible = "ibm,cffps1"; + reg = <0x68>; + }; + }; diff --git a/Documentation/devicetree/bindings/hwmon/ltq-cputemp.txt b/Documentation/devicetree/bindings/hwmon/ltq-cputemp.txt new file mode 100644 index 000000000000..33fd00a987c7 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/ltq-cputemp.txt @@ -0,0 +1,10 @@ +Lantiq cpu temperatur sensor + +Requires node properties: +- compatible value : + "lantiq,cputemp" + +Example: + cputemp@0 { + compatible = "lantiq,cputemp"; + }; diff --git a/Documentation/hwmon/ftsteutates b/Documentation/hwmon/ftsteutates index 8c10a916de20..af54db92391b 100644 --- a/Documentation/hwmon/ftsteutates +++ b/Documentation/hwmon/ftsteutates @@ -18,6 +18,10 @@ 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. +To clear a temperature or fan alarm, execute the following command with the +correct path to the alarm file: + echo 0 >XXXX_alarm + Specification of the chip can be found here: ftp://ftp.ts.fujitsu.com/pub/Mainboard-OEM-Sales/Services/Software&Tools/Linux_SystemMonitoring&Watchdog&GPIO/BMC-Teutates_Specification_V1.21.pdf ftp://ftp.ts.fujitsu.com/pub/Mainboard-OEM-Sales/Services/Software&Tools/Linux_SystemMonitoring&Watchdog&GPIO/Fujitsu_mainboards-1-Sensors_HowTo-en-US.pdf diff --git a/Documentation/hwmon/ibm-cffps b/Documentation/hwmon/ibm-cffps new file mode 100644 index 000000000000..e05ecd8ecfcf --- /dev/null +++ b/Documentation/hwmon/ibm-cffps @@ -0,0 +1,54 @@ +Kernel driver ibm-cffps +======================= + +Supported chips: + * IBM Common Form Factor power supply + +Author: Eddie James <eajames@us.ibm.com> + +Description +----------- + +This driver supports IBM Common Form Factor (CFF) power supplies. This driver +is a client to the core PMBus driver. + +Usage Notes +----------- + +This driver does not auto-detect devices. You will have to instantiate the +devices explicitly. Please see Documentation/i2c/instantiating-devices for +details. + +Sysfs entries +------------- + +The following attributes are supported: + +curr1_alarm Output current over-current alarm. +curr1_input Measured output current in mA. +curr1_label "iout1" + +fan1_alarm Fan 1 warning. +fan1_fault Fan 1 fault. +fan1_input Fan 1 speed in RPM. +fan2_alarm Fan 2 warning. +fan2_fault Fan 2 fault. +fan2_input Fan 2 speed in RPM. + +in1_alarm Input voltage under-voltage alarm. +in1_input Measured input voltage in mV. +in1_label "vin" +in2_alarm Output voltage over-voltage alarm. +in2_input Measured output voltage in mV. +in2_label "vout1" + +power1_alarm Input fault or alarm. +power1_input Measured input power in uW. +power1_label "pin" + +temp1_alarm PSU inlet ambient temperature over-temperature alarm. +temp1_input Measured PSU inlet ambient temp in millidegrees C. +temp2_alarm Secondary rectifier temp over-temperature alarm. +temp2_input Measured secondary rectifier temp in millidegrees C. +temp3_alarm ORing FET temperature over-temperature alarm. +temp3_input Measured ORing FET temperature in millidegrees C. diff --git a/Documentation/hwmon/lm25066 b/Documentation/hwmon/lm25066 index 2cb20ebb234d..3fa6bf820c88 100644 --- a/Documentation/hwmon/lm25066 +++ b/Documentation/hwmon/lm25066 @@ -29,6 +29,11 @@ Supported chips: Addresses scanned: - Datasheet: http://www.national.com/pf/LM/LM5066.html + * Texas Instruments LM5066I + Prefix: 'lm5066i' + Addresses scanned: - + Datasheet: + http://www.ti.com/product/LM5066I Author: Guenter Roeck <linux@roeck-us.net> @@ -37,8 +42,8 @@ Description ----------- This driver supports hardware monitoring for National Semiconductor / TI LM25056, -LM25063, LM25066, LM5064, and LM5066 Power Management, Monitoring, Control, and -Protection ICs. +LM25063, LM25066, LM5064, and LM5066/LM5066I Power Management, Monitoring, +Control, and Protection ICs. The driver is a client driver to the core PMBus driver. Please see Documentation/hwmon/pmbus for details on PMBus client drivers. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5ef2814345ef..d65431417b17 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -343,6 +343,7 @@ config SENSORS_ASB100 config SENSORS_ASPEED tristate "ASPEED AST2400/AST2500 PWM and Fan tach driver" + depends on THERMAL || THERMAL=n select REGMAP help This driver provides support for ASPEED AST2400/AST2500 PWM @@ -790,6 +791,13 @@ config SENSORS_LTC4261 This driver can also be built as a module. If so, the module will be called ltc4261. +config SENSORS_LTQ_CPUTEMP + bool "Lantiq cpu temperature sensor driver" + depends on LANTIQ + help + If you say yes here you get support for the temperature + sensor inside your CPU. + config SENSORS_MAX1111 tristate "Maxim MAX1111 Serial 8-bit ADC chip and compatibles" depends on SPI_MASTER diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index d4641a9f16c1..c84d9784be98 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -110,6 +110,7 @@ obj-$(CONFIG_SENSORS_LTC4222) += ltc4222.o obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o obj-$(CONFIG_SENSORS_LTC4260) += ltc4260.o obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o +obj-$(CONFIG_SENSORS_LTQ_CPUTEMP) += ltq-cputemp.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o obj-$(CONFIG_SENSORS_MAX16065) += max16065.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o diff --git a/drivers/hwmon/adc128d818.c b/drivers/hwmon/adc128d818.c index a557b46dbe8e..bd2ca315c9d8 100644 --- a/drivers/hwmon/adc128d818.c +++ b/drivers/hwmon/adc128d818.c @@ -384,7 +384,7 @@ static struct attribute *adc128_attrs[] = { NULL }; -static struct attribute_group adc128_group = { +static const struct attribute_group adc128_group = { .attrs = adc128_attrs, .is_visible = adc128_is_visible, }; diff --git a/drivers/hwmon/ads1015.c b/drivers/hwmon/ads1015.c index 357b42607164..98c704d3366a 100644 --- a/drivers/hwmon/ads1015.c +++ b/drivers/hwmon/ads1015.c @@ -191,24 +191,23 @@ static int ads1015_get_channels_config_of(struct i2c_client *client) unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE; if (of_property_read_u32(node, "reg", &pval)) { - dev_err(&client->dev, "invalid reg on %s\n", - node->full_name); + dev_err(&client->dev, "invalid reg on %pOF\n", node); continue; } channel = pval; if (channel >= ADS1015_CHANNELS) { dev_err(&client->dev, - "invalid channel index %d on %s\n", - channel, node->full_name); + "invalid channel index %d on %pOF\n", + channel, node); continue; } if (!of_property_read_u32(node, "ti,gain", &pval)) { pga = pval; if (pga > 6) { - dev_err(&client->dev, "invalid gain on %s\n", - node->full_name); + dev_err(&client->dev, "invalid gain on %pOF\n", + node); return -EINVAL; } } @@ -217,8 +216,7 @@ static int ads1015_get_channels_config_of(struct i2c_client *client) data_rate = pval; if (data_rate > 7) { dev_err(&client->dev, - "invalid data_rate on %s\n", - node->full_name); + "invalid data_rate on %pOF\n", node); return -EINVAL; } } diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index 1baa213a60bd..9ef84998c7f3 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -1319,14 +1319,14 @@ static struct attribute *vid_attrs[] = { NULL }; -static struct attribute_group adt7475_attr_group = { .attrs = adt7475_attrs }; -static struct attribute_group fan4_attr_group = { .attrs = fan4_attrs }; -static struct attribute_group pwm2_attr_group = { .attrs = pwm2_attrs }; -static struct attribute_group in0_attr_group = { .attrs = in0_attrs }; -static struct attribute_group in3_attr_group = { .attrs = in3_attrs }; -static struct attribute_group in4_attr_group = { .attrs = in4_attrs }; -static struct attribute_group in5_attr_group = { .attrs = in5_attrs }; -static struct attribute_group vid_attr_group = { .attrs = vid_attrs }; +static const struct attribute_group adt7475_attr_group = { .attrs = adt7475_attrs }; +static const struct attribute_group fan4_attr_group = { .attrs = fan4_attrs }; +static const struct attribute_group pwm2_attr_group = { .attrs = pwm2_attrs }; +static const struct attribute_group in0_attr_group = { .attrs = in0_attrs }; +static const struct attribute_group in3_attr_group = { .attrs = in3_attrs }; +static const struct attribute_group in4_attr_group = { .attrs = in4_attrs }; +static const struct attribute_group in5_attr_group = { .attrs = in5_attrs }; +static const struct attribute_group vid_attr_group = { .attrs = vid_attrs }; static int adt7475_detect(struct i2c_client *client, struct i2c_board_info *info) diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c index c77644d45a02..4875e99b59c9 100644 --- a/drivers/hwmon/asc7621.c +++ b/drivers/hwmon/asc7621.c @@ -512,7 +512,7 @@ static ssize_t show_pwm_ac(struct device *dev, { SETUP_SHOW_DATA_PARAM(dev, attr); u8 config, altbit, regval; - const u8 map[] = { + static const u8 map[] = { 0x01, 0x02, 0x04, 0x1f, 0x00, 0x06, 0x07, 0x10, 0x08, 0x0f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f }; @@ -533,7 +533,7 @@ static ssize_t store_pwm_ac(struct device *dev, SETUP_STORE_DATA_PARAM(dev, attr); unsigned long reqval; u8 currval, config, altbit, newval; - const u16 map[] = { + static const u16 map[] = { 0x04, 0x00, 0x01, 0xff, 0x02, 0xff, 0x05, 0x06, 0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c index ddfe66bdff86..69b97d45e3cb 100644 --- a/drivers/hwmon/aspeed-pwm-tacho.c +++ b/drivers/hwmon/aspeed-pwm-tacho.c @@ -20,6 +20,7 @@ #include <linux/platform_device.h> #include <linux/sysfs.h> #include <linux/regmap.h> +#include <linux/thermal.h> /* ASPEED PWM & FAN Tach Register Definition */ #define ASPEED_PTCR_CTRL 0x00 @@ -166,6 +167,18 @@ /* How long we sleep in us while waiting for an RPM result. */ #define ASPEED_RPM_STATUS_SLEEP_USEC 500 +#define MAX_CDEV_NAME_LEN 16 + +struct aspeed_cooling_device { + char name[16]; + struct aspeed_pwm_tacho_data *priv; + struct thermal_cooling_device *tcdev; + int pwm_port; + u8 *cooling_levels; + u8 max_state; + u8 cur_state; +}; + struct aspeed_pwm_tacho_data { struct regmap *regmap; unsigned long clk_freq; @@ -180,6 +193,7 @@ struct aspeed_pwm_tacho_data { u8 pwm_port_type[8]; u8 pwm_port_fan_ctrl[8]; u8 fan_tach_ch_source[16]; + struct aspeed_cooling_device *cdev[8]; const struct attribute_group *groups[3]; }; @@ -765,6 +779,94 @@ static void aspeed_create_fan_tach_channel(struct aspeed_pwm_tacho_data *priv, } } +static int +aspeed_pwm_cz_get_max_state(struct thermal_cooling_device *tcdev, + unsigned long *state) +{ + struct aspeed_cooling_device *cdev = tcdev->devdata; + + *state = cdev->max_state; + + return 0; +} + +static int +aspeed_pwm_cz_get_cur_state(struct thermal_cooling_device *tcdev, + unsigned long *state) +{ + struct aspeed_cooling_device *cdev = tcdev->devdata; + + *state = cdev->cur_state; + + return 0; +} + +static int +aspeed_pwm_cz_set_cur_state(struct thermal_cooling_device *tcdev, + unsigned long state) +{ + struct aspeed_cooling_device *cdev = tcdev->devdata; + + if (state > cdev->max_state) + return -EINVAL; + + cdev->cur_state = state; + cdev->priv->pwm_port_fan_ctrl[cdev->pwm_port] = + cdev->cooling_levels[cdev->cur_state]; + aspeed_set_pwm_port_fan_ctrl(cdev->priv, cdev->pwm_port, + cdev->cooling_levels[cdev->cur_state]); + + return 0; +} + +static const struct thermal_cooling_device_ops aspeed_pwm_cool_ops = { + .get_max_state = aspeed_pwm_cz_get_max_state, + .get_cur_state = aspeed_pwm_cz_get_cur_state, + .set_cur_state = aspeed_pwm_cz_set_cur_state, +}; + +static int aspeed_create_pwm_cooling(struct device *dev, + struct device_node *child, + struct aspeed_pwm_tacho_data *priv, + u32 pwm_port, u8 num_levels) +{ + int ret; + struct aspeed_cooling_device *cdev; + + cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL); + + if (!cdev) + return -ENOMEM; + + cdev->cooling_levels = devm_kzalloc(dev, num_levels, GFP_KERNEL); + if (!cdev->cooling_levels) + return -ENOMEM; + + cdev->max_state = num_levels - 1; + ret = of_property_read_u8_array(child, "cooling-levels", + cdev->cooling_levels, + num_levels); + if (ret) { + dev_err(dev, "Property 'cooling-levels' cannot be read.\n"); + return ret; + } + snprintf(cdev->name, MAX_CDEV_NAME_LEN, "%s%d", child->name, pwm_port); + + cdev->tcdev = thermal_of_cooling_device_register(child, + cdev->name, + cdev, + &aspeed_pwm_cool_ops); + if (IS_ERR(cdev->tcdev)) + return PTR_ERR(cdev->tcdev); + + cdev->priv = priv; + cdev->pwm_port = pwm_port; + + priv->cdev[pwm_port] = cdev; + + return 0; +} + static int aspeed_create_fan(struct device *dev, struct device_node *child, struct aspeed_pwm_tacho_data *priv) @@ -778,6 +880,15 @@ static int aspeed_create_fan(struct device *dev, return ret; aspeed_create_pwm_port(priv, (u8)pwm_port); + ret = of_property_count_u8_elems(child, "cooling-levels"); + + if (ret > 0) { + ret = aspeed_create_pwm_cooling(dev, child, priv, pwm_port, + ret); + if (ret) + return ret; + } + count = of_property_count_u8_elems(child, "aspeed,fan-tach-ch"); if (count < 1) return -EINVAL; @@ -834,9 +945,10 @@ static int aspeed_pwm_tacho_probe(struct platform_device *pdev) for_each_child_of_node(np, child) { ret = aspeed_create_fan(dev, child, priv); - of_node_put(child); - if (ret) + if (ret) { + of_node_put(child); return ret; + } } priv->groups[0] = &pwm_dev_group; diff --git a/drivers/hwmon/da9052-hwmon.c b/drivers/hwmon/da9052-hwmon.c index c9832bfacfe5..97a62f5b9ea4 100644 --- a/drivers/hwmon/da9052-hwmon.c +++ b/drivers/hwmon/da9052-hwmon.c @@ -20,13 +20,19 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/mfd/da9052/da9052.h> #include <linux/mfd/da9052/reg.h> +#include <linux/regulator/consumer.h> struct da9052_hwmon { - struct da9052 *da9052; - struct mutex hwmon_lock; + struct da9052 *da9052; + struct mutex hwmon_lock; + bool tsi_as_adc; + int tsiref_mv; + struct regulator *tsiref; + struct completion tsidone; }; static const char * const input_names[] = { @@ -37,6 +43,10 @@ static const char * const input_names[] = { [DA9052_ADC_IN4] = "ADC IN4", [DA9052_ADC_IN5] = "ADC IN5", [DA9052_ADC_IN6] = "ADC IN6", + [DA9052_ADC_TSI_XP] = "ADC TS X+", + [DA9052_ADC_TSI_YP] = "ADC TS Y+", + [DA9052_ADC_TSI_XN] = "ADC TS X-", + [DA9052_ADC_TSI_YN] = "ADC TS Y-", [DA9052_ADC_TJUNC] = "BATTERY JUNCTION TEMP", [DA9052_ADC_VBBAT] = "BACK-UP BATTERY VOLTAGE", }; @@ -59,6 +69,11 @@ static inline int vbbat_reg_to_mv(int value) return DIV_ROUND_CLOSEST(value * 5000, 1023); } +static inline int input_tsireg_to_mv(struct da9052_hwmon *hwmon, int value) +{ + return DIV_ROUND_CLOSEST(value * hwmon->tsiref_mv, 1023); +} + static inline int da9052_enable_vddout_channel(struct da9052 *da9052) { return da9052_reg_update(da9052, DA9052_ADC_CONT_REG, @@ -154,6 +169,97 @@ static ssize_t da9052_read_misc_channel(struct device *dev, return sprintf(buf, "%d\n", input_reg_to_mv(ret)); } +static int da9052_request_tsi_read(struct da9052_hwmon *hwmon, int channel) +{ + u8 val = DA9052_TSICONTB_TSIMAN; + + switch (channel) { + case DA9052_ADC_TSI_XP: + val |= DA9052_TSICONTB_TSIMUX_XP; + break; + case DA9052_ADC_TSI_YP: + val |= DA9052_TSICONTB_TSIMUX_YP; + break; + case DA9052_ADC_TSI_XN: + val |= DA9052_TSICONTB_TSIMUX_XN; + break; + case DA9052_ADC_TSI_YN: + val |= DA9052_TSICONTB_TSIMUX_YN; + break; + } + + return da9052_reg_write(hwmon->da9052, DA9052_TSI_CONT_B_REG, val); +} + +static int da9052_get_tsi_result(struct da9052_hwmon *hwmon, int channel) +{ + u8 regs[3]; + int msb, lsb, err; + + /* block read to avoid separation of MSB and LSB */ + err = da9052_group_read(hwmon->da9052, DA9052_TSI_X_MSB_REG, + ARRAY_SIZE(regs), regs); + if (err) + return err; + + switch (channel) { + case DA9052_ADC_TSI_XP: + case DA9052_ADC_TSI_XN: + msb = regs[0] << DA9052_TSILSB_TSIXL_BITS; + lsb = regs[2] & DA9052_TSILSB_TSIXL; + lsb >>= DA9052_TSILSB_TSIXL_SHIFT; + break; + case DA9052_ADC_TSI_YP: + case DA9052_ADC_TSI_YN: + msb = regs[1] << DA9052_TSILSB_TSIYL_BITS; + lsb = regs[2] & DA9052_TSILSB_TSIYL; + lsb >>= DA9052_TSILSB_TSIYL_SHIFT; + break; + default: + return -EINVAL; + } + + return msb | lsb; +} + + +static ssize_t __da9052_read_tsi(struct device *dev, int channel) +{ + struct da9052_hwmon *hwmon = dev_get_drvdata(dev); + int ret; + + reinit_completion(&hwmon->tsidone); + + ret = da9052_request_tsi_read(hwmon, channel); + if (ret < 0) + return ret; + + /* Wait for an conversion done interrupt */ + if (!wait_for_completion_timeout(&hwmon->tsidone, + msecs_to_jiffies(500))) + return -ETIMEDOUT; + + return da9052_get_tsi_result(hwmon, channel); +} + +static ssize_t da9052_read_tsi(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct da9052_hwmon *hwmon = dev_get_drvdata(dev); + int channel = to_sensor_dev_attr(devattr)->index; + int ret; + + mutex_lock(&hwmon->hwmon_lock); + ret = __da9052_read_tsi(dev, channel); + mutex_unlock(&hwmon->hwmon_lock); + + if (ret < 0) + return ret; + else + return sprintf(buf, "%d\n", input_tsireg_to_mv(hwmon, ret)); +} + static ssize_t da9052_read_tjunc(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -196,43 +302,82 @@ static ssize_t show_label(struct device *dev, input_names[to_sensor_dev_attr(devattr)->index]); } -static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, da9052_read_vddout, NULL, +static umode_t da9052_channel_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct da9052_hwmon *hwmon = dev_get_drvdata(dev); + struct device_attribute *dattr = container_of(attr, + struct device_attribute, attr); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(dattr); + + if (!hwmon->tsi_as_adc) { + switch (sattr->index) { + case DA9052_ADC_TSI_XP: + case DA9052_ADC_TSI_YP: + case DA9052_ADC_TSI_XN: + case DA9052_ADC_TSI_YN: + return 0; + } + } + + return attr->mode; +} + +static SENSOR_DEVICE_ATTR(in0_input, 0444, da9052_read_vddout, NULL, DA9052_ADC_VDDOUT); -static SENSOR_DEVICE_ATTR(in0_label, S_IRUGO, show_label, NULL, +static SENSOR_DEVICE_ATTR(in0_label, 0444, show_label, NULL, DA9052_ADC_VDDOUT); -static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, da9052_read_vbat, NULL, +static SENSOR_DEVICE_ATTR(in3_input, 0444, da9052_read_vbat, NULL, DA9052_ADC_VBAT); -static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, +static SENSOR_DEVICE_ATTR(in3_label, 0444, show_label, NULL, DA9052_ADC_VBAT); -static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, da9052_read_misc_channel, NULL, +static SENSOR_DEVICE_ATTR(in4_input, 0444, da9052_read_misc_channel, NULL, DA9052_ADC_IN4); -static SENSOR_DEVICE_ATTR(in4_label, S_IRUGO, show_label, NULL, +static SENSOR_DEVICE_ATTR(in4_label, 0444, show_label, NULL, DA9052_ADC_IN4); -static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, da9052_read_misc_channel, NULL, +static SENSOR_DEVICE_ATTR(in5_input, 0444, da9052_read_misc_channel, NULL, DA9052_ADC_IN5); -static SENSOR_DEVICE_ATTR(in5_label, S_IRUGO, show_label, NULL, +static SENSOR_DEVICE_ATTR(in5_label, 0444, show_label, NULL, DA9052_ADC_IN5); -static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, da9052_read_misc_channel, NULL, +static SENSOR_DEVICE_ATTR(in6_input, 0444, da9052_read_misc_channel, NULL, DA9052_ADC_IN6); -static SENSOR_DEVICE_ATTR(in6_label, S_IRUGO, show_label, NULL, +static SENSOR_DEVICE_ATTR(in6_label, 0444, show_label, NULL, DA9052_ADC_IN6); -static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, da9052_read_vbbat, NULL, +static SENSOR_DEVICE_ATTR(in9_input, 0444, da9052_read_vbbat, NULL, DA9052_ADC_VBBAT); -static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_label, NULL, +static SENSOR_DEVICE_ATTR(in9_label, 0444, show_label, NULL, DA9052_ADC_VBBAT); -static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, da9052_read_ich, NULL, +static SENSOR_DEVICE_ATTR(in70_input, 0444, da9052_read_tsi, NULL, + DA9052_ADC_TSI_XP); +static SENSOR_DEVICE_ATTR(in70_label, 0444, show_label, NULL, + DA9052_ADC_TSI_XP); +static SENSOR_DEVICE_ATTR(in71_input, 0444, da9052_read_tsi, NULL, + DA9052_ADC_TSI_XN); +static SENSOR_DEVICE_ATTR(in71_label, 0444, show_label, NULL, + DA9052_ADC_TSI_XN); +static SENSOR_DEVICE_ATTR(in72_input, 0444, da9052_read_tsi, NULL, + DA9052_ADC_TSI_YP); +static SENSOR_DEVICE_ATTR(in72_label, 0444, show_label, NULL, + DA9052_ADC_TSI_YP); +static SENSOR_DEVICE_ATTR(in73_input, 0444, da9052_read_tsi, NULL, + DA9052_ADC_TSI_YN); +static SENSOR_DEVICE_ATTR(in73_label, 0444, show_label, NULL, + DA9052_ADC_TSI_YN); + +static SENSOR_DEVICE_ATTR(curr1_input, 0444, da9052_read_ich, NULL, DA9052_ADC_ICH); -static SENSOR_DEVICE_ATTR(curr1_label, S_IRUGO, show_label, NULL, +static SENSOR_DEVICE_ATTR(curr1_label, 0444, show_label, NULL, DA9052_ADC_ICH); -static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, da9052_read_tbat, NULL, +static SENSOR_DEVICE_ATTR(temp2_input, 0444, da9052_read_tbat, NULL, DA9052_ADC_TBAT); -static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL, +static SENSOR_DEVICE_ATTR(temp2_label, 0444, show_label, NULL, DA9052_ADC_TBAT); -static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, da9052_read_tjunc, NULL, +static SENSOR_DEVICE_ATTR(temp8_input, 0444, da9052_read_tjunc, NULL, DA9052_ADC_TJUNC); -static SENSOR_DEVICE_ATTR(temp8_label, S_IRUGO, show_label, NULL, +static SENSOR_DEVICE_ATTR(temp8_label, 0444, show_label, NULL, DA9052_ADC_TJUNC); static struct attribute *da9052_attrs[] = { @@ -246,6 +391,14 @@ static struct attribute *da9052_attrs[] = { &sensor_dev_attr_in5_label.dev_attr.attr, &sensor_dev_attr_in6_input.dev_attr.attr, &sensor_dev_attr_in6_label.dev_attr.attr, + &sensor_dev_attr_in70_input.dev_attr.attr, + &sensor_dev_attr_in70_label.dev_attr.attr, + &sensor_dev_attr_in71_input.dev_attr.attr, + &sensor_dev_attr_in71_label.dev_attr.attr, + &sensor_dev_attr_in72_input.dev_attr.attr, + &sensor_dev_attr_in72_label.dev_attr.attr, + &sensor_dev_attr_in73_input.dev_attr.attr, + &sensor_dev_attr_in73_label.dev_attr.attr, &sensor_dev_attr_in9_input.dev_attr.attr, &sensor_dev_attr_in9_label.dev_attr.attr, &sensor_dev_attr_curr1_input.dev_attr.attr, @@ -257,29 +410,117 @@ static struct attribute *da9052_attrs[] = { NULL }; -ATTRIBUTE_GROUPS(da9052); +static const struct attribute_group da9052_group = { + .attrs = da9052_attrs, + .is_visible = da9052_channel_is_visible, +}; +__ATTRIBUTE_GROUPS(da9052); + +static irqreturn_t da9052_tsi_datardy_irq(int irq, void *data) +{ + struct da9052_hwmon *hwmon = data; + + complete(&hwmon->tsidone); + return IRQ_HANDLED; +} static int da9052_hwmon_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct da9052_hwmon *hwmon; struct device *hwmon_dev; + int err; hwmon = devm_kzalloc(dev, sizeof(struct da9052_hwmon), GFP_KERNEL); if (!hwmon) return -ENOMEM; + platform_set_drvdata(pdev, hwmon); + mutex_init(&hwmon->hwmon_lock); hwmon->da9052 = dev_get_drvdata(pdev->dev.parent); + init_completion(&hwmon->tsidone); + + hwmon->tsi_as_adc = + device_property_read_bool(pdev->dev.parent, "dlg,tsi-as-adc"); + + if (hwmon->tsi_as_adc) { + hwmon->tsiref = devm_regulator_get(pdev->dev.parent, "tsiref"); + if (IS_ERR(hwmon->tsiref)) { + err = PTR_ERR(hwmon->tsiref); + dev_err(&pdev->dev, "failed to get tsiref: %d", err); + return err; + } + + err = regulator_enable(hwmon->tsiref); + if (err) + return err; + + hwmon->tsiref_mv = regulator_get_voltage(hwmon->tsiref); + if (hwmon->tsiref_mv < 0) { + err = hwmon->tsiref_mv; + goto exit_regulator; + } + + /* convert from microvolt (DT) to millivolt (hwmon) */ + hwmon->tsiref_mv /= 1000; + + /* TSIREF limits from datasheet */ + if (hwmon->tsiref_mv < 1800 || hwmon->tsiref_mv > 2600) { + dev_err(hwmon->da9052->dev, "invalid TSIREF voltage: %d", + hwmon->tsiref_mv); + err = -ENXIO; + goto exit_regulator; + } + + /* disable touchscreen features */ + da9052_reg_write(hwmon->da9052, DA9052_TSI_CONT_A_REG, 0x00); + + err = da9052_request_irq(hwmon->da9052, DA9052_IRQ_TSIREADY, + "tsiready-irq", da9052_tsi_datardy_irq, + hwmon); + if (err) { + dev_err(&pdev->dev, "Failed to register TSIRDY IRQ: %d", + err); + goto exit_regulator; + } + } + hwmon_dev = devm_hwmon_device_register_with_groups(dev, "da9052", hwmon, da9052_groups); - return PTR_ERR_OR_ZERO(hwmon_dev); + err = PTR_ERR_OR_ZERO(hwmon_dev); + if (err) + goto exit_irq; + + return 0; + +exit_irq: + if (hwmon->tsi_as_adc) + da9052_free_irq(hwmon->da9052, DA9052_IRQ_TSIREADY, hwmon); +exit_regulator: + if (hwmon->tsiref) + regulator_disable(hwmon->tsiref); + + return err; +} + +static int da9052_hwmon_remove(struct platform_device *pdev) +{ + struct da9052_hwmon *hwmon = platform_get_drvdata(pdev); + + if (hwmon->tsi_as_adc) { + da9052_free_irq(hwmon->da9052, DA9052_IRQ_TSIREADY, hwmon); + regulator_disable(hwmon->tsiref); + } + + return 0; } static struct platform_driver da9052_hwmon_driver = { .probe = da9052_hwmon_probe, + .remove = da9052_hwmon_remove, .driver = { .name = "da9052-hwmon", }, diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c index 0f0277e7aae5..0801f48a41f7 100644 --- a/drivers/hwmon/ftsteutates.c +++ b/drivers/hwmon/ftsteutates.c @@ -60,7 +60,7 @@ static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; -static struct i2c_device_id fts_id[] = { +static const struct i2c_device_id fts_id[] = { { "ftsteutates", 0 }, { } }; @@ -435,6 +435,7 @@ clear_temp_alarm(struct device *dev, struct device_attribute *devattr, goto error; data->valid = false; + ret = count; error: mutex_unlock(&data->update_lock); return ret; @@ -508,6 +509,7 @@ clear_fan_alarm(struct device *dev, struct device_attribute *devattr, goto error; data->valid = false; + ret = count; error: mutex_unlock(&data->update_lock); return ret; diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index dd6e17c1076b..c9790e2c3440 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -85,7 +85,7 @@ static umode_t hwmon_dev_name_is_visible(struct kobject *kobj, return attr->mode; } -static struct attribute_group hwmon_dev_attr_group = { +static const struct attribute_group hwmon_dev_attr_group = { .attrs = hwmon_dev_attrs, .is_visible = hwmon_dev_name_is_visible, }; @@ -135,7 +135,7 @@ static int hwmon_thermal_get_temp(void *data, int *temp) return 0; } -static struct thermal_zone_of_device_ops hwmon_thermal_ops = { +static const struct thermal_zone_of_device_ops hwmon_thermal_ops = { .get_temp = hwmon_thermal_get_temp, }; diff --git a/drivers/hwmon/i5k_amb.c b/drivers/hwmon/i5k_amb.c index a5a9f457b7f7..9397d2f0e79a 100644 --- a/drivers/hwmon/i5k_amb.c +++ b/drivers/hwmon/i5k_amb.c @@ -495,7 +495,7 @@ static struct { }; #ifdef MODULE -static struct pci_device_id i5k_amb_ids[] = { +static const struct pci_device_id i5k_amb_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5000_ERR) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5400_ERR) }, { 0, } diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 4dfc7238313e..f8499cb95fec 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -497,12 +497,14 @@ static const struct it87_devices it87_devices[] = { #define has_vin3_5v(data) ((data)->features & FEAT_VIN3_5V) struct it87_sio_data { + int sioaddr; enum chips type; /* Values read from Super-I/O config space */ u8 revision; u8 vid_value; u8 beep_pin; u8 internal; /* Internal sensors can be labeled */ + bool need_in7_reroute; /* Features skipped based on config or DMI */ u16 skip_in; u8 skip_vid; @@ -517,6 +519,7 @@ struct it87_sio_data { */ struct it87_data { const struct attribute_group *groups[7]; + int sioaddr; enum chips type; u32 features; u8 peci_mask; @@ -532,6 +535,7 @@ struct it87_data { u16 in_internal; /* Bitfield, internal sensors (for labels) */ u16 has_in; /* Bitfield, voltage sensors enabled */ u8 in[NUM_VIN][3]; /* [nr][0]=in, [1]=min, [2]=max */ + bool need_in7_reroute; u8 has_fan; /* Bitfield, fans enabled */ u16 fan[NUM_FAN][2]; /* Register values, [nr][0]=fan, [1]=min */ u8 has_temp; /* Bitfield, temp sensors enabled */ @@ -2487,6 +2491,7 @@ static int __init it87_find(int sioaddr, unsigned short *address, } err = 0; + sio_data->sioaddr = sioaddr; sio_data->revision = superio_inb(sioaddr, DEVREV) & 0x0f; pr_info("Found IT%04x%s chip at 0x%x, revision %d\n", chip_type, it87_devices[sio_data->type].suffix, @@ -2575,6 +2580,7 @@ static int __init it87_find(int sioaddr, unsigned short *address, reg2c |= BIT(1); superio_outb(sioaddr, IT87_SIO_PINX2_REG, reg2c); + sio_data->need_in7_reroute = true; pr_notice("Routing internal VCCH5V to in7.\n"); } pr_notice("in7 routed to internal voltage divider, with external pin disabled.\n"); @@ -2761,13 +2767,13 @@ static int __init it87_find(int sioaddr, unsigned short *address, uart6 = sio_data->type == it8782 && (reg & BIT(2)); /* - * The IT8720F has no VIN7 pin, so VCCH should always be + * The IT8720F has no VIN7 pin, so VCCH5V should always be * routed internally to VIN7 with an internal divider. * Curiously, there still is a configuration bit to control * this, which means it can be set incorrectly. And even * more curiously, many boards out there are improperly * configured, even though the IT8720F datasheet claims - * that the internal routing of VCCH to VIN7 is the default + * that the internal routing of VCCH5V to VIN7 is the default * setting. So we force the internal routing in this case. * * On IT8782F, VIN7 is multiplexed with one of the UART6 pins. @@ -2777,7 +2783,8 @@ static int __init it87_find(int sioaddr, unsigned short *address, if ((sio_data->type == it8720 || uart6) && !(reg & BIT(1))) { reg |= BIT(1); superio_outb(sioaddr, IT87_SIO_PINX2_REG, reg); - pr_notice("Routing internal VCCH to in7\n"); + sio_data->need_in7_reroute = true; + pr_notice("Routing internal VCCH5V to in7\n"); } if (reg & BIT(0)) sio_data->internal |= BIT(0); @@ -2828,13 +2835,89 @@ exit: return err; } +/* + * Some chips seem to have default value 0xff for all limit + * registers. For low voltage limits it makes no sense and triggers + * alarms, so change to 0 instead. For high temperature limits, it + * means -1 degree C, which surprisingly doesn't trigger an alarm, + * but is still confusing, so change to 127 degrees C. + */ +static void it87_check_limit_regs(struct it87_data *data) +{ + int i, reg; + + for (i = 0; i < NUM_VIN_LIMIT; i++) { + reg = it87_read_value(data, IT87_REG_VIN_MIN(i)); + if (reg == 0xff) + it87_write_value(data, IT87_REG_VIN_MIN(i), 0); + } + for (i = 0; i < NUM_TEMP_LIMIT; i++) { + reg = it87_read_value(data, IT87_REG_TEMP_HIGH(i)); + if (reg == 0xff) + it87_write_value(data, IT87_REG_TEMP_HIGH(i), 127); + } +} + +/* Check if voltage monitors are reset manually or by some reason */ +static void it87_check_voltage_monitors_reset(struct it87_data *data) +{ + int reg; + + reg = it87_read_value(data, IT87_REG_VIN_ENABLE); + if ((reg & 0xff) == 0) { + /* Enable all voltage monitors */ + it87_write_value(data, IT87_REG_VIN_ENABLE, 0xff); + } +} + +/* Check if tachometers are reset manually or by some reason */ +static void it87_check_tachometers_reset(struct platform_device *pdev) +{ + struct it87_sio_data *sio_data = dev_get_platdata(&pdev->dev); + struct it87_data *data = platform_get_drvdata(pdev); + u8 mask, fan_main_ctrl; + + mask = 0x70 & ~(sio_data->skip_fan << 4); + fan_main_ctrl = it87_read_value(data, IT87_REG_FAN_MAIN_CTRL); + if ((fan_main_ctrl & mask) == 0) { + /* Enable all fan tachometers */ + fan_main_ctrl |= mask; + it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, + fan_main_ctrl); + } +} + +/* Set tachometers to 16-bit mode if needed */ +static void it87_check_tachometers_16bit_mode(struct platform_device *pdev) +{ + struct it87_data *data = platform_get_drvdata(pdev); + int reg; + + if (!has_fan16_config(data)) + return; + + reg = it87_read_value(data, IT87_REG_FAN_16BIT); + if (~reg & 0x07 & data->has_fan) { + dev_dbg(&pdev->dev, + "Setting fan1-3 to 16-bit mode\n"); + it87_write_value(data, IT87_REG_FAN_16BIT, + reg | 0x07); + } +} + +static void it87_start_monitoring(struct it87_data *data) +{ + it87_write_value(data, IT87_REG_CONFIG, + (it87_read_value(data, IT87_REG_CONFIG) & 0x3e) + | (update_vbat ? 0x41 : 0x01)); +} + /* Called when we have found a new IT87. */ static void it87_init_device(struct platform_device *pdev) { struct it87_sio_data *sio_data = dev_get_platdata(&pdev->dev); struct it87_data *data = platform_get_drvdata(pdev); int tmp, i; - u8 mask; /* * For each PWM channel: @@ -2855,23 +2938,7 @@ static void it87_init_device(struct platform_device *pdev) data->auto_pwm[i][3] = 0x7f; /* Full speed, hard-coded */ } - /* - * Some chips seem to have default value 0xff for all limit - * registers. For low voltage limits it makes no sense and triggers - * alarms, so change to 0 instead. For high temperature limits, it - * means -1 degree C, which surprisingly doesn't trigger an alarm, - * but is still confusing, so change to 127 degrees C. - */ - for (i = 0; i < NUM_VIN_LIMIT; i++) { - tmp = it87_read_value(data, IT87_REG_VIN_MIN(i)); - if (tmp == 0xff) - it87_write_value(data, IT87_REG_VIN_MIN(i), 0); - } - for (i = 0; i < NUM_TEMP_LIMIT; i++) { - tmp = it87_read_value(data, IT87_REG_TEMP_HIGH(i)); - if (tmp == 0xff) - it87_write_value(data, IT87_REG_TEMP_HIGH(i), 127); - } + it87_check_limit_regs(data); /* * Temperature channels are not forcibly enabled, as they can be @@ -2880,38 +2947,19 @@ static void it87_init_device(struct platform_device *pdev) * run-time through the temp{1-3}_type sysfs accessors if needed. */ - /* Check if voltage monitors are reset manually or by some reason */ - tmp = it87_read_value(data, IT87_REG_VIN_ENABLE); - if ((tmp & 0xff) == 0) { - /* Enable all voltage monitors */ - it87_write_value(data, IT87_REG_VIN_ENABLE, 0xff); - } + it87_check_voltage_monitors_reset(data); + + it87_check_tachometers_reset(pdev); - /* Check if tachometers are reset manually or by some reason */ - mask = 0x70 & ~(sio_data->skip_fan << 4); data->fan_main_ctrl = it87_read_value(data, IT87_REG_FAN_MAIN_CTRL); - if ((data->fan_main_ctrl & mask) == 0) { - /* Enable all fan tachometers */ - data->fan_main_ctrl |= mask; - it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, - data->fan_main_ctrl); - } data->has_fan = (data->fan_main_ctrl >> 4) & 0x07; - tmp = it87_read_value(data, IT87_REG_FAN_16BIT); - - /* Set tachometers to 16-bit mode if needed */ - if (has_fan16_config(data)) { - if (~tmp & 0x07 & data->has_fan) { - dev_dbg(&pdev->dev, - "Setting fan1-3 to 16-bit mode\n"); - it87_write_value(data, IT87_REG_FAN_16BIT, - tmp | 0x07); - } - } + it87_check_tachometers_16bit_mode(pdev); /* Check for additional fans */ if (has_five_fans(data)) { + tmp = it87_read_value(data, IT87_REG_FAN_16BIT); + if (tmp & BIT(4)) data->has_fan |= BIT(3); /* fan4 enabled */ if (tmp & BIT(5)) @@ -2933,10 +2981,7 @@ static void it87_init_device(struct platform_device *pdev) sio_data->skip_pwm |= BIT(5); } - /* Start monitoring */ - it87_write_value(data, IT87_REG_CONFIG, - (it87_read_value(data, IT87_REG_CONFIG) & 0x3e) - | (update_vbat ? 0x41 : 0x01)); + it87_start_monitoring(data); } /* Return 1 if and only if the PWM interface is safe to use */ @@ -2986,8 +3031,6 @@ static int it87_check_pwm(struct device *dev) "PWM configuration is too broken to be fixed\n"); } - dev_info(dev, - "Detected broken BIOS defaults, disabling PWM interface\n"); return 0; } else if (fix_pwm_polarity) { dev_info(dev, @@ -3020,6 +3063,7 @@ static int it87_probe(struct platform_device *pdev) return -ENOMEM; data->addr = res->start; + data->sioaddr = sio_data->sioaddr; data->type = sio_data->type; data->features = it87_devices[sio_data->type].features; data->peci_mask = it87_devices[sio_data->type].peci_mask; @@ -3058,6 +3102,9 @@ static int it87_probe(struct platform_device *pdev) /* Check PWM configuration */ enable_pwm_interface = it87_check_pwm(dev); + if (!enable_pwm_interface) + dev_info(dev, + "Detected broken BIOS defaults, disabling PWM interface\n"); /* Starting with IT8721F, we handle scaling of internal voltages */ if (has_12mv_adc(data)) { @@ -3085,6 +3132,7 @@ static int it87_probe(struct platform_device *pdev) } data->in_internal = sio_data->internal; + data->need_in7_reroute = sio_data->need_in7_reroute; data->has_in = 0x3ff & ~sio_data->skip_in; if (has_six_temp(data)) { @@ -3140,9 +3188,71 @@ static int it87_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(hwmon_dev); } +static void __maybe_unused it87_resume_sio(struct platform_device *pdev) +{ + struct it87_data *data = dev_get_drvdata(&pdev->dev); + int err; + int reg2c; + + if (!data->need_in7_reroute) + return; + + err = superio_enter(data->sioaddr); + if (err) { + dev_warn(&pdev->dev, + "Unable to enter Super I/O to reroute in7 (%d)", + err); + return; + } + + superio_select(data->sioaddr, GPIO); + + reg2c = superio_inb(data->sioaddr, IT87_SIO_PINX2_REG); + if (!(reg2c & BIT(1))) { + dev_dbg(&pdev->dev, + "Routing internal VCCH5V to in7 again"); + + reg2c |= BIT(1); + superio_outb(data->sioaddr, IT87_SIO_PINX2_REG, + reg2c); + } + + superio_exit(data->sioaddr); +} + +static int __maybe_unused it87_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct it87_data *data = dev_get_drvdata(dev); + + it87_resume_sio(pdev); + + mutex_lock(&data->update_lock); + + it87_check_pwm(dev); + it87_check_limit_regs(data); + it87_check_voltage_monitors_reset(data); + it87_check_tachometers_reset(pdev); + it87_check_tachometers_16bit_mode(pdev); + + it87_start_monitoring(data); + + /* force update */ + data->valid = 0; + + mutex_unlock(&data->update_lock); + + it87_update_device(dev); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(it87_dev_pm_ops, NULL, it87_resume); + static struct platform_driver it87_driver = { .driver = { .name = DRVNAME, + .pm = &it87_dev_pm_ops, }, .probe = it87_probe, }; diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 1bf22eff0b08..5f11dc014ed6 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -72,6 +72,8 @@ static const unsigned short normal_i2c[] = { #define NXP_MANID 0x1131 /* NXP Semiconductors */ #define ONS_MANID 0x1b09 /* ON Semiconductor */ #define STM_MANID 0x104a /* ST Microelectronics */ +#define GT_MANID 0x1c68 /* Giantec */ +#define GT_MANID2 0x132d /* Giantec, 2nd mfg ID */ /* Supported chips */ @@ -86,6 +88,13 @@ static const unsigned short normal_i2c[] = { #define AT30TSE004_DEVID 0x2200 #define AT30TSE004_DEVID_MASK 0xffff +/* Giantec */ +#define GT30TS00_DEVID 0x2200 +#define GT30TS00_DEVID_MASK 0xff00 + +#define GT34TS02_DEVID 0x3300 +#define GT34TS02_DEVID_MASK 0xff00 + /* IDT */ #define TSE2004_DEVID 0x2200 #define TSE2004_DEVID_MASK 0xff00 @@ -130,6 +139,12 @@ static const unsigned short normal_i2c[] = { #define CAT6095_DEVID 0x0800 /* Also matches CAT34TS02 */ #define CAT6095_DEVID_MASK 0xffe0 +#define CAT34TS02C_DEVID 0x0a00 +#define CAT34TS02C_DEVID_MASK 0xfff0 + +#define CAT34TS04_DEVID 0x2200 +#define CAT34TS04_DEVID_MASK 0xfff0 + /* ST Microelectronics */ #define STTS424_DEVID 0x0101 #define STTS424_DEVID_MASK 0xffff @@ -158,6 +173,8 @@ static struct jc42_chips jc42_chips[] = { { ADT_MANID, ADT7408_DEVID, ADT7408_DEVID_MASK }, { ATMEL_MANID, AT30TS00_DEVID, AT30TS00_DEVID_MASK }, { ATMEL_MANID2, AT30TSE004_DEVID, AT30TSE004_DEVID_MASK }, + { GT_MANID, GT30TS00_DEVID, GT30TS00_DEVID_MASK }, + { GT_MANID2, GT34TS02_DEVID, GT34TS02_DEVID_MASK }, { IDT_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK }, { IDT_MANID, TS3000_DEVID, TS3000_DEVID_MASK }, { IDT_MANID, TS3001_DEVID, TS3001_DEVID_MASK }, @@ -170,6 +187,8 @@ static struct jc42_chips jc42_chips[] = { { MCP_MANID, MCP9843_DEVID, MCP9843_DEVID_MASK }, { NXP_MANID, SE97_DEVID, SE97_DEVID_MASK }, { ONS_MANID, CAT6095_DEVID, CAT6095_DEVID_MASK }, + { ONS_MANID, CAT34TS02C_DEVID, CAT34TS02C_DEVID_MASK }, + { ONS_MANID, CAT34TS04_DEVID, CAT34TS04_DEVID_MASK }, { NXP_MANID, SE98_DEVID, SE98_DEVID_MASK }, { STM_MANID, STTS424_DEVID, STTS424_DEVID_MASK }, { STM_MANID, STTS424E_DEVID, STTS424E_DEVID_MASK }, diff --git a/drivers/hwmon/ltq-cputemp.c b/drivers/hwmon/ltq-cputemp.c new file mode 100644 index 000000000000..1d33f94594c1 --- /dev/null +++ b/drivers/hwmon/ltq-cputemp.c @@ -0,0 +1,163 @@ +/* Lantiq cpu temperature sensor driver + * + * Copyright (C) 2017 Florian Eckert <fe@dev.tdt.de> + * + * 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 + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/> + */ + +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/of_device.h> + +#include <lantiq_soc.h> + +/* gphy1 configuration register contains cpu temperature */ +#define CGU_GPHY1_CR 0x0040 +#define CGU_TEMP_PD BIT(19) + +static void ltq_cputemp_enable(void) +{ + ltq_cgu_w32(ltq_cgu_r32(CGU_GPHY1_CR) | CGU_TEMP_PD, CGU_GPHY1_CR); +} + +static void ltq_cputemp_disable(void *data) +{ + ltq_cgu_w32(ltq_cgu_r32(CGU_GPHY1_CR) & ~CGU_TEMP_PD, CGU_GPHY1_CR); +} + +static int ltq_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *temp) +{ + int value; + + switch (attr) { + case hwmon_temp_input: + /* get the temperature including one decimal place */ + value = (ltq_cgu_r32(CGU_GPHY1_CR) >> 9) & 0x01FF; + value = value * 5; + /* range -38 to +154 °C, register value zero is -38.0 °C */ + value -= 380; + /* scale temp to millidegree */ + value = value * 100; + break; + default: + return -EOPNOTSUPP; + } + + *temp = value; + return 0; +} + +static umode_t ltq_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: + return 0444; + default: + return 0; + } +} + +static const u32 ltq_chip_config[] = { + HWMON_C_REGISTER_TZ, + 0 +}; + +static const struct hwmon_channel_info ltq_chip = { + .type = hwmon_chip, + .config = ltq_chip_config, +}; + +static const u32 ltq_temp_config[] = { + HWMON_T_INPUT, + 0 +}; + +static const struct hwmon_channel_info ltq_temp = { + .type = hwmon_temp, + .config = ltq_temp_config, +}; + +static const struct hwmon_channel_info *ltq_info[] = { + <q_chip, + <q_temp, + NULL +}; + +static const struct hwmon_ops ltq_hwmon_ops = { + .is_visible = ltq_is_visible, + .read = ltq_read, +}; + +static const struct hwmon_chip_info ltq_chip_info = { + .ops = <q_hwmon_ops, + .info = ltq_info, +}; + +static int ltq_cputemp_probe(struct platform_device *pdev) +{ + struct device *hwmon_dev; + int err = 0; + + /* available on vr9 v1.2 SoCs only */ + if (ltq_soc_type() != SOC_TYPE_VR9_2) + return -ENODEV; + + err = devm_add_action(&pdev->dev, ltq_cputemp_disable, NULL); + if (err) + return err; + + ltq_cputemp_enable(); + + hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, + "ltq_cputemp", + NULL, + <q_chip_info, + NULL); + + if (IS_ERR(hwmon_dev)) { + dev_err(&pdev->dev, "Failed to register as hwmon device"); + return PTR_ERR(hwmon_dev); + } + + return 0; +} + +const struct of_device_id ltq_cputemp_match[] = { + { .compatible = "lantiq,cputemp" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ltq_cputemp_match); + +static struct platform_driver ltq_cputemp_driver = { + .probe = ltq_cputemp_probe, + .driver = { + .name = "ltq-cputemp", + .of_match_table = ltq_cputemp_match, + }, +}; + +module_platform_driver(ltq_cputemp_driver); + +MODULE_AUTHOR("Florian Eckert <fe@dev.tdt.de>"); +MODULE_DESCRIPTION("Lantiq cpu temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c index 12b94b094c0d..2876c18ed841 100644 --- a/drivers/hwmon/nct7802.c +++ b/drivers/hwmon/nct7802.c @@ -704,7 +704,7 @@ static umode_t nct7802_temp_is_visible(struct kobject *kobj, return attr->mode; } -static struct attribute_group nct7802_temp_group = { +static const struct attribute_group nct7802_temp_group = { .attrs = nct7802_temp_attrs, .is_visible = nct7802_temp_is_visible, }; @@ -802,7 +802,7 @@ static umode_t nct7802_in_is_visible(struct kobject *kobj, return attr->mode; } -static struct attribute_group nct7802_in_group = { +static const struct attribute_group nct7802_in_group = { .attrs = nct7802_in_attrs, .is_visible = nct7802_in_is_visible, }; @@ -880,7 +880,7 @@ static umode_t nct7802_fan_is_visible(struct kobject *kobj, return attr->mode; } -static struct attribute_group nct7802_fan_group = { +static const struct attribute_group nct7802_fan_group = { .attrs = nct7802_fan_attrs, .is_visible = nct7802_fan_is_visible, }; @@ -898,7 +898,7 @@ static struct attribute *nct7802_pwm_attrs[] = { NULL }; -static struct attribute_group nct7802_pwm_group = { +static const struct attribute_group nct7802_pwm_group = { .attrs = nct7802_pwm_attrs, }; @@ -1011,7 +1011,7 @@ static struct attribute *nct7802_auto_point_attrs[] = { NULL }; -static struct attribute_group nct7802_auto_point_group = { +static const struct attribute_group nct7802_auto_point_group = { .attrs = nct7802_auto_point_attrs, }; diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 68d717a3fd59..40019325b517 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -37,6 +37,15 @@ config SENSORS_ADM1275 This driver can also be built as a module. If so, the module will be called adm1275. +config SENSORS_IBM_CFFPS + tristate "IBM Common Form Factor Power Supply" + help + If you say yes here you get hardware monitoring support for the IBM + Common Form Factor power supply. + + This driver can also be built as a module. If so, the module will + be called ibm-cffps. + config SENSORS_IR35221 tristate "Infineon IR35221" default n @@ -135,6 +144,15 @@ config SENSORS_TPS40422 This driver can also be built as a module. If so, the module will be called tps40422. +config SENSORS_TPS53679 + tristate "TI TPS53679" + help + If you say yes here you get hardware monitoring support for TI + TPS53679. + + This driver can also be built as a module. If so, the module will + be called tps53679. + config SENSORS_UCD9000 tristate "TI UCD90120, UCD90124, UCD90160, UCD9090, UCD90910" default n diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 75bb7ca619d9..459a6be3390e 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_PMBUS) += pmbus_core.o obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o +obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o obj-$(CONFIG_SENSORS_IR35221) += ir35221.o obj-$(CONFIG_SENSORS_LM25066) += lm25066.o obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o @@ -14,6 +15,7 @@ obj-$(CONFIG_SENSORS_MAX20751) += max20751.o obj-$(CONFIG_SENSORS_MAX34440) += max34440.o obj-$(CONFIG_SENSORS_MAX8688) += max8688.o obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o +obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c new file mode 100644 index 000000000000..cb56da6834e5 --- /dev/null +++ b/drivers/hwmon/pmbus/ibm-cffps.c @@ -0,0 +1,151 @@ +/* + * Copyright 2017 IBM Corp. + * + * 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. + */ + +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/module.h> + +#include "pmbus.h" + +/* STATUS_MFR_SPECIFIC bits */ +#define CFFPS_MFR_FAN_FAULT BIT(0) +#define CFFPS_MFR_THERMAL_FAULT BIT(1) +#define CFFPS_MFR_OV_FAULT BIT(2) +#define CFFPS_MFR_UV_FAULT BIT(3) +#define CFFPS_MFR_PS_KILL BIT(4) +#define CFFPS_MFR_OC_FAULT BIT(5) +#define CFFPS_MFR_VAUX_FAULT BIT(6) +#define CFFPS_MFR_CURRENT_SHARE_WARNING BIT(7) + +static int ibm_cffps_read_byte_data(struct i2c_client *client, int page, + int reg) +{ + int rc, mfr; + + switch (reg) { + case PMBUS_STATUS_VOUT: + case PMBUS_STATUS_IOUT: + case PMBUS_STATUS_TEMPERATURE: + case PMBUS_STATUS_FAN_12: + rc = pmbus_read_byte_data(client, page, reg); + if (rc < 0) + return rc; + + mfr = pmbus_read_byte_data(client, page, + PMBUS_STATUS_MFR_SPECIFIC); + if (mfr < 0) + /* + * Return the status register instead of an error, + * since we successfully read status. + */ + return rc; + + /* Add MFR_SPECIFIC bits to the standard pmbus status regs. */ + if (reg == PMBUS_STATUS_FAN_12) { + if (mfr & CFFPS_MFR_FAN_FAULT) + rc |= PB_FAN_FAN1_FAULT; + } else if (reg == PMBUS_STATUS_TEMPERATURE) { + if (mfr & CFFPS_MFR_THERMAL_FAULT) + rc |= PB_TEMP_OT_FAULT; + } else if (reg == PMBUS_STATUS_VOUT) { + if (mfr & (CFFPS_MFR_OV_FAULT | CFFPS_MFR_VAUX_FAULT)) + rc |= PB_VOLTAGE_OV_FAULT; + if (mfr & CFFPS_MFR_UV_FAULT) + rc |= PB_VOLTAGE_UV_FAULT; + } else if (reg == PMBUS_STATUS_IOUT) { + if (mfr & CFFPS_MFR_OC_FAULT) + rc |= PB_IOUT_OC_FAULT; + if (mfr & CFFPS_MFR_CURRENT_SHARE_WARNING) + rc |= PB_CURRENT_SHARE_FAULT; + } + break; + default: + rc = -ENODATA; + break; + } + + return rc; +} + +static int ibm_cffps_read_word_data(struct i2c_client *client, int page, + int reg) +{ + int rc, mfr; + + switch (reg) { + case PMBUS_STATUS_WORD: + rc = pmbus_read_word_data(client, page, reg); + if (rc < 0) + return rc; + + mfr = pmbus_read_byte_data(client, page, + PMBUS_STATUS_MFR_SPECIFIC); + if (mfr < 0) + /* + * Return the status register instead of an error, + * since we successfully read status. + */ + return rc; + + if (mfr & CFFPS_MFR_PS_KILL) + rc |= PB_STATUS_OFF; + break; + default: + rc = -ENODATA; + break; + } + + return rc; +} + +static struct pmbus_driver_info ibm_cffps_info = { + .pages = 1, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP | + PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_FAN12, + .read_byte_data = ibm_cffps_read_byte_data, + .read_word_data = ibm_cffps_read_word_data, +}; + +static int ibm_cffps_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + return pmbus_do_probe(client, id, &ibm_cffps_info); +} + +static const struct i2c_device_id ibm_cffps_id[] = { + { "ibm_cffps1", 1 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ibm_cffps_id); + +static const struct of_device_id ibm_cffps_of_match[] = { + { .compatible = "ibm,cffps1" }, + {} +}; +MODULE_DEVICE_TABLE(of, ibm_cffps_of_match); + +static struct i2c_driver ibm_cffps_driver = { + .driver = { + .name = "ibm-cffps", + .of_match_table = ibm_cffps_of_match, + }, + .probe = ibm_cffps_probe, + .remove = pmbus_do_remove, + .id_table = ibm_cffps_id, +}; + +module_i2c_driver(ibm_cffps_driver); + +MODULE_AUTHOR("Eddie James"); +MODULE_DESCRIPTION("PMBus driver for IBM Common Form Factor power supplies"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index a3d912cd3b8d..10d17fb8f283 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -28,7 +28,7 @@ #include <linux/i2c.h> #include "pmbus.h" -enum chips { lm25056, lm25063, lm25066, lm5064, lm5066 }; +enum chips { lm25056, lm25063, lm25066, lm5064, lm5066, lm5066i }; #define LM25066_READ_VAUX 0xd0 #define LM25066_MFR_READ_IIN 0xd1 @@ -65,7 +65,7 @@ struct __coeff { #define PSC_CURRENT_IN_L (PSC_NUM_CLASSES) #define PSC_POWER_L (PSC_NUM_CLASSES + 1) -static struct __coeff lm25066_coeff[5][PSC_NUM_CLASSES + 2] = { +static struct __coeff lm25066_coeff[6][PSC_NUM_CLASSES + 2] = { [lm25056] = { [PSC_VOLTAGE_IN] = { .m = 16296, @@ -210,6 +210,41 @@ static struct __coeff lm25066_coeff[5][PSC_NUM_CLASSES + 2] = { .m = 16, }, }, + [lm5066i] = { + [PSC_VOLTAGE_IN] = { + .m = 4617, + .b = -140, + .R = -2, + }, + [PSC_VOLTAGE_OUT] = { + .m = 4602, + .b = 500, + .R = -2, + }, + [PSC_CURRENT_IN] = { + .m = 15076, + .b = -504, + .R = -2, + }, + [PSC_CURRENT_IN_L] = { + .m = 7645, + .b = 100, + .R = -2, + }, + [PSC_POWER] = { + .m = 1701, + .b = -4000, + .R = -3, + }, + [PSC_POWER_L] = { + .m = 861, + .b = -965, + .R = -3, + }, + [PSC_TEMPERATURE] = { + .m = 16, + }, + }, }; struct lm25066_data { @@ -250,6 +285,7 @@ static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) ret = DIV_ROUND_CLOSEST(ret * 70, 453); break; case lm5066: + case lm5066i: /* VIN: 2.18 mV VAUX: 725 uV LSB */ ret = DIV_ROUND_CLOSEST(ret * 725, 2180); break; @@ -488,16 +524,18 @@ static int lm25066_probe(struct i2c_client *client, info->m[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].m; info->b[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].b; info->R[PSC_VOLTAGE_OUT] = coeff[PSC_VOLTAGE_OUT].R; - info->b[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].b; info->R[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].R; - info->b[PSC_POWER] = coeff[PSC_POWER].b; info->R[PSC_POWER] = coeff[PSC_POWER].R; if (config & LM25066_DEV_SETUP_CL) { info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].m; + info->b[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN_L].b; info->m[PSC_POWER] = coeff[PSC_POWER_L].m; + info->b[PSC_POWER] = coeff[PSC_POWER_L].b; } else { info->m[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].m; + info->b[PSC_CURRENT_IN] = coeff[PSC_CURRENT_IN].b; info->m[PSC_POWER] = coeff[PSC_POWER].m; + info->b[PSC_POWER] = coeff[PSC_POWER].b; } return pmbus_do_probe(client, id, info); @@ -509,6 +547,7 @@ static const struct i2c_device_id lm25066_id[] = { {"lm25066", lm25066}, {"lm5064", lm5064}, {"lm5066", lm5066}, + {"lm5066i", lm5066i}, { } }; diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index bfcb13bae34b..4efa2bd4f6d8 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -341,7 +341,7 @@ enum pmbus_sensor_classes { #define PMBUS_HAVE_STATUS_VMON BIT(19) enum pmbus_data_format { linear = 0, direct, vid }; -enum vrm_version { vr11 = 0, vr12 }; +enum vrm_version { vr11 = 0, vr12, vr13 }; struct pmbus_driver_info { int pages; /* Total number of pages */ diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index f1eff6b6c798..302f0aef59de 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -19,6 +19,7 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <linux/debugfs.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -101,6 +102,7 @@ struct pmbus_data { int num_attributes; struct attribute_group group; const struct attribute_group *groups[2]; + struct dentry *debugfs; /* debugfs device directory */ struct pmbus_sensor *sensors; @@ -112,12 +114,20 @@ struct pmbus_data { * A single status register covers multiple attributes, * so we keep them all together. */ - u8 status[PB_NUM_STATUS_REG]; - u8 status_register; + u16 status[PB_NUM_STATUS_REG]; + + bool has_status_word; /* device uses STATUS_WORD register */ + int (*read_status)(struct i2c_client *client, int page); u8 currpage; }; +struct pmbus_debugfs_entry { + struct i2c_client *client; + u8 page; + u8 reg; +}; + void pmbus_clear_cache(struct i2c_client *client) { struct pmbus_data *data = i2c_get_clientdata(client); @@ -324,7 +334,7 @@ static int pmbus_check_status_cml(struct i2c_client *client) struct pmbus_data *data = i2c_get_clientdata(client); int status, status2; - status = _pmbus_read_byte_data(client, -1, data->status_register); + status = data->read_status(client, -1); if (status < 0 || (status & PB_STATUS_CML)) { status2 = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML); if (status2 < 0 || (status2 & PB_CML_FAULT_INVALID_COMMAND)) @@ -348,6 +358,23 @@ static bool pmbus_check_register(struct i2c_client *client, return rv >= 0; } +static bool pmbus_check_status_register(struct i2c_client *client, int page) +{ + int status; + struct pmbus_data *data = i2c_get_clientdata(client); + + status = data->read_status(client, page); + if (status >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK) && + (status & PB_STATUS_CML)) { + status = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML); + if (status < 0 || (status & PB_CML_FAULT_INVALID_COMMAND)) + status = -EIO; + } + + pmbus_clear_fault_page(client, -1); + return status >= 0; +} + bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg) { return pmbus_check_register(client, _pmbus_read_byte_data, page, reg); @@ -394,8 +421,7 @@ static struct pmbus_data *pmbus_update_device(struct device *dev) for (i = 0; i < info->pages; i++) { data->status[PB_STATUS_BASE + i] - = _pmbus_read_byte_data(client, i, - data->status_register); + = data->read_status(client, i); for (j = 0; j < ARRAY_SIZE(pmbus_status); j++) { struct _pmbus_status *s = &pmbus_status[j]; @@ -531,6 +557,10 @@ static long pmbus_reg2data_vid(struct pmbus_data *data, if (val >= 0x01) rv = 250 + (val - 1) * 5; break; + case vr13: + if (val >= 0x01) + rv = 500 + (val - 1) * 10; + break; } return rv; } @@ -716,10 +746,10 @@ static int pmbus_get_boolean(struct pmbus_data *data, struct pmbus_boolean *b, { struct pmbus_sensor *s1 = b->s1; struct pmbus_sensor *s2 = b->s2; - u16 reg = (index >> 8) & 0xffff; - u8 mask = index & 0xff; + u16 reg = (index >> 16) & 0xffff; + u16 mask = index & 0xffff; int ret, status; - u8 regval; + u16 regval; status = data->status[reg]; if (status < 0) @@ -860,7 +890,7 @@ static int pmbus_add_boolean(struct pmbus_data *data, const char *name, const char *type, int seq, struct pmbus_sensor *s1, struct pmbus_sensor *s2, - u16 reg, u8 mask) + u16 reg, u16 mask) { struct pmbus_boolean *boolean; struct sensor_device_attribute *a; @@ -876,7 +906,7 @@ static int pmbus_add_boolean(struct pmbus_data *data, boolean->s1 = s1; boolean->s2 = s2; pmbus_attr_init(a, boolean->name, S_IRUGO, pmbus_show_boolean, NULL, - (reg << 8) | mask); + (reg << 16) | mask); return pmbus_add_attribute(data, &a->dev_attr.attr); } @@ -962,7 +992,7 @@ struct pmbus_limit_attr { */ struct pmbus_sensor_attr { u16 reg; /* sensor register */ - u8 gbit; /* generic status bit */ + u16 gbit; /* generic status bit */ u8 nlimit; /* # of limit registers */ enum pmbus_sensor_classes class;/* sensor class */ const char *label; /* sensor label */ @@ -1028,6 +1058,7 @@ static int pmbus_add_sensor_attrs_one(struct i2c_client *client, const struct pmbus_sensor_attr *attr) { struct pmbus_sensor *base; + bool upper = !!(attr->gbit & 0xff00); /* need to check STATUS_WORD */ int ret; if (attr->label) { @@ -1048,11 +1079,12 @@ static int pmbus_add_sensor_attrs_one(struct i2c_client *client, /* * Add generic alarm attribute only if there are no individual * alarm attributes, if there is a global alarm bit, and if - * the generic status register for this page is accessible. + * the generic status register (word or byte, depending on + * which global bit is set) for this page is accessible. */ if (!ret && attr->gbit && - pmbus_check_byte_register(client, page, - data->status_register)) { + (!upper || (upper && data->has_status_word)) && + pmbus_check_status_register(client, page)) { ret = pmbus_add_boolean(data, name, "alarm", index, NULL, NULL, PB_STATUS_BASE + page, @@ -1308,6 +1340,7 @@ static const struct pmbus_sensor_attr current_attributes[] = { .func = PMBUS_HAVE_IIN, .sfunc = PMBUS_HAVE_STATUS_INPUT, .sbase = PB_STATUS_INPUT_BASE, + .gbit = PB_STATUS_INPUT, .limit = iin_limit_attrs, .nlimit = ARRAY_SIZE(iin_limit_attrs), }, { @@ -1392,6 +1425,7 @@ static const struct pmbus_sensor_attr power_attributes[] = { .func = PMBUS_HAVE_PIN, .sfunc = PMBUS_HAVE_STATUS_INPUT, .sbase = PB_STATUS_INPUT_BASE, + .gbit = PB_STATUS_INPUT, .limit = pin_limit_attrs, .nlimit = ARRAY_SIZE(pin_limit_attrs), }, { @@ -1729,6 +1763,16 @@ static int pmbus_identify_common(struct i2c_client *client, return 0; } +static int pmbus_read_status_byte(struct i2c_client *client, int page) +{ + return _pmbus_read_byte_data(client, page, PMBUS_STATUS_BYTE); +} + +static int pmbus_read_status_word(struct i2c_client *client, int page) +{ + return _pmbus_read_word_data(client, page, PMBUS_STATUS_WORD); +} + static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, struct pmbus_driver_info *info) { @@ -1736,19 +1780,21 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, int page, ret; /* - * Some PMBus chips don't support PMBUS_STATUS_BYTE, so try - * to use PMBUS_STATUS_WORD instead if that is the case. + * Some PMBus chips don't support PMBUS_STATUS_WORD, so try + * to use PMBUS_STATUS_BYTE instead if that is the case. * Bail out if both registers are not supported. */ - data->status_register = PMBUS_STATUS_BYTE; - ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE); - if (ret < 0 || ret == 0xff) { - data->status_register = PMBUS_STATUS_WORD; - ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD); - if (ret < 0 || ret == 0xffff) { + data->read_status = pmbus_read_status_word; + ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD); + if (ret < 0 || ret == 0xffff) { + data->read_status = pmbus_read_status_byte; + ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE); + if (ret < 0 || ret == 0xff) { dev_err(dev, "PMBus status register not found\n"); return -ENODEV; } + } else { + data->has_status_word = true; } /* Enable PEC if the controller supports it */ @@ -1859,6 +1905,184 @@ static int pmbus_regulator_register(struct pmbus_data *data) } #endif +static struct dentry *pmbus_debugfs_dir; /* pmbus debugfs directory */ + +#if IS_ENABLED(CONFIG_DEBUG_FS) +static int pmbus_debugfs_get(void *data, u64 *val) +{ + int rc; + struct pmbus_debugfs_entry *entry = data; + + rc = _pmbus_read_byte_data(entry->client, entry->page, entry->reg); + if (rc < 0) + return rc; + + *val = rc; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops, pmbus_debugfs_get, NULL, + "0x%02llx\n"); + +static int pmbus_debugfs_get_status(void *data, u64 *val) +{ + int rc; + struct pmbus_debugfs_entry *entry = data; + struct pmbus_data *pdata = i2c_get_clientdata(entry->client); + + rc = pdata->read_status(entry->client, entry->page); + if (rc < 0) + return rc; + + *val = rc; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_status, pmbus_debugfs_get_status, + NULL, "0x%04llx\n"); + +static int pmbus_init_debugfs(struct i2c_client *client, + struct pmbus_data *data) +{ + int i, idx = 0; + char name[PMBUS_NAME_SIZE]; + struct pmbus_debugfs_entry *entries; + + if (!pmbus_debugfs_dir) + return -ENODEV; + + /* + * Create the debugfs directory for this device. Use the hwmon device + * name to avoid conflicts (hwmon numbers are globally unique). + */ + data->debugfs = debugfs_create_dir(dev_name(data->hwmon_dev), + pmbus_debugfs_dir); + if (IS_ERR_OR_NULL(data->debugfs)) { + data->debugfs = NULL; + return -ENODEV; + } + + /* Allocate the max possible entries we need. */ + entries = devm_kzalloc(data->dev, + sizeof(*entries) * (data->info->pages * 10), + GFP_KERNEL); + if (!entries) + return -ENOMEM; + + for (i = 0; i < data->info->pages; ++i) { + /* Check accessibility of status register if it's not page 0 */ + if (!i || pmbus_check_status_register(client, i)) { + /* No need to set reg as we have special read op. */ + entries[idx].client = client; + entries[idx].page = i; + scnprintf(name, PMBUS_NAME_SIZE, "status%d", i); + debugfs_create_file(name, 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops_status); + } + + if (data->info->func[i] & PMBUS_HAVE_STATUS_VOUT) { + entries[idx].client = client; + entries[idx].page = i; + entries[idx].reg = PMBUS_STATUS_VOUT; + scnprintf(name, PMBUS_NAME_SIZE, "status%d_vout", i); + debugfs_create_file(name, 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } + + if (data->info->func[i] & PMBUS_HAVE_STATUS_IOUT) { + entries[idx].client = client; + entries[idx].page = i; + entries[idx].reg = PMBUS_STATUS_IOUT; + scnprintf(name, PMBUS_NAME_SIZE, "status%d_iout", i); + debugfs_create_file(name, 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } + + if (data->info->func[i] & PMBUS_HAVE_STATUS_INPUT) { + entries[idx].client = client; + entries[idx].page = i; + entries[idx].reg = PMBUS_STATUS_INPUT; + scnprintf(name, PMBUS_NAME_SIZE, "status%d_input", i); + debugfs_create_file(name, 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } + + if (data->info->func[i] & PMBUS_HAVE_STATUS_TEMP) { + entries[idx].client = client; + entries[idx].page = i; + entries[idx].reg = PMBUS_STATUS_TEMPERATURE; + scnprintf(name, PMBUS_NAME_SIZE, "status%d_temp", i); + debugfs_create_file(name, 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } + + if (pmbus_check_byte_register(client, i, PMBUS_STATUS_CML)) { + entries[idx].client = client; + entries[idx].page = i; + entries[idx].reg = PMBUS_STATUS_CML; + scnprintf(name, PMBUS_NAME_SIZE, "status%d_cml", i); + debugfs_create_file(name, 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } + + if (pmbus_check_byte_register(client, i, PMBUS_STATUS_OTHER)) { + entries[idx].client = client; + entries[idx].page = i; + entries[idx].reg = PMBUS_STATUS_OTHER; + scnprintf(name, PMBUS_NAME_SIZE, "status%d_other", i); + debugfs_create_file(name, 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } + + if (pmbus_check_byte_register(client, i, + PMBUS_STATUS_MFR_SPECIFIC)) { + entries[idx].client = client; + entries[idx].page = i; + entries[idx].reg = PMBUS_STATUS_MFR_SPECIFIC; + scnprintf(name, PMBUS_NAME_SIZE, "status%d_mfr", i); + debugfs_create_file(name, 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } + + if (data->info->func[i] & PMBUS_HAVE_STATUS_FAN12) { + entries[idx].client = client; + entries[idx].page = i; + entries[idx].reg = PMBUS_STATUS_FAN_12; + scnprintf(name, PMBUS_NAME_SIZE, "status%d_fan12", i); + debugfs_create_file(name, 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } + + if (data->info->func[i] & PMBUS_HAVE_STATUS_FAN34) { + entries[idx].client = client; + entries[idx].page = i; + entries[idx].reg = PMBUS_STATUS_FAN_34; + scnprintf(name, PMBUS_NAME_SIZE, "status%d_fan34", i); + debugfs_create_file(name, 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } + } + + return 0; +} +#else +static int pmbus_init_debugfs(struct i2c_client *client, + struct pmbus_data *data) +{ + return 0; +} +#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */ + int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, struct pmbus_driver_info *info) { @@ -1918,6 +2142,10 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, if (ret) goto out_unregister; + ret = pmbus_init_debugfs(client, data); + if (ret) + dev_warn(dev, "Failed to register debugfs\n"); + return 0; out_unregister: @@ -1931,12 +2159,32 @@ EXPORT_SYMBOL_GPL(pmbus_do_probe); int pmbus_do_remove(struct i2c_client *client) { struct pmbus_data *data = i2c_get_clientdata(client); + + debugfs_remove_recursive(data->debugfs); + hwmon_device_unregister(data->hwmon_dev); kfree(data->group.attrs); return 0; } EXPORT_SYMBOL_GPL(pmbus_do_remove); +static int __init pmbus_core_init(void) +{ + pmbus_debugfs_dir = debugfs_create_dir("pmbus", NULL); + if (IS_ERR(pmbus_debugfs_dir)) + pmbus_debugfs_dir = NULL; + + return 0; +} + +static void __exit pmbus_core_exit(void) +{ + debugfs_remove_recursive(pmbus_debugfs_dir); +} + +module_init(pmbus_core_init); +module_exit(pmbus_core_exit); + MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus core driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c new file mode 100644 index 000000000000..85b515cd9df0 --- /dev/null +++ b/drivers/hwmon/pmbus/tps53679.c @@ -0,0 +1,113 @@ +/* + * Hardware monitoring driver for Texas Instruments TPS53679 + * + * Copyright (c) 2017 Mellanox Technologies. All rights reserved. + * Copyright (c) 2017 Vadim Pasternak <vadimp@mellanox.com> + * + * 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 <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include "pmbus.h" + +#define TPS53679_PROT_VR12_5MV 0x01 /* VR12.0 mode, 5-mV DAC */ +#define TPS53679_PROT_VR12_5_10MV 0x02 /* VR12.5 mode, 10-mV DAC */ +#define TPS53679_PROT_VR13_10MV 0x04 /* VR13.0 mode, 10-mV DAC */ +#define TPS53679_PROT_IMVP8_5MV 0x05 /* IMVP8 mode, 5-mV DAC */ +#define TPS53679_PROT_VR13_5MV 0x07 /* VR13.0 mode, 5-mV DAC */ +#define TPS53679_PAGE_NUM 2 + +static int tps53679_identify(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + u8 vout_params; + int ret; + + /* Read the register with VOUT scaling value.*/ + ret = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); + if (ret < 0) + return ret; + + vout_params = ret & GENMASK(4, 0); + + switch (vout_params) { + case TPS53679_PROT_VR13_10MV: + case TPS53679_PROT_VR12_5_10MV: + info->vrm_version = vr13; + break; + case TPS53679_PROT_VR13_5MV: + case TPS53679_PROT_VR12_5MV: + case TPS53679_PROT_IMVP8_5MV: + info->vrm_version = vr12; + break; + default: + return -EINVAL; + } + + return 0; +} + +static struct pmbus_driver_info tps53679_info = { + .pages = TPS53679_PAGE_NUM, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = vid, + .format[PSC_TEMPERATURE] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_POUT, + .func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_POUT, + .identify = tps53679_identify, +}; + +static int tps53679_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + return pmbus_do_probe(client, id, &tps53679_info); +} + +static const struct i2c_device_id tps53679_id[] = { + {"tps53679", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, tps53679_id); + +static const struct of_device_id tps53679_of_match[] = { + {.compatible = "ti,tps53679"}, + {} +}; +MODULE_DEVICE_TABLE(of, tps53679_of_match); + +static struct i2c_driver tps53679_driver = { + .driver = { + .name = "tps53679", + .of_match_table = of_match_ptr(tps53679_of_match), + }, + .probe = tps53679_probe, + .remove = pmbus_do_remove, + .id_table = tps53679_id, +}; + +module_i2c_driver(tps53679_driver); + +MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); +MODULE_DESCRIPTION("PMBus driver for Texas Instruments TPS53679"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/scpi-hwmon.c b/drivers/hwmon/scpi-hwmon.c index a586480a7ca9..7e49da50bc69 100644 --- a/drivers/hwmon/scpi-hwmon.c +++ b/drivers/hwmon/scpi-hwmon.c @@ -120,7 +120,7 @@ scpi_show_label(struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, "%s\n", sensor->info.name); } -static struct thermal_zone_of_device_ops scpi_sensor_ops = { +static const struct thermal_zone_of_device_ops scpi_sensor_ops = { .get_temp = scpi_read_temp, }; diff --git a/drivers/hwmon/stts751.c b/drivers/hwmon/stts751.c index d56251d6eec2..3f940fb67dc6 100644 --- a/drivers/hwmon/stts751.c +++ b/drivers/hwmon/stts751.c @@ -718,6 +718,10 @@ static int stts751_read_chip_config(struct stts751_priv *priv) ret = i2c_smbus_read_byte_data(priv->client, STTS751_REG_RATE); if (ret < 0) return ret; + if (ret >= ARRAY_SIZE(stts751_intervals)) { + dev_err(priv->dev, "Unrecognized conversion rate 0x%x\n", ret); + return -ENODEV; + } priv->interval = ret; ret = stts751_read_reg16(priv, &priv->event_max, diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c index a88c2065d8ab..a23a3a1c7061 100644 --- a/drivers/mfd/da9052-core.c +++ b/drivers/mfd/da9052-core.c @@ -18,6 +18,7 @@ #include <linux/mfd/core.h> #include <linux/slab.h> #include <linux/module.h> +#include <linux/property.h> #include <linux/mfd/da9052/da9052.h> #include <linux/mfd/da9052/pdata.h> @@ -519,9 +520,6 @@ static const struct mfd_cell da9052_subdev_info[] = { .name = "da9052-wled3", }, { - .name = "da9052-tsi", - }, - { .name = "da9052-bat", }, { @@ -529,6 +527,10 @@ static const struct mfd_cell da9052_subdev_info[] = { }, }; +static const struct mfd_cell da9052_tsi_subdev_info[] = { + { .name = "da9052-tsi" }, +}; + const struct regmap_config da9052_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -619,9 +621,27 @@ int da9052_device_init(struct da9052 *da9052, u8 chip_id) goto err; } + /* + * Check if touchscreen pins are used are analogue input instead + * of having a touchscreen connected to them. The analogue input + * functionality will be provided by hwmon driver (if enabled). + */ + if (!device_property_read_bool(da9052->dev, "dlg,tsi-as-adc")) { + ret = mfd_add_devices(da9052->dev, PLATFORM_DEVID_AUTO, + da9052_tsi_subdev_info, + ARRAY_SIZE(da9052_tsi_subdev_info), + NULL, 0, NULL); + if (ret) { + dev_err(da9052->dev, "failed to add TSI subdev: %d\n", + ret); + goto err; + } + } + return 0; err: + mfd_remove_devices(da9052->dev); da9052_irq_exit(da9052); return ret; diff --git a/include/linux/mfd/da9052/da9052.h b/include/linux/mfd/da9052/da9052.h index ce9230af09c2..ae5b663836d0 100644 --- a/include/linux/mfd/da9052/da9052.h +++ b/include/linux/mfd/da9052/da9052.h @@ -45,6 +45,12 @@ #define DA9052_ADC_TJUNC 8 #define DA9052_ADC_VBBAT 9 +/* TSI channel has its own 4 channel mux */ +#define DA9052_ADC_TSI_XP 70 +#define DA9052_ADC_TSI_XN 71 +#define DA9052_ADC_TSI_YP 72 +#define DA9052_ADC_TSI_YN 73 + #define DA9052_IRQ_DCIN 0 #define DA9052_IRQ_VBUS 1 #define DA9052_IRQ_DCINREM 2 diff --git a/include/linux/mfd/da9052/reg.h b/include/linux/mfd/da9052/reg.h index 5010f978725c..76780ea8849c 100644 --- a/include/linux/mfd/da9052/reg.h +++ b/include/linux/mfd/da9052/reg.h @@ -690,7 +690,10 @@ /* TSI CONTROL REGISTER B BITS */ #define DA9052_TSICONTB_ADCREF 0X80 #define DA9052_TSICONTB_TSIMAN 0X40 -#define DA9052_TSICONTB_TSIMUX 0X30 +#define DA9052_TSICONTB_TSIMUX_XP 0X00 +#define DA9052_TSICONTB_TSIMUX_YP 0X10 +#define DA9052_TSICONTB_TSIMUX_XN 0X20 +#define DA9052_TSICONTB_TSIMUX_YN 0X30 #define DA9052_TSICONTB_TSISEL3 0X08 #define DA9052_TSICONTB_TSISEL2 0X04 #define DA9052_TSICONTB_TSISEL1 0X02 @@ -705,8 +708,14 @@ /* TSI CO-ORDINATE LSB RESULT REGISTER BITS */ #define DA9052_TSILSB_PENDOWN 0X40 #define DA9052_TSILSB_TSIZL 0X30 +#define DA9052_TSILSB_TSIZL_SHIFT 4 +#define DA9052_TSILSB_TSIZL_BITS 2 #define DA9052_TSILSB_TSIYL 0X0C +#define DA9052_TSILSB_TSIYL_SHIFT 2 +#define DA9052_TSILSB_TSIYL_BITS 2 #define DA9052_TSILSB_TSIXL 0X03 +#define DA9052_TSILSB_TSIXL_SHIFT 0 +#define DA9052_TSILSB_TSIXL_BITS 2 /* TSI Z MEASUREMENT MSB RESULT REGISTER BIT */ #define DA9052_TSIZMSB_TSIZM 0XFF |