From 7576750f036b5ec913aac2a165ce75ab3b7beee3 Mon Sep 17 00:00:00 2001 From: Thilo Cestonaro Date: Thu, 3 Aug 2017 11:43:51 +0200 Subject: hwmon: (ftsteutates) Fix clearing alarm sysfs entries sysfs store functions should return the number of bytes written. Returning zero results in an endless loop. Fixes: 08426eda58e0 ("hwmon: Add driver for FTS BMC chip "Teutates"") Signed-off-by: Thilo Cestonaro [groeck: Clean up documentation change and description] Signed-off-by: Guenter Roeck --- Documentation/hwmon/ftsteutates | 4 ++++ drivers/hwmon/ftsteutates.c | 2 ++ 2 files changed, 6 insertions(+) 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/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c index 0f0277e7aae5..a0fb9e9291f0 100644 --- a/drivers/hwmon/ftsteutates.c +++ b/drivers/hwmon/ftsteutates.c @@ -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; -- cgit v1.2.3 From 3be6bd690da0dafa55eb65279fb25297fcfe19d5 Mon Sep 17 00:00:00 2001 From: Anton Vasilyev Date: Thu, 10 Aug 2017 19:46:32 +0300 Subject: hwmon: (stts751) buffer overrun on wrong chip configuration If stts751 hw by some reason reports conversion rate bigger then 9: ret = i2c_smbus_read_byte_data(priv->client, STTS751_REG_RATE); then dereferencing stts751_intervals[priv->interval] leads to buffer overrun. The patch adds sanity check for value stored on chip. Found by Linux Driver Verification project (linuxtesting.org). Fixes: 7f07ec0fa17a ("hwmon: new driver for ST stts751 thermal sensor") Signed-off-by: Anton Vasilyev Signed-off-by: Guenter Roeck --- drivers/hwmon/stts751.c | 4 ++++ 1 file changed, 4 insertions(+) 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, -- cgit v1.2.3 From 524703ac1af4d6d79c37aafa99cdb6c293d62b7b Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 5 Jul 2017 10:31:05 +0530 Subject: hwmon: constify attribute_group structures. attribute_groups are not supposed to change at runtime. All functions working with attribute_groups provided by work with const attribute_group. So mark the non-const structs as const. File size before: text data bss dec hex filename 6655 304 0 6959 1b2f drivers/hwmon/hwmon.o File size After adding 'const': text data bss dec hex filename 6703 240 0 6943 1b1f drivers/hwmon/hwmon.o Signed-off-by: Arvind Yadav Signed-off-by: Guenter Roeck --- drivers/hwmon/hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index dd6e17c1076b..2ac578a94b1f 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, }; -- cgit v1.2.3 From afc680f38eb9b985eabf365aeb3afd6c5bbb6019 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 5 Jul 2017 10:41:18 +0530 Subject: hwmon: (nct7802) constify attribute_group structures. attribute_groups are not supposed to change at runtime. All functions working with attribute_groups provided by work with const attribute_group. So mark the non-const structs as const. File size before: text data bss dec hex filename 6161 9400 0 15561 3cc9 drivers/hwmon/nct7802.o File size After adding 'const': text data bss dec hex filename 6465 9080 0 15545 3cb9 drivers/hwmon/nct7802.o Signed-off-by: Arvind Yadav Signed-off-by: Guenter Roeck --- drivers/hwmon/nct7802.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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, }; -- cgit v1.2.3 From 33d62d11c3310aad523f7ee2c213851a61c50f8c Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 5 Jul 2017 10:52:13 +0530 Subject: hwmon: (adc128d818) constify attribute_group structures. attribute_groups are not supposed to change at runtime. All functions working with attribute_groups provided by work with const attribute_group. So mark the non-const structs as const. File size before: text data bss dec hex filename 2304 2936 0 5240 1478 drivers/hwmon/adc128d818.o File size After adding 'const': text data bss dec hex filename 2344 2872 0 5216 1460 drivers/hwmon/adc128d818.o Signed-off-by: Arvind Yadav Signed-off-by: Guenter Roeck --- drivers/hwmon/adc128d818.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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, }; -- cgit v1.2.3 From f5397be8adc271e11cba80ce8f57ffe193a897b5 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 5 Jul 2017 11:04:04 +0530 Subject: hwmon: (adt7475) constify attribute_group structures. attribute_groups are not supposed to change at runtime. All functions working with attribute_groups provided by work with const attribute_group. So mark the non-const structs as const. File size before: text data bss dec hex filename 10055 7032 0 17087 42bf drivers/hwmon/adt7475.o File size After adding 'const': text data bss dec hex filename 10567 6520 0 17087 42bf drivers/hwmon/adt7475.o Signed-off-by: Arvind Yadav Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7475.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) 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) -- cgit v1.2.3 From 568003cefe720b61e799a511257159392881844f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 15 Jul 2017 16:35:00 -0700 Subject: hwmon: (jc42) Add support for GT30TS00, GT34TS02, and CAT34TS04 Giantec GT30TS00 GT30TS00 and GT34TS02 as well as ONS CAT34TS04 are used on DDR4 DIMMs. Signed-off-by: Guenter Roeck --- drivers/hwmon/jc42.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 1bf22eff0b08..edb6a81a0e10 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,9 @@ static const unsigned short normal_i2c[] = { #define CAT6095_DEVID 0x0800 /* Also matches CAT34TS02 */ #define CAT6095_DEVID_MASK 0xffe0 +#define CAT34TS04_DEVID 0x2200 +#define CAT34TS04_DEVID_MASK 0xfff0 + /* ST Microelectronics */ #define STTS424_DEVID 0x0101 #define STTS424_DEVID_MASK 0xffff @@ -158,6 +170,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 +184,7 @@ 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, 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 }, -- cgit v1.2.3 From 99b981b23b8585ab8cd86a24178f03f17f2f6109 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 15 Jul 2017 19:41:58 -0700 Subject: hwmon: (jc42) Add support for CAT34TS02C CAT34TS02C is similar to CAT34TS02 but has a different device ID. Signed-off-by: Guenter Roeck --- drivers/hwmon/jc42.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index edb6a81a0e10..5f11dc014ed6 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -139,6 +139,9 @@ 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 @@ -184,6 +187,7 @@ 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 }, -- cgit v1.2.3 From bb923fdc374420116b8425a49b9bfb38caa35e7f Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 18 Jul 2017 16:43:05 -0500 Subject: hwmon: (ads1015) Convert to using %pOF instead of full_name Now that we have a custom printf format specifier, convert users of full_name to use %pOF instead. This is preparation to remove storing of the full path string for each node. Signed-off-by: Rob Herring Cc: Dirk Eibach Cc: Jean Delvare Cc: Guenter Roeck Signed-off-by: Guenter Roeck --- drivers/hwmon/ads1015.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) 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; } } -- cgit v1.2.3 From 4d3f24142c62ff9cee01c3ef532b9a658d184304 Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Mon, 17 Jul 2017 21:55:10 +0530 Subject: hwmon: (i5k_amb) constify pci_device_id pci_device_id are not supposed to change at runtime. All functions working with pci_device_id provided by work with const pci_device_id. So mark the non-const structs as const. File size before: text data bss dec hex filename 3562 320 8 3890 f32 drivers/hwmon/i5k_amb.o File size After adding 'const': text data bss dec hex filename 3658 224 8 3890 f32 drivers/hwmon/i5k_amb.o Signed-off-by: Arvind Yadav Signed-off-by: Guenter Roeck --- drivers/hwmon/i5k_amb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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, } -- cgit v1.2.3 From c9920650c7707ffb974ae1ee06cea2682c2ea90f Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 8 Aug 2017 17:09:02 +0200 Subject: hwmon: (core) constify thermal_zone_of_device_ops structures The thermal_zone_of_device_ops structure is only passed as the fourth argument to devm_thermal_zone_of_sensor_register, which is declared as const. Thus the thermal_zone_of_device_ops structure itself can be const. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Guenter Roeck --- drivers/hwmon/hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 2ac578a94b1f..c9790e2c3440 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -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, }; -- cgit v1.2.3 From 778fb961b03c37d8db3d7f418670e2fc09e4c371 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 8 Aug 2017 17:09:03 +0200 Subject: hwmon: (scpi) constify thermal_zone_of_device_ops structures The thermal_zone_of_device_ops structure is only passed as the fourth argument to devm_thermal_zone_of_sensor_register, which is declared as const. Thus the thermal_zone_of_device_ops structure itself can be const. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Guenter Roeck --- drivers/hwmon/scpi-hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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, }; -- cgit v1.2.3 From 557cbf49d20f371fb430335fa504f6a50a001705 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Wed, 9 Aug 2017 12:05:30 +0200 Subject: hwmon: (it87) Split out chip registers setting code on probe path This commit splits out chip registers setting code on probe path to separate functions so they can be reused for setting the device properly again when system resumes from suspend. While we are at it let's also make clear that on IT8720 and IT8782 it's the VCCH5V line that is (possibly) routed to in7. This will make it consistent with a similar message that it printed on IT8783. Signed-off-by: Maciej S. Szmigiero Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 138 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 88 insertions(+), 50 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 4dfc7238313e..818f123ac475 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -2761,13 +2761,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 +2777,7 @@ 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"); + pr_notice("Routing internal VCCH5V to in7\n"); } if (reg & BIT(0)) sio_data->internal |= BIT(0); @@ -2828,13 +2828,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 +2931,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 +2940,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 +2974,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 */ -- cgit v1.2.3 From 384548e569c82dc1e018dcdfd7e19c4b9f87c6b5 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Wed, 9 Aug 2017 17:15:46 +0200 Subject: hwmon: (it87) Reapply probe path chip registers settings after resume After a suspend / resume cycle we possibly need to reapply chip registers settings that we had set or fixed in a probe path, since they might have been reset to default values or set incorrectly by a BIOS again. Tested on a Gigabyte M720-US3 board, which requires routing internal VCCH5V to in7 (and had it wrong again on resume from S3). Signed-off-by: Maciej S. Szmigiero [groeck: Return value from it87_resume_sio() is unused; make it void] Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 818f123ac475..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"); @@ -2777,6 +2783,7 @@ 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); + sio_data->need_in7_reroute = true; pr_notice("Routing internal VCCH5V to in7\n"); } if (reg & BIT(0)) @@ -3024,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, @@ -3058,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; @@ -3096,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)) { @@ -3123,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)) { @@ -3178,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, }; -- cgit v1.2.3 From a66a6eb9db10bd630bacf31e33505302c8e8303c Mon Sep 17 00:00:00 2001 From: "Edward A. James" Date: Thu, 10 Aug 2017 16:57:47 -0500 Subject: hwmon: (pmbus): Switch status registers to 16 bit Switch the storage of status registers to 16 bit values. This allows us to store all the bits of STATUS_WORD. Signed-off-by: Edward A. James Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index f1eff6b6c798..4ec75862ca7e 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -112,7 +112,7 @@ struct pmbus_data { * A single status register covers multiple attributes, * so we keep them all together. */ - u8 status[PB_NUM_STATUS_REG]; + u16 status[PB_NUM_STATUS_REG]; u8 status_register; u8 currpage; @@ -716,10 +716,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 +860,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 +876,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 +962,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 */ -- cgit v1.2.3 From cbcdec6202c934bd72fca4fe9db255d84f907dbe Mon Sep 17 00:00:00 2001 From: "Edward A. James" Date: Thu, 10 Aug 2017 16:57:48 -0500 Subject: hwmon: (pmbus): Access word data for STATUS_WORD Pmbus always reads byte data from the status register, even if configured to use STATUS_WORD. Use a function pointer to read the correct amount of data from the registers. Also switch to try STATUS_WORD first before STATUS_BYTE on init. Signed-off-by: Edward A. James Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 54 +++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 4ec75862ca7e..277d17051f7a 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -113,7 +113,8 @@ struct pmbus_data { * so we keep them all together. */ u16 status[PB_NUM_STATUS_REG]; - u8 status_register; + + int (*read_status)(struct i2c_client *client, int page); u8 currpage; }; @@ -324,7 +325,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 +349,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 +412,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]; @@ -1051,8 +1068,7 @@ static int pmbus_add_sensor_attrs_one(struct i2c_client *client, * the generic status register for this page is accessible. */ if (!ret && attr->gbit && - pmbus_check_byte_register(client, page, - data->status_register)) { + pmbus_check_status_register(client, page)) { ret = pmbus_add_boolean(data, name, "alarm", index, NULL, NULL, PB_STATUS_BASE + page, @@ -1729,6 +1745,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,16 +1762,16 @@ 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; } -- cgit v1.2.3 From c159be9e902a59a117e67d466c872ecc46240ba5 Mon Sep 17 00:00:00 2001 From: "Edward A. James" Date: Thu, 10 Aug 2017 16:57:49 -0500 Subject: hwmon: (pmbus): Add generic alarm bit for iin and pin Add PB_STATUS_INPUT as the generic alarm bit for iin and pin. We also need to redo the status register checking before setting up the boolean attribute, since it won't necessarily check STATUS_WORD if the device doesn't support it, which we need for this bit. Signed-off-by: Edward A. James Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 277d17051f7a..ef135d882be0 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -114,6 +114,7 @@ struct pmbus_data { */ 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; @@ -1045,6 +1046,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) { @@ -1065,9 +1067,11 @@ 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 && + (!upper || (upper && data->has_status_word)) && pmbus_check_status_register(client, page)) { ret = pmbus_add_boolean(data, name, "alarm", index, NULL, NULL, @@ -1324,6 +1328,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), }, { @@ -1408,6 +1413,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), }, { @@ -1775,6 +1781,8 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, dev_err(dev, "PMBus status register not found\n"); return -ENODEV; } + } else { + data->has_status_word = true; } /* Enable PEC if the controller supports it */ -- cgit v1.2.3 From 5e047541c1412f22d1c0950260da337ebc9bdb49 Mon Sep 17 00:00:00 2001 From: Mykola Kostenok Date: Thu, 3 Aug 2017 11:46:50 +0300 Subject: Documentation: dt-bindings: aspeed-pwm-tacho cooling device. It's add support for cooling device creation to aspeed-pwm-tacho. Cooling device could be bound to a thermal zone for the thermal control. Signed-off-by: Mykola Kostenok Acked-by: Rob Herring Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/aspeed-pwm-tacho.txt | 9 +++++++++ 1 file changed, 9 insertions(+) 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>; }; -- cgit v1.2.3 From f198907d2ff6db9541863764576aaf3bc9f58ec0 Mon Sep 17 00:00:00 2001 From: Mykola Kostenok Date: Thu, 3 Aug 2017 11:50:44 +0300 Subject: hwmon: (aspeed-pwm-tacho) cooling device support. Add support in aspeed-pwm-tacho driver for cooling device creation. This cooling device could be bound to a thermal zone for the thermal control. Device will appear in /sys/class/thermal folder as cooling_deviceX. Then it could be bound to particular thermal zones. Allow specification of the cooling levels vector - PWM duty cycle values in a range from 0 to 255 which correspond to thermal cooling states. Signed-off-by: Mykola Kostenok Reviewed-by: Joel Stanley Signed-off-by: Guenter Roeck --- drivers/hwmon/aspeed-pwm-tacho.c | 116 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 2 deletions(-) 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 #include #include +#include /* 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; -- cgit v1.2.3 From 1e069dfd96dfeacf1d781f6eaea9426aed0b0909 Mon Sep 17 00:00:00 2001 From: "Edward A. James" Date: Mon, 14 Aug 2017 13:55:41 -0500 Subject: hwmon: (pmbus) Add debugfs for status registers Export all the available status registers through debugfs. This is useful for hardware diagnostics, especially on multi-page pmbus devices, as user-space access of the i2c space could corrupt the pmbus page accounting. Signed-off-by: Edward A. James Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 210 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index ef135d882be0..6ac89c93bd2e 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 #include #include #include @@ -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; @@ -120,6 +122,12 @@ struct pmbus_data { 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); @@ -1893,6 +1901,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) { @@ -1952,6 +2138,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: @@ -1965,12 +2155,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"); -- cgit v1.2.3 From d5553c26198f0eea3e6f1d13c51cb548a0a4f260 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 15 Aug 2017 16:47:47 +0200 Subject: hwmon: (aspeed-pwm) add THERMAL dependency With CONFIG_THERMAL=m, a built-in aspeed pwm tacho driver causes a link error: drivers/hwmon/aspeed-pwm-tacho.o: In function `aspeed_pwm_tacho_probe': aspeed-pwm-tacho.c:(.text+0x7f0): undefined reference to `thermal_of_cooling_device_register' This adds a dependency similar to what other hwmon drivers use, ensuring that the aspeed driver cannot be built-in in this case but has to be a module. With THERMAL=n, we still allow building it. Fixes: 2d7a548a3eff ("drivers: hwmon: Support for ASPEED PWM/Fan tach") Signed-off-by: Arnd Bergmann Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5ef2814345ef..5b9a61f766ac 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 -- cgit v1.2.3 From 67dfabe3a01be6b0cc4c0056aa546f0279ed6279 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Mon, 21 Aug 2017 16:54:01 +0200 Subject: mfd: da9052: Add register details for TSI Add register details an channels definition for using the TSI registers in the hwmon driver. Signed-off-by: Sebastian Reichel Signed-off-by: Lee Jones --- include/linux/mfd/da9052/da9052.h | 6 ++++++ include/linux/mfd/da9052/reg.h | 11 ++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) 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 -- cgit v1.2.3 From 7fe9899ef4e4e6d152a3a54e262cda38e1e1a1d3 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Mon, 21 Aug 2017 16:54:03 +0200 Subject: hwmon: da9052: Replace S_IRUGO with 0444 Fix checkpatch warnings about S_IRUGO being less readable than providing the permissions octal as '0444'. Acked-by: Guenter Roeck Signed-off-by: Sebastian Reichel Signed-off-by: Lee Jones --- drivers/hwmon/da9052-hwmon.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/hwmon/da9052-hwmon.c b/drivers/hwmon/da9052-hwmon.c index c9832bfacfe5..708c91ac601f 100644 --- a/drivers/hwmon/da9052-hwmon.c +++ b/drivers/hwmon/da9052-hwmon.c @@ -196,43 +196,43 @@ 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 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(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[] = { -- cgit v1.2.3 From ebf555111bc11a5da9144e4af524260731a8b968 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Mon, 21 Aug 2017 16:54:02 +0200 Subject: mfd: da9052: Make touchscreen registration optional If the touchscreen pins are used as general purpose analogue input, the touchscreen driver should not be used. The pins will be handled by the existing hwmon driver instead. Signed-off-by: Sebastian Reichel Signed-off-by: Lee Jones --- drivers/mfd/da9052-core.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) 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 #include #include +#include #include #include @@ -518,9 +519,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; -- cgit v1.2.3 From 4f16cab19a3d57638dd5d962ff6e87c154a6cab2 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Mon, 21 Aug 2017 16:54:04 +0200 Subject: hwmon: da9052: Add support for TSI channel TSI channel has a 4 channel mux connected to it and is normally used for touchscreen support. The hardware may alternatively use it as general purpose adc. Acked-by: Guenter Roeck Signed-off-by: Sebastian Reichel Signed-off-by: Lee Jones --- drivers/hwmon/da9052-hwmon.c | 249 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 245 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/da9052-hwmon.c b/drivers/hwmon/da9052-hwmon.c index 708c91ac601f..97a62f5b9ea4 100644 --- a/drivers/hwmon/da9052-hwmon.c +++ b/drivers/hwmon/da9052-hwmon.c @@ -20,13 +20,19 @@ #include #include #include +#include #include #include +#include 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,6 +302,28 @@ static ssize_t show_label(struct device *dev, input_names[to_sensor_dev_attr(devattr)->index]); } +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, 0444, show_label, NULL, @@ -221,6 +349,23 @@ static SENSOR_DEVICE_ATTR(in9_input, 0444, da9052_read_vbbat, NULL, static SENSOR_DEVICE_ATTR(in9_label, 0444, show_label, NULL, DA9052_ADC_VBBAT); +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, 0444, show_label, NULL, @@ -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", }, -- cgit v1.2.3 From 1dad2e958a0b0e402adfd1546d15e7b628ac2c5a Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Sun, 20 Aug 2017 00:56:34 +0530 Subject: hwmon: (ftsteutates) constify i2c_device_id i2c_device_id are not supposed to change at runtime. All functions working with i2c_device_id provided by work with const i2c_device_id. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: Guenter Roeck --- drivers/hwmon/ftsteutates.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c index a0fb9e9291f0..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 }, { } }; -- cgit v1.2.3 From 74560e4a440891649971a11e518681b088c6a1f9 Mon Sep 17 00:00:00 2001 From: "Edward A. James" Date: Mon, 21 Aug 2017 14:46:11 -0500 Subject: dt-bindings: hwmon: Document the IBM CCF power supply version 1 Signed-off-by: Edward A. James Acked-by: Rob Herring Signed-off-by: Guenter Roeck --- .../devicetree/bindings/hwmon/ibm,cffps1.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt 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>; + }; + }; -- cgit v1.2.3 From f69316d62c7066edc4693b85c6e9f987eed62772 Mon Sep 17 00:00:00 2001 From: "Edward A. James" Date: Mon, 21 Aug 2017 14:46:12 -0500 Subject: hwmon: (pmbus) Add IBM Common Form Factor (CFF) power supply driver Add the driver to monitor IBM CFF power supplies with hwmon over pmbus. Signed-off-by: Edward A. James [groeck: drop 'default n'; include bitops.h instead of jiffies.h] Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/Kconfig | 9 +++ drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/ibm-cffps.c | 151 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 drivers/hwmon/pmbus/ibm-cffps.c diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 68d717a3fd59..522ac37d8a69 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 diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 75bb7ca619d9..737fa4efb3cc 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 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 +#include +#include +#include + +#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"); -- cgit v1.2.3 From caf59a5b63051f955152ec0acc64de0d5784e72e Mon Sep 17 00:00:00 2001 From: "Edward A. James" Date: Mon, 21 Aug 2017 14:46:13 -0500 Subject: Documentation: hwmon: Document the IBM CFF power supply Signed-off-by: Edward A. James Signed-off-by: Guenter Roeck --- Documentation/hwmon/ibm-cffps | 54 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 Documentation/hwmon/ibm-cffps 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 + +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. -- cgit v1.2.3 From d4977c083aeb28cf72c1b019e3f9df13608126dd Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Tue, 29 Aug 2017 20:06:21 +0000 Subject: hwmon: (pmbus) Add support for Intel VID protocol VR13 The below lists of VOUT_MODE command readout with their related VID protocols, Digital to Analog Converter steps: - VR13.0 mode, 10-mV DAC - 0x24 - VR13.0 mode, 5-mV DAC - 0x27 Signed-off-by: Vadim Pasternak Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus.h | 2 +- drivers/hwmon/pmbus/pmbus_core.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) 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 6ac89c93bd2e..302f0aef59de 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -557,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; } -- cgit v1.2.3 From ee983171d49b4b5c957d3f94c68ee1fd7a007281 Mon Sep 17 00:00:00 2001 From: Xo Wang Date: Tue, 29 Aug 2017 14:21:16 -0700 Subject: hwmon: (pmbus/lm25066) Offset coefficient depends on CL When converting the DIRECT format CURRENT_IN and POWER commands, make the offset coefficient ("b") predicate on the value of the current limit setting. Signed-off-by: Xo Wang Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/lm25066.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index a3d912cd3b8d..232b4af13e12 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -488,16 +488,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); -- cgit v1.2.3 From 5783ec2e5c102a6a04d17e07bd9d008a464ed9bc Mon Sep 17 00:00:00 2001 From: Xo Wang Date: Tue, 29 Aug 2017 14:21:17 -0700 Subject: hwmon: (pmbus/lm25066) Add support for TI LM5066I The TI LM5066I hotswap controller is a more accurate version of the LM5066 device already supported. It has different measurement conversion coefficients than the LM5066, so it needs to be recognized as a different device. Signed-off-by: Xo Wang Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm25066 | 9 +++++++-- drivers/hwmon/pmbus/lm25066.c | 41 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 4 deletions(-) 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 @@ -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/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index 232b4af13e12..10d17fb8f283 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -28,7 +28,7 @@ #include #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; @@ -511,6 +547,7 @@ static const struct i2c_device_id lm25066_id[] = { {"lm25066", lm25066}, {"lm5064", lm5064}, {"lm5066", lm5066}, + {"lm5066i", lm5066i}, { } }; -- cgit v1.2.3 From 6930125858c71a62a964437c360f188e96879438 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 30 Aug 2017 09:32:25 +0100 Subject: hwmon: (asc7621) make several arrays static const Don't populate the arrays on the stack, instead make them static. Makes the object code smaller by over 950 bytes: Before: text data bss dec hex filename 26144 18768 352 45264 b0d0 drivers/hwmon/asc7621.o After: text data bss dec hex filename 25029 18928 352 44309 ad15 drivers/hwmon/asc7621.o Signed-off-by: Colin Ian King Signed-off-by: Guenter Roeck --- drivers/hwmon/asc7621.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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, -- cgit v1.2.3 From 610526527a13e4c91e64ec3dfb4626c5043291c9 Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Wed, 30 Aug 2017 22:02:14 +0000 Subject: hwmon: (pmbus) Add support for Texas Instruments tps53679 device The below lists of VOUT_MODE command readout with their related VID protocols, Digital to Analog Converter steps, supported by the device: VR12.0 mode, 5-mV DAC - 0x21 VR12.5 mode, 10-mV DAC - 0x22 VR13.0 mode, 10-mV DAC - 0x24 IMVP8 mode, 5-mV DAC - 0x25 VR13.0 mode, 5-mV DAC - 0x27 Signed-off-by: Vadim Pasternak Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/Kconfig | 9 ++++ drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/tps53679.c | 113 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 drivers/hwmon/pmbus/tps53679.c diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 522ac37d8a69..40019325b517 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -144,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 737fa4efb3cc..459a6be3390e 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -15,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/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 + * + * 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 "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 "); +MODULE_DESCRIPTION("PMBus driver for Texas Instruments TPS53679"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 7a3b68b9075cf9b67692bb97497740bab1dfab41 Mon Sep 17 00:00:00 2001 From: Florian Eckert Date: Fri, 1 Sep 2017 08:58:18 +0200 Subject: hwmon: (ltq-cputemp) add devicetree bindings documentation Document the devicetree bindings for the ltq-cputemp Signed-off-by: Florian Eckert Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/ltq-cputemp.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/ltq-cputemp.txt 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"; + }; -- cgit v1.2.3 From 7074d0a92758603369655ef5d4f49e6caaae0b4e Mon Sep 17 00:00:00 2001 From: Florian Eckert Date: Fri, 1 Sep 2017 08:58:17 +0200 Subject: hwmon: (ltq-cputemp) add cpu temp sensor driver Add the lantiq cpu temperature sensor support for xrx200. Signed-off-by: Florian Eckert Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 7 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/ltq-cputemp.c | 163 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 drivers/hwmon/ltq-cputemp.c diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5b9a61f766ac..d65431417b17 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -791,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/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 + * + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* 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 "); +MODULE_DESCRIPTION("Lantiq cpu temperature sensor driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3