summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-05-25 00:23:10 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2022-05-25 00:23:10 +0300
commit076f222a690e11b433d2b1e218dbd9bdb08fb190 (patch)
treeb0d0e04f6e5aca9f7bcb6f00f00f8814ca74b5ab /drivers
parent0350785b0a092c99c5ddd2ace0260dbe7b3f919f (diff)
parent8877ecb0fc8d7662218a8e7ebb0650f320467935 (diff)
downloadlinux-076f222a690e11b433d2b1e218dbd9bdb08fb190.tar.xz
Merge tag 'hwmon-for-v5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck: "New drivers: - Driver for the Microchip LAN966x SoC - PMBus driver for Infineon Digital Multi-phase xdp152 family controllers Chip support added to existing drivers: - asus-ec-sensors: - Support for ROG STRIX X570-E GAMING WIFI II, PRIME X470-PRO, and ProArt X570 Creator WIFI - External temperature sensor support for ASUS WS X570-ACE - nct6775: - Support for I2C driver - Support for ASUS PRO H410T / PRIME H410M-R / ROG X570-E GAMING WIFI II - lm75: - Support for - Atmel AT30TS74 - pmbus/max16601: - Support for MAX16602 - aquacomputer_d5next: - Support for Aquacomputer Farbwerk - Support for Aquacomputer Octo - jc42: - Support for S-34TS04A Kernel API changes / clarifications: - The chip parameter of with_info API is now mandatory - New hwmon_device_register_for_thermal API call for use by the thermal subsystem Improvements: - PMBus and JC42 drivers now register with thermal subsystem - PMBus drivers now support get_voltage/set_voltage power operations - The adt7475 driver now supports pin configuration - The lm90 driver now supports setting extended range temperatures configuration with a devicetree property - The dell-smm driver now registers as cooling device - The OCC driver delays hwmon registration until requested by userspace ... and various other minor fixes and improvements" * tag 'hwmon-for-v5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (71 commits) hwmon: (aquacomputer_d5next) Fix an error handling path in aqc_probe() hwmon: (sl28cpld) Fix typo in comment hwmon: (pmbus) Check PEC support before reading other registers hwmon: (dimmtemp) Fix bitmap handling hwmon: (lm90) enable extended range according to DTS node dt-bindings: hwmon: lm90: add ti,extended-range-enable property dt-bindings: hwmon: lm90: add missing ti,tmp461 hwmon: (ibmaem) Directly use ida_alloc()/free() hwmon: Directly use ida_alloc()/free() hwmon: (asus-ec-sensors) fix Formula VIII definition dt-bindings: trivial-devices: Add xdp152 hwmon: (sl28cpld-hwmon) Use HWMON_CHANNEL_INFO macro hwmon: (pwm-fan) Use HWMON_CHANNEL_INFO macro hwmon: (peci/dimmtemp) Use HWMON_CHANNEL_INFO macro hwmon: (peci/cputemp) Use HWMON_CHANNEL_INFO macro hwmon: (mr75203) Use HWMON_CHANNEL_INFO macro hwmon: (ltc2992) Use HWMON_CHANNEL_INFO macro hwmon: (as370-hwmon) Use HWMON_CHANNEL_INFO macro hwmon: Make chip parameter for with_info API mandatory thermal/drivers/thermal_hwmon: Use hwmon_device_register_for_thermal() ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hwmon/Kconfig55
-rw-r--r--drivers/hwmon/Makefile4
-rw-r--r--drivers/hwmon/acpi_power_meter.c17
-rw-r--r--drivers/hwmon/adt7475.c119
-rw-r--r--drivers/hwmon/aquacomputer_d5next.c466
-rw-r--r--drivers/hwmon/as370-hwmon.c12
-rw-r--r--drivers/hwmon/asus-ec-sensors.c414
-rw-r--r--drivers/hwmon/bt1-pvt.c50
-rw-r--r--drivers/hwmon/dell-smm-hwmon.c147
-rw-r--r--drivers/hwmon/hwmon.c100
-rw-r--r--drivers/hwmon/ibmaem.c10
-rw-r--r--drivers/hwmon/intel-m10-bmc-hwmon.c11
-rw-r--r--drivers/hwmon/jc42.c8
-rw-r--r--drivers/hwmon/lan966x-hwmon.c418
-rw-r--r--drivers/hwmon/lm75.c14
-rw-r--r--drivers/hwmon/lm83.c2
-rw-r--r--drivers/hwmon/lm90.c4
-rw-r--r--drivers/hwmon/ltc2992.c86
-rw-r--r--drivers/hwmon/mr75203.c12
-rw-r--r--drivers/hwmon/nct6775-core.c (renamed from drivers/hwmon/nct6775.c)2437
-rw-r--r--drivers/hwmon/nct6775-i2c.c195
-rw-r--r--drivers/hwmon/nct6775-platform.c1229
-rw-r--r--drivers/hwmon/nct6775.h252
-rw-r--r--drivers/hwmon/occ/common.c100
-rw-r--r--drivers/hwmon/occ/common.h5
-rw-r--r--drivers/hwmon/occ/p8_i2c.c2
-rw-r--r--drivers/hwmon/occ/p9_sbe.c2
-rw-r--r--drivers/hwmon/occ/sysfs.c137
-rw-r--r--drivers/hwmon/peci/cputemp.c38
-rw-r--r--drivers/hwmon/peci/dimmtemp.c33
-rw-r--r--drivers/hwmon/pmbus/Kconfig13
-rw-r--r--drivers/hwmon/pmbus/Makefile1
-rw-r--r--drivers/hwmon/pmbus/ltc2978.c12
-rw-r--r--drivers/hwmon/pmbus/max16601.c13
-rw-r--r--drivers/hwmon/pmbus/pmbus.h2
-rw-r--r--drivers/hwmon/pmbus/pmbus_core.c233
-rw-r--r--drivers/hwmon/pmbus/xdpe152c4.c75
-rw-r--r--drivers/hwmon/pwm-fan.c12
-rw-r--r--drivers/hwmon/sl28cpld-hwmon.c14
-rw-r--r--drivers/hwmon/tmp401.c46
-rw-r--r--drivers/thermal/thermal_hwmon.c6
41 files changed, 4470 insertions, 2336 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index f2b038fa3b84..590d3d550acb 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -256,11 +256,14 @@ config SENSORS_AHT10
will be called aht10.
config SENSORS_AQUACOMPUTER_D5NEXT
- tristate "Aquacomputer D5 Next watercooling pump"
+ tristate "Aquacomputer D5 Next, Octo, Farbwerk, and Farbwerk 360"
depends on USB_HID
+ select CRC16
help
- If you say yes here you get support for the Aquacomputer D5 Next
- watercooling pump sensors.
+ If you say yes here you get support for sensors and fans of
+ the Aquacomputer D5 Next watercooling pump, Octo fan
+ controller, Farbwerk and Farbwerk 360 RGB controllers, where
+ available.
This driver can also be built as a module. If so, the module
will be called aquacomputer_d5next.
@@ -415,6 +418,7 @@ config SENSORS_ATXP1
config SENSORS_BT1_PVT
tristate "Baikal-T1 Process, Voltage, Temperature sensor driver"
depends on MIPS_BAIKAL_T1 || COMPILE_TEST
+ select POLYNOMIAL
help
If you say yes here you get support for Baikal-T1 PVT sensor
embedded into the SoC.
@@ -498,6 +502,7 @@ config SENSORS_DS1621
config SENSORS_DELL_SMM
tristate "Dell laptop SMM BIOS hwmon driver"
depends on X86
+ imply THERMAL
help
This hwmon driver adds support for reporting temperature of different
sensors and controls the fans on Dell laptops via System Management
@@ -814,6 +819,18 @@ config SENSORS_POWR1220
This driver can also be built as a module. If so, the module
will be called powr1220.
+config SENSORS_LAN966X
+ tristate "Microchip LAN966x Hardware Monitoring"
+ depends on SOC_LAN966 || COMPILE_TEST
+ select REGMAP
+ select POLYNOMIAL
+ help
+ If you say yes here you get support for temperature monitoring
+ on the Microchip LAN966x SoC.
+
+ This driver can also be built as a module. If so, the module
+ will be called lan966x-hwmon.
+
config SENSORS_LINEAGE
tristate "Lineage Compact Power Line Power Entry Module"
depends on I2C
@@ -1248,6 +1265,7 @@ config SENSORS_LM75
temperature sensor chip, with models including:
- Analog Devices ADT75
+ - Atmel (now Microchip) AT30TS74
- Dallas Semiconductor DS75, DS1775 and DS7505
- Global Mixed-mode Technology (GMT) G751
- Maxim MAX6625 and MAX6626
@@ -1457,11 +1475,23 @@ config SENSORS_NCT6683
This driver can also be built as a module. If so, the module
will be called nct6683.
+config SENSORS_NCT6775_CORE
+ tristate
+ select REGMAP
+ help
+ This module contains common code shared by the platform and
+ i2c versions of the nct6775 driver; it is not useful on its
+ own.
+
+ If built as a module, the module will be called
+ nct6775-core.
+
config SENSORS_NCT6775
- tristate "Nuvoton NCT6775F and compatibles"
+ tristate "Platform driver for Nuvoton NCT6775F and compatibles"
depends on !PPC
depends on ACPI_WMI || ACPI_WMI=n
select HWMON_VID
+ select SENSORS_NCT6775_CORE
help
If you say yes here you get support for the hardware monitoring
functionality of the Nuvoton NCT6106D, NCT6775F, NCT6776F, NCT6779D,
@@ -1472,6 +1502,23 @@ config SENSORS_NCT6775
This driver can also be built as a module. If so, the module
will be called nct6775.
+config SENSORS_NCT6775_I2C
+ tristate "I2C driver for Nuvoton NCT6775F and compatibles"
+ depends on I2C
+ select REGMAP_I2C
+ select SENSORS_NCT6775_CORE
+ help
+ If you say yes here you get support for the hardware monitoring
+ functionality of the Nuvoton NCT6106D, NCT6775F, NCT6776F, NCT6779D,
+ NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D, and compatible
+ Super-I/O chips via their I2C interface.
+
+ If you're not building a kernel for a BMC, this is probably
+ not the driver you want (see CONFIG_SENSORS_NCT6775).
+
+ This driver can also be built as a module. If so, the module
+ will be called nct6775-i2c.
+
config SENSORS_NCT7802
tristate "Nuvoton NCT7802Y"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 8a03289e2aa4..007e829d1d0d 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -100,6 +100,7 @@ obj-$(CONFIG_SENSORS_IT87) += it87.o
obj-$(CONFIG_SENSORS_JC42) += jc42.o
obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o
obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o
+obj-$(CONFIG_SENSORS_LAN966X) += lan966x-hwmon.o
obj-$(CONFIG_SENSORS_LINEAGE) += lineage-pem.o
obj-$(CONFIG_SENSORS_LOCHNAGAR) += lochnagar-hwmon.o
obj-$(CONFIG_SENSORS_LM63) += lm63.o
@@ -154,7 +155,10 @@ obj-$(CONFIG_SENSORS_MLXREG_FAN) += mlxreg-fan.o
obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
obj-$(CONFIG_SENSORS_MR75203) += mr75203.o
obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o
+obj-$(CONFIG_SENSORS_NCT6775_CORE) += nct6775-core.o
+nct6775-objs := nct6775-platform.o
obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o
+obj-$(CONFIG_SENSORS_NCT6775_I2C) += nct6775-i2c.o
obj-$(CONFIG_SENSORS_NCT7802) += nct7802.o
obj-$(CONFIG_SENSORS_NCT7904) += nct7904.o
obj-$(CONFIG_SENSORS_NPCM7XX) += npcm750-pwm-fan.o
diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c
index c405a5869581..d2545a1be9fc 100644
--- a/drivers/hwmon/acpi_power_meter.c
+++ b/drivers/hwmon/acpi_power_meter.c
@@ -481,7 +481,7 @@ static struct sensor_template meter_attrs[] = {
RO_SENSOR_TEMPLATE("power1_average_interval_max", show_val, 1),
RO_SENSOR_TEMPLATE("power1_is_battery", show_val, 5),
RW_SENSOR_TEMPLATE(POWER_AVG_INTERVAL_NAME, show_avg_interval,
- set_avg_interval, 0),
+ set_avg_interval, 0),
{},
};
@@ -530,6 +530,7 @@ static void remove_domain_devices(struct acpi_power_meter_resource *resource)
for (i = 0; i < resource->num_domain_devices; i++) {
struct acpi_device *obj = resource->domain_devices[i];
+
if (!obj)
continue;
@@ -580,7 +581,7 @@ static int read_domain_devices(struct acpi_power_meter_resource *resource)
}
resource->holders_dir = kobject_create_and_add("measures",
- &resource->acpi_dev->dev.kobj);
+ &resource->acpi_dev->dev.kobj);
if (!resource->holders_dir) {
res = -ENOMEM;
goto exit_free;
@@ -590,7 +591,7 @@ static int read_domain_devices(struct acpi_power_meter_resource *resource)
for (i = 0; i < pss->package.count; i++) {
struct acpi_device *obj;
- union acpi_object *element = &(pss->package.elements[i]);
+ union acpi_object *element = &pss->package.elements[i];
/* Refuse non-references */
if (element->type != ACPI_TYPE_LOCAL_REFERENCE)
@@ -603,7 +604,7 @@ static int read_domain_devices(struct acpi_power_meter_resource *resource)
continue;
res = sysfs_create_link(resource->holders_dir, &obj->dev.kobj,
- kobject_name(&obj->dev.kobj));
+ kobject_name(&obj->dev.kobj));
if (res) {
acpi_dev_put(obj);
resource->domain_devices[i] = NULL;
@@ -788,7 +789,7 @@ static int read_capabilities(struct acpi_power_meter_resource *resource)
str = &resource->model_number;
for (i = 11; i < 14; i++) {
- union acpi_object *element = &(pss->package.elements[i]);
+ union acpi_object *element = &pss->package.elements[i];
if (element->type != ACPI_TYPE_STRING) {
res = -EINVAL;
@@ -868,8 +869,7 @@ static int acpi_power_meter_add(struct acpi_device *device)
if (!device)
return -EINVAL;
- resource = kzalloc(sizeof(struct acpi_power_meter_resource),
- GFP_KERNEL);
+ resource = kzalloc(sizeof(*resource), GFP_KERNEL);
if (!resource)
return -ENOMEM;
@@ -884,7 +884,8 @@ static int acpi_power_meter_add(struct acpi_device *device)
if (res)
goto exit_free;
- resource->trip[0] = resource->trip[1] = -1;
+ resource->trip[0] = -1;
+ resource->trip[1] = -1;
res = setup_attrs(resource);
if (res)
diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c
index 9d5b019651f2..ac480e6e4818 100644
--- a/drivers/hwmon/adt7475.c
+++ b/drivers/hwmon/adt7475.c
@@ -112,6 +112,8 @@
#define CONFIG3_THERM 0x02
#define CONFIG4_PINFUNC 0x03
+#define CONFIG4_THERM 0x01
+#define CONFIG4_SMBALERT 0x02
#define CONFIG4_MAXDUTY 0x08
#define CONFIG4_ATTN_IN10 0x30
#define CONFIG4_ATTN_IN43 0xC0
@@ -1460,6 +1462,96 @@ static int adt7475_update_limits(struct i2c_client *client)
return 0;
}
+static int load_config3(const struct i2c_client *client, const char *propname)
+{
+ const char *function;
+ u8 config3;
+ int ret;
+
+ ret = of_property_read_string(client->dev.of_node, propname, &function);
+ if (!ret) {
+ ret = adt7475_read(REG_CONFIG3);
+ if (ret < 0)
+ return ret;
+
+ config3 = ret & ~CONFIG3_SMBALERT;
+ if (!strcmp("pwm2", function))
+ ;
+ else if (!strcmp("smbalert#", function))
+ config3 |= CONFIG3_SMBALERT;
+ else
+ return -EINVAL;
+
+ return i2c_smbus_write_byte_data(client, REG_CONFIG3, config3);
+ }
+
+ return 0;
+}
+
+static int load_config4(const struct i2c_client *client, const char *propname)
+{
+ const char *function;
+ u8 config4;
+ int ret;
+
+ ret = of_property_read_string(client->dev.of_node, propname, &function);
+ if (!ret) {
+ ret = adt7475_read(REG_CONFIG4);
+ if (ret < 0)
+ return ret;
+
+ config4 = ret & ~CONFIG4_PINFUNC;
+
+ if (!strcmp("tach4", function))
+ ;
+ else if (!strcmp("therm#", function))
+ config4 |= CONFIG4_THERM;
+ else if (!strcmp("smbalert#", function))
+ config4 |= CONFIG4_SMBALERT;
+ else if (!strcmp("gpio", function))
+ config4 |= CONFIG4_PINFUNC;
+ else
+ return -EINVAL;
+
+ return i2c_smbus_write_byte_data(client, REG_CONFIG4, config4);
+ }
+
+ return 0;
+}
+
+static int load_config(const struct i2c_client *client, enum chips chip)
+{
+ int err;
+ const char *prop1, *prop2;
+
+ switch (chip) {
+ case adt7473:
+ case adt7475:
+ prop1 = "adi,pin5-function";
+ prop2 = "adi,pin9-function";
+ break;
+ case adt7476:
+ case adt7490:
+ prop1 = "adi,pin10-function";
+ prop2 = "adi,pin14-function";
+ break;
+ }
+
+ err = load_config3(client, prop1);
+ if (err) {
+ dev_err(&client->dev, "failed to configure %s\n", prop1);
+ return err;
+ }
+
+ err = load_config4(client, prop2);
+ if (err) {
+ dev_err(&client->dev, "failed to configure %s\n", prop2);
+ return err;
+ }
+
+ return 0;
+}
+
static int set_property_bit(const struct i2c_client *client, char *property,
u8 *config, u8 bit_index)
{
@@ -1477,12 +1569,12 @@ static int set_property_bit(const struct i2c_client *client, char *property,
return ret;
}
-static int load_attenuators(const struct i2c_client *client, int chip,
+static int load_attenuators(const struct i2c_client *client, enum chips chip,
struct adt7475_data *data)
{
- int ret;
-
- if (chip == adt7476 || chip == adt7490) {
+ switch (chip) {
+ case adt7476:
+ case adt7490:
set_property_bit(client, "adi,bypass-attenuator-in0",
&data->config4, 4);
set_property_bit(client, "adi,bypass-attenuator-in1",
@@ -1492,18 +1584,15 @@ static int load_attenuators(const struct i2c_client *client, int chip,
set_property_bit(client, "adi,bypass-attenuator-in4",
&data->config4, 7);
- ret = i2c_smbus_write_byte_data(client, REG_CONFIG4,
- data->config4);
- if (ret < 0)
- return ret;
- } else if (chip == adt7473 || chip == adt7475) {
+ return i2c_smbus_write_byte_data(client, REG_CONFIG4,
+ data->config4);
+ case adt7473:
+ case adt7475:
set_property_bit(client, "adi,bypass-attenuator-in1",
&data->config2, 5);
- ret = i2c_smbus_write_byte_data(client, REG_CONFIG2,
- data->config2);
- if (ret < 0)
- return ret;
+ return i2c_smbus_write_byte_data(client, REG_CONFIG2,
+ data->config2);
}
return 0;
@@ -1585,6 +1674,10 @@ static int adt7475_probe(struct i2c_client *client)
revision = adt7475_read(REG_DEVID2) & 0x07;
}
+ ret = load_config(client, chip);
+ if (ret)
+ return ret;
+
config3 = adt7475_read(REG_CONFIG3);
/* Pin PWM2 may alternatively be used for ALERT output */
if (!(config3 & CONFIG3_SMBALERT))
diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c
index 525809cf7c95..a0e69f7ece36 100644
--- a/drivers/hwmon/aquacomputer_d5next.c
+++ b/drivers/hwmon/aquacomputer_d5next.c
@@ -1,30 +1,37 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * hwmon driver for Aquacomputer devices (D5 Next, Farbwerk 360)
+ * hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360, Octo)
*
* Aquacomputer devices send HID reports (with ID 0x01) every second to report
* sensor values.
*
* Copyright 2021 Aleksa Savic <savicaleksa83@gmail.com>
+ * Copyright 2022 Jack Doan <me@jackdoan.com>
*/
+#include <linux/crc16.h>
#include <linux/debugfs.h>
#include <linux/hid.h>
#include <linux/hwmon.h>
#include <linux/jiffies.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/seq_file.h>
#include <asm/unaligned.h>
#define USB_VENDOR_ID_AQUACOMPUTER 0x0c70
+#define USB_PRODUCT_ID_FARBWERK 0xf00a
#define USB_PRODUCT_ID_D5NEXT 0xf00e
#define USB_PRODUCT_ID_FARBWERK360 0xf010
+#define USB_PRODUCT_ID_OCTO 0xf011
-enum kinds { d5next, farbwerk360 };
+enum kinds { d5next, farbwerk, farbwerk360, octo };
static const char *const aqc_device_names[] = {
[d5next] = "d5next",
- [farbwerk360] = "farbwerk360"
+ [farbwerk] = "farbwerk",
+ [farbwerk360] = "farbwerk360",
+ [octo] = "octo"
};
#define DRIVER_NAME "aquacomputer_d5next"
@@ -35,6 +42,18 @@ static const char *const aqc_device_names[] = {
#define SERIAL_SECOND_PART 5
#define FIRMWARE_VERSION 13
+#define CTRL_REPORT_ID 0x03
+
+/* The HID report that the official software always sends
+ * after writing values, currently same for all devices
+ */
+#define SECONDARY_CTRL_REPORT_ID 0x02
+#define SECONDARY_CTRL_REPORT_SIZE 0x0B
+
+static u8 secondary_ctrl_report[] = {
+ 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x34, 0xC6
+};
+
/* Register offsets for the D5 Next pump */
#define D5NEXT_POWER_CYCLES 24
@@ -53,14 +72,46 @@ static const char *const aqc_device_names[] = {
#define D5NEXT_PUMP_CURRENT 112
#define D5NEXT_FAN_CURRENT 99
+/* Register offsets for the Farbwerk RGB controller */
+#define FARBWERK_NUM_SENSORS 4
+#define FARBWERK_SENSOR_START 0x2f
+#define FARBWERK_SENSOR_SIZE 0x02
+#define FARBWERK_SENSOR_DISCONNECTED 0x7FFF
+
/* Register offsets for the Farbwerk 360 RGB controller */
#define FARBWERK360_NUM_SENSORS 4
-#define FARBWERK360_SENSOR_START 0x32
+#define FARBWERK360_SENSOR_START 0x32
#define FARBWERK360_SENSOR_SIZE 0x02
#define FARBWERK360_SENSOR_DISCONNECTED 0x7FFF
+/* Register offsets for the Octo fan controller */
+#define OCTO_POWER_CYCLES 0x18
+#define OCTO_NUM_FANS 8
+#define OCTO_FAN_PERCENT_OFFSET 0x00
+#define OCTO_FAN_VOLTAGE_OFFSET 0x02
+#define OCTO_FAN_CURRENT_OFFSET 0x04
+#define OCTO_FAN_POWER_OFFSET 0x06
+#define OCTO_FAN_SPEED_OFFSET 0x08
+
+static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 };
+
+#define OCTO_NUM_SENSORS 4
+#define OCTO_SENSOR_START 0x3D
+#define OCTO_SENSOR_SIZE 0x02
+#define OCTO_SENSOR_DISCONNECTED 0x7FFF
+
+#define OCTO_CTRL_REPORT_SIZE 0x65F
+#define OCTO_CTRL_REPORT_CHECKSUM_OFFSET 0x65D
+#define OCTO_CTRL_REPORT_CHECKSUM_START 0x01
+#define OCTO_CTRL_REPORT_CHECKSUM_LENGTH 0x65C
+
+/* Fan speed registers in Octo control report (from 0-100%) */
+static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0x259, 0x2AE };
+
/* Labels for D5 Next */
-#define L_D5NEXT_COOLANT_TEMP "Coolant temp"
+static const char *const label_d5next_temp[] = {
+ "Coolant temp"
+};
static const char *const label_d5next_speeds[] = {
"Pump speed",
@@ -83,7 +134,7 @@ static const char *const label_d5next_current[] = {
"Fan current"
};
-/* Labels for Farbwerk 360 temperature sensors */
+/* Labels for Farbwerk, Farbwerk 360 and Octo temperature sensors */
static const char *const label_temp_sensors[] = {
"Sensor 1",
"Sensor 2",
@@ -91,32 +142,182 @@ static const char *const label_temp_sensors[] = {
"Sensor 4"
};
+/* Labels for Octo */
+static const char *const label_fan_speed[] = {
+ "Fan 1 speed",
+ "Fan 2 speed",
+ "Fan 3 speed",
+ "Fan 4 speed",
+ "Fan 5 speed",
+ "Fan 6 speed",
+ "Fan 7 speed",
+ "Fan 8 speed"
+};
+
+static const char *const label_fan_power[] = {
+ "Fan 1 power",
+ "Fan 2 power",
+ "Fan 3 power",
+ "Fan 4 power",
+ "Fan 5 power",
+ "Fan 6 power",
+ "Fan 7 power",
+ "Fan 8 power"
+};
+
+static const char *const label_fan_voltage[] = {
+ "Fan 1 voltage",
+ "Fan 2 voltage",
+ "Fan 3 voltage",
+ "Fan 4 voltage",
+ "Fan 5 voltage",
+ "Fan 6 voltage",
+ "Fan 7 voltage",
+ "Fan 8 voltage"
+};
+
+static const char *const label_fan_current[] = {
+ "Fan 1 current",
+ "Fan 2 current",
+ "Fan 3 current",
+ "Fan 4 current",
+ "Fan 5 current",
+ "Fan 6 current",
+ "Fan 7 current",
+ "Fan 8 current"
+};
+
struct aqc_data {
struct hid_device *hdev;
struct device *hwmon_dev;
struct dentry *debugfs;
+ struct mutex mutex; /* Used for locking access when reading and writing PWM values */
enum kinds kind;
const char *name;
+ int buffer_size;
+ u8 *buffer;
+ int checksum_start;
+ int checksum_length;
+ int checksum_offset;
+
/* General info, same across all devices */
u32 serial_number[2];
u16 firmware_version;
- /* D5 Next specific - how many times the device was powered on */
+ /* How many times the device was powered on */
u32 power_cycles;
/* Sensor values */
s32 temp_input[4];
- u16 speed_input[2];
- u32 power_input[2];
- u16 voltage_input[3];
- u16 current_input[2];
+ u16 speed_input[8];
+ u32 power_input[8];
+ u16 voltage_input[8];
+ u16 current_input[8];
+
+ /* Label values */
+ const char *const *temp_label;
+ const char *const *speed_label;
+ const char *const *power_label;
+ const char *const *voltage_label;
+ const char *const *current_label;
unsigned long updated;
};
-static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr,
- int channel)
+/* Converts from centi-percent */
+static int aqc_percent_to_pwm(u16 val)
+{
+ return DIV_ROUND_CLOSEST(val * 255, 100 * 100);
+}
+
+/* Converts to centi-percent */
+static int aqc_pwm_to_percent(long val)
+{
+ if (val < 0 || val > 255)
+ return -EINVAL;
+
+ return DIV_ROUND_CLOSEST(val * 100 * 100, 255);
+}
+
+/* Expects the mutex to be locked */
+static int aqc_get_ctrl_data(struct aqc_data *priv)
+{
+ int ret;
+
+ memset(priv->buffer, 0x00, priv->buffer_size);
+ ret = hid_hw_raw_request(priv->hdev, CTRL_REPORT_ID, priv->buffer, priv->buffer_size,
+ HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
+ if (ret < 0)
+ ret = -ENODATA;
+
+ return ret;
+}
+
+/* Expects the mutex to be locked */
+static int aqc_send_ctrl_data(struct aqc_data *priv)
+{
+ int ret;
+ u16 checksum;
+
+ /* Init and xorout value for CRC-16/USB is 0xffff */
+ checksum = crc16(0xffff, priv->buffer + priv->checksum_start, priv->checksum_length);
+ checksum ^= 0xffff;
+
+ /* Place the new checksum at the end of the report */
+ put_unaligned_be16(checksum, priv->buffer + priv->checksum_offset);
+
+ /* Send the patched up report back to the device */
+ ret = hid_hw_raw_request(priv->hdev, CTRL_REPORT_ID, priv->buffer, priv->buffer_size,
+ HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+ if (ret < 0)
+ return ret;
+
+ /* The official software sends this report after every change, so do it here as well */
+ ret = hid_hw_raw_request(priv->hdev, SECONDARY_CTRL_REPORT_ID, secondary_ctrl_report,
+ SECONDARY_CTRL_REPORT_SIZE, HID_FEATURE_REPORT,
+ HID_REQ_SET_REPORT);
+ return ret;
+}
+
+/* Refreshes the control buffer and returns value at offset */
+static int aqc_get_ctrl_val(struct aqc_data *priv, int offset)
+{
+ int ret;
+
+ mutex_lock(&priv->mutex);
+
+ ret = aqc_get_ctrl_data(priv);
+ if (ret < 0)
+ goto unlock_and_return;
+
+ ret = get_unaligned_be16(priv->buffer + offset);
+
+unlock_and_return:
+ mutex_unlock(&priv->mutex);
+ return ret;
+}
+
+static int aqc_set_ctrl_val(struct aqc_data *priv, int offset, long val)
+{
+ int ret;
+
+ mutex_lock(&priv->mutex);
+
+ ret = aqc_get_ctrl_data(priv);
+ if (ret < 0)
+ goto unlock_and_return;
+
+ put_unaligned_be16((u16)val, priv->buffer + offset);
+
+ ret = aqc_send_ctrl_data(priv);
+
+unlock_and_return:
+ mutex_unlock(&priv->mutex);
+ return ret;
+}
+
+static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel)
{
const struct aqc_data *priv = data;
@@ -127,18 +328,49 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
if (channel == 0)
return 0444;
break;
+ case farbwerk:
case farbwerk360:
+ case octo:
return 0444;
default:
break;
}
break;
+ case hwmon_pwm:
+ switch (priv->kind) {
+ case octo:
+ switch (attr) {
+ case hwmon_pwm_input:
+ return 0644;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
case hwmon_fan:
case hwmon_power:
- case hwmon_in:
case hwmon_curr:
switch (priv->kind) {
case d5next:
+ if (channel < 2)
+ return 0444;
+ break;
+ case octo:
+ return 0444;
+ default:
+ break;
+ }
+ break;
+ case hwmon_in:
+ switch (priv->kind) {
+ case d5next:
+ if (channel < 3)
+ return 0444;
+ break;
+ case octo:
return 0444;
default:
break;
@@ -154,6 +386,7 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, long *val)
{
+ int ret;
struct aqc_data *priv = dev_get_drvdata(dev);
if (time_after(jiffies, priv->updated + STATUS_UPDATE_INTERVAL))
@@ -172,6 +405,19 @@ static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
case hwmon_power:
*val = priv->power_input[channel];
break;
+ case hwmon_pwm:
+ switch (priv->kind) {
+ case octo:
+ ret = aqc_get_ctrl_val(priv, octo_ctrl_fan_offsets[channel]);
+ if (ret < 0)
+ return ret;
+
+ *val = aqc_percent_to_pwm(ret);
+ break;
+ default:
+ break;
+ }
+ break;
case hwmon_in:
*val = priv->voltage_input[channel];
break;
@@ -192,48 +438,51 @@ static int aqc_read_string(struct device *dev, enum hwmon_sensor_types type, u32
switch (type) {
case hwmon_temp:
- switch (priv->kind) {
- case d5next:
- *str = L_D5NEXT_COOLANT_TEMP;
- break;
- case farbwerk360:
- *str = label_temp_sensors[channel];
- break;
- default:
- break;
- }
+ *str = priv->temp_label[channel];
break;
case hwmon_fan:
- switch (priv->kind) {
- case d5next:
- *str = label_d5next_speeds[channel];
- break;
- default:
- break;
- }
+ *str = priv->speed_label[channel];
break;
case hwmon_power:
- switch (priv->kind) {
- case d5next:
- *str = label_d5next_power[channel];
- break;
- default:
- break;
- }
+ *str = priv->power_label[channel];
break;
case hwmon_in:
- switch (priv->kind) {
- case d5next:
- *str = label_d5next_voltages[channel];
- break;
- default:
- break;
- }
+ *str = priv->voltage_label[channel];
break;
case hwmon_curr:
- switch (priv->kind) {
- case d5next:
- *str = label_d5next_current[channel];
+ *str = priv->current_label[channel];
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int aqc_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
+ long val)
+{
+ int ret, pwm_value;
+ struct aqc_data *priv = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_pwm:
+ switch (attr) {
+ case hwmon_pwm_input:
+ switch (priv->kind) {
+ case octo:
+ pwm_value = aqc_pwm_to_percent(val);
+ if (pwm_value < 0)
+ return pwm_value;
+
+ ret = aqc_set_ctrl_val(priv, octo_ctrl_fan_offsets[channel],
+ pwm_value);
+ if (ret < 0)
+ return ret;
+ break;
+ default:
+ break;
+ }
break;
default:
break;
@@ -250,6 +499,7 @@ static const struct hwmon_ops aqc_hwmon_ops = {
.is_visible = aqc_is_visible,
.read = aqc_read,
.read_string = aqc_read_string,
+ .write = aqc_write
};
static const struct hwmon_channel_info *aqc_info[] = {
@@ -260,16 +510,48 @@ static const struct hwmon_channel_info *aqc_info[] = {
HWMON_T_INPUT | HWMON_T_LABEL),
HWMON_CHANNEL_INFO(fan,
HWMON_F_INPUT | HWMON_F_LABEL,
+ HWMON_F_INPUT | HWMON_F_LABEL,
+ HWMON_F_INPUT | HWMON_F_LABEL,
+ HWMON_F_INPUT | HWMON_F_LABEL,
+ HWMON_F_INPUT | HWMON_F_LABEL,
+ HWMON_F_INPUT | HWMON_F_LABEL,
+ HWMON_F_INPUT | HWMON_F_LABEL,
HWMON_F_INPUT | HWMON_F_LABEL),
HWMON_CHANNEL_INFO(power,
HWMON_P_INPUT | HWMON_P_LABEL,
+ HWMON_P_INPUT | HWMON_P_LABEL,
+ HWMON_P_INPUT | HWMON_P_LABEL,
+ HWMON_P_INPUT | HWMON_P_LABEL,
+ HWMON_P_INPUT | HWMON_P_LABEL,
+ HWMON_P_INPUT | HWMON_P_LABEL,
+ HWMON_P_INPUT | HWMON_P_LABEL,
HWMON_P_INPUT | HWMON_P_LABEL),
+ HWMON_CHANNEL_INFO(pwm,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT),
HWMON_CHANNEL_INFO(in,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
+ HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL),
HWMON_CHANNEL_INFO(curr,
HWMON_C_INPUT | HWMON_C_LABEL,
+ HWMON_C_INPUT | HWMON_C_LABEL,
+ HWMON_C_INPUT | HWMON_C_LABEL,
+ HWMON_C_INPUT | HWMON_C_LABEL,
+ HWMON_C_INPUT | HWMON_C_LABEL,
+ HWMON_C_INPUT | HWMON_C_LABEL,
+ HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL),
NULL
};
@@ -279,8 +561,7 @@ static const struct hwmon_chip_info aqc_chip_info = {
.info = aqc_info,
};
-static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data,
- int size)
+static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size)
{
int i, sensor_value;
struct aqc_data *priv;
@@ -315,6 +596,17 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
priv->current_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_CURRENT);
priv->current_input[1] = get_unaligned_be16(data + D5NEXT_FAN_CURRENT);
break;
+ case farbwerk:
+ /* Temperature sensor readings */
+ for (i = 0; i < FARBWERK_NUM_SENSORS; i++) {
+ sensor_value = get_unaligned_be16(data + FARBWERK_SENSOR_START +
+ i * FARBWERK_SENSOR_SIZE);
+ if (sensor_value == FARBWERK_SENSOR_DISCONNECTED)
+ priv->temp_input[i] = -ENODATA;
+ else
+ priv->temp_input[i] = sensor_value * 10;
+ }
+ break;
case farbwerk360:
/* Temperature sensor readings */
for (i = 0; i < FARBWERK360_NUM_SENSORS; i++) {
@@ -326,6 +618,35 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
priv->temp_input[i] = sensor_value * 10;
}
break;
+ case octo:
+ priv->power_cycles = get_unaligned_be32(data + OCTO_POWER_CYCLES);
+
+ /* Fan speed and related readings */
+ for (i = 0; i < OCTO_NUM_FANS; i++) {
+ priv->speed_input[i] =
+ get_unaligned_be16(data + octo_sensor_fan_offsets[i] +
+ OCTO_FAN_SPEED_OFFSET);
+ priv->power_input[i] =
+ get_unaligned_be16(data + octo_sensor_fan_offsets[i] +
+ OCTO_FAN_POWER_OFFSET) * 10000;
+ priv->voltage_input[i] =
+ get_unaligned_be16(data + octo_sensor_fan_offsets[i] +
+ OCTO_FAN_VOLTAGE_OFFSET) * 10;
+ priv->current_input[i] =
+ get_unaligned_be16(data + octo_sensor_fan_offsets[i] +
+ OCTO_FAN_CURRENT_OFFSET);
+ }
+
+ /* Temperature sensor readings */
+ for (i = 0; i < OCTO_NUM_SENSORS; i++) {
+ sensor_value = get_unaligned_be16(data + OCTO_SENSOR_START +
+ i * OCTO_SENSOR_SIZE);
+ if (sensor_value == OCTO_SENSOR_DISCONNECTED)
+ priv->temp_input[i] = -ENODATA;
+ else
+ priv->temp_input[i] = sensor_value * 10;
+ }
+ break;
default:
break;
}
@@ -378,8 +699,14 @@ static void aqc_debugfs_init(struct aqc_data *priv)
debugfs_create_file("serial_number", 0444, priv->debugfs, priv, &serial_number_fops);
debugfs_create_file("firmware_version", 0444, priv->debugfs, priv, &firmware_version_fops);
- if (priv->kind == d5next)
+ switch (priv->kind) {
+ case d5next:
+ case octo:
debugfs_create_file("power_cycles", 0444, priv->debugfs, priv, &power_cycles_fops);
+ break;
+ default:
+ break;
+ }
}
#else
@@ -419,9 +746,35 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
switch (hdev->product) {
case USB_PRODUCT_ID_D5NEXT:
priv->kind = d5next;
+
+ priv->temp_label = label_d5next_temp;
+ priv->speed_label = label_d5next_speeds;
+ priv->power_label = label_d5next_power;
+ priv->voltage_label = label_d5next_voltages;
+ priv->current_label = label_d5next_current;
+ break;
+ case USB_PRODUCT_ID_FARBWERK:
+ priv->kind = farbwerk;
+
+ priv->temp_label = label_temp_sensors;
break;
case USB_PRODUCT_ID_FARBWERK360:
priv->kind = farbwerk360;
+
+ priv->temp_label = label_temp_sensors;
+ break;
+ case USB_PRODUCT_ID_OCTO:
+ priv->kind = octo;
+ priv->buffer_size = OCTO_CTRL_REPORT_SIZE;
+ priv->checksum_start = OCTO_CTRL_REPORT_CHECKSUM_START;
+ priv->checksum_length = OCTO_CTRL_REPORT_CHECKSUM_LENGTH;
+ priv->checksum_offset = OCTO_CTRL_REPORT_CHECKSUM_OFFSET;
+
+ priv->temp_label = label_temp_sensors;
+ priv->speed_label = label_fan_speed;
+ priv->power_label = label_fan_power;
+ priv->voltage_label = label_fan_voltage;
+ priv->current_label = label_fan_current;
break;
default:
break;
@@ -429,6 +782,14 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
priv->name = aqc_device_names[priv->kind];
+ priv->buffer = devm_kzalloc(&hdev->dev, priv->buffer_size, GFP_KERNEL);
+ if (!priv->buffer) {
+ ret = -ENOMEM;
+ goto fail_and_close;
+ }
+
+ mutex_init(&priv->mutex);
+
priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, priv->name, priv,
&aqc_chip_info, NULL);
@@ -461,7 +822,9 @@ static void aqc_remove(struct hid_device *hdev)
static const struct hid_device_id aqc_table[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_D5NEXT) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK360) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_OCTO) },
{ }
};
@@ -491,4 +854,5 @@ module_exit(aqc_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Aleksa Savic <savicaleksa83@gmail.com>");
+MODULE_AUTHOR("Jack Doan <me@jackdoan.com>");
MODULE_DESCRIPTION("Hwmon driver for Aquacomputer devices");
diff --git a/drivers/hwmon/as370-hwmon.c b/drivers/hwmon/as370-hwmon.c
index 464244ba8d58..63b5b2d6e593 100644
--- a/drivers/hwmon/as370-hwmon.c
+++ b/drivers/hwmon/as370-hwmon.c
@@ -76,18 +76,8 @@ as370_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
}
}
-static const u32 as370_hwmon_temp_config[] = {
- HWMON_T_INPUT,
- 0
-};
-
-static const struct hwmon_channel_info as370_hwmon_temp = {
- .type = hwmon_temp,
- .config = as370_hwmon_temp_config,
-};
-
static const struct hwmon_channel_info *as370_hwmon_info[] = {
- &as370_hwmon_temp,
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
NULL
};
diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c
index b5cf0136360c..57e11b2bab74 100644
--- a/drivers/hwmon/asus-ec-sensors.c
+++ b/drivers/hwmon/asus-ec-sensors.c
@@ -54,8 +54,10 @@ static char *mutex_path_override;
/* ACPI mutex for locking access to the EC for the firmware */
#define ASUS_HW_ACCESS_MUTEX_ASMX "\\AMW0.ASMX"
-/* There are two variants of the vendor spelling */
-#define VENDOR_ASUS_UPPER_CASE "ASUSTeK COMPUTER INC."
+#define MAX_IDENTICAL_BOARD_VARIATIONS 3
+
+/* Moniker for the ACPI global lock (':' is not allowed in ASL identifiers) */
+#define ACPI_GLOBAL_LOCK_PSEUDO_PATH ":GLOBAL_LOCK"
typedef union {
u32 value;
@@ -133,8 +135,44 @@ enum ec_sensors {
#define SENSOR_TEMP_WATER_IN BIT(ec_sensor_temp_water_in)
#define SENSOR_TEMP_WATER_OUT BIT(ec_sensor_temp_water_out)
+enum board_family {
+ family_unknown,
+ family_amd_400_series,
+ family_amd_500_series,
+};
+
/* All the known sensors for ASUS EC controllers */
-static const struct ec_sensor_info known_ec_sensors[] = {
+static const struct ec_sensor_info sensors_family_amd_400[] = {
+ [ec_sensor_temp_chipset] =
+ EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a),
+ [ec_sensor_temp_cpu] =
+ EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x3b),
+ [ec_sensor_temp_mb] =
+ EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x3c),
+ [ec_sensor_temp_t_sensor] =
+ EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d),
+ [ec_sensor_temp_vrm] =
+ EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e),
+ [ec_sensor_in_cpu_core] =
+ EC_SENSOR("CPU Core", hwmon_in, 2, 0x00, 0xa2),
+ [ec_sensor_fan_cpu_opt] =
+ EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xbc),
+ [ec_sensor_fan_vrm_hs] =
+ EC_SENSOR("VRM HS", hwmon_fan, 2, 0x00, 0xb2),
+ [ec_sensor_fan_chipset] =
+ /* no chipset fans in this generation */
+ EC_SENSOR("Chipset", hwmon_fan, 0, 0x00, 0x00),
+ [ec_sensor_fan_water_flow] =
+ EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xb4),
+ [ec_sensor_curr_cpu] =
+ EC_SENSOR("CPU", hwmon_curr, 1, 0x00, 0xf4),
+ [ec_sensor_temp_water_in] =
+ EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x0d),
+ [ec_sensor_temp_water_out] =
+ EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x0b),
+};
+
+static const struct ec_sensor_info sensors_family_amd_500[] = {
[ec_sensor_temp_chipset] =
EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a),
[ec_sensor_temp_cpu] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x3b),
@@ -164,68 +202,134 @@ static const struct ec_sensor_info known_ec_sensors[] = {
(SENSOR_TEMP_CHIPSET | SENSOR_TEMP_CPU | SENSOR_TEMP_MB)
#define SENSOR_SET_TEMP_WATER (SENSOR_TEMP_WATER_IN | SENSOR_TEMP_WATER_OUT)
-#define DMI_EXACT_MATCH_BOARD(vendor, name, sensors) { \
- .matches = { \
- DMI_EXACT_MATCH(DMI_BOARD_VENDOR, vendor), \
- DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \
- }, \
- .driver_data = (void *)(sensors), \
-}
+struct ec_board_info {
+ const char *board_names[MAX_IDENTICAL_BOARD_VARIATIONS];
+ unsigned long sensors;
+ /*
+ * Defines which mutex to use for guarding access to the state and the
+ * hardware. Can be either a full path to an AML mutex or the
+ * pseudo-path ACPI_GLOBAL_LOCK_PSEUDO_PATH to use the global ACPI lock,
+ * or left empty to use a regular mutex object, in which case access to
+ * the hardware is not guarded.
+ */
+ const char *mutex_path;
+ enum board_family family;
+};
-static const struct dmi_system_id asus_ec_dmi_table[] __initconst = {
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE, "PRIME X570-PRO",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM |
- SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE, "Pro WS X570-ACE",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM |
- SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE,
- "ROG CROSSHAIR VIII DARK HERO",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR |
- SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
- SENSOR_FAN_CPU_OPT | SENSOR_FAN_WATER_FLOW |
- SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE,
- "ROG CROSSHAIR VIII FORMULA",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR |
- SENSOR_TEMP_VRM | SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET |
- SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE, "ROG CROSSHAIR VIII HERO",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR |
- SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
- SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET |
- SENSOR_FAN_WATER_FLOW | SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE,
- "ROG CROSSHAIR VIII HERO (WI-FI)",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR |
- SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
- SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET |
- SENSOR_FAN_WATER_FLOW | SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE,
- "ROG CROSSHAIR VIII IMPACT",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR |
- SENSOR_TEMP_VRM | SENSOR_FAN_CHIPSET |
- SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE, "ROG STRIX B550-E GAMING",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB |
- SENSOR_TEMP_T_SENSOR |
- SENSOR_TEMP_VRM | SENSOR_FAN_CPU_OPT),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE, "ROG STRIX B550-I GAMING",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB |
- SENSOR_TEMP_T_SENSOR |
- SENSOR_TEMP_VRM | SENSOR_FAN_VRM_HS |
- SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE, "ROG STRIX X570-E GAMING",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB |
- SENSOR_TEMP_T_SENSOR |
- SENSOR_TEMP_VRM | SENSOR_FAN_CHIPSET |
- SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE, "ROG STRIX X570-F GAMING",
- SENSOR_SET_TEMP_CHIPSET_CPU_MB |
- SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET),
- DMI_EXACT_MATCH_BOARD(VENDOR_ASUS_UPPER_CASE, "ROG STRIX X570-I GAMING",
- SENSOR_TEMP_T_SENSOR | SENSOR_FAN_VRM_HS |
- SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE),
+static const struct ec_board_info board_info[] = {
+ {
+ .board_names = {"PRIME X470-PRO"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
+ SENSOR_FAN_CPU_OPT |
+ SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
+ .mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH,
+ .family = family_amd_400_series,
+ },
+ {
+ .board_names = {"PRIME X570-PRO"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM |
+ SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
+ {
+ .board_names = {"ProArt X570-CREATOR WIFI"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM |
+ SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT |
+ SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
+ },
+ {
+ .board_names = {"Pro WS X570-ACE"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM |
+ SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET |
+ SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
+ {
+ .board_names = {"ROG CROSSHAIR VIII DARK HERO"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR |
+ SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
+ SENSOR_FAN_CPU_OPT | SENSOR_FAN_WATER_FLOW |
+ SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
+ {
+ .board_names = {
+ "ROG CROSSHAIR VIII FORMULA"
+ "ROG CROSSHAIR VIII HERO",
+ "ROG CROSSHAIR VIII HERO (WI-FI)",
+ },
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR |
+ SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
+ SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET |
+ SENSOR_FAN_WATER_FLOW | SENSOR_CURR_CPU |
+ SENSOR_IN_CPU_CORE,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
+ {
+ .board_names = {"ROG CROSSHAIR VIII IMPACT"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
+ SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU |
+ SENSOR_IN_CPU_CORE,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
+ {
+ .board_names = {"ROG STRIX B550-E GAMING"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
+ SENSOR_FAN_CPU_OPT,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
+ {
+ .board_names = {"ROG STRIX B550-I GAMING"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
+ SENSOR_FAN_VRM_HS | SENSOR_CURR_CPU |
+ SENSOR_IN_CPU_CORE,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
+ {
+ .board_names = {"ROG STRIX X570-E GAMING"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
+ SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU |
+ SENSOR_IN_CPU_CORE,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
+ {
+ .board_names = {"ROG STRIX X570-E GAMING WIFI II"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR | SENSOR_CURR_CPU |
+ SENSOR_IN_CPU_CORE,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
+ {
+ .board_names = {"ROG STRIX X570-F GAMING"},
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CHIPSET,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
+ {
+ .board_names = {"ROG STRIX X570-I GAMING"},
+ .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_FAN_VRM_HS |
+ SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU |
+ SENSOR_IN_CPU_CORE,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
+ .family = family_amd_500_series,
+ },
{}
};
@@ -234,8 +338,49 @@ struct ec_sensor {
s32 cached_value;
};
+struct lock_data {
+ union {
+ acpi_handle aml;
+ /* global lock handle */
+ u32 glk;
+ } mutex;
+ bool (*lock)(struct lock_data *data);
+ bool (*unlock)(struct lock_data *data);
+};
+
+/*
+ * The next function pairs implement options for locking access to the
+ * state and the EC
+ */
+static bool lock_via_acpi_mutex(struct lock_data *data)
+{
+ /*
+ * ASUS DSDT does not specify that access to the EC has to be guarded,
+ * but firmware does access it via ACPI
+ */
+ return ACPI_SUCCESS(acpi_acquire_mutex(data->mutex.aml,
+ NULL, ACPI_LOCK_DELAY_MS));
+}
+
+static bool unlock_acpi_mutex(struct lock_data *data)
+{
+ return ACPI_SUCCESS(acpi_release_mutex(data->mutex.aml, NULL));
+}
+
+static bool lock_via_global_acpi_lock(struct lock_data *data)
+{
+ return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS,
+ &data->mutex.glk));
+}
+
+static bool unlock_global_acpi_lock(struct lock_data *data)
+{
+ return ACPI_SUCCESS(acpi_release_global_lock(data->mutex.glk));
+}
+
struct ec_sensors_data {
- unsigned long board_sensors;
+ const struct ec_board_info *board_info;
+ const struct ec_sensor_info *sensors_info;
struct ec_sensor *sensors;
/* EC registers to read from */
u16 *registers;
@@ -244,7 +389,7 @@ struct ec_sensors_data {
u8 banks[ASUS_EC_MAX_BANK + 1];
/* in jiffies */
unsigned long last_updated;
- acpi_handle aml_mutex;
+ struct lock_data lock_data;
/* number of board EC sensors */
u8 nr_sensors;
/*
@@ -278,7 +423,7 @@ static bool is_sensor_data_signed(const struct ec_sensor_info *si)
static const struct ec_sensor_info *
get_sensor_info(const struct ec_sensors_data *state, int index)
{
- return &known_ec_sensors[state->sensors[index].info_index];
+ return state->sensors_info + state->sensors[index].info_index;
}
static int find_ec_sensor_index(const struct ec_sensors_data *ec,
@@ -301,11 +446,6 @@ static int __init bank_compare(const void *a, const void *b)
return *((const s8 *)a) - *((const s8 *)b);
}
-static int __init board_sensors_count(unsigned long sensors)
-{
- return hweight_long(sensors);
-}
-
static void __init setup_sensor_data(struct ec_sensors_data *ec)
{
struct ec_sensor *s = ec->sensors;
@@ -316,14 +456,14 @@ static void __init setup_sensor_data(struct ec_sensors_data *ec)
ec->nr_banks = 0;
ec->nr_registers = 0;
- for_each_set_bit(i, &ec->board_sensors,
- BITS_PER_TYPE(ec->board_sensors)) {
+ for_each_set_bit(i, &ec->board_info->sensors,
+ BITS_PER_TYPE(ec->board_info->sensors)) {
s->info_index = i;
s->cached_value = 0;
ec->nr_registers +=
- known_ec_sensors[s->info_index].addr.components.size;
+ ec->sensors_info[s->info_index].addr.components.size;
bank_found = false;
- bank = known_ec_sensors[s->info_index].addr.components.bank;
+ bank = ec->sensors_info[s->info_index].addr.components.bank;
for (j = 0; j < ec->nr_banks; j++) {
if (ec->banks[j] == bank) {
bank_found = true;
@@ -353,23 +493,36 @@ static void __init fill_ec_registers(struct ec_sensors_data *ec)
}
}
-static acpi_handle __init asus_hw_access_mutex(struct device *dev)
+static int __init setup_lock_data(struct device *dev)
{
const char *mutex_path;
- acpi_handle res;
int status;
+ struct ec_sensors_data *state = dev_get_drvdata(dev);
mutex_path = mutex_path_override ?
- mutex_path_override : ASUS_HW_ACCESS_MUTEX_ASMX;
+ mutex_path_override : state->board_info->mutex_path;
- status = acpi_get_handle(NULL, (acpi_string)mutex_path, &res);
- if (ACPI_FAILURE(status)) {
- dev_err(dev,
- "Could not get hardware access guard mutex '%s': error %d",
- mutex_path, status);
- return NULL;
+ if (!mutex_path || !strlen(mutex_path)) {
+ dev_err(dev, "Hardware access guard mutex name is empty");
+ return -EINVAL;
}
- return res;
+ if (!strcmp(mutex_path, ACPI_GLOBAL_LOCK_PSEUDO_PATH)) {
+ state->lock_data.mutex.glk = 0;
+ state->lock_data.lock = lock_via_global_acpi_lock;
+ state->lock_data.unlock = unlock_global_acpi_lock;
+ } else {
+ status = acpi_get_handle(NULL, (acpi_string)mutex_path,
+ &state->lock_data.mutex.aml);
+ if (ACPI_FAILURE(status)) {
+ dev_err(dev,
+ "Failed to get hardware access guard AML mutex '%s': error %d",
+ mutex_path, status);
+ return -ENOENT;
+ }
+ state->lock_data.lock = lock_via_acpi_mutex;
+ state->lock_data.unlock = unlock_acpi_mutex;
+ }
+ return 0;
}
static int asus_ec_bank_switch(u8 bank, u8 *old)
@@ -457,10 +610,11 @@ static inline s32 get_sensor_value(const struct ec_sensor_info *si, u8 *data)
static void update_sensor_values(struct ec_sensors_data *ec, u8 *data)
{
const struct ec_sensor_info *si;
- struct ec_sensor *s;
+ struct ec_sensor *s, *sensor_end;
- for (s = ec->sensors; s != ec->sensors + ec->nr_sensors; s++) {
- si = &known_ec_sensors[s->info_index];
+ sensor_end = ec->sensors + ec->nr_sensors;
+ for (s = ec->sensors; s != sensor_end; s++) {
+ si = ec->sensors_info + s->info_index;
s->cached_value = get_sensor_value(si, data);
data += si->addr.components.size;
}
@@ -471,15 +625,9 @@ static int update_ec_sensors(const struct device *dev,
{
int status;
- /*
- * ASUS DSDT does not specify that access to the EC has to be guarded,
- * but firmware does access it via ACPI
- */
- if (ACPI_FAILURE(acpi_acquire_mutex(ec->aml_mutex, NULL,
- ACPI_LOCK_DELAY_MS))) {
- dev_err(dev, "Failed to acquire AML mutex");
- status = -EBUSY;
- goto cleanup;
+ if (!ec->lock_data.lock(&ec->lock_data)) {
+ dev_warn(dev, "Failed to acquire mutex");
+ return -EBUSY;
}
status = asus_ec_block_read(dev, ec);
@@ -487,10 +635,10 @@ static int update_ec_sensors(const struct device *dev,
if (!status) {
update_sensor_values(ec, ec->read_buffer);
}
- if (ACPI_FAILURE(acpi_release_mutex(ec->aml_mutex, NULL))) {
- dev_err(dev, "Failed to release AML mutex");
- }
-cleanup:
+
+ if (!ec->lock_data.unlock(&ec->lock_data))
+ dev_err(dev, "Failed to release mutex");
+
return status;
}
@@ -597,12 +745,24 @@ static struct hwmon_chip_info asus_ec_chip_info = {
.ops = &asus_ec_hwmon_ops,
};
-static unsigned long __init get_board_sensors(void)
+static const struct ec_board_info * __init get_board_info(void)
{
- const struct dmi_system_id *dmi_entry =
- dmi_first_match(asus_ec_dmi_table);
+ const char *dmi_board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
+ const char *dmi_board_name = dmi_get_system_info(DMI_BOARD_NAME);
+ const struct ec_board_info *board;
- return dmi_entry ? (unsigned long)dmi_entry->driver_data : 0;
+ if (!dmi_board_vendor || !dmi_board_name ||
+ strcasecmp(dmi_board_vendor, "ASUSTeK COMPUTER INC."))
+ return NULL;
+
+ for (board = board_info; board->sensors; board++) {
+ if (match_string(board->board_names,
+ MAX_IDENTICAL_BOARD_VARIATIONS,
+ dmi_board_name) >= 0)
+ return board;
+ }
+
+ return NULL;
}
static int __init asus_ec_probe(struct platform_device *pdev)
@@ -610,17 +770,18 @@ static int __init asus_ec_probe(struct platform_device *pdev)
const struct hwmon_channel_info **ptr_asus_ec_ci;
int nr_count[hwmon_max] = { 0 }, nr_types = 0;
struct hwmon_channel_info *asus_ec_hwmon_chan;
+ const struct ec_board_info *pboard_info;
const struct hwmon_chip_info *chip_info;
struct device *dev = &pdev->dev;
struct ec_sensors_data *ec_data;
const struct ec_sensor_info *si;
enum hwmon_sensor_types type;
- unsigned long board_sensors;
struct device *hwdev;
unsigned int i;
+ int status;
- board_sensors = get_board_sensors();
- if (!board_sensors)
+ pboard_info = get_board_info();
+ if (!pboard_info)
return -ENODEV;
ec_data = devm_kzalloc(dev, sizeof(struct ec_sensors_data),
@@ -629,11 +790,31 @@ static int __init asus_ec_probe(struct platform_device *pdev)
return -ENOMEM;
dev_set_drvdata(dev, ec_data);
- ec_data->board_sensors = board_sensors;
- ec_data->nr_sensors = board_sensors_count(ec_data->board_sensors);
+ ec_data->board_info = pboard_info;
+
+ switch (ec_data->board_info->family) {
+ case family_amd_400_series:
+ ec_data->sensors_info = sensors_family_amd_400;
+ break;
+ case family_amd_500_series:
+ ec_data->sensors_info = sensors_family_amd_500;
+ break;
+ default:
+ dev_err(dev, "Unknown board family: %d",
+ ec_data->board_info->family);
+ return -EINVAL;
+ }
+
+ ec_data->nr_sensors = hweight_long(ec_data->board_info->sensors);
ec_data->sensors = devm_kcalloc(dev, ec_data->nr_sensors,
sizeof(struct ec_sensor), GFP_KERNEL);
+ status = setup_lock_data(dev);
+ if (status) {
+ dev_err(dev, "Failed to setup state/EC locking: %d", status);
+ return status;
+ }
+
setup_sensor_data(ec_data);
ec_data->registers = devm_kcalloc(dev, ec_data->nr_registers,
sizeof(u16), GFP_KERNEL);
@@ -645,8 +826,6 @@ static int __init asus_ec_probe(struct platform_device *pdev)
fill_ec_registers(ec_data);
- ec_data->aml_mutex = asus_hw_access_mutex(dev);
-
for (i = 0; i < ec_data->nr_sensors; ++i) {
si = get_sensor_info(ec_data, i);
if (!nr_count[si->type])
@@ -703,7 +882,14 @@ static struct platform_driver asus_ec_sensors_platform_driver = {
},
};
-MODULE_DEVICE_TABLE(dmi, asus_ec_dmi_table);
+MODULE_DEVICE_TABLE(acpi, acpi_ec_ids);
+/*
+ * we use module_platform_driver_probe() rather than module_platform_driver()
+ * because the probe function (and its dependants) are marked with __init, which
+ * means we can't put it into the .probe member of the platform_driver struct
+ * above, and we can't mark the asus_ec_sensors_platform_driver object as __init
+ * because the object is referenced from the module exit code.
+ */
module_platform_driver_probe(asus_ec_sensors_platform_driver, asus_ec_probe);
module_param_named(mutex_path, mutex_path_override, charp, 0);
diff --git a/drivers/hwmon/bt1-pvt.c b/drivers/hwmon/bt1-pvt.c
index 74ce5211eb75..21ab172774ec 100644
--- a/drivers/hwmon/bt1-pvt.c
+++ b/drivers/hwmon/bt1-pvt.c
@@ -26,6 +26,7 @@
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/polynomial.h>
#include <linux/seqlock.h>
#include <linux/sysfs.h>
#include <linux/types.h>
@@ -65,7 +66,7 @@ static const struct pvt_sensor_info pvt_info[] = {
* 48380,
* where T = [-48380, 147438] mC and N = [0, 1023].
*/
-static const struct pvt_poly __maybe_unused poly_temp_to_N = {
+static const struct polynomial __maybe_unused poly_temp_to_N = {
.total_divider = 10000,
.terms = {
{4, 18322, 10000, 10000},
@@ -76,7 +77,7 @@ static const struct pvt_poly __maybe_unused poly_temp_to_N = {
}
};
-static const struct pvt_poly poly_N_to_temp = {
+static const struct polynomial poly_N_to_temp = {
.total_divider = 1,
.terms = {
{4, -16743, 1000, 1},
@@ -97,7 +98,7 @@ static const struct pvt_poly poly_N_to_temp = {
* N = (18658e-3*V - 11572) / 10,
* V = N * 10^5 / 18658 + 11572 * 10^4 / 18658.
*/
-static const struct pvt_poly __maybe_unused poly_volt_to_N = {
+static const struct polynomial __maybe_unused poly_volt_to_N = {
.total_divider = 10,
.terms = {
{1, 18658, 1000, 1},
@@ -105,7 +106,7 @@ static const struct pvt_poly __maybe_unused poly_volt_to_N = {
}
};
-static const struct pvt_poly poly_N_to_volt = {
+static const struct polynomial poly_N_to_volt = {
.total_divider = 10,
.terms = {
{1, 100000, 18658, 1},
@@ -113,31 +114,6 @@ static const struct pvt_poly poly_N_to_volt = {
}
};
-/*
- * Here is the polynomial calculation function, which performs the
- * redistributed terms calculations. It's pretty straightforward. We walk
- * over each degree term up to the free one, and perform the redistributed
- * multiplication of the term coefficient, its divider (as for the rationale
- * fraction representation), data power and the rational fraction divider
- * leftover. Then all of this is collected in a total sum variable, which
- * value is normalized by the total divider before being returned.
- */
-static long pvt_calc_poly(const struct pvt_poly *poly, long data)
-{
- const struct pvt_poly_term *term = poly->terms;
- long tmp, ret = 0;
- int deg;
-
- do {
- tmp = term->coef;
- for (deg = 0; deg < term->deg; ++deg)
- tmp = mult_frac(tmp, data, term->divider);
- ret += tmp / term->divider_leftover;
- } while ((term++)->deg);
-
- return ret / poly->total_divider;
-}
-
static inline u32 pvt_update(void __iomem *reg, u32 mask, u32 data)
{
u32 old;
@@ -324,9 +300,9 @@ static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
} while (read_seqretry(&cache->data_seqlock, seq));
if (type == PVT_TEMP)
- *val = pvt_calc_poly(&poly_N_to_temp, data);
+ *val = polynomial_calc(&poly_N_to_temp, data);
else
- *val = pvt_calc_poly(&poly_N_to_volt, data);
+ *val = polynomial_calc(&poly_N_to_volt, data);
return 0;
}
@@ -345,9 +321,9 @@ static int pvt_read_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
data = FIELD_GET(PVT_THRES_HI_MASK, data);
if (type == PVT_TEMP)
- *val = pvt_calc_poly(&poly_N_to_temp, data);
+ *val = polynomial_calc(&poly_N_to_temp, data);
else
- *val = pvt_calc_poly(&poly_N_to_volt, data);
+ *val = polynomial_calc(&poly_N_to_volt, data);
return 0;
}
@@ -360,10 +336,10 @@ static int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
if (type == PVT_TEMP) {
val = clamp(val, PVT_TEMP_MIN, PVT_TEMP_MAX);
- data = pvt_calc_poly(&poly_temp_to_N, val);
+ data = polynomial_calc(&poly_temp_to_N, val);
} else {
val = clamp(val, PVT_VOLT_MIN, PVT_VOLT_MAX);
- data = pvt_calc_poly(&poly_volt_to_N, val);
+ data = polynomial_calc(&poly_volt_to_N, val);
}
/* Serialize limit update, since a part of the register is changed. */
@@ -522,9 +498,9 @@ static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
return -ETIMEDOUT;
if (type == PVT_TEMP)
- *val = pvt_calc_poly(&poly_N_to_temp, data);
+ *val = polynomial_calc(&poly_N_to_temp, data);
else
- *val = pvt_calc_poly(&poly_N_to_volt, data);
+ *val = polynomial_calc(&poly_N_to_volt, data);
return 0;
}
diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c
index 84cb1ede7bc0..071aa6f4e109 100644
--- a/drivers/hwmon/dell-smm-hwmon.c
+++ b/drivers/hwmon/dell-smm-hwmon.c
@@ -21,14 +21,17 @@
#include <linux/errno.h>
#include <linux/hwmon.h>
#include <linux/init.h>
+#include <linux/kconfig.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
-#include <linux/string.h>
+#include <linux/slab.h>
#include <linux/smp.h>
+#include <linux/string.h>
+#include <linux/thermal.h>
#include <linux/types.h>
#include <linux/uaccess.h>
@@ -46,8 +49,11 @@
#define I8K_SMM_GET_DELL_SIG1 0xfea3
#define I8K_SMM_GET_DELL_SIG2 0xffa3
+/* in usecs */
+#define DELL_SMM_MAX_DURATION 250000
+
#define I8K_FAN_MULT 30
-#define I8K_FAN_MAX_RPM 30000
+#define I8K_FAN_RPM_THRESHOLD 1000
#define I8K_MAX_TEMP 127
#define I8K_FN_NONE 0x00
@@ -80,6 +86,11 @@ struct dell_smm_data {
int *fan_nominal_speed[DELL_SMM_NO_FANS];
};
+struct dell_smm_cooling_data {
+ u8 fan_num;
+ struct dell_smm_data *data;
+};
+
MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
MODULE_DESCRIPTION("Dell laptop SMM BIOS hwmon driver");
@@ -231,6 +242,9 @@ static int i8k_smm_func(void *par)
pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x (took %7lld usecs)\n", eax, ebx,
(rc ? 0xffff : regs->eax & 0xffff), duration);
+ if (duration > DELL_SMM_MAX_DURATION)
+ pr_warn_once("SMM call took %lld usecs!\n", duration);
+
return rc;
}
@@ -318,7 +332,7 @@ static int __init i8k_get_fan_nominal_speed(const struct dell_smm_data *data, u8
if (data->disallow_fan_support)
return -EINVAL;
- return i8k_smm(&regs) ? : (regs.eax & 0xffff) * data->i8k_fan_mult;
+ return i8k_smm(&regs) ? : (regs.eax & 0xffff);
}
/*
@@ -638,9 +652,50 @@ static void __init i8k_init_procfs(struct device *dev)
#endif
-/*
- * Hwmon interface
- */
+static int dell_smm_get_max_state(struct thermal_cooling_device *dev, unsigned long *state)
+{
+ struct dell_smm_cooling_data *cdata = dev->devdata;
+
+ *state = cdata->data->i8k_fan_max;
+
+ return 0;
+}
+
+static int dell_smm_get_cur_state(struct thermal_cooling_device *dev, unsigned long *state)
+{
+ struct dell_smm_cooling_data *cdata = dev->devdata;
+ int ret;
+
+ ret = i8k_get_fan_status(cdata->data, cdata->fan_num);
+ if (ret < 0)
+ return ret;
+
+ *state = ret;
+
+ return 0;
+}
+
+static int dell_smm_set_cur_state(struct thermal_cooling_device *dev, unsigned long state)
+{
+ struct dell_smm_cooling_data *cdata = dev->devdata;
+ struct dell_smm_data *data = cdata->data;
+ int ret;
+
+ if (state > data->i8k_fan_max)
+ return -EINVAL;
+
+ mutex_lock(&data->i8k_mutex);
+ ret = i8k_set_fan(data, cdata->fan_num, (int)state);
+ mutex_unlock(&data->i8k_mutex);
+
+ return ret;
+}
+
+static const struct thermal_cooling_device_ops dell_smm_cooling_ops = {
+ .get_max_state = dell_smm_get_max_state,
+ .get_cur_state = dell_smm_get_cur_state,
+ .set_cur_state = dell_smm_set_cur_state,
+};
static umode_t dell_smm_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr,
int channel)
@@ -727,6 +782,7 @@ static int dell_smm_read(struct device *dev, enum hwmon_sensor_types type, u32 a
long *val)
{
struct dell_smm_data *data = dev_get_drvdata(dev);
+ int mult = data->i8k_fan_mult;
int ret;
switch (type) {
@@ -755,11 +811,11 @@ static int dell_smm_read(struct device *dev, enum hwmon_sensor_types type, u32 a
return 0;
case hwmon_fan_min:
- *val = data->fan_nominal_speed[channel][0];
+ *val = data->fan_nominal_speed[channel][0] * mult;
return 0;
case hwmon_fan_max:
- *val = data->fan_nominal_speed[channel][data->i8k_fan_max];
+ *val = data->fan_nominal_speed[channel][data->i8k_fan_max] * mult;
return 0;
case hwmon_fan_target:
@@ -770,7 +826,7 @@ static int dell_smm_read(struct device *dev, enum hwmon_sensor_types type, u32 a
if (ret > data->i8k_fan_max)
ret = data->i8k_fan_max;
- *val = data->fan_nominal_speed[channel][ret];
+ *val = data->fan_nominal_speed[channel][ret] * mult;
return 0;
default:
@@ -941,6 +997,37 @@ static const struct hwmon_chip_info dell_smm_chip_info = {
.info = dell_smm_info,
};
+static int __init dell_smm_init_cdev(struct device *dev, u8 fan_num)
+{
+ struct dell_smm_data *data = dev_get_drvdata(dev);
+ struct thermal_cooling_device *cdev;
+ struct dell_smm_cooling_data *cdata;
+ int ret = 0;
+ char *name;
+
+ name = kasprintf(GFP_KERNEL, "dell-smm-fan%u", fan_num + 1);
+ if (!name)
+ return -ENOMEM;
+
+ cdata = devm_kmalloc(dev, sizeof(*cdata), GFP_KERNEL);
+ if (cdata) {
+ cdata->fan_num = fan_num;
+ cdata->data = data;
+ cdev = devm_thermal_of_cooling_device_register(dev, NULL, name, cdata,
+ &dell_smm_cooling_ops);
+ if (IS_ERR(cdev)) {
+ devm_kfree(dev, cdata);
+ ret = PTR_ERR(cdev);
+ }
+ } else {
+ ret = -ENOMEM;
+ }
+
+ kfree(name);
+
+ return ret;
+}
+
static int __init dell_smm_init_hwmon(struct device *dev)
{
struct dell_smm_data *data = dev_get_drvdata(dev);
@@ -967,6 +1054,15 @@ static int __init dell_smm_init_hwmon(struct device *dev)
continue;
data->fan[i] = true;
+
+ /* the cooling device is not critical, ignore failures */
+ if (IS_REACHABLE(CONFIG_THERMAL)) {
+ err = dell_smm_init_cdev(dev, i);
+ if (err < 0)
+ dev_warn(dev, "Failed to register cooling device for fan %u\n",
+ i + 1);
+ }
+
data->fan_nominal_speed[i] = devm_kmalloc_array(dev, data->i8k_fan_max + 1,
sizeof(*data->fan_nominal_speed[i]),
GFP_KERNEL);
@@ -982,6 +1078,13 @@ static int __init dell_smm_init_hwmon(struct device *dev)
break;
}
data->fan_nominal_speed[i][state] = err;
+ /*
+ * Autodetect fan multiplier based on nominal rpm if multiplier
+ * was not specified as module param or in DMI. If fan reports
+ * rpm value too high then set multiplier to 1.
+ */
+ if (!fan_mult && err > I8K_FAN_RPM_THRESHOLD)
+ data->i8k_fan_mult = 1;
}
}
@@ -1270,15 +1373,12 @@ static int __init dell_smm_probe(struct platform_device *pdev)
struct dell_smm_data *data;
const struct dmi_system_id *id, *fan_control;
int ret;
- u8 fan;
data = devm_kzalloc(&pdev->dev, sizeof(struct dell_smm_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
mutex_init(&data->i8k_mutex);
- data->i8k_fan_mult = I8K_FAN_MULT;
- data->i8k_fan_max = I8K_FAN_HIGH;
platform_set_drvdata(pdev, data);
if (dmi_check_system(i8k_blacklist_fan_support_dmi_table)) {
@@ -1313,7 +1413,9 @@ static int __init dell_smm_probe(struct platform_device *pdev)
fan_max = conf->fan_max;
}
- data->i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */
+ /* All options must not be 0 */
+ data->i8k_fan_mult = fan_mult ? : I8K_FAN_MULT;
+ data->i8k_fan_max = fan_max ? : I8K_FAN_HIGH;
data->i8k_pwm_mult = DIV_ROUND_UP(255, data->i8k_fan_max);
fan_control = dmi_first_match(i8k_whitelist_fan_control);
@@ -1325,25 +1427,6 @@ static int __init dell_smm_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "enabling support for setting automatic/manual fan control\n");
}
- if (!fan_mult) {
- /*
- * Autodetect fan multiplier based on nominal rpm
- * If fan reports rpm value too high then set multiplier to 1
- */
- for (fan = 0; fan < DELL_SMM_NO_FANS; ++fan) {
- ret = i8k_get_fan_nominal_speed(data, fan, data->i8k_fan_max);
- if (ret < 0)
- continue;
-
- if (ret > I8K_FAN_MAX_RPM)
- data->i8k_fan_mult = 1;
- break;
- }
- } else {
- /* Fan multiplier was specified in module param or in dmi */
- data->i8k_fan_mult = fan_mult;
- }
-
ret = dell_smm_init_hwmon(&pdev->dev);
if (ret)
return ret;
diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
index 989e2c8496dd..2e2cd79d89eb 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -764,7 +764,7 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata,
"hwmon: '%s' is not a valid name attribute, please fix\n",
name);
- id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL);
+ id = ida_alloc(&hwmon_ida, GFP_KERNEL);
if (id < 0)
return ERR_PTR(id);
@@ -856,7 +856,7 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata,
free_hwmon:
hwmon_dev_release(hdev);
ida_remove:
- ida_simple_remove(&hwmon_ida, id);
+ ida_free(&hwmon_ida, id);
return ERR_PTR(err);
}
@@ -886,11 +886,12 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups);
/**
* hwmon_device_register_with_info - register w/ hwmon
- * @dev: the parent device
- * @name: hwmon name attribute
- * @drvdata: driver data to attach to created device
- * @chip: pointer to hwmon chip information
+ * @dev: the parent device (mandatory)
+ * @name: hwmon name attribute (mandatory)
+ * @drvdata: driver data to attach to created device (optional)
+ * @chip: pointer to hwmon chip information (mandatory)
* @extra_groups: pointer to list of additional non-standard attribute groups
+ * (optional)
*
* hwmon_device_unregister() must be called when the device is no
* longer needed.
@@ -903,13 +904,10 @@ hwmon_device_register_with_info(struct device *dev, const char *name,
const struct hwmon_chip_info *chip,
const struct attribute_group **extra_groups)
{
- if (!name)
- return ERR_PTR(-EINVAL);
-
- if (chip && (!chip->ops || !chip->ops->is_visible || !chip->info))
+ if (!dev || !name || !chip)
return ERR_PTR(-EINVAL);
- if (chip && !dev)
+ if (!chip->ops || !chip->ops->is_visible || !chip->info)
return ERR_PTR(-EINVAL);
return __hwmon_device_register(dev, name, drvdata, chip, extra_groups);
@@ -917,6 +915,31 @@ hwmon_device_register_with_info(struct device *dev, const char *name,
EXPORT_SYMBOL_GPL(hwmon_device_register_with_info);
/**
+ * hwmon_device_register_for_thermal - register hwmon device for thermal subsystem
+ * @dev: the parent device
+ * @name: hwmon name attribute
+ * @drvdata: driver data to attach to created device
+ *
+ * The use of this function is restricted. It is provided for legacy reasons
+ * and must only be called from the thermal subsystem.
+ *
+ * hwmon_device_unregister() must be called when the device is no
+ * longer needed.
+ *
+ * Returns the pointer to the new device.
+ */
+struct device *
+hwmon_device_register_for_thermal(struct device *dev, const char *name,
+ void *drvdata)
+{
+ if (!name || !dev)
+ return ERR_PTR(-EINVAL);
+
+ return __hwmon_device_register(dev, name, drvdata, NULL, NULL);
+}
+EXPORT_SYMBOL_NS_GPL(hwmon_device_register_for_thermal, HWMON_THERMAL);
+
+/**
* hwmon_device_register - register w/ hwmon
* @dev: the device to register
*
@@ -945,7 +968,7 @@ void hwmon_device_unregister(struct device *dev)
if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) {
device_unregister(dev);
- ida_simple_remove(&hwmon_ida, id);
+ ida_free(&hwmon_ida, id);
} else
dev_dbg(dev->parent,
"hwmon_device_unregister() failed: bad class ID!\n");
@@ -1057,6 +1080,59 @@ void devm_hwmon_device_unregister(struct device *dev)
}
EXPORT_SYMBOL_GPL(devm_hwmon_device_unregister);
+static char *__hwmon_sanitize_name(struct device *dev, const char *old_name)
+{
+ char *name, *p;
+
+ if (dev)
+ name = devm_kstrdup(dev, old_name, GFP_KERNEL);
+ else
+ name = kstrdup(old_name, GFP_KERNEL);
+ if (!name)
+ return ERR_PTR(-ENOMEM);
+
+ for (p = name; *p; p++)
+ if (hwmon_is_bad_char(*p))
+ *p = '_';
+
+ return name;
+}
+
+/**
+ * hwmon_sanitize_name - Replaces invalid characters in a hwmon name
+ * @name: NUL-terminated name
+ *
+ * Allocates a new string where any invalid characters will be replaced
+ * by an underscore. It is the responsibility of the caller to release
+ * the memory.
+ *
+ * Returns newly allocated name, or ERR_PTR on error.
+ */
+char *hwmon_sanitize_name(const char *name)
+{
+ return __hwmon_sanitize_name(NULL, name);
+}
+EXPORT_SYMBOL_GPL(hwmon_sanitize_name);
+
+/**
+ * devm_hwmon_sanitize_name - resource managed hwmon_sanitize_name()
+ * @dev: device to allocate memory for
+ * @name: NUL-terminated name
+ *
+ * Allocates a new string where any invalid characters will be replaced
+ * by an underscore.
+ *
+ * Returns newly allocated name, or ERR_PTR on error.
+ */
+char *devm_hwmon_sanitize_name(struct device *dev, const char *name)
+{
+ if (!dev)
+ return ERR_PTR(-EINVAL);
+
+ return __hwmon_sanitize_name(dev, name);
+}
+EXPORT_SYMBOL_GPL(devm_hwmon_sanitize_name);
+
static void __init hwmon_pci_quirks(void)
{
#if defined CONFIG_X86 && defined CONFIG_PCI
diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c
index de6baf6ca3d1..5c4cf742f5ae 100644
--- a/drivers/hwmon/ibmaem.c
+++ b/drivers/hwmon/ibmaem.c
@@ -482,7 +482,7 @@ static void aem_delete(struct aem_data *data)
ipmi_destroy_user(data->ipmi.user);
platform_set_drvdata(data->pdev, NULL);
platform_device_unregister(data->pdev);
- ida_simple_remove(&aem_ida, data->id);
+ ida_free(&aem_ida, data->id);
kfree(data);
}
@@ -539,7 +539,7 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle)
data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL;
/* Create sub-device for this fw instance */
- data->id = ida_simple_get(&aem_ida, 0, 0, GFP_KERNEL);
+ data->id = ida_alloc(&aem_ida, GFP_KERNEL);
if (data->id < 0)
goto id_err;
@@ -600,7 +600,7 @@ ipmi_err:
platform_set_drvdata(data->pdev, NULL);
platform_device_unregister(data->pdev);
dev_err:
- ida_simple_remove(&aem_ida, data->id);
+ ida_free(&aem_ida, data->id);
id_err:
kfree(data);
@@ -679,7 +679,7 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe,
data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL;
/* Create sub-device for this fw instance */
- data->id = ida_simple_get(&aem_ida, 0, 0, GFP_KERNEL);
+ data->id = ida_alloc(&aem_ida, GFP_KERNEL);
if (data->id < 0)
goto id_err;
@@ -740,7 +740,7 @@ ipmi_err:
platform_set_drvdata(data->pdev, NULL);
platform_device_unregister(data->pdev);
dev_err:
- ida_simple_remove(&aem_ida, data->id);
+ ida_free(&aem_ida, data->id);
id_err:
kfree(data);
diff --git a/drivers/hwmon/intel-m10-bmc-hwmon.c b/drivers/hwmon/intel-m10-bmc-hwmon.c
index 7a08e4c44a4b..6e82f7200d1c 100644
--- a/drivers/hwmon/intel-m10-bmc-hwmon.c
+++ b/drivers/hwmon/intel-m10-bmc-hwmon.c
@@ -515,7 +515,6 @@ static int m10bmc_hwmon_probe(struct platform_device *pdev)
struct intel_m10bmc *m10bmc = dev_get_drvdata(pdev->dev.parent);
struct device *hwmon_dev, *dev = &pdev->dev;
struct m10bmc_hwmon *hw;
- int i;
hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
if (!hw)
@@ -528,13 +527,9 @@ static int m10bmc_hwmon_probe(struct platform_device *pdev)
hw->chip.info = hw->bdata->hinfo;
hw->chip.ops = &m10bmc_hwmon_ops;
- hw->hw_name = devm_kstrdup(dev, id->name, GFP_KERNEL);
- if (!hw->hw_name)
- return -ENOMEM;
-
- for (i = 0; hw->hw_name[i]; i++)
- if (hwmon_is_bad_char(hw->hw_name[i]))
- hw->hw_name[i] = '_';
+ hw->hw_name = devm_hwmon_sanitize_name(dev, id->name);
+ if (IS_ERR(hw->hw_name))
+ return PTR_ERR(hw->hw_name);
hwmon_dev = devm_hwmon_device_register_with_info(dev, hw->hw_name,
hw, &hw->chip, NULL);
diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c
index cb347a6bd8d9..07f7f8b5b73d 100644
--- a/drivers/hwmon/jc42.c
+++ b/drivers/hwmon/jc42.c
@@ -63,6 +63,7 @@ static const unsigned short normal_i2c[] = {
#define STM_MANID 0x104a /* ST Microelectronics */
#define GT_MANID 0x1c68 /* Giantec */
#define GT_MANID2 0x132d /* Giantec, 2nd mfg ID */
+#define SI_MANID 0x1c85 /* Seiko Instruments */
/* SMBUS register */
#define SMBUS_STMOUT BIT(7) /* SMBus time-out, active low */
@@ -156,6 +157,10 @@ static const unsigned short normal_i2c[] = {
#define STTS3000_DEVID 0x0200
#define STTS3000_DEVID_MASK 0xffff
+/* Seiko Instruments */
+#define S34TS04A_DEVID 0x2221
+#define S34TS04A_DEVID_MASK 0xffff
+
static u16 jc42_hysteresis[] = { 0, 1500, 3000, 6000 };
struct jc42_chips {
@@ -186,6 +191,7 @@ static struct jc42_chips jc42_chips[] = {
{ ONS_MANID, CAT34TS04_DEVID, CAT34TS04_DEVID_MASK },
{ ONS_MANID, N34TS04_DEVID, N34TS04_DEVID_MASK },
{ NXP_MANID, SE98_DEVID, SE98_DEVID_MASK },
+ { SI_MANID, S34TS04A_DEVID, S34TS04A_DEVID_MASK },
{ STM_MANID, STTS424_DEVID, STTS424_DEVID_MASK },
{ STM_MANID, STTS424E_DEVID, STTS424E_DEVID_MASK },
{ STM_MANID, STTS2002_DEVID, STTS2002_DEVID_MASK },
@@ -443,6 +449,8 @@ static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info)
}
static const struct hwmon_channel_info *jc42_info[] = {
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
HWMON_T_CRIT | HWMON_T_MAX_HYST |
diff --git a/drivers/hwmon/lan966x-hwmon.c b/drivers/hwmon/lan966x-hwmon.c
new file mode 100644
index 000000000000..f41df053ac31
--- /dev/null
+++ b/drivers/hwmon/lan966x-hwmon.c
@@ -0,0 +1,418 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/hwmon.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/polynomial.h>
+#include <linux/regmap.h>
+
+/*
+ * The original translation formulae of the temperature (in degrees of Celsius)
+ * are as follows:
+ *
+ * T = -3.4627e-11*(N^4) + 1.1023e-7*(N^3) + -1.9165e-4*(N^2) +
+ * 3.0604e-1*(N^1) + -5.6197e1
+ *
+ * where [-56.197, 136.402]C and N = [0, 1023].
+ *
+ * They must be accordingly altered to be suitable for the integer arithmetics.
+ * The technique is called 'factor redistribution', which just makes sure the
+ * multiplications and divisions are made so to have a result of the operations
+ * within the integer numbers limit. In addition we need to translate the
+ * formulae to accept millidegrees of Celsius. Here what it looks like after
+ * the alterations:
+ *
+ * T = -34627e-12*(N^4) + 110230e-9*(N^3) + -191650e-6*(N^2) +
+ * 306040e-3*(N^1) + -56197
+ *
+ * where T = [-56197, 136402]mC and N = [0, 1023].
+ */
+
+static const struct polynomial poly_N_to_temp = {
+ .terms = {
+ {4, -34627, 1000, 1},
+ {3, 110230, 1000, 1},
+ {2, -191650, 1000, 1},
+ {1, 306040, 1000, 1},
+ {0, -56197, 1, 1}
+ }
+};
+
+#define PVT_SENSOR_CTRL 0x0 /* unused */
+#define PVT_SENSOR_CFG 0x4
+#define SENSOR_CFG_CLK_CFG GENMASK(27, 20)
+#define SENSOR_CFG_TRIM_VAL GENMASK(13, 9)
+#define SENSOR_CFG_SAMPLE_ENA BIT(8)
+#define SENSOR_CFG_START_CAPTURE BIT(7)
+#define SENSOR_CFG_CONTINIOUS_MODE BIT(6)
+#define SENSOR_CFG_PSAMPLE_ENA GENMASK(1, 0)
+#define PVT_SENSOR_STAT 0x8
+#define SENSOR_STAT_DATA_VALID BIT(10)
+#define SENSOR_STAT_DATA GENMASK(9, 0)
+
+#define FAN_CFG 0x0
+#define FAN_CFG_DUTY_CYCLE GENMASK(23, 16)
+#define INV_POL BIT(3)
+#define GATE_ENA BIT(2)
+#define PWM_OPEN_COL_ENA BIT(1)
+#define FAN_STAT_CFG BIT(0)
+#define FAN_PWM_FREQ 0x4
+#define FAN_PWM_CYC_10US GENMASK(25, 15)
+#define FAN_PWM_FREQ_FREQ GENMASK(14, 0)
+#define FAN_CNT 0xc
+#define FAN_CNT_DATA GENMASK(15, 0)
+
+#define LAN966X_PVT_CLK 1200000 /* 1.2 MHz */
+
+struct lan966x_hwmon {
+ struct regmap *regmap_pvt;
+ struct regmap *regmap_fan;
+ struct clk *clk;
+ unsigned long clk_rate;
+};
+
+static int lan966x_hwmon_read_temp(struct device *dev, long *val)
+{
+ struct lan966x_hwmon *hwmon = dev_get_drvdata(dev);
+ unsigned int data;
+ int ret;
+
+ ret = regmap_read(hwmon->regmap_pvt, PVT_SENSOR_STAT, &data);
+ if (ret < 0)
+ return ret;
+
+ if (!(data & SENSOR_STAT_DATA_VALID))
+ return -ENODATA;
+
+ *val = polynomial_calc(&poly_N_to_temp,
+ FIELD_GET(SENSOR_STAT_DATA, data));
+
+ return 0;
+}
+
+static int lan966x_hwmon_read_fan(struct device *dev, long *val)
+{
+ struct lan966x_hwmon *hwmon = dev_get_drvdata(dev);
+ unsigned int data;
+ int ret;
+
+ ret = regmap_read(hwmon->regmap_fan, FAN_CNT, &data);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Data is given in pulses per second. Assume two pulses
+ * per revolution.
+ */
+ *val = FIELD_GET(FAN_CNT_DATA, data) * 60 / 2;
+
+ return 0;
+}
+
+static int lan966x_hwmon_read_pwm(struct device *dev, long *val)
+{
+ struct lan966x_hwmon *hwmon = dev_get_drvdata(dev);
+ unsigned int data;
+ int ret;
+
+ ret = regmap_read(hwmon->regmap_fan, FAN_CFG, &data);
+ if (ret < 0)
+ return ret;
+
+ *val = FIELD_GET(FAN_CFG_DUTY_CYCLE, data);
+
+ return 0;
+}
+
+static int lan966x_hwmon_read_pwm_freq(struct device *dev, long *val)
+{
+ struct lan966x_hwmon *hwmon = dev_get_drvdata(dev);
+ unsigned long tmp;
+ unsigned int data;
+ int ret;
+
+ ret = regmap_read(hwmon->regmap_fan, FAN_PWM_FREQ, &data);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Datasheet says it is sys_clk / 256 / pwm_freq. But in reality
+ * it is sys_clk / 256 / (pwm_freq + 1).
+ */
+ data = FIELD_GET(FAN_PWM_FREQ_FREQ, data) + 1;
+ tmp = DIV_ROUND_CLOSEST(hwmon->clk_rate, 256);
+ *val = DIV_ROUND_CLOSEST(tmp, data);
+
+ return 0;
+}
+
+static int lan966x_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ switch (type) {
+ case hwmon_temp:
+ return lan966x_hwmon_read_temp(dev, val);
+ case hwmon_fan:
+ return lan966x_hwmon_read_fan(dev, val);
+ case hwmon_pwm:
+ switch (attr) {
+ case hwmon_pwm_input:
+ return lan966x_hwmon_read_pwm(dev, val);
+ case hwmon_pwm_freq:
+ return lan966x_hwmon_read_pwm_freq(dev, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int lan966x_hwmon_write_pwm(struct device *dev, long val)
+{
+ struct lan966x_hwmon *hwmon = dev_get_drvdata(dev);
+
+ if (val < 0 || val > 255)
+ return -EINVAL;
+
+ return regmap_update_bits(hwmon->regmap_fan, FAN_CFG,
+ FAN_CFG_DUTY_CYCLE,
+ FIELD_PREP(FAN_CFG_DUTY_CYCLE, val));
+}
+
+static int lan966x_hwmon_write_pwm_freq(struct device *dev, long val)
+{
+ struct lan966x_hwmon *hwmon = dev_get_drvdata(dev);
+
+ if (val <= 0)
+ return -EINVAL;
+
+ val = DIV_ROUND_CLOSEST(hwmon->clk_rate, val);
+ val = DIV_ROUND_CLOSEST(val, 256) - 1;
+ val = clamp_val(val, 0, FAN_PWM_FREQ_FREQ);
+
+ return regmap_update_bits(hwmon->regmap_fan, FAN_PWM_FREQ,
+ FAN_PWM_FREQ_FREQ,
+ FIELD_PREP(FAN_PWM_FREQ_FREQ, val));
+}
+
+static int lan966x_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_pwm:
+ switch (attr) {
+ case hwmon_pwm_input:
+ return lan966x_hwmon_write_pwm(dev, val);
+ case hwmon_pwm_freq:
+ return lan966x_hwmon_write_pwm_freq(dev, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t lan966x_hwmon_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ umode_t mode = 0;
+
+ switch (type) {
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ mode = 0444;
+ break;
+ default:
+ break;
+ }
+ break;
+ case hwmon_fan:
+ switch (attr) {
+ case hwmon_fan_input:
+ mode = 0444;
+ break;
+ default:
+ break;
+ }
+ break;
+ case hwmon_pwm:
+ switch (attr) {
+ case hwmon_pwm_input:
+ case hwmon_pwm_freq:
+ mode = 0644;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return mode;
+}
+
+static const struct hwmon_channel_info *lan966x_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
+ HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
+ HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_FREQ),
+ NULL
+};
+
+static const struct hwmon_ops lan966x_hwmon_ops = {
+ .is_visible = lan966x_hwmon_is_visible,
+ .read = lan966x_hwmon_read,
+ .write = lan966x_hwmon_write,
+};
+
+static const struct hwmon_chip_info lan966x_hwmon_chip_info = {
+ .ops = &lan966x_hwmon_ops,
+ .info = lan966x_hwmon_info,
+};
+
+static void lan966x_hwmon_disable(void *data)
+{
+ struct lan966x_hwmon *hwmon = data;
+
+ regmap_update_bits(hwmon->regmap_pvt, PVT_SENSOR_CFG,
+ SENSOR_CFG_SAMPLE_ENA | SENSOR_CFG_CONTINIOUS_MODE,
+ 0);
+}
+
+static int lan966x_hwmon_enable(struct device *dev,
+ struct lan966x_hwmon *hwmon)
+{
+ unsigned int mask = SENSOR_CFG_CLK_CFG |
+ SENSOR_CFG_SAMPLE_ENA |
+ SENSOR_CFG_START_CAPTURE |
+ SENSOR_CFG_CONTINIOUS_MODE |
+ SENSOR_CFG_PSAMPLE_ENA;
+ unsigned int val;
+ unsigned int div;
+ int ret;
+
+ /* enable continuous mode */
+ val = SENSOR_CFG_SAMPLE_ENA | SENSOR_CFG_CONTINIOUS_MODE;
+
+ /* set PVT clock to be between 1.15 and 1.25 MHz */
+ div = DIV_ROUND_CLOSEST(hwmon->clk_rate, LAN966X_PVT_CLK);
+ val |= FIELD_PREP(SENSOR_CFG_CLK_CFG, div);
+
+ ret = regmap_update_bits(hwmon->regmap_pvt, PVT_SENSOR_CFG,
+ mask, val);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, lan966x_hwmon_disable, hwmon);
+}
+
+static struct regmap *lan966x_init_regmap(struct platform_device *pdev,
+ const char *name)
+{
+ struct regmap_config regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ };
+ void __iomem *base;
+
+ base = devm_platform_ioremap_resource_byname(pdev, name);
+ if (IS_ERR(base))
+ return ERR_CAST(base);
+
+ regmap_config.name = name;
+
+ return devm_regmap_init_mmio(&pdev->dev, base, &regmap_config);
+}
+
+static void lan966x_clk_disable(void *data)
+{
+ struct lan966x_hwmon *hwmon = data;
+
+ clk_disable_unprepare(hwmon->clk);
+}
+
+static int lan966x_clk_enable(struct device *dev, struct lan966x_hwmon *hwmon)
+{
+ int ret;
+
+ ret = clk_prepare_enable(hwmon->clk);
+ if (ret)
+ return ret;
+
+ return devm_add_action_or_reset(dev, lan966x_clk_disable, hwmon);
+}
+
+static int lan966x_hwmon_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct lan966x_hwmon *hwmon;
+ struct device *hwmon_dev;
+ int ret;
+
+ hwmon = devm_kzalloc(dev, sizeof(*hwmon), GFP_KERNEL);
+ if (!hwmon)
+ return -ENOMEM;
+
+ hwmon->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(hwmon->clk))
+ return dev_err_probe(dev, PTR_ERR(hwmon->clk),
+ "failed to get clock\n");
+
+ ret = lan966x_clk_enable(dev, hwmon);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable clock\n");
+
+ hwmon->clk_rate = clk_get_rate(hwmon->clk);
+
+ hwmon->regmap_pvt = lan966x_init_regmap(pdev, "pvt");
+ if (IS_ERR(hwmon->regmap_pvt))
+ return dev_err_probe(dev, PTR_ERR(hwmon->regmap_pvt),
+ "failed to get regmap for PVT registers\n");
+
+ hwmon->regmap_fan = lan966x_init_regmap(pdev, "fan");
+ if (IS_ERR(hwmon->regmap_fan))
+ return dev_err_probe(dev, PTR_ERR(hwmon->regmap_fan),
+ "failed to get regmap for fan registers\n");
+
+ ret = lan966x_hwmon_enable(dev, hwmon);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable sensor\n");
+
+ hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,
+ "lan966x_hwmon", hwmon,
+ &lan966x_hwmon_chip_info, NULL);
+ if (IS_ERR(hwmon_dev))
+ return dev_err_probe(dev, PTR_ERR(hwmon_dev),
+ "failed to register hwmon device\n");
+
+ return 0;
+}
+
+static const struct of_device_id lan966x_hwmon_of_match[] = {
+ { .compatible = "microchip,lan9668-hwmon" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, lan966x_hwmon_of_match);
+
+static struct platform_driver lan966x_hwmon_driver = {
+ .probe = lan966x_hwmon_probe,
+ .driver = {
+ .name = "lan966x-hwmon",
+ .of_match_table = lan966x_hwmon_of_match,
+ },
+};
+module_platform_driver(lan966x_hwmon_driver);
+
+MODULE_DESCRIPTION("LAN966x Hardware Monitoring Driver");
+MODULE_AUTHOR("Michael Walle <michael@walle.cc>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
index afdbb63237b9..66dc826f7962 100644
--- a/drivers/hwmon/lm75.c
+++ b/drivers/hwmon/lm75.c
@@ -26,6 +26,7 @@
enum lm75_type { /* keep sorted in alphabetical order */
adt75,
+ at30ts74,
ds1775,
ds75,
ds7505,
@@ -128,6 +129,14 @@ static const struct lm75_params device_params[] = {
.default_resolution = 12,
.default_sample_time = MSEC_PER_SEC / 10,
},
+ [at30ts74] = {
+ .set_mask = 3 << 5, /* 12-bit mode*/
+ .default_resolution = 12,
+ .default_sample_time = 200,
+ .num_sample_times = 4,
+ .sample_times = (unsigned int []){ 25, 50, 100, 200 },
+ .resolutions = (u8 []) {9, 10, 11, 12 },
+ },
[ds1775] = {
.clr_mask = 3 << 5,
.set_mask = 2 << 5, /* 11-bit mode */
@@ -645,6 +654,7 @@ static int lm75_probe(struct i2c_client *client)
static const struct i2c_device_id lm75_ids[] = {
{ "adt75", adt75, },
+ { "at30ts74", at30ts74, },
{ "ds1775", ds1775, },
{ "ds75", ds75, },
{ "ds7505", ds7505, },
@@ -681,6 +691,10 @@ static const struct of_device_id __maybe_unused lm75_of_match[] = {
.data = (void *)adt75
},
{
+ .compatible = "atmel,at30ts74",
+ .data = (void *)at30ts74
+ },
+ {
.compatible = "dallas,ds1775",
.data = (void *)ds1775
},
diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c
index 12370dcefa6a..905f5689f907 100644
--- a/drivers/hwmon/lm83.c
+++ b/drivers/hwmon/lm83.c
@@ -24,10 +24,8 @@
#include <linux/init.h>
#include <linux/hwmon.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/slab.h>
-#include <linux/sysfs.h>
/*
* Addresses to scan
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 1c9493c70813..3820f0e61510 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -1707,6 +1707,7 @@ static void lm90_restore_conf(void *_data)
static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
{
+ struct device_node *np = client->dev.of_node;
int config, convrate;
convrate = lm90_read_reg(client, LM90_REG_R_CONVRATE);
@@ -1727,6 +1728,9 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
/* Check Temperature Range Select */
if (data->flags & LM90_HAVE_EXTENDED_TEMP) {
+ if (of_property_read_bool(np, "ti,extended-range-enable"))
+ config |= 0x04;
+
if (config & 0x04)
data->flags |= LM90_FLAG_ADT7461_EXT;
}
diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c
index 7352d2b3c756..72489d5d7eaf 100644
--- a/drivers/hwmon/ltc2992.c
+++ b/drivers/hwmon/ltc2992.c
@@ -811,68 +811,32 @@ static const struct hwmon_ops ltc2992_hwmon_ops = {
.write = ltc2992_write,
};
-static const u32 ltc2992_chip_config[] = {
- HWMON_C_IN_RESET_HISTORY,
- 0
-};
-
-static const struct hwmon_channel_info ltc2992_chip = {
- .type = hwmon_chip,
- .config = ltc2992_chip_config,
-};
-
-static const u32 ltc2992_in_config[] = {
- HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | HWMON_I_MAX |
- HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
- HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | HWMON_I_MAX |
- HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
- HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | HWMON_I_MAX |
- HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
- HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | HWMON_I_MAX |
- HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
- HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | HWMON_I_MAX |
- HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
- HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | HWMON_I_MAX |
- HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
- 0
-};
-
-static const struct hwmon_channel_info ltc2992_in = {
- .type = hwmon_in,
- .config = ltc2992_in_config,
-};
-
-static const u32 ltc2992_curr_config[] = {
- HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST | HWMON_C_MIN | HWMON_C_MAX |
- HWMON_C_MIN_ALARM | HWMON_C_MAX_ALARM,
- HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST | HWMON_C_MIN | HWMON_C_MAX |
- HWMON_C_MIN_ALARM | HWMON_C_MAX_ALARM,
- 0
-};
-
-static const struct hwmon_channel_info ltc2992_curr = {
- .type = hwmon_curr,
- .config = ltc2992_curr_config,
-};
-
-static const u32 ltc2992_power_config[] = {
- HWMON_P_INPUT | HWMON_P_INPUT_LOWEST | HWMON_P_INPUT_HIGHEST | HWMON_P_MIN | HWMON_P_MAX |
- HWMON_P_MIN_ALARM | HWMON_P_MAX_ALARM,
- HWMON_P_INPUT | HWMON_P_INPUT_LOWEST | HWMON_P_INPUT_HIGHEST | HWMON_P_MIN | HWMON_P_MAX |
- HWMON_P_MIN_ALARM | HWMON_P_MAX_ALARM,
- 0
-};
-
-static const struct hwmon_channel_info ltc2992_power = {
- .type = hwmon_power,
- .config = ltc2992_power_config,
-};
-
static const struct hwmon_channel_info *ltc2992_info[] = {
- &ltc2992_chip,
- &ltc2992_in,
- &ltc2992_curr,
- &ltc2992_power,
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_IN_RESET_HISTORY),
+ HWMON_CHANNEL_INFO(in,
+ HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN |
+ HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
+ HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN |
+ HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
+ HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN |
+ HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
+ HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN |
+ HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
+ HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN |
+ HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
+ HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN |
+ HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM),
+ HWMON_CHANNEL_INFO(curr,
+ HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST | HWMON_C_MIN |
+ HWMON_C_MAX | HWMON_C_MIN_ALARM | HWMON_C_MAX_ALARM,
+ HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST | HWMON_C_MIN |
+ HWMON_C_MAX | HWMON_C_MIN_ALARM | HWMON_C_MAX_ALARM),
+ HWMON_CHANNEL_INFO(power,
+ HWMON_P_INPUT | HWMON_P_INPUT_LOWEST | HWMON_P_INPUT_HIGHEST |
+ HWMON_P_MIN | HWMON_P_MAX | HWMON_P_MIN_ALARM | HWMON_P_MAX_ALARM,
+ HWMON_P_INPUT | HWMON_P_INPUT_LOWEST | HWMON_P_INPUT_HIGHEST |
+ HWMON_P_MIN | HWMON_P_MAX | HWMON_P_MIN_ALARM | HWMON_P_MAX_ALARM),
NULL
};
diff --git a/drivers/hwmon/mr75203.c b/drivers/hwmon/mr75203.c
index 1ba1e3145969..26278b0f17a9 100644
--- a/drivers/hwmon/mr75203.c
+++ b/drivers/hwmon/mr75203.c
@@ -223,16 +223,6 @@ static int pvt_read(struct device *dev, enum hwmon_sensor_types type,
}
}
-static const u32 pvt_chip_config[] = {
- HWMON_C_REGISTER_TZ,
- 0
-};
-
-static const struct hwmon_channel_info pvt_chip = {
- .type = hwmon_chip,
- .config = pvt_chip_config,
-};
-
static struct hwmon_channel_info pvt_temp = {
.type = hwmon_temp,
};
@@ -555,7 +545,7 @@ static int mr75203_probe(struct platform_device *pdev)
pvt_info = devm_kcalloc(dev, val + 2, sizeof(*pvt_info), GFP_KERNEL);
if (!pvt_info)
return -ENOMEM;
- pvt_info[0] = &pvt_chip;
+ pvt_info[0] = HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ);
index = 1;
if (ts_num) {
diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775-core.c
index 2b91f7e05126..446964cbae4c 100644
--- a/drivers/hwmon/nct6775.c
+++ b/drivers/hwmon/nct6775-core.c
@@ -44,24 +44,20 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
-#include <linux/platform_device.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
-#include <linux/hwmon-vid.h>
#include <linux/err.h>
#include <linux/mutex.h>
-#include <linux/acpi.h>
#include <linux/bitops.h>
-#include <linux/dmi.h>
-#include <linux/io.h>
#include <linux/nospec.h>
-#include <linux/wmi.h>
+#include <linux/regmap.h>
#include "lm75.h"
+#include "nct6775.h"
-#define USE_ALTERNATE
+#undef DEFAULT_SYMBOL_NAMESPACE
+#define DEFAULT_SYMBOL_NAMESPACE HWMON_NCT6775
-enum kinds { nct6106, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792,
- nct6793, nct6795, nct6796, nct6797, nct6798 };
+#define USE_ALTERNATE
/* used to set data->name = nct6775_device_names[data->sio_kind] */
static const char * const nct6775_device_names[] = {
@@ -79,242 +75,6 @@ static const char * const nct6775_device_names[] = {
"nct6798",
};
-static const char * const nct6775_sio_names[] __initconst = {
- "NCT6106D",
- "NCT6116D",
- "NCT6775F",
- "NCT6776D/F",
- "NCT6779D",
- "NCT6791D",
- "NCT6792D",
- "NCT6793D",
- "NCT6795D",
- "NCT6796D",
- "NCT6797D",
- "NCT6798D",
-};
-
-static unsigned short force_id;
-module_param(force_id, ushort, 0);
-MODULE_PARM_DESC(force_id, "Override the detected device ID");
-
-static unsigned short fan_debounce;
-module_param(fan_debounce, ushort, 0);
-MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
-
-#define DRVNAME "nct6775"
-
-/*
- * Super-I/O constants and functions
- */
-
-#define NCT6775_LD_ACPI 0x0a
-#define NCT6775_LD_HWM 0x0b
-#define NCT6775_LD_VID 0x0d
-#define NCT6775_LD_12 0x12
-
-#define SIO_REG_LDSEL 0x07 /* Logical device select */
-#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
-#define SIO_REG_ENABLE 0x30 /* Logical device enable */
-#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
-
-#define SIO_NCT6106_ID 0xc450
-#define SIO_NCT6116_ID 0xd280
-#define SIO_NCT6775_ID 0xb470
-#define SIO_NCT6776_ID 0xc330
-#define SIO_NCT6779_ID 0xc560
-#define SIO_NCT6791_ID 0xc800
-#define SIO_NCT6792_ID 0xc910
-#define SIO_NCT6793_ID 0xd120
-#define SIO_NCT6795_ID 0xd350
-#define SIO_NCT6796_ID 0xd420
-#define SIO_NCT6797_ID 0xd450
-#define SIO_NCT6798_ID 0xd428
-#define SIO_ID_MASK 0xFFF8
-
-enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };
-enum sensor_access { access_direct, access_asuswmi };
-
-struct nct6775_sio_data {
- int sioreg;
- int ld;
- enum kinds kind;
- enum sensor_access access;
-
- /* superio_() callbacks */
- void (*sio_outb)(struct nct6775_sio_data *sio_data, int reg, int val);
- int (*sio_inb)(struct nct6775_sio_data *sio_data, int reg);
- void (*sio_select)(struct nct6775_sio_data *sio_data, int ld);
- int (*sio_enter)(struct nct6775_sio_data *sio_data);
- void (*sio_exit)(struct nct6775_sio_data *sio_data);
-};
-
-#define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66"
-#define ASUSWMI_METHODID_RSIO 0x5253494F
-#define ASUSWMI_METHODID_WSIO 0x5753494F
-#define ASUSWMI_METHODID_RHWM 0x5248574D
-#define ASUSWMI_METHODID_WHWM 0x5748574D
-#define ASUSWMI_UNSUPPORTED_METHOD 0xFFFFFFFE
-
-static int nct6775_asuswmi_evaluate_method(u32 method_id, u8 bank, u8 reg, u8 val, u32 *retval)
-{
-#if IS_ENABLED(CONFIG_ACPI_WMI)
- u32 args = bank | (reg << 8) | (val << 16);
- struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
- struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
- acpi_status status;
- union acpi_object *obj;
- u32 tmp = ASUSWMI_UNSUPPORTED_METHOD;
-
- status = wmi_evaluate_method(ASUSWMI_MONITORING_GUID, 0,
- method_id, &input, &output);
-
- if (ACPI_FAILURE(status))
- return -EIO;
-
- obj = output.pointer;
- if (obj && obj->type == ACPI_TYPE_INTEGER)
- tmp = obj->integer.value;
-
- if (retval)
- *retval = tmp;
-
- kfree(obj);
-
- if (tmp == ASUSWMI_UNSUPPORTED_METHOD)
- return -ENODEV;
- return 0;
-#else
- return -EOPNOTSUPP;
-#endif
-}
-
-static inline int nct6775_asuswmi_write(u8 bank, u8 reg, u8 val)
-{
- return nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_WHWM, bank,
- reg, val, NULL);
-}
-
-static inline int nct6775_asuswmi_read(u8 bank, u8 reg, u8 *val)
-{
- u32 ret, tmp = 0;
-
- ret = nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_RHWM, bank,
- reg, 0, &tmp);
- *val = tmp;
- return ret;
-}
-
-static int superio_wmi_inb(struct nct6775_sio_data *sio_data, int reg)
-{
- int tmp = 0;
-
- nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_RSIO, sio_data->ld,
- reg, 0, &tmp);
- return tmp;
-}
-
-static void superio_wmi_outb(struct nct6775_sio_data *sio_data, int reg, int val)
-{
- nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_WSIO, sio_data->ld,
- reg, val, NULL);
-}
-
-static void superio_wmi_select(struct nct6775_sio_data *sio_data, int ld)
-{
- sio_data->ld = ld;
-}
-
-static int superio_wmi_enter(struct nct6775_sio_data *sio_data)
-{
- return 0;
-}
-
-static void superio_wmi_exit(struct nct6775_sio_data *sio_data)
-{
-}
-
-static void superio_outb(struct nct6775_sio_data *sio_data, int reg, int val)
-{
- int ioreg = sio_data->sioreg;
-
- outb(reg, ioreg);
- outb(val, ioreg + 1);
-}
-
-static int superio_inb(struct nct6775_sio_data *sio_data, int reg)
-{
- int ioreg = sio_data->sioreg;
-
- outb(reg, ioreg);
- return inb(ioreg + 1);
-}
-
-static void superio_select(struct nct6775_sio_data *sio_data, int ld)
-{
- int ioreg = sio_data->sioreg;
-
- outb(SIO_REG_LDSEL, ioreg);
- outb(ld, ioreg + 1);
-}
-
-static int superio_enter(struct nct6775_sio_data *sio_data)
-{
- int ioreg = sio_data->sioreg;
-
- /*
- * Try to reserve <ioreg> and <ioreg + 1> for exclusive access.
- */
- if (!request_muxed_region(ioreg, 2, DRVNAME))
- return -EBUSY;
-
- outb(0x87, ioreg);
- outb(0x87, ioreg);
-
- return 0;
-}
-
-static void superio_exit(struct nct6775_sio_data *sio_data)
-{
- int ioreg = sio_data->sioreg;
-
- outb(0xaa, ioreg);
- outb(0x02, ioreg);
- outb(0x02, ioreg + 1);
- release_region(ioreg, 2);
-}
-
-/*
- * ISA constants
- */
-
-#define IOREGION_ALIGNMENT (~7)
-#define IOREGION_OFFSET 5
-#define IOREGION_LENGTH 2
-#define ADDR_REG_OFFSET 0
-#define DATA_REG_OFFSET 1
-
-#define NCT6775_REG_BANK 0x4E
-#define NCT6775_REG_CONFIG 0x40
-#define NCT6775_PORT_CHIPID 0x58
-
-/*
- * Not currently used:
- * REG_MAN_ID has the value 0x5ca3 for all supported chips.
- * REG_CHIP_ID == 0x88/0xa1/0xc1 depending on chip model.
- * REG_MAN_ID is at port 0x4f
- * REG_CHIP_ID is at port 0x58
- */
-
-#define NUM_TEMP 10 /* Max number of temp attribute sets w/ limits*/
-#define NUM_TEMP_FIXED 6 /* Max number of fixed temp attribute sets */
-#define NUM_TSI_TEMP 8 /* Max number of TSI temp register pairs */
-
-#define NUM_REG_ALARM 7 /* Max number of alarm registers */
-#define NUM_REG_BEEP 5 /* Max number of beep registers */
-
-#define NUM_FAN 7
-
/* Common and NCT6775 specific data */
/* Voltage min/max registers for nr=7..14 are in bank 5 */
@@ -333,11 +93,6 @@ static const u16 NCT6775_REG_IN[] = {
#define NCT6775_REG_DIODE 0x5E
#define NCT6775_DIODE_MASK 0x02
-#define NCT6775_REG_FANDIV1 0x506
-#define NCT6775_REG_FANDIV2 0x507
-
-#define NCT6775_REG_CR_FAN_DEBOUNCE 0xf0
-
static const u16 NCT6775_REG_ALARM[NUM_REG_ALARM] = { 0x459, 0x45A, 0x45B };
/* 0..15 voltages, 16..23 fans, 24..29 temperatures, 30..31 intrusion */
@@ -351,10 +106,6 @@ static const s8 NCT6775_ALARM_BITS[] = {
4, 5, 13, -1, -1, -1, /* temp1..temp6 */
12, -1 }; /* intrusion0, intrusion1 */
-#define FAN_ALARM_BASE 16
-#define TEMP_ALARM_BASE 24
-#define INTRUSION_ALARM_BASE 30
-
static const u16 NCT6775_REG_BEEP[NUM_REG_BEEP] = { 0x56, 0x57, 0x453, 0x4e };
/*
@@ -370,11 +121,6 @@ static const s8 NCT6775_BEEP_BITS[] = {
4, 5, 13, -1, -1, -1, /* temp1..temp6 */
12, -1 }; /* intrusion0, intrusion1 */
-#define BEEP_ENABLE_BASE 15
-
-static const u8 NCT6775_REG_CR_CASEOPEN_CLR[] = { 0xe6, 0xee };
-static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 };
-
/* DC or PWM output fan configuration */
static const u8 NCT6775_REG_PWM_MODE[] = { 0x04, 0x04, 0x12 };
static const u8 NCT6775_PWM_MODE_MASK[] = { 0x01, 0x02, 0x01 };
@@ -690,8 +436,6 @@ static const u16 NCT6779_REG_TEMP_CRIT[32] = {
/* NCT6791 specific data */
-#define NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE 0x28
-
static const u16 NCT6791_REG_WEIGHT_TEMP_SEL[NUM_FAN] = { 0, 0x239 };
static const u16 NCT6791_REG_WEIGHT_TEMP_STEP[NUM_FAN] = { 0, 0x23a };
static const u16 NCT6791_REG_WEIGHT_TEMP_STEP_TOL[NUM_FAN] = { 0, 0x23b };
@@ -1191,165 +935,6 @@ static inline unsigned int tsi_temp_from_reg(unsigned int reg)
* Data structures and manipulation thereof
*/
-struct nct6775_data {
- int addr; /* IO base of hw monitor block */
- struct nct6775_sio_data *sio_data;
- enum kinds kind;
- const char *name;
-
- const struct attribute_group *groups[7];
-
- u16 reg_temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst,
- * 3=temp_crit, 4=temp_lcrit
- */
- u8 temp_src[NUM_TEMP];
- u16 reg_temp_config[NUM_TEMP];
- const char * const *temp_label;
- u32 temp_mask;
- u32 virt_temp_mask;
-
- u16 REG_CONFIG;
- u16 REG_VBAT;
- u16 REG_DIODE;
- u8 DIODE_MASK;
-
- const s8 *ALARM_BITS;
- const s8 *BEEP_BITS;
-
- const u16 *REG_VIN;
- const u16 *REG_IN_MINMAX[2];
-
- const u16 *REG_TARGET;
- const u16 *REG_FAN;
- const u16 *REG_FAN_MODE;
- const u16 *REG_FAN_MIN;
- const u16 *REG_FAN_PULSES;
- const u16 *FAN_PULSE_SHIFT;
- const u16 *REG_FAN_TIME[3];
-
- const u16 *REG_TOLERANCE_H;
-
- const u8 *REG_PWM_MODE;
- const u8 *PWM_MODE_MASK;
-
- const u16 *REG_PWM[7]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor,
- * [3]=pwm_max, [4]=pwm_step,
- * [5]=weight_duty_step, [6]=weight_duty_base
- */
- const u16 *REG_PWM_READ;
-
- const u16 *REG_CRITICAL_PWM_ENABLE;
- u8 CRITICAL_PWM_ENABLE_MASK;
- const u16 *REG_CRITICAL_PWM;
-
- const u16 *REG_AUTO_TEMP;
- const u16 *REG_AUTO_PWM;
-
- const u16 *REG_CRITICAL_TEMP;
- const u16 *REG_CRITICAL_TEMP_TOLERANCE;
-
- const u16 *REG_TEMP_SOURCE; /* temp register sources */
- const u16 *REG_TEMP_SEL;
- const u16 *REG_WEIGHT_TEMP_SEL;
- const u16 *REG_WEIGHT_TEMP[3]; /* 0=base, 1=tolerance, 2=step */
-
- const u16 *REG_TEMP_OFFSET;
-
- const u16 *REG_ALARM;
- const u16 *REG_BEEP;
-
- const u16 *REG_TSI_TEMP;
-
- unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg);
- unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg);
-
- struct mutex update_lock;
- bool valid; /* true if following fields are valid */
- unsigned long last_updated; /* In jiffies */
-
- /* Register values */
- u8 bank; /* current register bank */
- u8 in_num; /* number of in inputs we have */
- u8 in[15][3]; /* [0]=in, [1]=in_max, [2]=in_min */
- unsigned int rpm[NUM_FAN];
- u16 fan_min[NUM_FAN];
- u8 fan_pulses[NUM_FAN];
- u8 fan_div[NUM_FAN];
- u8 has_pwm;
- u8 has_fan; /* some fan inputs can be disabled */
- u8 has_fan_min; /* some fans don't have min register */
- bool has_fan_div;
-
- u8 num_temp_alarms; /* 2, 3, or 6 */
- u8 num_temp_beeps; /* 2, 3, or 6 */
- u8 temp_fixed_num; /* 3 or 6 */
- u8 temp_type[NUM_TEMP_FIXED];
- s8 temp_offset[NUM_TEMP_FIXED];
- s16 temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst,
- * 3=temp_crit, 4=temp_lcrit */
- s16 tsi_temp[NUM_TSI_TEMP];
- u64 alarms;
- u64 beeps;
-
- u8 pwm_num; /* number of pwm */
- u8 pwm_mode[NUM_FAN]; /* 0->DC variable voltage,
- * 1->PWM variable duty cycle
- */
- enum pwm_enable pwm_enable[NUM_FAN];
- /* 0->off
- * 1->manual
- * 2->thermal cruise mode (also called SmartFan I)
- * 3->fan speed cruise mode
- * 4->SmartFan III
- * 5->enhanced variable thermal cruise (SmartFan IV)
- */
- u8 pwm[7][NUM_FAN]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor,
- * [3]=pwm_max, [4]=pwm_step,
- * [5]=weight_duty_step, [6]=weight_duty_base
- */
-
- u8 target_temp[NUM_FAN];
- u8 target_temp_mask;
- u32 target_speed[NUM_FAN];
- u32 target_speed_tolerance[NUM_FAN];
- u8 speed_tolerance_limit;
-
- u8 temp_tolerance[2][NUM_FAN];
- u8 tolerance_mask;
-
- u8 fan_time[3][NUM_FAN]; /* 0 = stop_time, 1 = step_up, 2 = step_down */
-
- /* Automatic fan speed control registers */
- int auto_pwm_num;
- u8 auto_pwm[NUM_FAN][7];
- u8 auto_temp[NUM_FAN][7];
- u8 pwm_temp_sel[NUM_FAN];
- u8 pwm_weight_temp_sel[NUM_FAN];
- u8 weight_temp[3][NUM_FAN]; /* 0->temp_step, 1->temp_step_tol,
- * 2->temp_base
- */
-
- u8 vid;
- u8 vrm;
-
- bool have_vid;
-
- u16 have_temp;
- u16 have_temp_fixed;
- u16 have_tsi_temp;
- u16 have_in;
-
- /* Remember extra register values over suspend/resume */
- u8 vbat;
- u8 fandiv1;
- u8 fandiv2;
- u8 sio_reg_enable;
-
- /* nct6775_*() callbacks */
- u16 (*read_value)(struct nct6775_data *data, u16 reg);
- int (*write_value)(struct nct6775_data *data, u16 reg, u16 value);
-};
-
struct sensor_device_template {
struct device_attribute dev_attr;
union {
@@ -1405,10 +990,8 @@ struct sensor_template_group {
int base;
};
-static struct attribute_group *
-nct6775_create_attr_group(struct device *dev,
- const struct sensor_template_group *tg,
- int repeat)
+static int nct6775_add_template_attr_group(struct device *dev, struct nct6775_data *data,
+ const struct sensor_template_group *tg, int repeat)
{
struct attribute_group *group;
struct sensor_device_attr_u *su;
@@ -1419,28 +1002,28 @@ nct6775_create_attr_group(struct device *dev,
int i, count;
if (repeat <= 0)
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
t = tg->templates;
for (count = 0; *t; t++, count++)
;
if (count == 0)
- return ERR_PTR(-EINVAL);
+ return -EINVAL;
group = devm_kzalloc(dev, sizeof(*group), GFP_KERNEL);
if (group == NULL)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
attrs = devm_kcalloc(dev, repeat * count + 1, sizeof(*attrs),
GFP_KERNEL);
if (attrs == NULL)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
su = devm_kzalloc(dev, array3_size(repeat, count, sizeof(*su)),
GFP_KERNEL);
if (su == NULL)
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
group->attrs = attrs;
group->is_visible = tg->is_visible;
@@ -1478,10 +1061,10 @@ nct6775_create_attr_group(struct device *dev,
}
}
- return group;
+ return nct6775_add_attr_group(data, group);
}
-static bool is_word_sized(struct nct6775_data *data, u16 reg)
+bool nct6775_reg_is_word_sized(struct nct6775_data *data, u16 reg)
{
switch (data->kind) {
case nct6106:
@@ -1538,180 +1121,81 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg)
}
return false;
}
+EXPORT_SYMBOL_GPL(nct6775_reg_is_word_sized);
-static inline void nct6775_wmi_set_bank(struct nct6775_data *data, u16 reg)
-{
- u8 bank = reg >> 8;
-
- data->bank = bank;
-}
-
-static u16 nct6775_wmi_read_value(struct nct6775_data *data, u16 reg)
+/* We left-align 8-bit temperature values to make the code simpler */
+static int nct6775_read_temp(struct nct6775_data *data, u16 reg, u16 *val)
{
- int res, err, word_sized = is_word_sized(data, reg);
- u8 tmp = 0;
-
- nct6775_wmi_set_bank(data, reg);
+ int err;
- err = nct6775_asuswmi_read(data->bank, reg & 0xff, &tmp);
+ err = nct6775_read_value(data, reg, val);
if (err)
- return 0;
-
- res = tmp;
- if (word_sized) {
- err = nct6775_asuswmi_read(data->bank, (reg & 0xff) + 1, &tmp);
- if (err)
- return 0;
-
- res = (res << 8) + tmp;
- }
- return res;
-}
-
-static int nct6775_wmi_write_value(struct nct6775_data *data, u16 reg, u16 value)
-{
- int res, word_sized = is_word_sized(data, reg);
-
- nct6775_wmi_set_bank(data, reg);
-
- if (word_sized) {
- res = nct6775_asuswmi_write(data->bank, reg & 0xff, value >> 8);
- if (res)
- return res;
-
- res = nct6775_asuswmi_write(data->bank, (reg & 0xff) + 1, value);
- } else {
- res = nct6775_asuswmi_write(data->bank, reg & 0xff, value);
- }
-
- return res;
-}
-
-/*
- * On older chips, only registers 0x50-0x5f are banked.
- * On more recent chips, all registers are banked.
- * Assume that is the case and set the bank number for each access.
- * Cache the bank number so it only needs to be set if it changes.
- */
-static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg)
-{
- u8 bank = reg >> 8;
-
- if (data->bank != bank) {
- outb_p(NCT6775_REG_BANK, data->addr + ADDR_REG_OFFSET);
- outb_p(bank, data->addr + DATA_REG_OFFSET);
- data->bank = bank;
- }
-}
+ return err;
-static u16 nct6775_read_value(struct nct6775_data *data, u16 reg)
-{
- int res, word_sized = is_word_sized(data, reg);
-
- nct6775_set_bank(data, reg);
- outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET);
- res = inb_p(data->addr + DATA_REG_OFFSET);
- if (word_sized) {
- outb_p((reg & 0xff) + 1,
- data->addr + ADDR_REG_OFFSET);
- res = (res << 8) + inb_p(data->addr + DATA_REG_OFFSET);
- }
- return res;
-}
+ if (!nct6775_reg_is_word_sized(data, reg))
+ *val <<= 8;
-static int nct6775_write_value(struct nct6775_data *data, u16 reg, u16 value)
-{
- int word_sized = is_word_sized(data, reg);
-
- nct6775_set_bank(data, reg);
- outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET);
- if (word_sized) {
- outb_p(value >> 8, data->addr + DATA_REG_OFFSET);
- outb_p((reg & 0xff) + 1,
- data->addr + ADDR_REG_OFFSET);
- }
- outb_p(value & 0xff, data->addr + DATA_REG_OFFSET);
return 0;
}
-/* We left-align 8-bit temperature values to make the code simpler */
-static u16 nct6775_read_temp(struct nct6775_data *data, u16 reg)
-{
- u16 res;
-
- res = data->read_value(data, reg);
- if (!is_word_sized(data, reg))
- res <<= 8;
-
- return res;
-}
-
-static int nct6775_write_temp(struct nct6775_data *data, u16 reg, u16 value)
-{
- if (!is_word_sized(data, reg))
- value >>= 8;
- return data->write_value(data, reg, value);
-}
-
/* This function assumes that the caller holds data->update_lock */
-static void nct6775_write_fan_div(struct nct6775_data *data, int nr)
+static int nct6775_write_fan_div(struct nct6775_data *data, int nr)
{
- u8 reg;
+ u16 reg;
+ int err;
+ u16 fandiv_reg = nr < 2 ? NCT6775_REG_FANDIV1 : NCT6775_REG_FANDIV2;
+ unsigned int oddshift = (nr & 1) * 4; /* masks shift by four if nr is odd */
- switch (nr) {
- case 0:
- reg = (data->read_value(data, NCT6775_REG_FANDIV1) & 0x70)
- | (data->fan_div[0] & 0x7);
- data->write_value(data, NCT6775_REG_FANDIV1, reg);
- break;
- case 1:
- reg = (data->read_value(data, NCT6775_REG_FANDIV1) & 0x7)
- | ((data->fan_div[1] << 4) & 0x70);
- data->write_value(data, NCT6775_REG_FANDIV1, reg);
- break;
- case 2:
- reg = (data->read_value(data, NCT6775_REG_FANDIV2) & 0x70)
- | (data->fan_div[2] & 0x7);
- data->write_value(data, NCT6775_REG_FANDIV2, reg);
- break;
- case 3:
- reg = (data->read_value(data, NCT6775_REG_FANDIV2) & 0x7)
- | ((data->fan_div[3] << 4) & 0x70);
- data->write_value(data, NCT6775_REG_FANDIV2, reg);
- break;
- }
+ err = nct6775_read_value(data, fandiv_reg, &reg);
+ if (err)
+ return err;
+ reg &= 0x70 >> oddshift;
+ reg |= data->fan_div[nr] & (0x7 << oddshift);
+ return nct6775_write_value(data, fandiv_reg, reg);
}
-static void nct6775_write_fan_div_common(struct nct6775_data *data, int nr)
+static int nct6775_write_fan_div_common(struct nct6775_data *data, int nr)
{
if (data->kind == nct6775)
- nct6775_write_fan_div(data, nr);
+ return nct6775_write_fan_div(data, nr);
+ return 0;
}
-static void nct6775_update_fan_div(struct nct6775_data *data)
+static int nct6775_update_fan_div(struct nct6775_data *data)
{
- u8 i;
+ int err;
+ u16 i;
- i = data->read_value(data, NCT6775_REG_FANDIV1);
+ err = nct6775_read_value(data, NCT6775_REG_FANDIV1, &i);
+ if (err)
+ return err;
data->fan_div[0] = i & 0x7;
data->fan_div[1] = (i & 0x70) >> 4;
- i = data->read_value(data, NCT6775_REG_FANDIV2);
+ err = nct6775_read_value(data, NCT6775_REG_FANDIV2, &i);
+ if (err)
+ return err;
data->fan_div[2] = i & 0x7;
if (data->has_fan & BIT(3))
data->fan_div[3] = (i & 0x70) >> 4;
+
+ return 0;
}
-static void nct6775_update_fan_div_common(struct nct6775_data *data)
+static int nct6775_update_fan_div_common(struct nct6775_data *data)
{
if (data->kind == nct6775)
- nct6775_update_fan_div(data);
+ return nct6775_update_fan_div(data);
+ return 0;
}
-static void nct6775_init_fan_div(struct nct6775_data *data)
+static int nct6775_init_fan_div(struct nct6775_data *data)
{
- int i;
+ int i, err;
+
+ err = nct6775_update_fan_div_common(data);
+ if (err)
+ return err;
- nct6775_update_fan_div_common(data);
/*
* For all fans, start with highest divider value if the divider
* register is not initialized. This ensures that we get a
@@ -1723,19 +1207,26 @@ static void nct6775_init_fan_div(struct nct6775_data *data)
continue;
if (data->fan_div[i] == 0) {
data->fan_div[i] = 7;
- nct6775_write_fan_div_common(data, i);
+ err = nct6775_write_fan_div_common(data, i);
+ if (err)
+ return err;
}
}
+
+ return 0;
}
-static void nct6775_init_fan_common(struct device *dev,
- struct nct6775_data *data)
+static int nct6775_init_fan_common(struct device *dev,
+ struct nct6775_data *data)
{
- int i;
- u8 reg;
+ int i, err;
+ u16 reg;
- if (data->has_fan_div)
- nct6775_init_fan_div(data);
+ if (data->has_fan_div) {
+ err = nct6775_init_fan_div(data);
+ if (err)
+ return err;
+ }
/*
* If fan_min is not set (0), set it to 0xff to disable it. This
@@ -1743,23 +1234,30 @@ static void nct6775_init_fan_common(struct device *dev,
*/
for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) {
if (data->has_fan_min & BIT(i)) {
- reg = data->read_value(data, data->REG_FAN_MIN[i]);
- if (!reg)
- data->write_value(data, data->REG_FAN_MIN[i],
- data->has_fan_div ? 0xff
- : 0xff1f);
+ err = nct6775_read_value(data, data->REG_FAN_MIN[i], &reg);
+ if (err)
+ return err;
+ if (!reg) {
+ err = nct6775_write_value(data, data->REG_FAN_MIN[i],
+ data->has_fan_div ? 0xff : 0xff1f);
+ if (err)
+ return err;
+ }
}
}
+
+ return 0;
}
-static void nct6775_select_fan_div(struct device *dev,
- struct nct6775_data *data, int nr, u16 reg)
+static int nct6775_select_fan_div(struct device *dev,
+ struct nct6775_data *data, int nr, u16 reg)
{
+ int err;
u8 fan_div = data->fan_div[nr];
u16 fan_min;
if (!data->has_fan_div)
- return;
+ return 0;
/*
* If we failed to measure the fan speed, or the reported value is not
@@ -1791,36 +1289,46 @@ static void nct6775_select_fan_div(struct device *dev,
}
if (fan_min != data->fan_min[nr]) {
data->fan_min[nr] = fan_min;
- data->write_value(data, data->REG_FAN_MIN[nr],
- fan_min);
+ err = nct6775_write_value(data, data->REG_FAN_MIN[nr], fan_min);
+ if (err)
+ return err;
}
}
data->fan_div[nr] = fan_div;
- nct6775_write_fan_div_common(data, nr);
+ err = nct6775_write_fan_div_common(data, nr);
+ if (err)
+ return err;
}
+
+ return 0;
}
-static void nct6775_update_pwm(struct device *dev)
+static int nct6775_update_pwm(struct device *dev)
{
struct nct6775_data *data = dev_get_drvdata(dev);
- int i, j;
- int fanmodecfg, reg;
+ int i, j, err;
+ u16 fanmodecfg, reg;
bool duty_is_dc;
for (i = 0; i < data->pwm_num; i++) {
if (!(data->has_pwm & BIT(i)))
continue;
- duty_is_dc = data->REG_PWM_MODE[i] &&
- (data->read_value(data, data->REG_PWM_MODE[i])
- & data->PWM_MODE_MASK[i]);
+ err = nct6775_read_value(data, data->REG_PWM_MODE[i], &reg);
+ if (err)
+ return err;
+ duty_is_dc = data->REG_PWM_MODE[i] && (reg & data->PWM_MODE_MASK[i]);
data->pwm_mode[i] = !duty_is_dc;
- fanmodecfg = data->read_value(data, data->REG_FAN_MODE[i]);
+ err = nct6775_read_value(data, data->REG_FAN_MODE[i], &fanmodecfg);
+ if (err)
+ return err;
for (j = 0; j < ARRAY_SIZE(data->REG_PWM); j++) {
if (data->REG_PWM[j] && data->REG_PWM[j][i]) {
- data->pwm[j][i] = data->read_value(data,
- data->REG_PWM[j][i]);
+ err = nct6775_read_value(data, data->REG_PWM[j][i], &reg);
+ if (err)
+ return err;
+ data->pwm[j][i] = reg;
}
}
@@ -1835,17 +1343,22 @@ static void nct6775_update_pwm(struct device *dev)
u8 t = fanmodecfg & 0x0f;
if (data->REG_TOLERANCE_H) {
- t |= (data->read_value(data,
- data->REG_TOLERANCE_H[i]) & 0x70) >> 1;
+ err = nct6775_read_value(data, data->REG_TOLERANCE_H[i], &reg);
+ if (err)
+ return err;
+ t |= (reg & 0x70) >> 1;
}
data->target_speed_tolerance[i] = t;
}
- data->temp_tolerance[1][i] =
- data->read_value(data,
- data->REG_CRITICAL_TEMP_TOLERANCE[i]);
+ err = nct6775_read_value(data, data->REG_CRITICAL_TEMP_TOLERANCE[i], &reg);
+ if (err)
+ return err;
+ data->temp_tolerance[1][i] = reg;
- reg = data->read_value(data, data->REG_TEMP_SEL[i]);
+ err = nct6775_read_value(data, data->REG_TEMP_SEL[i], &reg);
+ if (err)
+ return err;
data->pwm_temp_sel[i] = reg & 0x1f;
/* If fan can stop, report floor as 0 */
if (reg & 0x80)
@@ -1854,7 +1367,9 @@ static void nct6775_update_pwm(struct device *dev)
if (!data->REG_WEIGHT_TEMP_SEL[i])
continue;
- reg = data->read_value(data, data->REG_WEIGHT_TEMP_SEL[i]);
+ err = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[i], &reg);
+ if (err)
+ return err;
data->pwm_weight_temp_sel[i] = reg & 0x1f;
/* If weight is disabled, report weight source as 0 */
if (!(reg & 0x80))
@@ -1862,29 +1377,37 @@ static void nct6775_update_pwm(struct device *dev)
/* Weight temp data */
for (j = 0; j < ARRAY_SIZE(data->weight_temp); j++) {
- data->weight_temp[j][i] = data->read_value(data,
- data->REG_WEIGHT_TEMP[j][i]);
+ err = nct6775_read_value(data, data->REG_WEIGHT_TEMP[j][i], &reg);
+ if (err)
+ return err;
+ data->weight_temp[j][i] = reg;
}
}
+
+ return 0;
}
-static void nct6775_update_pwm_limits(struct device *dev)
+static int nct6775_update_pwm_limits(struct device *dev)
{
struct nct6775_data *data = dev_get_drvdata(dev);
- int i, j;
- u8 reg;
- u16 reg_t;
+ int i, j, err;
+ u16 reg, reg_t;
for (i = 0; i < data->pwm_num; i++) {
if (!(data->has_pwm & BIT(i)))
continue;
for (j = 0; j < ARRAY_SIZE(data->fan_time); j++) {
- data->fan_time[j][i] =
- data->read_value(data, data->REG_FAN_TIME[j][i]);
+ err = nct6775_read_value(data, data->REG_FAN_TIME[j][i], &reg);
+ if (err)
+ return err;
+ data->fan_time[j][i] = reg;
}
- reg_t = data->read_value(data, data->REG_TARGET[i]);
+ err = nct6775_read_value(data, data->REG_TARGET[i], &reg_t);
+ if (err)
+ return err;
+
/* Update only in matching mode or if never updated */
if (!data->target_temp[i] ||
data->pwm_enable[i] == thermal_cruise)
@@ -1892,29 +1415,37 @@ static void nct6775_update_pwm_limits(struct device *dev)
if (!data->target_speed[i] ||
data->pwm_enable[i] == speed_cruise) {
if (data->REG_TOLERANCE_H) {
- reg_t |= (data->read_value(data,
- data->REG_TOLERANCE_H[i]) & 0x0f) << 8;
+ err = nct6775_read_value(data, data->REG_TOLERANCE_H[i], &reg);
+ if (err)
+ return err;
+ reg_t |= (reg & 0x0f) << 8;
}
data->target_speed[i] = reg_t;
}
for (j = 0; j < data->auto_pwm_num; j++) {
- data->auto_pwm[i][j] =
- data->read_value(data,
- NCT6775_AUTO_PWM(data, i, j));
- data->auto_temp[i][j] =
- data->read_value(data,
- NCT6775_AUTO_TEMP(data, i, j));
+ err = nct6775_read_value(data, NCT6775_AUTO_PWM(data, i, j), &reg);
+ if (err)
+ return err;
+ data->auto_pwm[i][j] = reg;
+
+ err = nct6775_read_value(data, NCT6775_AUTO_TEMP(data, i, j), &reg);
+ if (err)
+ return err;
+ data->auto_temp[i][j] = reg;
}
/* critical auto_pwm temperature data */
- data->auto_temp[i][data->auto_pwm_num] =
- data->read_value(data, data->REG_CRITICAL_TEMP[i]);
+ err = nct6775_read_value(data, data->REG_CRITICAL_TEMP[i], &reg);
+ if (err)
+ return err;
+ data->auto_temp[i][data->auto_pwm_num] = reg;
switch (data->kind) {
case nct6775:
- reg = data->read_value(data,
- NCT6775_REG_CRITICAL_ENAB[i]);
+ err = nct6775_read_value(data, NCT6775_REG_CRITICAL_ENAB[i], &reg);
+ if (err)
+ return err;
data->auto_pwm[i][data->auto_pwm_num] =
(reg & 0x02) ? 0xff : 0x00;
break;
@@ -1931,120 +1462,158 @@ static void nct6775_update_pwm_limits(struct device *dev)
case nct6796:
case nct6797:
case nct6798:
- reg = data->read_value(data,
- data->REG_CRITICAL_PWM_ENABLE[i]);
- if (reg & data->CRITICAL_PWM_ENABLE_MASK)
- reg = data->read_value(data,
- data->REG_CRITICAL_PWM[i]);
- else
+ err = nct6775_read_value(data, data->REG_CRITICAL_PWM_ENABLE[i], &reg);
+ if (err)
+ return err;
+ if (reg & data->CRITICAL_PWM_ENABLE_MASK) {
+ err = nct6775_read_value(data, data->REG_CRITICAL_PWM[i], &reg);
+ if (err)
+ return err;
+ } else {
reg = 0xff;
+ }
data->auto_pwm[i][data->auto_pwm_num] = reg;
break;
}
}
+
+ return 0;
}
static struct nct6775_data *nct6775_update_device(struct device *dev)
{
struct nct6775_data *data = dev_get_drvdata(dev);
- int i, j;
+ int i, j, err = 0;
+ u16 reg;
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
/* Fan clock dividers */
- nct6775_update_fan_div_common(data);
+ err = nct6775_update_fan_div_common(data);
+ if (err)
+ goto out;
/* Measured voltages and limits */
for (i = 0; i < data->in_num; i++) {
if (!(data->have_in & BIT(i)))
continue;
- data->in[i][0] = data->read_value(data,
- data->REG_VIN[i]);
- data->in[i][1] = data->read_value(data,
- data->REG_IN_MINMAX[0][i]);
- data->in[i][2] = data->read_value(data,
- data->REG_IN_MINMAX[1][i]);
+ err = nct6775_read_value(data, data->REG_VIN[i], &reg);
+ if (err)
+ goto out;
+ data->in[i][0] = reg;
+
+ err = nct6775_read_value(data, data->REG_IN_MINMAX[0][i], &reg);
+ if (err)
+ goto out;
+ data->in[i][1] = reg;
+
+ err = nct6775_read_value(data, data->REG_IN_MINMAX[1][i], &reg);
+ if (err)
+ goto out;
+ data->in[i][2] = reg;
}
/* Measured fan speeds and limits */
for (i = 0; i < ARRAY_SIZE(data->rpm); i++) {
- u16 reg;
-
if (!(data->has_fan & BIT(i)))
continue;
- reg = data->read_value(data, data->REG_FAN[i]);
+ err = nct6775_read_value(data, data->REG_FAN[i], &reg);
+ if (err)
+ goto out;
data->rpm[i] = data->fan_from_reg(reg,
data->fan_div[i]);
- if (data->has_fan_min & BIT(i))
- data->fan_min[i] = data->read_value(data,
- data->REG_FAN_MIN[i]);
+ if (data->has_fan_min & BIT(i)) {
+ err = nct6775_read_value(data, data->REG_FAN_MIN[i], &reg);
+ if (err)
+ goto out;
+ data->fan_min[i] = reg;
+ }
if (data->REG_FAN_PULSES[i]) {
- data->fan_pulses[i] =
- (data->read_value(data,
- data->REG_FAN_PULSES[i])
- >> data->FAN_PULSE_SHIFT[i]) & 0x03;
+ err = nct6775_read_value(data, data->REG_FAN_PULSES[i], &reg);
+ if (err)
+ goto out;
+ data->fan_pulses[i] = (reg >> data->FAN_PULSE_SHIFT[i]) & 0x03;
}
- nct6775_select_fan_div(dev, data, i, reg);
+ err = nct6775_select_fan_div(dev, data, i, reg);
+ if (err)
+ goto out;
}
- nct6775_update_pwm(dev);
- nct6775_update_pwm_limits(dev);
+ err = nct6775_update_pwm(dev);
+ if (err)
+ goto out;
+
+ err = nct6775_update_pwm_limits(dev);
+ if (err)
+ goto out;
/* Measured temperatures and limits */
for (i = 0; i < NUM_TEMP; i++) {
if (!(data->have_temp & BIT(i)))
continue;
for (j = 0; j < ARRAY_SIZE(data->reg_temp); j++) {
- if (data->reg_temp[j][i])
- data->temp[j][i] = nct6775_read_temp(data,
- data->reg_temp[j][i]);
+ if (data->reg_temp[j][i]) {
+ err = nct6775_read_temp(data, data->reg_temp[j][i], &reg);
+ if (err)
+ goto out;
+ data->temp[j][i] = reg;
+ }
}
if (i >= NUM_TEMP_FIXED ||
!(data->have_temp_fixed & BIT(i)))
continue;
- data->temp_offset[i] = data->read_value(data,
- data->REG_TEMP_OFFSET[i]);
+ err = nct6775_read_value(data, data->REG_TEMP_OFFSET[i], &reg);
+ if (err)
+ goto out;
+ data->temp_offset[i] = reg;
}
for (i = 0; i < NUM_TSI_TEMP; i++) {
if (!(data->have_tsi_temp & BIT(i)))
continue;
- data->tsi_temp[i] = data->read_value(data, data->REG_TSI_TEMP[i]);
+ err = nct6775_read_value(data, data->REG_TSI_TEMP[i], &reg);
+ if (err)
+ goto out;
+ data->tsi_temp[i] = reg;
}
data->alarms = 0;
for (i = 0; i < NUM_REG_ALARM; i++) {
- u8 alarm;
+ u16 alarm;
if (!data->REG_ALARM[i])
continue;
- alarm = data->read_value(data, data->REG_ALARM[i]);
+ err = nct6775_read_value(data, data->REG_ALARM[i], &alarm);
+ if (err)
+ goto out;
data->alarms |= ((u64)alarm) << (i << 3);
}
data->beeps = 0;
for (i = 0; i < NUM_REG_BEEP; i++) {
- u8 beep;
+ u16 beep;
if (!data->REG_BEEP[i])
continue;
- beep = data->read_value(data, data->REG_BEEP[i]);
+ err = nct6775_read_value(data, data->REG_BEEP[i], &beep);
+ if (err)
+ goto out;
data->beeps |= ((u64)beep) << (i << 3);
}
data->last_updated = jiffies;
data->valid = true;
}
-
+out:
mutex_unlock(&data->update_lock);
- return data;
+ return err ? ERR_PTR(err) : data;
}
/*
@@ -2058,6 +1627,9 @@ show_in_reg(struct device *dev, struct device_attribute *attr, char *buf)
int index = sattr->index;
int nr = sattr->nr;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%ld\n", in_from_reg(data->in[nr][index], nr));
}
@@ -2077,34 +1649,39 @@ store_in_reg(struct device *dev, struct device_attribute *attr, const char *buf,
return err;
mutex_lock(&data->update_lock);
data->in[nr][index] = in_to_reg(val, nr);
- data->write_value(data, data->REG_IN_MINMAX[index - 1][nr],
- data->in[nr][index]);
+ err = nct6775_write_value(data, data->REG_IN_MINMAX[index - 1][nr], data->in[nr][index]);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
-static ssize_t
-show_alarm(struct device *dev, struct device_attribute *attr, char *buf)
+ssize_t
+nct6775_show_alarm(struct device *dev, struct device_attribute *attr, char *buf)
{
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
- int nr = data->ALARM_BITS[sattr->index];
+ int nr;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ nr = data->ALARM_BITS[sattr->index];
return sprintf(buf, "%u\n",
(unsigned int)((data->alarms >> nr) & 0x01));
}
+EXPORT_SYMBOL_GPL(nct6775_show_alarm);
static int find_temp_source(struct nct6775_data *data, int index, int count)
{
int source = data->temp_src[index];
- int nr;
+ int nr, err;
for (nr = 0; nr < count; nr++) {
- int src;
+ u16 src;
- src = data->read_value(data,
- data->REG_TEMP_SOURCE[nr]) & 0x1f;
- if (src == source)
+ err = nct6775_read_value(data, data->REG_TEMP_SOURCE[nr], &src);
+ if (err)
+ return err;
+ if ((src & 0x1f) == source)
return nr;
}
return -ENODEV;
@@ -2118,6 +1695,9 @@ show_temp_alarm(struct device *dev, struct device_attribute *attr, char *buf)
unsigned int alarm = 0;
int nr;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
/*
* For temperatures, there is no fixed mapping from registers to alarm
* bits. Alarm bits are determined by the temperature source mapping.
@@ -2131,20 +1711,25 @@ show_temp_alarm(struct device *dev, struct device_attribute *attr, char *buf)
return sprintf(buf, "%u\n", alarm);
}
-static ssize_t
-show_beep(struct device *dev, struct device_attribute *attr, char *buf)
+ssize_t
+nct6775_show_beep(struct device *dev, struct device_attribute *attr, char *buf)
{
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
struct nct6775_data *data = nct6775_update_device(dev);
- int nr = data->BEEP_BITS[sattr->index];
+ int nr;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ nr = data->BEEP_BITS[sattr->index];
return sprintf(buf, "%u\n",
(unsigned int)((data->beeps >> nr) & 0x01));
}
+EXPORT_SYMBOL_GPL(nct6775_show_beep);
-static ssize_t
-store_beep(struct device *dev, struct device_attribute *attr, const char *buf,
- size_t count)
+ssize_t
+nct6775_store_beep(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
struct nct6775_data *data = dev_get_drvdata(dev);
@@ -2164,11 +1749,12 @@ store_beep(struct device *dev, struct device_attribute *attr, const char *buf,
data->beeps |= (1ULL << nr);
else
data->beeps &= ~(1ULL << nr);
- data->write_value(data, data->REG_BEEP[regindex],
- (data->beeps >> (regindex << 3)) & 0xff);
+ err = nct6775_write_value(data, data->REG_BEEP[regindex],
+ (data->beeps >> (regindex << 3)) & 0xff);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
+EXPORT_SYMBOL_GPL(nct6775_store_beep);
static ssize_t
show_temp_beep(struct device *dev, struct device_attribute *attr, char *buf)
@@ -2178,6 +1764,9 @@ show_temp_beep(struct device *dev, struct device_attribute *attr, char *buf)
unsigned int beep = 0;
int nr;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
/*
* For temperatures, there is no fixed mapping from registers to beep
* enable bits. Beep enable bits are determined by the temperature
@@ -2220,11 +1809,11 @@ store_temp_beep(struct device *dev, struct device_attribute *attr,
data->beeps |= (1ULL << bit);
else
data->beeps &= ~(1ULL << bit);
- data->write_value(data, data->REG_BEEP[regindex],
- (data->beeps >> (regindex << 3)) & 0xff);
+ err = nct6775_write_value(data, data->REG_BEEP[regindex],
+ (data->beeps >> (regindex << 3)) & 0xff);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static umode_t nct6775_in_is_visible(struct kobject *kobj,
@@ -2237,17 +1826,14 @@ static umode_t nct6775_in_is_visible(struct kobject *kobj,
if (!(data->have_in & BIT(in)))
return 0;
- return attr->mode;
+ return nct6775_attr_mode(data, attr);
}
-SENSOR_TEMPLATE_2(in_input, "in%d_input", S_IRUGO, show_in_reg, NULL, 0, 0);
-SENSOR_TEMPLATE(in_alarm, "in%d_alarm", S_IRUGO, show_alarm, NULL, 0);
-SENSOR_TEMPLATE(in_beep, "in%d_beep", S_IWUSR | S_IRUGO, show_beep, store_beep,
- 0);
-SENSOR_TEMPLATE_2(in_min, "in%d_min", S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 0, 1);
-SENSOR_TEMPLATE_2(in_max, "in%d_max", S_IWUSR | S_IRUGO, show_in_reg,
- store_in_reg, 0, 2);
+SENSOR_TEMPLATE_2(in_input, "in%d_input", 0444, show_in_reg, NULL, 0, 0);
+SENSOR_TEMPLATE(in_alarm, "in%d_alarm", 0444, nct6775_show_alarm, NULL, 0);
+SENSOR_TEMPLATE(in_beep, "in%d_beep", 0644, nct6775_show_beep, nct6775_store_beep, 0);
+SENSOR_TEMPLATE_2(in_min, "in%d_min", 0644, show_in_reg, store_in_reg, 0, 1);
+SENSOR_TEMPLATE_2(in_max, "in%d_max", 0644, show_in_reg, store_in_reg, 0, 2);
/*
* nct6775_in_is_visible uses the index into the following array
@@ -2275,6 +1861,9 @@ show_fan(struct device *dev, struct device_attribute *attr, char *buf)
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->rpm[nr]);
}
@@ -2285,6 +1874,9 @@ show_fan_min(struct device *dev, struct device_attribute *attr, char *buf)
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n",
data->fan_from_reg_min(data->fan_min[nr],
data->fan_div[nr]));
@@ -2297,6 +1889,9 @@ show_fan_div(struct device *dev, struct device_attribute *attr, char *buf)
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr]));
}
@@ -2382,16 +1977,18 @@ write_div:
nr + 1, div_from_reg(data->fan_div[nr]),
div_from_reg(new_div));
data->fan_div[nr] = new_div;
- nct6775_write_fan_div_common(data, nr);
+ err = nct6775_write_fan_div_common(data, nr);
+ if (err)
+ goto write_min;
/* Give the chip time to sample a new speed value */
data->last_updated = jiffies;
}
write_min:
- data->write_value(data, data->REG_FAN_MIN[nr], data->fan_min[nr]);
+ err = nct6775_write_value(data, data->REG_FAN_MIN[nr], data->fan_min[nr]);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -2399,8 +1996,12 @@ show_fan_pulses(struct device *dev, struct device_attribute *attr, char *buf)
{
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
- int p = data->fan_pulses[sattr->index];
+ int p;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+ p = data->fan_pulses[sattr->index];
return sprintf(buf, "%d\n", p ? : 4);
}
@@ -2413,7 +2014,7 @@ store_fan_pulses(struct device *dev, struct device_attribute *attr,
int nr = sattr->index;
unsigned long val;
int err;
- u8 reg;
+ u16 reg;
err = kstrtoul(buf, 10, &val);
if (err < 0)
@@ -2424,13 +2025,16 @@ store_fan_pulses(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
data->fan_pulses[nr] = val & 3;
- reg = data->read_value(data, data->REG_FAN_PULSES[nr]);
+ err = nct6775_read_value(data, data->REG_FAN_PULSES[nr], &reg);
+ if (err)
+ goto out;
reg &= ~(0x03 << data->FAN_PULSE_SHIFT[nr]);
reg |= (val & 3) << data->FAN_PULSE_SHIFT[nr];
- data->write_value(data, data->REG_FAN_PULSES[nr], reg);
+ err = nct6775_write_value(data, data->REG_FAN_PULSES[nr], reg);
+out:
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static umode_t nct6775_fan_is_visible(struct kobject *kobj,
@@ -2455,19 +2059,16 @@ static umode_t nct6775_fan_is_visible(struct kobject *kobj,
if (nr == 5 && data->kind != nct6775)
return 0;
- return attr->mode;
+ return nct6775_attr_mode(data, attr);
}
-SENSOR_TEMPLATE(fan_input, "fan%d_input", S_IRUGO, show_fan, NULL, 0);
-SENSOR_TEMPLATE(fan_alarm, "fan%d_alarm", S_IRUGO, show_alarm, NULL,
- FAN_ALARM_BASE);
-SENSOR_TEMPLATE(fan_beep, "fan%d_beep", S_IWUSR | S_IRUGO, show_beep,
- store_beep, FAN_ALARM_BASE);
-SENSOR_TEMPLATE(fan_pulses, "fan%d_pulses", S_IWUSR | S_IRUGO, show_fan_pulses,
- store_fan_pulses, 0);
-SENSOR_TEMPLATE(fan_min, "fan%d_min", S_IWUSR | S_IRUGO, show_fan_min,
- store_fan_min, 0);
-SENSOR_TEMPLATE(fan_div, "fan%d_div", S_IRUGO, show_fan_div, NULL, 0);
+SENSOR_TEMPLATE(fan_input, "fan%d_input", 0444, show_fan, NULL, 0);
+SENSOR_TEMPLATE(fan_alarm, "fan%d_alarm", 0444, nct6775_show_alarm, NULL, FAN_ALARM_BASE);
+SENSOR_TEMPLATE(fan_beep, "fan%d_beep", 0644, nct6775_show_beep,
+ nct6775_store_beep, FAN_ALARM_BASE);
+SENSOR_TEMPLATE(fan_pulses, "fan%d_pulses", 0644, show_fan_pulses, store_fan_pulses, 0);
+SENSOR_TEMPLATE(fan_min, "fan%d_min", 0644, show_fan_min, store_fan_min, 0);
+SENSOR_TEMPLATE(fan_div, "fan%d_div", 0444, show_fan_div, NULL, 0);
/*
* nct6775_fan_is_visible uses the index into the following array
@@ -2497,6 +2098,9 @@ show_temp_label(struct device *dev, struct device_attribute *attr, char *buf)
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]);
}
@@ -2508,6 +2112,9 @@ show_temp(struct device *dev, struct device_attribute *attr, char *buf)
int nr = sattr->nr;
int index = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->temp[index][nr]));
}
@@ -2528,10 +2135,9 @@ store_temp(struct device *dev, struct device_attribute *attr, const char *buf,
mutex_lock(&data->update_lock);
data->temp[index][nr] = LM75_TEMP_TO_REG(val);
- nct6775_write_temp(data, data->reg_temp[index][nr],
- data->temp[index][nr]);
+ err = nct6775_write_temp(data, data->reg_temp[index][nr], data->temp[index][nr]);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -2540,6 +2146,9 @@ show_temp_offset(struct device *dev, struct device_attribute *attr, char *buf)
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->temp_offset[sattr->index] * 1000);
}
@@ -2561,10 +2170,10 @@ store_temp_offset(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
data->temp_offset[nr] = val;
- data->write_value(data, data->REG_TEMP_OFFSET[nr], val);
+ err = nct6775_write_value(data, data->REG_TEMP_OFFSET[nr], val);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -2574,6 +2183,9 @@ show_temp_type(struct device *dev, struct device_attribute *attr, char *buf)
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", (int)data->temp_type[nr]);
}
@@ -2586,7 +2198,11 @@ store_temp_type(struct device *dev, struct device_attribute *attr,
int nr = sattr->index;
unsigned long val;
int err;
- u8 vbat, diode, vbit, dbit;
+ u8 vbit, dbit;
+ u16 vbat, diode;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
err = kstrtoul(buf, 10, &val);
if (err < 0)
@@ -2600,8 +2216,17 @@ store_temp_type(struct device *dev, struct device_attribute *attr,
data->temp_type[nr] = val;
vbit = 0x02 << nr;
dbit = data->DIODE_MASK << nr;
- vbat = data->read_value(data, data->REG_VBAT) & ~vbit;
- diode = data->read_value(data, data->REG_DIODE) & ~dbit;
+
+ err = nct6775_read_value(data, data->REG_VBAT, &vbat);
+ if (err)
+ goto out;
+ vbat &= ~vbit;
+
+ err = nct6775_read_value(data, data->REG_DIODE, &diode);
+ if (err)
+ goto out;
+ diode &= ~dbit;
+
switch (val) {
case 1: /* CPU diode (diode, current mode) */
vbat |= vbit;
@@ -2613,11 +2238,13 @@ store_temp_type(struct device *dev, struct device_attribute *attr,
case 4: /* thermistor */
break;
}
- data->write_value(data, data->REG_VBAT, vbat);
- data->write_value(data, data->REG_DIODE, diode);
-
+ err = nct6775_write_value(data, data->REG_VBAT, vbat);
+ if (err)
+ goto out;
+ err = nct6775_write_value(data, data->REG_DIODE, diode);
+out:
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static umode_t nct6775_temp_is_visible(struct kobject *kobj,
@@ -2656,26 +2283,19 @@ static umode_t nct6775_temp_is_visible(struct kobject *kobj,
if (nr > 7 && !(data->have_temp_fixed & BIT(temp)))
return 0;
- return attr->mode;
+ return nct6775_attr_mode(data, attr);
}
-SENSOR_TEMPLATE_2(temp_input, "temp%d_input", S_IRUGO, show_temp, NULL, 0, 0);
-SENSOR_TEMPLATE(temp_label, "temp%d_label", S_IRUGO, show_temp_label, NULL, 0);
-SENSOR_TEMPLATE_2(temp_max, "temp%d_max", S_IRUGO | S_IWUSR, show_temp,
- store_temp, 0, 1);
-SENSOR_TEMPLATE_2(temp_max_hyst, "temp%d_max_hyst", S_IRUGO | S_IWUSR,
- show_temp, store_temp, 0, 2);
-SENSOR_TEMPLATE_2(temp_crit, "temp%d_crit", S_IRUGO | S_IWUSR, show_temp,
- store_temp, 0, 3);
-SENSOR_TEMPLATE_2(temp_lcrit, "temp%d_lcrit", S_IRUGO | S_IWUSR, show_temp,
- store_temp, 0, 4);
-SENSOR_TEMPLATE(temp_offset, "temp%d_offset", S_IRUGO | S_IWUSR,
- show_temp_offset, store_temp_offset, 0);
-SENSOR_TEMPLATE(temp_type, "temp%d_type", S_IRUGO | S_IWUSR, show_temp_type,
- store_temp_type, 0);
-SENSOR_TEMPLATE(temp_alarm, "temp%d_alarm", S_IRUGO, show_temp_alarm, NULL, 0);
-SENSOR_TEMPLATE(temp_beep, "temp%d_beep", S_IRUGO | S_IWUSR, show_temp_beep,
- store_temp_beep, 0);
+SENSOR_TEMPLATE_2(temp_input, "temp%d_input", 0444, show_temp, NULL, 0, 0);
+SENSOR_TEMPLATE(temp_label, "temp%d_label", 0444, show_temp_label, NULL, 0);
+SENSOR_TEMPLATE_2(temp_max, "temp%d_max", 0644, show_temp, store_temp, 0, 1);
+SENSOR_TEMPLATE_2(temp_max_hyst, "temp%d_max_hyst", 0644, show_temp, store_temp, 0, 2);
+SENSOR_TEMPLATE_2(temp_crit, "temp%d_crit", 0644, show_temp, store_temp, 0, 3);
+SENSOR_TEMPLATE_2(temp_lcrit, "temp%d_lcrit", 0644, show_temp, store_temp, 0, 4);
+SENSOR_TEMPLATE(temp_offset, "temp%d_offset", 0644, show_temp_offset, store_temp_offset, 0);
+SENSOR_TEMPLATE(temp_type, "temp%d_type", 0644, show_temp_type, store_temp_type, 0);
+SENSOR_TEMPLATE(temp_alarm, "temp%d_alarm", 0444, show_temp_alarm, NULL, 0);
+SENSOR_TEMPLATE(temp_beep, "temp%d_beep", 0644, show_temp_beep, store_temp_beep, 0);
/*
* nct6775_temp_is_visible uses the index into the following array
@@ -2707,6 +2327,9 @@ static ssize_t show_tsi_temp(struct device *dev, struct device_attribute *attr,
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sysfs_emit(buf, "%u\n", tsi_temp_from_reg(data->tsi_temp[sattr->index]));
}
@@ -2727,7 +2350,7 @@ static umode_t nct6775_tsi_temp_is_visible(struct kobject *kobj, struct attribut
struct nct6775_data *data = dev_get_drvdata(dev);
int temp = index / 2;
- return (data->have_tsi_temp & BIT(temp)) ? attr->mode : 0;
+ return (data->have_tsi_temp & BIT(temp)) ? nct6775_attr_mode(data, attr) : 0;
}
/*
@@ -2746,6 +2369,9 @@ show_pwm_mode(struct device *dev, struct device_attribute *attr, char *buf)
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->pwm_mode[sattr->index]);
}
@@ -2758,7 +2384,7 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr,
int nr = sattr->index;
unsigned long val;
int err;
- u8 reg;
+ u16 reg;
err = kstrtoul(buf, 10, &val);
if (err < 0)
@@ -2776,13 +2402,16 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
data->pwm_mode[nr] = val;
- reg = data->read_value(data, data->REG_PWM_MODE[nr]);
+ err = nct6775_read_value(data, data->REG_PWM_MODE[nr], &reg);
+ if (err)
+ goto out;
reg &= ~data->PWM_MODE_MASK[nr];
if (!val)
reg |= data->PWM_MODE_MASK[nr];
- data->write_value(data, data->REG_PWM_MODE[nr], reg);
+ err = nct6775_write_value(data, data->REG_PWM_MODE[nr], reg);
+out:
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -2792,16 +2421,23 @@ show_pwm(struct device *dev, struct device_attribute *attr, char *buf)
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
int nr = sattr->nr;
int index = sattr->index;
- int pwm;
+ int err;
+ u16 pwm;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
/*
* For automatic fan control modes, show current pwm readings.
* Otherwise, show the configured value.
*/
- if (index == 0 && data->pwm_enable[nr] > manual)
- pwm = data->read_value(data, data->REG_PWM_READ[nr]);
- else
+ if (index == 0 && data->pwm_enable[nr] > manual) {
+ err = nct6775_read_value(data, data->REG_PWM_READ[nr], &pwm);
+ if (err)
+ return err;
+ } else {
pwm = data->pwm[index][nr];
+ }
return sprintf(buf, "%d\n", pwm);
}
@@ -2819,7 +2455,7 @@ store_pwm(struct device *dev, struct device_attribute *attr, const char *buf,
int maxval[7]
= { 255, 255, data->pwm[3][nr] ? : 255, 255, 255, 255, 255 };
int err;
- u8 reg;
+ u16 reg;
err = kstrtoul(buf, 10, &val);
if (err < 0)
@@ -2828,16 +2464,21 @@ store_pwm(struct device *dev, struct device_attribute *attr, const char *buf,
mutex_lock(&data->update_lock);
data->pwm[index][nr] = val;
- data->write_value(data, data->REG_PWM[index][nr], val);
+ err = nct6775_write_value(data, data->REG_PWM[index][nr], val);
+ if (err)
+ goto out;
if (index == 2) { /* floor: disable if val == 0 */
- reg = data->read_value(data, data->REG_TEMP_SEL[nr]);
+ err = nct6775_read_value(data, data->REG_TEMP_SEL[nr], &reg);
+ if (err)
+ goto out;
reg &= 0x7f;
if (val)
reg |= 0x80;
- data->write_value(data, data->REG_TEMP_SEL[nr], reg);
+ err = nct6775_write_value(data, data->REG_TEMP_SEL[nr], reg);
}
+out:
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
/* Returns 0 if OK, -EINVAL otherwise */
@@ -2864,40 +2505,54 @@ static int check_trip_points(struct nct6775_data *data, int nr)
return 0;
}
-static void pwm_update_registers(struct nct6775_data *data, int nr)
+static int pwm_update_registers(struct nct6775_data *data, int nr)
{
- u8 reg;
+ u16 reg;
+ int err;
switch (data->pwm_enable[nr]) {
case off:
case manual:
break;
case speed_cruise:
- reg = data->read_value(data, data->REG_FAN_MODE[nr]);
+ err = nct6775_read_value(data, data->REG_FAN_MODE[nr], &reg);
+ if (err)
+ return err;
reg = (reg & ~data->tolerance_mask) |
(data->target_speed_tolerance[nr] & data->tolerance_mask);
- data->write_value(data, data->REG_FAN_MODE[nr], reg);
- data->write_value(data, data->REG_TARGET[nr],
- data->target_speed[nr] & 0xff);
+ err = nct6775_write_value(data, data->REG_FAN_MODE[nr], reg);
+ if (err)
+ return err;
+ err = nct6775_write_value(data, data->REG_TARGET[nr],
+ data->target_speed[nr] & 0xff);
+ if (err)
+ return err;
if (data->REG_TOLERANCE_H) {
reg = (data->target_speed[nr] >> 8) & 0x0f;
reg |= (data->target_speed_tolerance[nr] & 0x38) << 1;
- data->write_value(data,
- data->REG_TOLERANCE_H[nr],
- reg);
+ err = nct6775_write_value(data, data->REG_TOLERANCE_H[nr], reg);
+ if (err)
+ return err;
}
break;
case thermal_cruise:
- data->write_value(data, data->REG_TARGET[nr],
- data->target_temp[nr]);
+ err = nct6775_write_value(data, data->REG_TARGET[nr], data->target_temp[nr]);
+ if (err)
+ return err;
fallthrough;
default:
- reg = data->read_value(data, data->REG_FAN_MODE[nr]);
+ err = nct6775_read_value(data, data->REG_FAN_MODE[nr], &reg);
+ if (err)
+ return err;
reg = (reg & ~data->tolerance_mask) |
data->temp_tolerance[0][nr];
- data->write_value(data, data->REG_FAN_MODE[nr], reg);
+ err = nct6775_write_value(data, data->REG_FAN_MODE[nr], reg);
+ if (err)
+ return err;
break;
}
+
+ return 0;
}
static ssize_t
@@ -2906,6 +2561,9 @@ show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf)
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->pwm_enable[sattr->index]);
}
@@ -2943,15 +2601,22 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr,
* turn off pwm control: select manual mode, set pwm to maximum
*/
data->pwm[0][nr] = 255;
- data->write_value(data, data->REG_PWM[0][nr], 255);
+ err = nct6775_write_value(data, data->REG_PWM[0][nr], 255);
+ if (err)
+ goto out;
}
- pwm_update_registers(data, nr);
- reg = data->read_value(data, data->REG_FAN_MODE[nr]);
+ err = pwm_update_registers(data, nr);
+ if (err)
+ goto out;
+ err = nct6775_read_value(data, data->REG_FAN_MODE[nr], &reg);
+ if (err)
+ goto out;
reg &= 0x0f;
reg |= pwm_enable_to_reg(val) << 4;
- data->write_value(data, data->REG_FAN_MODE[nr], reg);
+ err = nct6775_write_value(data, data->REG_FAN_MODE[nr], reg);
+out:
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -2978,6 +2643,9 @@ show_pwm_temp_sel(struct device *dev, struct device_attribute *attr, char *buf)
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int index = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return show_pwm_temp_sel_common(data, buf, data->pwm_temp_sel[index]);
}
@@ -2989,7 +2657,11 @@ store_pwm_temp_sel(struct device *dev, struct device_attribute *attr,
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
unsigned long val;
- int err, reg, src;
+ int err, src;
+ u16 reg;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
err = kstrtoul(buf, 10, &val);
if (err < 0)
@@ -3002,13 +2674,16 @@ store_pwm_temp_sel(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
src = data->temp_src[val - 1];
data->pwm_temp_sel[nr] = src;
- reg = data->read_value(data, data->REG_TEMP_SEL[nr]);
+ err = nct6775_read_value(data, data->REG_TEMP_SEL[nr], &reg);
+ if (err)
+ goto out;
reg &= 0xe0;
reg |= src;
- data->write_value(data, data->REG_TEMP_SEL[nr], reg);
+ err = nct6775_write_value(data, data->REG_TEMP_SEL[nr], reg);
+out:
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -3019,6 +2694,9 @@ show_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr,
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int index = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return show_pwm_temp_sel_common(data, buf,
data->pwm_weight_temp_sel[index]);
}
@@ -3031,7 +2709,11 @@ store_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr,
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
unsigned long val;
- int err, reg, src;
+ int err, src;
+ u16 reg;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
err = kstrtoul(buf, 10, &val);
if (err < 0)
@@ -3047,19 +2729,24 @@ store_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr,
if (val) {
src = data->temp_src[val - 1];
data->pwm_weight_temp_sel[nr] = src;
- reg = data->read_value(data, data->REG_WEIGHT_TEMP_SEL[nr]);
+ err = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[nr], &reg);
+ if (err)
+ goto out;
reg &= 0xe0;
reg |= (src | 0x80);
- data->write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg);
+ err = nct6775_write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg);
} else {
data->pwm_weight_temp_sel[nr] = 0;
- reg = data->read_value(data, data->REG_WEIGHT_TEMP_SEL[nr]);
+ err = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[nr], &reg);
+ if (err)
+ goto out;
reg &= 0x7f;
- data->write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg);
+ err = nct6775_write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg);
}
+out:
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -3068,6 +2755,9 @@ show_target_temp(struct device *dev, struct device_attribute *attr, char *buf)
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->target_temp[sattr->index] * 1000);
}
@@ -3090,9 +2780,9 @@ store_target_temp(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
data->target_temp[nr] = val;
- pwm_update_registers(data, nr);
+ err = pwm_update_registers(data, nr);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -3102,6 +2792,9 @@ show_target_speed(struct device *dev, struct device_attribute *attr, char *buf)
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n",
fan_from_reg16(data->target_speed[nr],
data->fan_div[nr]));
@@ -3127,9 +2820,9 @@ store_target_speed(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
data->target_speed[nr] = speed;
- pwm_update_registers(data, nr);
+ err = pwm_update_registers(data, nr);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -3141,6 +2834,9 @@ show_temp_tolerance(struct device *dev, struct device_attribute *attr,
int nr = sattr->nr;
int index = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->temp_tolerance[index][nr] * 1000);
}
@@ -3165,13 +2861,11 @@ store_temp_tolerance(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
data->temp_tolerance[index][nr] = val;
if (index)
- pwm_update_registers(data, nr);
+ err = pwm_update_registers(data, nr);
else
- data->write_value(data,
- data->REG_CRITICAL_TEMP_TOLERANCE[nr],
- val);
+ err = nct6775_write_value(data, data->REG_CRITICAL_TEMP_TOLERANCE[nr], val);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
/*
@@ -3188,8 +2882,12 @@ show_speed_tolerance(struct device *dev, struct device_attribute *attr,
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
int nr = sattr->index;
- int target = data->target_speed[nr];
- int tolerance = 0;
+ int target, tolerance = 0;
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ target = data->target_speed[nr];
if (target) {
int low = target - data->target_speed_tolerance[nr];
@@ -3239,24 +2937,19 @@ store_speed_tolerance(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
data->target_speed_tolerance[nr] = val;
- pwm_update_registers(data, nr);
+ err = pwm_update_registers(data, nr);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
-SENSOR_TEMPLATE_2(pwm, "pwm%d", S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 0);
-SENSOR_TEMPLATE(pwm_mode, "pwm%d_mode", S_IWUSR | S_IRUGO, show_pwm_mode,
- store_pwm_mode, 0);
-SENSOR_TEMPLATE(pwm_enable, "pwm%d_enable", S_IWUSR | S_IRUGO, show_pwm_enable,
- store_pwm_enable, 0);
-SENSOR_TEMPLATE(pwm_temp_sel, "pwm%d_temp_sel", S_IWUSR | S_IRUGO,
- show_pwm_temp_sel, store_pwm_temp_sel, 0);
-SENSOR_TEMPLATE(pwm_target_temp, "pwm%d_target_temp", S_IWUSR | S_IRUGO,
- show_target_temp, store_target_temp, 0);
-SENSOR_TEMPLATE(fan_target, "fan%d_target", S_IWUSR | S_IRUGO,
- show_target_speed, store_target_speed, 0);
-SENSOR_TEMPLATE(fan_tolerance, "fan%d_tolerance", S_IWUSR | S_IRUGO,
- show_speed_tolerance, store_speed_tolerance, 0);
+SENSOR_TEMPLATE_2(pwm, "pwm%d", 0644, show_pwm, store_pwm, 0, 0);
+SENSOR_TEMPLATE(pwm_mode, "pwm%d_mode", 0644, show_pwm_mode, store_pwm_mode, 0);
+SENSOR_TEMPLATE(pwm_enable, "pwm%d_enable", 0644, show_pwm_enable, store_pwm_enable, 0);
+SENSOR_TEMPLATE(pwm_temp_sel, "pwm%d_temp_sel", 0644, show_pwm_temp_sel, store_pwm_temp_sel, 0);
+SENSOR_TEMPLATE(pwm_target_temp, "pwm%d_target_temp", 0644, show_target_temp, store_target_temp, 0);
+SENSOR_TEMPLATE(fan_target, "fan%d_target", 0644, show_target_speed, store_target_speed, 0);
+SENSOR_TEMPLATE(fan_tolerance, "fan%d_tolerance", 0644, show_speed_tolerance,
+ store_speed_tolerance, 0);
/* Smart Fan registers */
@@ -3268,6 +2961,9 @@ show_weight_temp(struct device *dev, struct device_attribute *attr, char *buf)
int nr = sattr->nr;
int index = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->weight_temp[index][nr] * 1000);
}
@@ -3290,23 +2986,21 @@ store_weight_temp(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
data->weight_temp[index][nr] = val;
- data->write_value(data, data->REG_WEIGHT_TEMP[index][nr], val);
+ err = nct6775_write_value(data, data->REG_WEIGHT_TEMP[index][nr], val);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
-SENSOR_TEMPLATE(pwm_weight_temp_sel, "pwm%d_weight_temp_sel", S_IWUSR | S_IRUGO,
- show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, 0);
+SENSOR_TEMPLATE(pwm_weight_temp_sel, "pwm%d_weight_temp_sel", 0644,
+ show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, 0);
SENSOR_TEMPLATE_2(pwm_weight_temp_step, "pwm%d_weight_temp_step",
- S_IWUSR | S_IRUGO, show_weight_temp, store_weight_temp, 0, 0);
+ 0644, show_weight_temp, store_weight_temp, 0, 0);
SENSOR_TEMPLATE_2(pwm_weight_temp_step_tol, "pwm%d_weight_temp_step_tol",
- S_IWUSR | S_IRUGO, show_weight_temp, store_weight_temp, 0, 1);
+ 0644, show_weight_temp, store_weight_temp, 0, 1);
SENSOR_TEMPLATE_2(pwm_weight_temp_step_base, "pwm%d_weight_temp_step_base",
- S_IWUSR | S_IRUGO, show_weight_temp, store_weight_temp, 0, 2);
-SENSOR_TEMPLATE_2(pwm_weight_duty_step, "pwm%d_weight_duty_step",
- S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 5);
-SENSOR_TEMPLATE_2(pwm_weight_duty_base, "pwm%d_weight_duty_base",
- S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 6);
+ 0644, show_weight_temp, store_weight_temp, 0, 2);
+SENSOR_TEMPLATE_2(pwm_weight_duty_step, "pwm%d_weight_duty_step", 0644, show_pwm, store_pwm, 0, 5);
+SENSOR_TEMPLATE_2(pwm_weight_duty_base, "pwm%d_weight_duty_base", 0644, show_pwm, store_pwm, 0, 6);
static ssize_t
show_fan_time(struct device *dev, struct device_attribute *attr, char *buf)
@@ -3316,6 +3010,9 @@ show_fan_time(struct device *dev, struct device_attribute *attr, char *buf)
int nr = sattr->nr;
int index = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n",
step_time_from_reg(data->fan_time[index][nr],
data->pwm_mode[nr]));
@@ -3339,9 +3036,9 @@ store_fan_time(struct device *dev, struct device_attribute *attr,
val = step_time_to_reg(val, data->pwm_mode[nr]);
mutex_lock(&data->update_lock);
data->fan_time[index][nr] = val;
- data->write_value(data, data->REG_FAN_TIME[index][nr], val);
+ err = nct6775_write_value(data, data->REG_FAN_TIME[index][nr], val);
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -3350,6 +3047,9 @@ show_auto_pwm(struct device *dev, struct device_attribute *attr, char *buf)
struct nct6775_data *data = nct6775_update_device(dev);
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
return sprintf(buf, "%d\n", data->auto_pwm[sattr->nr][sattr->index]);
}
@@ -3363,7 +3063,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr,
int point = sattr->index;
unsigned long val;
int err;
- u8 reg;
+ u16 reg;
err = kstrtoul(buf, 10, &val);
if (err < 0)
@@ -3381,21 +3081,20 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
data->auto_pwm[nr][point] = val;
if (point < data->auto_pwm_num) {
- data->write_value(data,
- NCT6775_AUTO_PWM(data, nr, point),
- data->auto_pwm[nr][point]);
+ err = nct6775_write_value(data, NCT6775_AUTO_PWM(data, nr, point),
+ data->auto_pwm[nr][point]);
} else {
switch (data->kind) {
case nct6775:
/* disable if needed (pwm == 0) */
- reg = data->read_value(data,
- NCT6775_REG_CRITICAL_ENAB[nr]);
+ err = nct6775_read_value(data, NCT6775_REG_CRITICAL_ENAB[nr], &reg);
+ if (err)
+ break;
if (val)
reg |= 0x02;
else
reg &= ~0x02;
- data->write_value(data, NCT6775_REG_CRITICAL_ENAB[nr],
- reg);
+ err = nct6775_write_value(data, NCT6775_REG_CRITICAL_ENAB[nr], reg);
break;
case nct6776:
break; /* always enabled, nothing to do */
@@ -3409,22 +3108,22 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr,
case nct6796:
case nct6797:
case nct6798:
- data->write_value(data, data->REG_CRITICAL_PWM[nr],
- val);
- reg = data->read_value(data,
- data->REG_CRITICAL_PWM_ENABLE[nr]);
+ err = nct6775_write_value(data, data->REG_CRITICAL_PWM[nr], val);
+ if (err)
+ break;
+ err = nct6775_read_value(data, data->REG_CRITICAL_PWM_ENABLE[nr], &reg);
+ if (err)
+ break;
if (val == 255)
reg &= ~data->CRITICAL_PWM_ENABLE_MASK;
else
reg |= data->CRITICAL_PWM_ENABLE_MASK;
- data->write_value(data,
- data->REG_CRITICAL_PWM_ENABLE[nr],
- reg);
+ err = nct6775_write_value(data, data->REG_CRITICAL_PWM_ENABLE[nr], reg);
break;
}
}
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static ssize_t
@@ -3435,6 +3134,9 @@ show_auto_temp(struct device *dev, struct device_attribute *attr, char *buf)
int nr = sattr->nr;
int point = sattr->index;
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
/*
* We don't know for sure if the temperature is signed or unsigned.
* Assume it is unsigned.
@@ -3462,15 +3164,14 @@ store_auto_temp(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
data->auto_temp[nr][point] = DIV_ROUND_CLOSEST(val, 1000);
if (point < data->auto_pwm_num) {
- data->write_value(data,
- NCT6775_AUTO_TEMP(data, nr, point),
- data->auto_temp[nr][point]);
+ err = nct6775_write_value(data, NCT6775_AUTO_TEMP(data, nr, point),
+ data->auto_temp[nr][point]);
} else {
- data->write_value(data, data->REG_CRITICAL_TEMP[nr],
- data->auto_temp[nr][point]);
+ err = nct6775_write_value(data, data->REG_CRITICAL_TEMP[nr],
+ data->auto_temp[nr][point]);
}
mutex_unlock(&data->update_lock);
- return count;
+ return err ? : count;
}
static umode_t nct6775_pwm_is_visible(struct kobject *kobj,
@@ -3500,65 +3201,59 @@ static umode_t nct6775_pwm_is_visible(struct kobject *kobj,
if (api > data->auto_pwm_num)
return 0;
}
- return attr->mode;
+ return nct6775_attr_mode(data, attr);
}
-SENSOR_TEMPLATE_2(pwm_stop_time, "pwm%d_stop_time", S_IWUSR | S_IRUGO,
- show_fan_time, store_fan_time, 0, 0);
-SENSOR_TEMPLATE_2(pwm_step_up_time, "pwm%d_step_up_time", S_IWUSR | S_IRUGO,
+SENSOR_TEMPLATE_2(pwm_stop_time, "pwm%d_stop_time", 0644, show_fan_time, store_fan_time, 0, 0);
+SENSOR_TEMPLATE_2(pwm_step_up_time, "pwm%d_step_up_time", 0644,
show_fan_time, store_fan_time, 0, 1);
-SENSOR_TEMPLATE_2(pwm_step_down_time, "pwm%d_step_down_time", S_IWUSR | S_IRUGO,
+SENSOR_TEMPLATE_2(pwm_step_down_time, "pwm%d_step_down_time", 0644,
show_fan_time, store_fan_time, 0, 2);
-SENSOR_TEMPLATE_2(pwm_start, "pwm%d_start", S_IWUSR | S_IRUGO, show_pwm,
- store_pwm, 0, 1);
-SENSOR_TEMPLATE_2(pwm_floor, "pwm%d_floor", S_IWUSR | S_IRUGO, show_pwm,
- store_pwm, 0, 2);
-SENSOR_TEMPLATE_2(pwm_temp_tolerance, "pwm%d_temp_tolerance", S_IWUSR | S_IRUGO,
+SENSOR_TEMPLATE_2(pwm_start, "pwm%d_start", 0644, show_pwm, store_pwm, 0, 1);
+SENSOR_TEMPLATE_2(pwm_floor, "pwm%d_floor", 0644, show_pwm, store_pwm, 0, 2);
+SENSOR_TEMPLATE_2(pwm_temp_tolerance, "pwm%d_temp_tolerance", 0644,
show_temp_tolerance, store_temp_tolerance, 0, 0);
SENSOR_TEMPLATE_2(pwm_crit_temp_tolerance, "pwm%d_crit_temp_tolerance",
- S_IWUSR | S_IRUGO, show_temp_tolerance, store_temp_tolerance,
- 0, 1);
+ 0644, show_temp_tolerance, store_temp_tolerance, 0, 1);
-SENSOR_TEMPLATE_2(pwm_max, "pwm%d_max", S_IWUSR | S_IRUGO, show_pwm, store_pwm,
- 0, 3);
+SENSOR_TEMPLATE_2(pwm_max, "pwm%d_max", 0644, show_pwm, store_pwm, 0, 3);
-SENSOR_TEMPLATE_2(pwm_step, "pwm%d_step", S_IWUSR | S_IRUGO, show_pwm,
- store_pwm, 0, 4);
+SENSOR_TEMPLATE_2(pwm_step, "pwm%d_step", 0644, show_pwm, store_pwm, 0, 4);
SENSOR_TEMPLATE_2(pwm_auto_point1_pwm, "pwm%d_auto_point1_pwm",
- S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 0);
+ 0644, show_auto_pwm, store_auto_pwm, 0, 0);
SENSOR_TEMPLATE_2(pwm_auto_point1_temp, "pwm%d_auto_point1_temp",
- S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 0);
+ 0644, show_auto_temp, store_auto_temp, 0, 0);
SENSOR_TEMPLATE_2(pwm_auto_point2_pwm, "pwm%d_auto_point2_pwm",
- S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 1);
+ 0644, show_auto_pwm, store_auto_pwm, 0, 1);
SENSOR_TEMPLATE_2(pwm_auto_point2_temp, "pwm%d_auto_point2_temp",
- S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 1);
+ 0644, show_auto_temp, store_auto_temp, 0, 1);
SENSOR_TEMPLATE_2(pwm_auto_point3_pwm, "pwm%d_auto_point3_pwm",
- S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 2);
+ 0644, show_auto_pwm, store_auto_pwm, 0, 2);
SENSOR_TEMPLATE_2(pwm_auto_point3_temp, "pwm%d_auto_point3_temp",
- S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 2);
+ 0644, show_auto_temp, store_auto_temp, 0, 2);
SENSOR_TEMPLATE_2(pwm_auto_point4_pwm, "pwm%d_auto_point4_pwm",
- S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 3);
+ 0644, show_auto_pwm, store_auto_pwm, 0, 3);
SENSOR_TEMPLATE_2(pwm_auto_point4_temp, "pwm%d_auto_point4_temp",
- S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 3);
+ 0644, show_auto_temp, store_auto_temp, 0, 3);
SENSOR_TEMPLATE_2(pwm_auto_point5_pwm, "pwm%d_auto_point5_pwm",
- S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 4);
+ 0644, show_auto_pwm, store_auto_pwm, 0, 4);
SENSOR_TEMPLATE_2(pwm_auto_point5_temp, "pwm%d_auto_point5_temp",
- S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 4);
+ 0644, show_auto_temp, store_auto_temp, 0, 4);
SENSOR_TEMPLATE_2(pwm_auto_point6_pwm, "pwm%d_auto_point6_pwm",
- S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 5);
+ 0644, show_auto_pwm, store_auto_pwm, 0, 5);
SENSOR_TEMPLATE_2(pwm_auto_point6_temp, "pwm%d_auto_point6_temp",
- S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 5);
+ 0644, show_auto_temp, store_auto_temp, 0, 5);
SENSOR_TEMPLATE_2(pwm_auto_point7_pwm, "pwm%d_auto_point7_pwm",
- S_IWUSR | S_IRUGO, show_auto_pwm, store_auto_pwm, 0, 6);
+ 0644, show_auto_pwm, store_auto_pwm, 0, 6);
SENSOR_TEMPLATE_2(pwm_auto_point7_temp, "pwm%d_auto_point7_temp",
- S_IWUSR | S_IRUGO, show_auto_temp, store_auto_temp, 0, 6);
+ 0644, show_auto_temp, store_auto_temp, 0, 6);
/*
* nct6775_pwm_is_visible uses the index into the following array
@@ -3612,123 +3307,21 @@ static const struct sensor_template_group nct6775_pwm_template_group = {
.base = 1,
};
-static ssize_t
-cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct nct6775_data *data = dev_get_drvdata(dev);
-
- return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
-}
-
-static DEVICE_ATTR_RO(cpu0_vid);
-
-/* Case open detection */
-
-static ssize_t
-clear_caseopen(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static inline int nct6775_init_device(struct nct6775_data *data)
{
- struct nct6775_data *data = dev_get_drvdata(dev);
- struct nct6775_sio_data *sio_data = data->sio_data;
- int nr = to_sensor_dev_attr(attr)->index - INTRUSION_ALARM_BASE;
- unsigned long val;
- u8 reg;
- int ret;
-
- if (kstrtoul(buf, 10, &val) || val != 0)
- return -EINVAL;
-
- mutex_lock(&data->update_lock);
-
- /*
- * Use CR registers to clear caseopen status.
- * The CR registers are the same for all chips, and not all chips
- * support clearing the caseopen status through "regular" registers.
- */
- ret = sio_data->sio_enter(sio_data);
- if (ret) {
- count = ret;
- goto error;
- }
-
- sio_data->sio_select(sio_data, NCT6775_LD_ACPI);
- reg = sio_data->sio_inb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr]);
- reg |= NCT6775_CR_CASEOPEN_CLR_MASK[nr];
- sio_data->sio_outb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg);
- reg &= ~NCT6775_CR_CASEOPEN_CLR_MASK[nr];
- sio_data->sio_outb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg);
- sio_data->sio_exit(sio_data);
-
- data->valid = false; /* Force cache refresh */
-error:
- mutex_unlock(&data->update_lock);
- return count;
-}
-
-static SENSOR_DEVICE_ATTR(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm,
- clear_caseopen, INTRUSION_ALARM_BASE);
-static SENSOR_DEVICE_ATTR(intrusion1_alarm, S_IWUSR | S_IRUGO, show_alarm,
- clear_caseopen, INTRUSION_ALARM_BASE + 1);
-static SENSOR_DEVICE_ATTR(intrusion0_beep, S_IWUSR | S_IRUGO, show_beep,
- store_beep, INTRUSION_ALARM_BASE);
-static SENSOR_DEVICE_ATTR(intrusion1_beep, S_IWUSR | S_IRUGO, show_beep,
- store_beep, INTRUSION_ALARM_BASE + 1);
-static SENSOR_DEVICE_ATTR(beep_enable, S_IWUSR | S_IRUGO, show_beep,
- store_beep, BEEP_ENABLE_BASE);
-
-static umode_t nct6775_other_is_visible(struct kobject *kobj,
- struct attribute *attr, int index)
-{
- struct device *dev = kobj_to_dev(kobj);
- struct nct6775_data *data = dev_get_drvdata(dev);
-
- if (index == 0 && !data->have_vid)
- return 0;
-
- if (index == 1 || index == 2) {
- if (data->ALARM_BITS[INTRUSION_ALARM_BASE + index - 1] < 0)
- return 0;
- }
-
- if (index == 3 || index == 4) {
- if (data->BEEP_BITS[INTRUSION_ALARM_BASE + index - 3] < 0)
- return 0;
- }
-
- return attr->mode;
-}
-
-/*
- * nct6775_other_is_visible uses the index into the following array
- * to determine if attributes should be created or not.
- * Any change in order or content must be matched.
- */
-static struct attribute *nct6775_attributes_other[] = {
- &dev_attr_cpu0_vid.attr, /* 0 */
- &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, /* 1 */
- &sensor_dev_attr_intrusion1_alarm.dev_attr.attr, /* 2 */
- &sensor_dev_attr_intrusion0_beep.dev_attr.attr, /* 3 */
- &sensor_dev_attr_intrusion1_beep.dev_attr.attr, /* 4 */
- &sensor_dev_attr_beep_enable.dev_attr.attr, /* 5 */
-
- NULL
-};
-
-static const struct attribute_group nct6775_group_other = {
- .attrs = nct6775_attributes_other,
- .is_visible = nct6775_other_is_visible,
-};
-
-static inline void nct6775_init_device(struct nct6775_data *data)
-{
- int i;
- u8 tmp, diode;
+ int i, err;
+ u16 tmp, diode;
/* Start monitoring if needed */
if (data->REG_CONFIG) {
- tmp = data->read_value(data, data->REG_CONFIG);
- if (!(tmp & 0x01))
- data->write_value(data, data->REG_CONFIG, tmp | 0x01);
+ err = nct6775_read_value(data, data->REG_CONFIG, &tmp);
+ if (err)
+ return err;
+ if (!(tmp & 0x01)) {
+ err = nct6775_write_value(data, data->REG_CONFIG, tmp | 0x01);
+ if (err)
+ return err;
+ }
}
/* Enable temperature sensors if needed */
@@ -3737,18 +3330,29 @@ static inline void nct6775_init_device(struct nct6775_data *data)
continue;
if (!data->reg_temp_config[i])
continue;
- tmp = data->read_value(data, data->reg_temp_config[i]);
- if (tmp & 0x01)
- data->write_value(data, data->reg_temp_config[i],
- tmp & 0xfe);
+ err = nct6775_read_value(data, data->reg_temp_config[i], &tmp);
+ if (err)
+ return err;
+ if (tmp & 0x01) {
+ err = nct6775_write_value(data, data->reg_temp_config[i], tmp & 0xfe);
+ if (err)
+ return err;
+ }
}
/* Enable VBAT monitoring if needed */
- tmp = data->read_value(data, data->REG_VBAT);
- if (!(tmp & 0x01))
- data->write_value(data, data->REG_VBAT, tmp | 0x01);
+ err = nct6775_read_value(data, data->REG_VBAT, &tmp);
+ if (err)
+ return err;
+ if (!(tmp & 0x01)) {
+ err = nct6775_write_value(data, data->REG_VBAT, tmp | 0x01);
+ if (err)
+ return err;
+ }
- diode = data->read_value(data, data->REG_DIODE);
+ err = nct6775_read_value(data, data->REG_DIODE, &diode);
+ if (err)
+ return err;
for (i = 0; i < data->temp_fixed_num; i++) {
if (!(data->have_temp_fixed & BIT(i)))
@@ -3759,241 +3363,24 @@ static inline void nct6775_init_device(struct nct6775_data *data)
else /* thermistor */
data->temp_type[i] = 4;
}
-}
-
-static void
-nct6775_check_fan_inputs(struct nct6775_data *data, struct nct6775_sio_data *sio_data)
-{
- bool fan3pin = false, fan4pin = false, fan4min = false;
- bool fan5pin = false, fan6pin = false, fan7pin = false;
- bool pwm3pin = false, pwm4pin = false, pwm5pin = false;
- bool pwm6pin = false, pwm7pin = false;
-
- /* Store SIO_REG_ENABLE for use during resume */
- sio_data->sio_select(sio_data, NCT6775_LD_HWM);
- data->sio_reg_enable = sio_data->sio_inb(sio_data, SIO_REG_ENABLE);
-
- /* fan4 and fan5 share some pins with the GPIO and serial flash */
- if (data->kind == nct6775) {
- int cr2c = sio_data->sio_inb(sio_data, 0x2c);
-
- fan3pin = cr2c & BIT(6);
- pwm3pin = cr2c & BIT(7);
-
- /* On NCT6775, fan4 shares pins with the fdc interface */
- fan4pin = !(sio_data->sio_inb(sio_data, 0x2A) & 0x80);
- } else if (data->kind == nct6776) {
- bool gpok = sio_data->sio_inb(sio_data, 0x27) & 0x80;
- const char *board_vendor, *board_name;
-
- board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
- board_name = dmi_get_system_info(DMI_BOARD_NAME);
-
- if (board_name && board_vendor &&
- !strcmp(board_vendor, "ASRock")) {
- /*
- * Auxiliary fan monitoring is not enabled on ASRock
- * Z77 Pro4-M if booted in UEFI Ultra-FastBoot mode.
- * Observed with BIOS version 2.00.
- */
- if (!strcmp(board_name, "Z77 Pro4-M")) {
- if ((data->sio_reg_enable & 0xe0) != 0xe0) {
- data->sio_reg_enable |= 0xe0;
- sio_data->sio_outb(sio_data, SIO_REG_ENABLE,
- data->sio_reg_enable);
- }
- }
- }
-
- if (data->sio_reg_enable & 0x80)
- fan3pin = gpok;
- else
- fan3pin = !(sio_data->sio_inb(sio_data, 0x24) & 0x40);
-
- if (data->sio_reg_enable & 0x40)
- fan4pin = gpok;
- else
- fan4pin = sio_data->sio_inb(sio_data, 0x1C) & 0x01;
-
- if (data->sio_reg_enable & 0x20)
- fan5pin = gpok;
- else
- fan5pin = sio_data->sio_inb(sio_data, 0x1C) & 0x02;
-
- fan4min = fan4pin;
- pwm3pin = fan3pin;
- } else if (data->kind == nct6106) {
- int cr24 = sio_data->sio_inb(sio_data, 0x24);
-
- fan3pin = !(cr24 & 0x80);
- pwm3pin = cr24 & 0x08;
- } else if (data->kind == nct6116) {
- int cr1a = sio_data->sio_inb(sio_data, 0x1a);
- int cr1b = sio_data->sio_inb(sio_data, 0x1b);
- int cr24 = sio_data->sio_inb(sio_data, 0x24);
- int cr2a = sio_data->sio_inb(sio_data, 0x2a);
- int cr2b = sio_data->sio_inb(sio_data, 0x2b);
- int cr2f = sio_data->sio_inb(sio_data, 0x2f);
-
- fan3pin = !(cr2b & 0x10);
- fan4pin = (cr2b & 0x80) || // pin 1(2)
- (!(cr2f & 0x10) && (cr1a & 0x04)); // pin 65(66)
- fan5pin = (cr2b & 0x80) || // pin 126(127)
- (!(cr1b & 0x03) && (cr2a & 0x02)); // pin 94(96)
-
- pwm3pin = fan3pin && (cr24 & 0x08);
- pwm4pin = fan4pin;
- pwm5pin = fan5pin;
- } else {
- /*
- * NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D,
- * NCT6797D, NCT6798D
- */
- int cr1a = sio_data->sio_inb(sio_data, 0x1a);
- int cr1b = sio_data->sio_inb(sio_data, 0x1b);
- int cr1c = sio_data->sio_inb(sio_data, 0x1c);
- int cr1d = sio_data->sio_inb(sio_data, 0x1d);
- int cr2a = sio_data->sio_inb(sio_data, 0x2a);
- int cr2b = sio_data->sio_inb(sio_data, 0x2b);
- int cr2d = sio_data->sio_inb(sio_data, 0x2d);
- int cr2f = sio_data->sio_inb(sio_data, 0x2f);
- bool dsw_en = cr2f & BIT(3);
- bool ddr4_en = cr2f & BIT(4);
- int cre0;
- int creb;
- int cred;
-
- sio_data->sio_select(sio_data, NCT6775_LD_12);
- cre0 = sio_data->sio_inb(sio_data, 0xe0);
- creb = sio_data->sio_inb(sio_data, 0xeb);
- cred = sio_data->sio_inb(sio_data, 0xed);
-
- fan3pin = !(cr1c & BIT(5));
- fan4pin = !(cr1c & BIT(6));
- fan5pin = !(cr1c & BIT(7));
-
- pwm3pin = !(cr1c & BIT(0));
- pwm4pin = !(cr1c & BIT(1));
- pwm5pin = !(cr1c & BIT(2));
-
- switch (data->kind) {
- case nct6791:
- fan6pin = cr2d & BIT(1);
- pwm6pin = cr2d & BIT(0);
- break;
- case nct6792:
- fan6pin = !dsw_en && (cr2d & BIT(1));
- pwm6pin = !dsw_en && (cr2d & BIT(0));
- break;
- case nct6793:
- fan5pin |= cr1b & BIT(5);
- fan5pin |= creb & BIT(5);
-
- fan6pin = !dsw_en && (cr2d & BIT(1));
- fan6pin |= creb & BIT(3);
-
- pwm5pin |= cr2d & BIT(7);
- pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
-
- pwm6pin = !dsw_en && (cr2d & BIT(0));
- pwm6pin |= creb & BIT(2);
- break;
- case nct6795:
- fan5pin |= cr1b & BIT(5);
- fan5pin |= creb & BIT(5);
-
- fan6pin = (cr2a & BIT(4)) &&
- (!dsw_en || (cred & BIT(4)));
- fan6pin |= creb & BIT(3);
-
- pwm5pin |= cr2d & BIT(7);
- pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
-
- pwm6pin = (cr2a & BIT(3)) && (cred & BIT(2));
- pwm6pin |= creb & BIT(2);
- break;
- case nct6796:
- fan5pin |= cr1b & BIT(5);
- fan5pin |= (cre0 & BIT(3)) && !(cr1b & BIT(0));
- fan5pin |= creb & BIT(5);
-
- fan6pin = (cr2a & BIT(4)) &&
- (!dsw_en || (cred & BIT(4)));
- fan6pin |= creb & BIT(3);
-
- fan7pin = !(cr2b & BIT(2));
-
- pwm5pin |= cr2d & BIT(7);
- pwm5pin |= (cre0 & BIT(4)) && !(cr1b & BIT(0));
- pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
-
- pwm6pin = (cr2a & BIT(3)) && (cred & BIT(2));
- pwm6pin |= creb & BIT(2);
-
- pwm7pin = !(cr1d & (BIT(2) | BIT(3)));
- break;
- case nct6797:
- fan5pin |= !ddr4_en && (cr1b & BIT(5));
- fan5pin |= creb & BIT(5);
-
- fan6pin = cr2a & BIT(4);
- fan6pin |= creb & BIT(3);
-
- fan7pin = cr1a & BIT(1);
-
- pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
- pwm5pin |= !ddr4_en && (cr2d & BIT(7));
-
- pwm6pin = creb & BIT(2);
- pwm6pin |= cred & BIT(2);
-
- pwm7pin = cr1d & BIT(4);
- break;
- case nct6798:
- fan6pin = !(cr1b & BIT(0)) && (cre0 & BIT(3));
- fan6pin |= cr2a & BIT(4);
- fan6pin |= creb & BIT(5);
-
- fan7pin = cr1b & BIT(5);
- fan7pin |= !(cr2b & BIT(2));
- fan7pin |= creb & BIT(3);
-
- pwm6pin = !(cr1b & BIT(0)) && (cre0 & BIT(4));
- pwm6pin |= !(cred & BIT(2)) && (cr2a & BIT(3));
- pwm6pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
-
- pwm7pin = !(cr1d & (BIT(2) | BIT(3)));
- pwm7pin |= cr2d & BIT(7);
- pwm7pin |= creb & BIT(2);
- break;
- default: /* NCT6779D */
- break;
- }
-
- fan4min = fan4pin;
- }
- /* fan 1 and 2 (0x03) are always present */
- data->has_fan = 0x03 | (fan3pin << 2) | (fan4pin << 3) |
- (fan5pin << 4) | (fan6pin << 5) | (fan7pin << 6);
- data->has_fan_min = 0x03 | (fan3pin << 2) | (fan4min << 3) |
- (fan5pin << 4) | (fan6pin << 5) | (fan7pin << 6);
- data->has_pwm = 0x03 | (pwm3pin << 2) | (pwm4pin << 3) |
- (pwm5pin << 4) | (pwm6pin << 5) | (pwm7pin << 6);
+ return 0;
}
-static void add_temp_sensors(struct nct6775_data *data, const u16 *regp,
- int *available, int *mask)
+static int add_temp_sensors(struct nct6775_data *data, const u16 *regp,
+ int *available, int *mask)
{
- int i;
- u8 src;
+ int i, err;
+ u16 src;
for (i = 0; i < data->pwm_num && *available; i++) {
int index;
if (!regp[i])
continue;
- src = data->read_value(data, regp[i]);
+ err = nct6775_read_value(data, regp[i], &src);
+ if (err)
+ return err;
src &= 0x1f;
if (!src || (*mask & BIT(src)))
continue;
@@ -4001,58 +3388,36 @@ static void add_temp_sensors(struct nct6775_data *data, const u16 *regp,
continue;
index = __ffs(*available);
- data->write_value(data, data->REG_TEMP_SOURCE[index], src);
+ err = nct6775_write_value(data, data->REG_TEMP_SOURCE[index], src);
+ if (err)
+ return err;
*available &= ~BIT(index);
*mask |= BIT(src);
}
+
+ return 0;
}
-static int nct6775_probe(struct platform_device *pdev)
+int nct6775_probe(struct device *dev, struct nct6775_data *data,
+ const struct regmap_config *regmapcfg)
{
- struct device *dev = &pdev->dev;
- struct nct6775_sio_data *sio_data = dev_get_platdata(dev);
- struct nct6775_data *data;
- struct resource *res;
int i, s, err = 0;
- int src, mask, available;
+ int mask, available;
+ u16 src;
const u16 *reg_temp, *reg_temp_over, *reg_temp_hyst, *reg_temp_config;
const u16 *reg_temp_mon, *reg_temp_alternate, *reg_temp_crit;
const u16 *reg_temp_crit_l = NULL, *reg_temp_crit_h = NULL;
int num_reg_temp, num_reg_temp_mon, num_reg_tsi_temp;
- u8 cr2a;
- struct attribute_group *group;
struct device *hwmon_dev;
struct sensor_template_group tsi_temp_tg;
- int num_attr_groups = 0;
-
- if (sio_data->access == access_direct) {
- res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- if (!devm_request_region(&pdev->dev, res->start, IOREGION_LENGTH,
- DRVNAME))
- return -EBUSY;
- }
-
- data = devm_kzalloc(&pdev->dev, sizeof(struct nct6775_data),
- GFP_KERNEL);
- if (!data)
- return -ENOMEM;
- data->kind = sio_data->kind;
- data->sio_data = sio_data;
-
- if (sio_data->access == access_direct) {
- data->addr = res->start;
- data->read_value = nct6775_read_value;
- data->write_value = nct6775_write_value;
- } else {
- data->read_value = nct6775_wmi_read_value;
- data->write_value = nct6775_wmi_write_value;
- }
+ data->regmap = devm_regmap_init(dev, NULL, data, regmapcfg);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
mutex_init(&data->update_lock);
data->name = nct6775_device_names[data->kind];
data->bank = 0xff; /* Force initial bank selection */
- platform_set_drvdata(pdev, data);
switch (data->kind) {
case nct6106:
@@ -4596,7 +3961,10 @@ static int nct6775_probe(struct platform_device *pdev)
if (reg_temp[i] == 0)
continue;
- src = data->read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f;
+ err = nct6775_read_value(data, data->REG_TEMP_SOURCE[i], &src);
+ if (err)
+ return err;
+ src &= 0x1f;
if (!src || (mask & BIT(src)))
available |= BIT(i);
@@ -4607,8 +3975,12 @@ static int nct6775_probe(struct platform_device *pdev)
* Now find unmonitored temperature registers and enable monitoring
* if additional monitoring registers are available.
*/
- add_temp_sensors(data, data->REG_TEMP_SEL, &available, &mask);
- add_temp_sensors(data, data->REG_WEIGHT_TEMP_SEL, &available, &mask);
+ err = add_temp_sensors(data, data->REG_TEMP_SEL, &available, &mask);
+ if (err)
+ return err;
+ err = add_temp_sensors(data, data->REG_WEIGHT_TEMP_SEL, &available, &mask);
+ if (err)
+ return err;
mask = 0;
s = NUM_TEMP_FIXED; /* First dynamic temperature attribute */
@@ -4616,7 +3988,10 @@ static int nct6775_probe(struct platform_device *pdev)
if (reg_temp[i] == 0)
continue;
- src = data->read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f;
+ err = nct6775_read_value(data, data->REG_TEMP_SOURCE[i], &src);
+ if (err)
+ return err;
+ src &= 0x1f;
if (!src || (mask & BIT(src)))
continue;
@@ -4676,7 +4051,10 @@ static int nct6775_probe(struct platform_device *pdev)
if (reg_temp_mon[i] == 0)
continue;
- src = data->read_value(data, data->REG_TEMP_SEL[i]) & 0x1f;
+ err = nct6775_read_value(data, data->REG_TEMP_SEL[i], &src);
+ if (err)
+ return err;
+ src &= 0x1f;
if (!src)
continue;
@@ -4760,525 +4138,68 @@ static int nct6775_probe(struct platform_device *pdev)
/* Check which TSIx_TEMP registers are active */
for (i = 0; i < num_reg_tsi_temp; i++) {
- if (data->read_value(data, data->REG_TSI_TEMP[i]))
+ u16 tmp;
+
+ err = nct6775_read_value(data, data->REG_TSI_TEMP[i], &tmp);
+ if (err)
+ return err;
+ if (tmp)
data->have_tsi_temp |= BIT(i);
}
/* Initialize the chip */
- nct6775_init_device(data);
-
- err = sio_data->sio_enter(sio_data);
+ err = nct6775_init_device(data);
if (err)
return err;
- cr2a = sio_data->sio_inb(sio_data, 0x2a);
- switch (data->kind) {
- case nct6775:
- data->have_vid = (cr2a & 0x40);
- break;
- case nct6776:
- data->have_vid = (cr2a & 0x60) == 0x40;
- break;
- case nct6106:
- case nct6116:
- case nct6779:
- case nct6791:
- case nct6792:
- case nct6793:
- case nct6795:
- case nct6796:
- case nct6797:
- case nct6798:
- break;
- }
-
- /*
- * Read VID value
- * We can get the VID input values directly at logical device D 0xe3.
- */
- if (data->have_vid) {
- sio_data->sio_select(sio_data, NCT6775_LD_VID);
- data->vid = sio_data->sio_inb(sio_data, 0xe3);
- data->vrm = vid_which_vrm();
- }
-
- if (fan_debounce) {
- u8 tmp;
-
- sio_data->sio_select(sio_data, NCT6775_LD_HWM);
- tmp = sio_data->sio_inb(sio_data,
- NCT6775_REG_CR_FAN_DEBOUNCE);
- switch (data->kind) {
- case nct6106:
- case nct6116:
- tmp |= 0xe0;
- break;
- case nct6775:
- tmp |= 0x1e;
- break;
- case nct6776:
- case nct6779:
- tmp |= 0x3e;
- break;
- case nct6791:
- case nct6792:
- case nct6793:
- case nct6795:
- case nct6796:
- case nct6797:
- case nct6798:
- tmp |= 0x7e;
- break;
- }
- sio_data->sio_outb(sio_data, NCT6775_REG_CR_FAN_DEBOUNCE,
- tmp);
- dev_info(&pdev->dev, "Enabled fan debounce for chip %s\n",
- data->name);
+ if (data->driver_init) {
+ err = data->driver_init(data);
+ if (err)
+ return err;
}
- nct6775_check_fan_inputs(data, sio_data);
-
- sio_data->sio_exit(sio_data);
-
/* Read fan clock dividers immediately */
- nct6775_init_fan_common(dev, data);
+ err = nct6775_init_fan_common(dev, data);
+ if (err)
+ return err;
/* Register sysfs hooks */
- group = nct6775_create_attr_group(dev, &nct6775_pwm_template_group,
- data->pwm_num);
- if (IS_ERR(group))
- return PTR_ERR(group);
-
- data->groups[num_attr_groups++] = group;
-
- group = nct6775_create_attr_group(dev, &nct6775_in_template_group,
- fls(data->have_in));
- if (IS_ERR(group))
- return PTR_ERR(group);
-
- data->groups[num_attr_groups++] = group;
-
- group = nct6775_create_attr_group(dev, &nct6775_fan_template_group,
- fls(data->has_fan));
- if (IS_ERR(group))
- return PTR_ERR(group);
+ err = nct6775_add_template_attr_group(dev, data, &nct6775_pwm_template_group,
+ data->pwm_num);
+ if (err)
+ return err;
- data->groups[num_attr_groups++] = group;
+ err = nct6775_add_template_attr_group(dev, data, &nct6775_in_template_group,
+ fls(data->have_in));
+ if (err)
+ return err;
- group = nct6775_create_attr_group(dev, &nct6775_temp_template_group,
- fls(data->have_temp));
- if (IS_ERR(group))
- return PTR_ERR(group);
+ err = nct6775_add_template_attr_group(dev, data, &nct6775_fan_template_group,
+ fls(data->has_fan));
+ if (err)
+ return err;
- data->groups[num_attr_groups++] = group;
+ err = nct6775_add_template_attr_group(dev, data, &nct6775_temp_template_group,
+ fls(data->have_temp));
+ if (err)
+ return err;
if (data->have_tsi_temp) {
tsi_temp_tg.templates = nct6775_tsi_temp_template;
tsi_temp_tg.is_visible = nct6775_tsi_temp_is_visible;
tsi_temp_tg.base = fls(data->have_temp) + 1;
- group = nct6775_create_attr_group(dev, &tsi_temp_tg, fls(data->have_tsi_temp));
- if (IS_ERR(group))
- return PTR_ERR(group);
-
- data->groups[num_attr_groups++] = group;
+ err = nct6775_add_template_attr_group(dev, data, &tsi_temp_tg,
+ fls(data->have_tsi_temp));
+ if (err)
+ return err;
}
- data->groups[num_attr_groups++] = &nct6775_group_other;
-
hwmon_dev = devm_hwmon_device_register_with_groups(dev, data->name,
data, data->groups);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
-
-static void nct6791_enable_io_mapping(struct nct6775_sio_data *sio_data)
-{
- int val;
-
- val = sio_data->sio_inb(sio_data, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE);
- if (val & 0x10) {
- pr_info("Enabling hardware monitor logical device mappings.\n");
- sio_data->sio_outb(sio_data, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE,
- val & ~0x10);
- }
-}
-
-static int __maybe_unused nct6775_suspend(struct device *dev)
-{
- struct nct6775_data *data = nct6775_update_device(dev);
-
- mutex_lock(&data->update_lock);
- data->vbat = data->read_value(data, data->REG_VBAT);
- if (data->kind == nct6775) {
- data->fandiv1 = data->read_value(data, NCT6775_REG_FANDIV1);
- data->fandiv2 = data->read_value(data, NCT6775_REG_FANDIV2);
- }
- mutex_unlock(&data->update_lock);
-
- return 0;
-}
-
-static int __maybe_unused nct6775_resume(struct device *dev)
-{
- struct nct6775_data *data = dev_get_drvdata(dev);
- struct nct6775_sio_data *sio_data = dev_get_platdata(dev);
- int i, j, err = 0;
- u8 reg;
-
- mutex_lock(&data->update_lock);
- data->bank = 0xff; /* Force initial bank selection */
-
- err = sio_data->sio_enter(sio_data);
- if (err)
- goto abort;
-
- sio_data->sio_select(sio_data, NCT6775_LD_HWM);
- reg = sio_data->sio_inb(sio_data, SIO_REG_ENABLE);
- if (reg != data->sio_reg_enable)
- sio_data->sio_outb(sio_data, SIO_REG_ENABLE, data->sio_reg_enable);
-
- if (data->kind == nct6791 || data->kind == nct6792 ||
- data->kind == nct6793 || data->kind == nct6795 ||
- data->kind == nct6796 || data->kind == nct6797 ||
- data->kind == nct6798)
- nct6791_enable_io_mapping(sio_data);
-
- sio_data->sio_exit(sio_data);
-
- /* Restore limits */
- for (i = 0; i < data->in_num; i++) {
- if (!(data->have_in & BIT(i)))
- continue;
-
- data->write_value(data, data->REG_IN_MINMAX[0][i],
- data->in[i][1]);
- data->write_value(data, data->REG_IN_MINMAX[1][i],
- data->in[i][2]);
- }
-
- for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) {
- if (!(data->has_fan_min & BIT(i)))
- continue;
-
- data->write_value(data, data->REG_FAN_MIN[i],
- data->fan_min[i]);
- }
-
- for (i = 0; i < NUM_TEMP; i++) {
- if (!(data->have_temp & BIT(i)))
- continue;
-
- for (j = 1; j < ARRAY_SIZE(data->reg_temp); j++)
- if (data->reg_temp[j][i])
- nct6775_write_temp(data, data->reg_temp[j][i],
- data->temp[j][i]);
- }
-
- /* Restore other settings */
- data->write_value(data, data->REG_VBAT, data->vbat);
- if (data->kind == nct6775) {
- data->write_value(data, NCT6775_REG_FANDIV1, data->fandiv1);
- data->write_value(data, NCT6775_REG_FANDIV2, data->fandiv2);
- }
-
-abort:
- /* Force re-reading all values */
- data->valid = false;
- mutex_unlock(&data->update_lock);
-
- return err;
-}
-
-static SIMPLE_DEV_PM_OPS(nct6775_dev_pm_ops, nct6775_suspend, nct6775_resume);
-
-static struct platform_driver nct6775_driver = {
- .driver = {
- .name = DRVNAME,
- .pm = &nct6775_dev_pm_ops,
- },
- .probe = nct6775_probe,
-};
-
-/* nct6775_find() looks for a '627 in the Super-I/O config space */
-static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
-{
- u16 val;
- int err;
- int addr;
-
- sio_data->access = access_direct;
- sio_data->sioreg = sioaddr;
-
- err = sio_data->sio_enter(sio_data);
- if (err)
- return err;
-
- val = (sio_data->sio_inb(sio_data, SIO_REG_DEVID) << 8) |
- sio_data->sio_inb(sio_data, SIO_REG_DEVID + 1);
- if (force_id && val != 0xffff)
- val = force_id;
-
- switch (val & SIO_ID_MASK) {
- case SIO_NCT6106_ID:
- sio_data->kind = nct6106;
- break;
- case SIO_NCT6116_ID:
- sio_data->kind = nct6116;
- break;
- case SIO_NCT6775_ID:
- sio_data->kind = nct6775;
- break;
- case SIO_NCT6776_ID:
- sio_data->kind = nct6776;
- break;
- case SIO_NCT6779_ID:
- sio_data->kind = nct6779;
- break;
- case SIO_NCT6791_ID:
- sio_data->kind = nct6791;
- break;
- case SIO_NCT6792_ID:
- sio_data->kind = nct6792;
- break;
- case SIO_NCT6793_ID:
- sio_data->kind = nct6793;
- break;
- case SIO_NCT6795_ID:
- sio_data->kind = nct6795;
- break;
- case SIO_NCT6796_ID:
- sio_data->kind = nct6796;
- break;
- case SIO_NCT6797_ID:
- sio_data->kind = nct6797;
- break;
- case SIO_NCT6798_ID:
- sio_data->kind = nct6798;
- break;
- default:
- if (val != 0xffff)
- pr_debug("unsupported chip ID: 0x%04x\n", val);
- sio_data->sio_exit(sio_data);
- return -ENODEV;
- }
-
- /* We have a known chip, find the HWM I/O address */
- sio_data->sio_select(sio_data, NCT6775_LD_HWM);
- val = (sio_data->sio_inb(sio_data, SIO_REG_ADDR) << 8)
- | sio_data->sio_inb(sio_data, SIO_REG_ADDR + 1);
- addr = val & IOREGION_ALIGNMENT;
- if (addr == 0) {
- pr_err("Refusing to enable a Super-I/O device with a base I/O port 0\n");
- sio_data->sio_exit(sio_data);
- return -ENODEV;
- }
-
- /* Activate logical device if needed */
- val = sio_data->sio_inb(sio_data, SIO_REG_ENABLE);
- if (!(val & 0x01)) {
- pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n");
- sio_data->sio_outb(sio_data, SIO_REG_ENABLE, val | 0x01);
- }
-
- if (sio_data->kind == nct6791 || sio_data->kind == nct6792 ||
- sio_data->kind == nct6793 || sio_data->kind == nct6795 ||
- sio_data->kind == nct6796 || sio_data->kind == nct6797 ||
- sio_data->kind == nct6798)
- nct6791_enable_io_mapping(sio_data);
-
- sio_data->sio_exit(sio_data);
- pr_info("Found %s or compatible chip at %#x:%#x\n",
- nct6775_sio_names[sio_data->kind], sioaddr, addr);
-
- return addr;
-}
-
-/*
- * when Super-I/O functions move to a separate file, the Super-I/O
- * bus will manage the lifetime of the device and this module will only keep
- * track of the nct6775 driver. But since we use platform_device_alloc(), we
- * must keep track of the device
- */
-static struct platform_device *pdev[2];
-
-static const char * const asus_wmi_boards[] = {
- "ProArt X570-CREATOR WIFI",
- "Pro B550M-C",
- "Pro WS X570-ACE",
- "PRIME B360-PLUS",
- "PRIME B460-PLUS",
- "PRIME B550-PLUS",
- "PRIME B550M-A",
- "PRIME B550M-A (WI-FI)",
- "PRIME X570-P",
- "PRIME X570-PRO",
- "ROG CROSSHAIR VIII DARK HERO",
- "ROG CROSSHAIR VIII FORMULA",
- "ROG CROSSHAIR VIII HERO",
- "ROG CROSSHAIR VIII IMPACT",
- "ROG STRIX B550-A GAMING",
- "ROG STRIX B550-E GAMING",
- "ROG STRIX B550-F GAMING",
- "ROG STRIX B550-F GAMING (WI-FI)",
- "ROG STRIX B550-F GAMING WIFI II",
- "ROG STRIX B550-I GAMING",
- "ROG STRIX B550-XE GAMING (WI-FI)",
- "ROG STRIX X570-E GAMING",
- "ROG STRIX X570-F GAMING",
- "ROG STRIX X570-I GAMING",
- "ROG STRIX Z390-E GAMING",
- "ROG STRIX Z390-F GAMING",
- "ROG STRIX Z390-H GAMING",
- "ROG STRIX Z390-I GAMING",
- "ROG STRIX Z490-A GAMING",
- "ROG STRIX Z490-E GAMING",
- "ROG STRIX Z490-F GAMING",
- "ROG STRIX Z490-G GAMING",
- "ROG STRIX Z490-G GAMING (WI-FI)",
- "ROG STRIX Z490-H GAMING",
- "ROG STRIX Z490-I GAMING",
- "TUF GAMING B550M-PLUS",
- "TUF GAMING B550M-PLUS (WI-FI)",
- "TUF GAMING B550-PLUS",
- "TUF GAMING B550-PRO",
- "TUF GAMING X570-PLUS",
- "TUF GAMING X570-PLUS (WI-FI)",
- "TUF GAMING X570-PRO (WI-FI)",
- "TUF GAMING Z490-PLUS",
- "TUF GAMING Z490-PLUS (WI-FI)",
-};
-
-static int __init sensors_nct6775_init(void)
-{
- int i, err;
- bool found = false;
- int address;
- struct resource res;
- struct nct6775_sio_data sio_data;
- int sioaddr[2] = { 0x2e, 0x4e };
- enum sensor_access access = access_direct;
- const char *board_vendor, *board_name;
- u8 tmp;
-
- err = platform_driver_register(&nct6775_driver);
- if (err)
- return err;
-
- board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
- board_name = dmi_get_system_info(DMI_BOARD_NAME);
-
- if (board_name && board_vendor &&
- !strcmp(board_vendor, "ASUSTeK COMPUTER INC.")) {
- err = match_string(asus_wmi_boards, ARRAY_SIZE(asus_wmi_boards),
- board_name);
- if (err >= 0) {
- /* if reading chip id via WMI succeeds, use WMI */
- if (!nct6775_asuswmi_read(0, NCT6775_PORT_CHIPID, &tmp) && tmp) {
- pr_info("Using Asus WMI to access %#x chip.\n", tmp);
- access = access_asuswmi;
- } else {
- pr_err("Can't read ChipID by Asus WMI.\n");
- }
- }
- }
-
- /*
- * initialize sio_data->kind and sio_data->sioreg.
- *
- * when Super-I/O functions move to a separate file, the Super-I/O
- * driver will probe 0x2e and 0x4e and auto-detect the presence of a
- * nct6775 hardware monitor, and call probe()
- */
- for (i = 0; i < ARRAY_SIZE(pdev); i++) {
- sio_data.sio_outb = superio_outb;
- sio_data.sio_inb = superio_inb;
- sio_data.sio_select = superio_select;
- sio_data.sio_enter = superio_enter;
- sio_data.sio_exit = superio_exit;
-
- address = nct6775_find(sioaddr[i], &sio_data);
- if (address <= 0)
- continue;
-
- found = true;
-
- sio_data.access = access;
-
- if (access == access_asuswmi) {
- sio_data.sio_outb = superio_wmi_outb;
- sio_data.sio_inb = superio_wmi_inb;
- sio_data.sio_select = superio_wmi_select;
- sio_data.sio_enter = superio_wmi_enter;
- sio_data.sio_exit = superio_wmi_exit;
- }
-
- pdev[i] = platform_device_alloc(DRVNAME, address);
- if (!pdev[i]) {
- err = -ENOMEM;
- goto exit_device_unregister;
- }
-
- err = platform_device_add_data(pdev[i], &sio_data,
- sizeof(struct nct6775_sio_data));
- if (err)
- goto exit_device_put;
-
- if (sio_data.access == access_direct) {
- memset(&res, 0, sizeof(res));
- res.name = DRVNAME;
- res.start = address + IOREGION_OFFSET;
- res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1;
- res.flags = IORESOURCE_IO;
-
- err = acpi_check_resource_conflict(&res);
- if (err) {
- platform_device_put(pdev[i]);
- pdev[i] = NULL;
- continue;
- }
-
- err = platform_device_add_resources(pdev[i], &res, 1);
- if (err)
- goto exit_device_put;
- }
-
- /* platform_device_add calls probe() */
- err = platform_device_add(pdev[i]);
- if (err)
- goto exit_device_put;
- }
- if (!found) {
- err = -ENODEV;
- goto exit_unregister;
- }
-
- return 0;
-
-exit_device_put:
- platform_device_put(pdev[i]);
-exit_device_unregister:
- while (--i >= 0) {
- if (pdev[i])
- platform_device_unregister(pdev[i]);
- }
-exit_unregister:
- platform_driver_unregister(&nct6775_driver);
- return err;
-}
-
-static void __exit sensors_nct6775_exit(void)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(pdev); i++) {
- if (pdev[i])
- platform_device_unregister(pdev[i]);
- }
- platform_driver_unregister(&nct6775_driver);
-}
+EXPORT_SYMBOL_GPL(nct6775_probe);
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
-MODULE_DESCRIPTION("Driver for NCT6775F and compatible chips");
+MODULE_DESCRIPTION("Core driver for NCT6775F and compatible chips");
MODULE_LICENSE("GPL");
-
-module_init(sensors_nct6775_init);
-module_exit(sensors_nct6775_exit);
diff --git a/drivers/hwmon/nct6775-i2c.c b/drivers/hwmon/nct6775-i2c.c
new file mode 100644
index 000000000000..e1bcd1146191
--- /dev/null
+++ b/drivers/hwmon/nct6775-i2c.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * nct6775-i2c - I2C driver for the hardware monitoring functionality of
+ * Nuvoton NCT677x Super-I/O chips
+ *
+ * Copyright (C) 2022 Zev Weiss <zev@bewilderbeest.net>
+ *
+ * This driver interacts with the chip via it's "back door" i2c interface, as
+ * is often exposed to a BMC. Because the host may still be operating the
+ * chip via the ("front door") LPC interface, this driver cannot assume that
+ * it actually has full control of the chip, and in particular must avoid
+ * making any changes that could confuse the host's LPC usage of it. It thus
+ * operates in a strictly read-only fashion, with the only exception being the
+ * bank-select register (which seems, thankfully, to be replicated for the i2c
+ * interface so it doesn't affect the LPC interface).
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include "nct6775.h"
+
+static int nct6775_i2c_read(void *ctx, unsigned int reg, unsigned int *val)
+{
+ int ret;
+ u32 tmp;
+ u8 bank = reg >> 8;
+ struct nct6775_data *data = ctx;
+ struct i2c_client *client = data->driver_data;
+
+ if (bank != data->bank) {
+ ret = i2c_smbus_write_byte_data(client, NCT6775_REG_BANK, bank);
+ if (ret)
+ return ret;
+ data->bank = bank;
+ }
+
+ ret = i2c_smbus_read_byte_data(client, reg & 0xff);
+ if (ret < 0)
+ return ret;
+ tmp = ret;
+
+ if (nct6775_reg_is_word_sized(data, reg)) {
+ ret = i2c_smbus_read_byte_data(client, (reg & 0xff) + 1);
+ if (ret < 0)
+ return ret;
+ tmp = (tmp << 8) | ret;
+ }
+
+ *val = tmp;
+ return 0;
+}
+
+/*
+ * The write operation is a dummy so as not to disturb anything being done
+ * with the chip via LPC.
+ */
+static int nct6775_i2c_write(void *ctx, unsigned int reg, unsigned int value)
+{
+ struct nct6775_data *data = ctx;
+ struct i2c_client *client = data->driver_data;
+
+ dev_dbg(&client->dev, "skipping attempted write: %02x -> %03x\n", value, reg);
+
+ /*
+ * This is a lie, but writing anything but the bank-select register is
+ * something this driver shouldn't be doing.
+ */
+ return 0;
+}
+
+static const struct of_device_id __maybe_unused nct6775_i2c_of_match[] = {
+ { .compatible = "nuvoton,nct6106", .data = (void *)nct6106, },
+ { .compatible = "nuvoton,nct6116", .data = (void *)nct6116, },
+ { .compatible = "nuvoton,nct6775", .data = (void *)nct6775, },
+ { .compatible = "nuvoton,nct6776", .data = (void *)nct6776, },
+ { .compatible = "nuvoton,nct6779", .data = (void *)nct6779, },
+ { .compatible = "nuvoton,nct6791", .data = (void *)nct6791, },
+ { .compatible = "nuvoton,nct6792", .data = (void *)nct6792, },
+ { .compatible = "nuvoton,nct6793", .data = (void *)nct6793, },
+ { .compatible = "nuvoton,nct6795", .data = (void *)nct6795, },
+ { .compatible = "nuvoton,nct6796", .data = (void *)nct6796, },
+ { .compatible = "nuvoton,nct6797", .data = (void *)nct6797, },
+ { .compatible = "nuvoton,nct6798", .data = (void *)nct6798, },
+ { },
+};
+MODULE_DEVICE_TABLE(of, nct6775_i2c_of_match);
+
+static const struct i2c_device_id nct6775_i2c_id[] = {
+ { "nct6106", nct6106 },
+ { "nct6116", nct6116 },
+ { "nct6775", nct6775 },
+ { "nct6776", nct6776 },
+ { "nct6779", nct6779 },
+ { "nct6791", nct6791 },
+ { "nct6792", nct6792 },
+ { "nct6793", nct6793 },
+ { "nct6795", nct6795 },
+ { "nct6796", nct6796 },
+ { "nct6797", nct6797 },
+ { "nct6798", nct6798 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nct6775_i2c_id);
+
+static int nct6775_i2c_probe_init(struct nct6775_data *data)
+{
+ u32 tsi_channel_mask;
+ struct i2c_client *client = data->driver_data;
+
+ /*
+ * The i2c interface doesn't provide access to the control registers
+ * needed to determine the presence of other fans, but fans 1 and 2
+ * are (in principle) always there.
+ *
+ * In practice this is perhaps a little silly, because the system
+ * using this driver is mostly likely a BMC, and hence probably has
+ * totally separate fan tachs & pwms of its own that are actually
+ * controlling/monitoring the fans -- these are thus unlikely to be
+ * doing anything actually useful.
+ */
+ data->has_fan = 0x03;
+ data->has_fan_min = 0x03;
+ data->has_pwm = 0x03;
+
+ /*
+ * Because on a BMC this driver may be bound very shortly after power
+ * is first applied to the device, the automatic TSI channel detection
+ * in nct6775_probe() (which has already been run at this point) may
+ * not find anything if a channel hasn't yet produced a temperature
+ * reading. Augment whatever was found via autodetection (if
+ * anything) with the channels DT says should be active.
+ */
+ if (!of_property_read_u32(client->dev.of_node, "nuvoton,tsi-channel-mask",
+ &tsi_channel_mask))
+ data->have_tsi_temp |= tsi_channel_mask & GENMASK(NUM_TSI_TEMP - 1, 0);
+
+ return 0;
+}
+
+static const struct regmap_config nct6775_i2c_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .reg_read = nct6775_i2c_read,
+ .reg_write = nct6775_i2c_write,
+};
+
+static int nct6775_i2c_probe(struct i2c_client *client)
+{
+ struct nct6775_data *data;
+ const struct of_device_id *of_id;
+ const struct i2c_device_id *i2c_id;
+ struct device *dev = &client->dev;
+
+ of_id = of_match_device(nct6775_i2c_of_match, dev);
+ i2c_id = i2c_match_id(nct6775_i2c_id, client);
+
+ if (of_id && (unsigned long)of_id->data != i2c_id->driver_data)
+ dev_notice(dev, "Device mismatch: %s in device tree, %s detected\n",
+ of_id->name, i2c_id->name);
+
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->kind = i2c_id->driver_data;
+
+ data->read_only = true;
+ data->driver_data = client;
+ data->driver_init = nct6775_i2c_probe_init;
+
+ return nct6775_probe(dev, data, &nct6775_i2c_regmap_config);
+}
+
+static struct i2c_driver nct6775_i2c_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "nct6775-i2c",
+ .of_match_table = of_match_ptr(nct6775_i2c_of_match),
+ },
+ .probe_new = nct6775_i2c_probe,
+ .id_table = nct6775_i2c_id,
+};
+
+module_i2c_driver(nct6775_i2c_driver);
+
+MODULE_AUTHOR("Zev Weiss <zev@bewilderbeest.net>");
+MODULE_DESCRIPTION("I2C driver for NCT6775F and compatible chips");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(HWMON_NCT6775);
diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c
new file mode 100644
index 000000000000..6d46c9401898
--- /dev/null
+++ b/drivers/hwmon/nct6775-platform.c
@@ -0,0 +1,1229 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * nct6775 - Platform driver for the hardware monitoring
+ * functionality of Nuvoton NCT677x Super-I/O chips
+ *
+ * Copyright (C) 2012 Guenter Roeck <linux@roeck-us.net>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon-vid.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/wmi.h>
+
+#include "nct6775.h"
+
+enum sensor_access { access_direct, access_asuswmi };
+
+static const char * const nct6775_sio_names[] __initconst = {
+ "NCT6106D",
+ "NCT6116D",
+ "NCT6775F",
+ "NCT6776D/F",
+ "NCT6779D",
+ "NCT6791D",
+ "NCT6792D",
+ "NCT6793D",
+ "NCT6795D",
+ "NCT6796D",
+ "NCT6797D",
+ "NCT6798D",
+};
+
+static unsigned short force_id;
+module_param(force_id, ushort, 0);
+MODULE_PARM_DESC(force_id, "Override the detected device ID");
+
+static unsigned short fan_debounce;
+module_param(fan_debounce, ushort, 0);
+MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
+
+#define DRVNAME "nct6775"
+
+#define NCT6775_PORT_CHIPID 0x58
+
+/*
+ * ISA constants
+ */
+
+#define IOREGION_ALIGNMENT (~7)
+#define IOREGION_OFFSET 5
+#define IOREGION_LENGTH 2
+#define ADDR_REG_OFFSET 0
+#define DATA_REG_OFFSET 1
+
+/*
+ * Super-I/O constants and functions
+ */
+
+#define NCT6775_LD_ACPI 0x0a
+#define NCT6775_LD_HWM 0x0b
+#define NCT6775_LD_VID 0x0d
+#define NCT6775_LD_12 0x12
+
+#define SIO_REG_LDSEL 0x07 /* Logical device select */
+#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
+#define SIO_REG_ENABLE 0x30 /* Logical device enable */
+#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
+
+#define SIO_NCT6106_ID 0xc450
+#define SIO_NCT6116_ID 0xd280
+#define SIO_NCT6775_ID 0xb470
+#define SIO_NCT6776_ID 0xc330
+#define SIO_NCT6779_ID 0xc560
+#define SIO_NCT6791_ID 0xc800
+#define SIO_NCT6792_ID 0xc910
+#define SIO_NCT6793_ID 0xd120
+#define SIO_NCT6795_ID 0xd350
+#define SIO_NCT6796_ID 0xd420
+#define SIO_NCT6797_ID 0xd450
+#define SIO_NCT6798_ID 0xd428
+#define SIO_ID_MASK 0xFFF8
+
+/*
+ * Control registers
+ */
+#define NCT6775_REG_CR_FAN_DEBOUNCE 0xf0
+
+struct nct6775_sio_data {
+ int sioreg;
+ int ld;
+ enum kinds kind;
+ enum sensor_access access;
+
+ /* superio_() callbacks */
+ void (*sio_outb)(struct nct6775_sio_data *sio_data, int reg, int val);
+ int (*sio_inb)(struct nct6775_sio_data *sio_data, int reg);
+ void (*sio_select)(struct nct6775_sio_data *sio_data, int ld);
+ int (*sio_enter)(struct nct6775_sio_data *sio_data);
+ void (*sio_exit)(struct nct6775_sio_data *sio_data);
+};
+
+#define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66"
+#define ASUSWMI_METHODID_RSIO 0x5253494F
+#define ASUSWMI_METHODID_WSIO 0x5753494F
+#define ASUSWMI_METHODID_RHWM 0x5248574D
+#define ASUSWMI_METHODID_WHWM 0x5748574D
+#define ASUSWMI_UNSUPPORTED_METHOD 0xFFFFFFFE
+
+static int nct6775_asuswmi_evaluate_method(u32 method_id, u8 bank, u8 reg, u8 val, u32 *retval)
+{
+#if IS_ENABLED(CONFIG_ACPI_WMI)
+ u32 args = bank | (reg << 8) | (val << 16);
+ struct acpi_buffer input = { (acpi_size) sizeof(args), &args };
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ acpi_status status;
+ union acpi_object *obj;
+ u32 tmp = ASUSWMI_UNSUPPORTED_METHOD;
+
+ status = wmi_evaluate_method(ASUSWMI_MONITORING_GUID, 0,
+ method_id, &input, &output);
+
+ if (ACPI_FAILURE(status))
+ return -EIO;
+
+ obj = output.pointer;
+ if (obj && obj->type == ACPI_TYPE_INTEGER)
+ tmp = obj->integer.value;
+
+ if (retval)
+ *retval = tmp;
+
+ kfree(obj);
+
+ if (tmp == ASUSWMI_UNSUPPORTED_METHOD)
+ return -ENODEV;
+ return 0;
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+static inline int nct6775_asuswmi_write(u8 bank, u8 reg, u8 val)
+{
+ return nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_WHWM, bank,
+ reg, val, NULL);
+}
+
+static inline int nct6775_asuswmi_read(u8 bank, u8 reg, u8 *val)
+{
+ u32 ret, tmp = 0;
+
+ ret = nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_RHWM, bank,
+ reg, 0, &tmp);
+ *val = tmp;
+ return ret;
+}
+
+static int superio_wmi_inb(struct nct6775_sio_data *sio_data, int reg)
+{
+ int tmp = 0;
+
+ nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_RSIO, sio_data->ld,
+ reg, 0, &tmp);
+ return tmp;
+}
+
+static void superio_wmi_outb(struct nct6775_sio_data *sio_data, int reg, int val)
+{
+ nct6775_asuswmi_evaluate_method(ASUSWMI_METHODID_WSIO, sio_data->ld,
+ reg, val, NULL);
+}
+
+static void superio_wmi_select(struct nct6775_sio_data *sio_data, int ld)
+{
+ sio_data->ld = ld;
+}
+
+static int superio_wmi_enter(struct nct6775_sio_data *sio_data)
+{
+ return 0;
+}
+
+static void superio_wmi_exit(struct nct6775_sio_data *sio_data)
+{
+}
+
+static void superio_outb(struct nct6775_sio_data *sio_data, int reg, int val)
+{
+ int ioreg = sio_data->sioreg;
+
+ outb(reg, ioreg);
+ outb(val, ioreg + 1);
+}
+
+static int superio_inb(struct nct6775_sio_data *sio_data, int reg)
+{
+ int ioreg = sio_data->sioreg;
+
+ outb(reg, ioreg);
+ return inb(ioreg + 1);
+}
+
+static void superio_select(struct nct6775_sio_data *sio_data, int ld)
+{
+ int ioreg = sio_data->sioreg;
+
+ outb(SIO_REG_LDSEL, ioreg);
+ outb(ld, ioreg + 1);
+}
+
+static int superio_enter(struct nct6775_sio_data *sio_data)
+{
+ int ioreg = sio_data->sioreg;
+
+ /*
+ * Try to reserve <ioreg> and <ioreg + 1> for exclusive access.
+ */
+ if (!request_muxed_region(ioreg, 2, DRVNAME))
+ return -EBUSY;
+
+ outb(0x87, ioreg);
+ outb(0x87, ioreg);
+
+ return 0;
+}
+
+static void superio_exit(struct nct6775_sio_data *sio_data)
+{
+ int ioreg = sio_data->sioreg;
+
+ outb(0xaa, ioreg);
+ outb(0x02, ioreg);
+ outb(0x02, ioreg + 1);
+ release_region(ioreg, 2);
+}
+
+static inline void nct6775_wmi_set_bank(struct nct6775_data *data, u16 reg)
+{
+ u8 bank = reg >> 8;
+
+ data->bank = bank;
+}
+
+static int nct6775_wmi_reg_read(void *ctx, unsigned int reg, unsigned int *val)
+{
+ struct nct6775_data *data = ctx;
+ int err, word_sized = nct6775_reg_is_word_sized(data, reg);
+ u8 tmp = 0;
+ u16 res;
+
+ nct6775_wmi_set_bank(data, reg);
+
+ err = nct6775_asuswmi_read(data->bank, reg & 0xff, &tmp);
+ if (err)
+ return err;
+
+ res = tmp;
+ if (word_sized) {
+ err = nct6775_asuswmi_read(data->bank, (reg & 0xff) + 1, &tmp);
+ if (err)
+ return err;
+
+ res = (res << 8) + tmp;
+ }
+ *val = res;
+ return 0;
+}
+
+static int nct6775_wmi_reg_write(void *ctx, unsigned int reg, unsigned int value)
+{
+ struct nct6775_data *data = ctx;
+ int res, word_sized = nct6775_reg_is_word_sized(data, reg);
+
+ nct6775_wmi_set_bank(data, reg);
+
+ if (word_sized) {
+ res = nct6775_asuswmi_write(data->bank, reg & 0xff, value >> 8);
+ if (res)
+ return res;
+
+ res = nct6775_asuswmi_write(data->bank, (reg & 0xff) + 1, value);
+ } else {
+ res = nct6775_asuswmi_write(data->bank, reg & 0xff, value);
+ }
+
+ return res;
+}
+
+/*
+ * On older chips, only registers 0x50-0x5f are banked.
+ * On more recent chips, all registers are banked.
+ * Assume that is the case and set the bank number for each access.
+ * Cache the bank number so it only needs to be set if it changes.
+ */
+static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg)
+{
+ u8 bank = reg >> 8;
+
+ if (data->bank != bank) {
+ outb_p(NCT6775_REG_BANK, data->addr + ADDR_REG_OFFSET);
+ outb_p(bank, data->addr + DATA_REG_OFFSET);
+ data->bank = bank;
+ }
+}
+
+static int nct6775_reg_read(void *ctx, unsigned int reg, unsigned int *val)
+{
+ struct nct6775_data *data = ctx;
+ int word_sized = nct6775_reg_is_word_sized(data, reg);
+
+ nct6775_set_bank(data, reg);
+ outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET);
+ *val = inb_p(data->addr + DATA_REG_OFFSET);
+ if (word_sized) {
+ outb_p((reg & 0xff) + 1,
+ data->addr + ADDR_REG_OFFSET);
+ *val = (*val << 8) + inb_p(data->addr + DATA_REG_OFFSET);
+ }
+ return 0;
+}
+
+static int nct6775_reg_write(void *ctx, unsigned int reg, unsigned int value)
+{
+ struct nct6775_data *data = ctx;
+ int word_sized = nct6775_reg_is_word_sized(data, reg);
+
+ nct6775_set_bank(data, reg);
+ outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET);
+ if (word_sized) {
+ outb_p(value >> 8, data->addr + DATA_REG_OFFSET);
+ outb_p((reg & 0xff) + 1,
+ data->addr + ADDR_REG_OFFSET);
+ }
+ outb_p(value & 0xff, data->addr + DATA_REG_OFFSET);
+ return 0;
+}
+
+static void nct6791_enable_io_mapping(struct nct6775_sio_data *sio_data)
+{
+ int val;
+
+ val = sio_data->sio_inb(sio_data, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE);
+ if (val & 0x10) {
+ pr_info("Enabling hardware monitor logical device mappings.\n");
+ sio_data->sio_outb(sio_data, NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE,
+ val & ~0x10);
+ }
+}
+
+static int __maybe_unused nct6775_suspend(struct device *dev)
+{
+ int err;
+ u16 tmp;
+ struct nct6775_data *data = dev_get_drvdata(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ mutex_lock(&data->update_lock);
+ err = nct6775_read_value(data, data->REG_VBAT, &tmp);
+ if (err)
+ goto out;
+ data->vbat = tmp;
+ if (data->kind == nct6775) {
+ err = nct6775_read_value(data, NCT6775_REG_FANDIV1, &tmp);
+ if (err)
+ goto out;
+ data->fandiv1 = tmp;
+
+ err = nct6775_read_value(data, NCT6775_REG_FANDIV2, &tmp);
+ if (err)
+ goto out;
+ data->fandiv2 = tmp;
+ }
+out:
+ mutex_unlock(&data->update_lock);
+
+ return err;
+}
+
+static int __maybe_unused nct6775_resume(struct device *dev)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct nct6775_sio_data *sio_data = dev_get_platdata(dev);
+ int i, j, err = 0;
+ u8 reg;
+
+ mutex_lock(&data->update_lock);
+ data->bank = 0xff; /* Force initial bank selection */
+
+ err = sio_data->sio_enter(sio_data);
+ if (err)
+ goto abort;
+
+ sio_data->sio_select(sio_data, NCT6775_LD_HWM);
+ reg = sio_data->sio_inb(sio_data, SIO_REG_ENABLE);
+ if (reg != data->sio_reg_enable)
+ sio_data->sio_outb(sio_data, SIO_REG_ENABLE, data->sio_reg_enable);
+
+ if (data->kind == nct6791 || data->kind == nct6792 ||
+ data->kind == nct6793 || data->kind == nct6795 ||
+ data->kind == nct6796 || data->kind == nct6797 ||
+ data->kind == nct6798)
+ nct6791_enable_io_mapping(sio_data);
+
+ sio_data->sio_exit(sio_data);
+
+ /* Restore limits */
+ for (i = 0; i < data->in_num; i++) {
+ if (!(data->have_in & BIT(i)))
+ continue;
+
+ err = nct6775_write_value(data, data->REG_IN_MINMAX[0][i], data->in[i][1]);
+ if (err)
+ goto abort;
+ err = nct6775_write_value(data, data->REG_IN_MINMAX[1][i], data->in[i][2]);
+ if (err)
+ goto abort;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) {
+ if (!(data->has_fan_min & BIT(i)))
+ continue;
+
+ err = nct6775_write_value(data, data->REG_FAN_MIN[i], data->fan_min[i]);
+ if (err)
+ goto abort;
+ }
+
+ for (i = 0; i < NUM_TEMP; i++) {
+ if (!(data->have_temp & BIT(i)))
+ continue;
+
+ for (j = 1; j < ARRAY_SIZE(data->reg_temp); j++)
+ if (data->reg_temp[j][i]) {
+ err = nct6775_write_temp(data, data->reg_temp[j][i],
+ data->temp[j][i]);
+ if (err)
+ goto abort;
+ }
+ }
+
+ /* Restore other settings */
+ err = nct6775_write_value(data, data->REG_VBAT, data->vbat);
+ if (err)
+ goto abort;
+ if (data->kind == nct6775) {
+ err = nct6775_write_value(data, NCT6775_REG_FANDIV1, data->fandiv1);
+ if (err)
+ goto abort;
+ err = nct6775_write_value(data, NCT6775_REG_FANDIV2, data->fandiv2);
+ }
+
+abort:
+ /* Force re-reading all values */
+ data->valid = false;
+ mutex_unlock(&data->update_lock);
+
+ return err;
+}
+
+static SIMPLE_DEV_PM_OPS(nct6775_dev_pm_ops, nct6775_suspend, nct6775_resume);
+
+static void
+nct6775_check_fan_inputs(struct nct6775_data *data, struct nct6775_sio_data *sio_data)
+{
+ bool fan3pin = false, fan4pin = false, fan4min = false;
+ bool fan5pin = false, fan6pin = false, fan7pin = false;
+ bool pwm3pin = false, pwm4pin = false, pwm5pin = false;
+ bool pwm6pin = false, pwm7pin = false;
+
+ /* Store SIO_REG_ENABLE for use during resume */
+ sio_data->sio_select(sio_data, NCT6775_LD_HWM);
+ data->sio_reg_enable = sio_data->sio_inb(sio_data, SIO_REG_ENABLE);
+
+ /* fan4 and fan5 share some pins with the GPIO and serial flash */
+ if (data->kind == nct6775) {
+ int cr2c = sio_data->sio_inb(sio_data, 0x2c);
+
+ fan3pin = cr2c & BIT(6);
+ pwm3pin = cr2c & BIT(7);
+
+ /* On NCT6775, fan4 shares pins with the fdc interface */
+ fan4pin = !(sio_data->sio_inb(sio_data, 0x2A) & 0x80);
+ } else if (data->kind == nct6776) {
+ bool gpok = sio_data->sio_inb(sio_data, 0x27) & 0x80;
+ const char *board_vendor, *board_name;
+
+ board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
+ board_name = dmi_get_system_info(DMI_BOARD_NAME);
+
+ if (board_name && board_vendor &&
+ !strcmp(board_vendor, "ASRock")) {
+ /*
+ * Auxiliary fan monitoring is not enabled on ASRock
+ * Z77 Pro4-M if booted in UEFI Ultra-FastBoot mode.
+ * Observed with BIOS version 2.00.
+ */
+ if (!strcmp(board_name, "Z77 Pro4-M")) {
+ if ((data->sio_reg_enable & 0xe0) != 0xe0) {
+ data->sio_reg_enable |= 0xe0;
+ sio_data->sio_outb(sio_data, SIO_REG_ENABLE,
+ data->sio_reg_enable);
+ }
+ }
+ }
+
+ if (data->sio_reg_enable & 0x80)
+ fan3pin = gpok;
+ else
+ fan3pin = !(sio_data->sio_inb(sio_data, 0x24) & 0x40);
+
+ if (data->sio_reg_enable & 0x40)
+ fan4pin = gpok;
+ else
+ fan4pin = sio_data->sio_inb(sio_data, 0x1C) & 0x01;
+
+ if (data->sio_reg_enable & 0x20)
+ fan5pin = gpok;
+ else
+ fan5pin = sio_data->sio_inb(sio_data, 0x1C) & 0x02;
+
+ fan4min = fan4pin;
+ pwm3pin = fan3pin;
+ } else if (data->kind == nct6106) {
+ int cr24 = sio_data->sio_inb(sio_data, 0x24);
+
+ fan3pin = !(cr24 & 0x80);
+ pwm3pin = cr24 & 0x08;
+ } else if (data->kind == nct6116) {
+ int cr1a = sio_data->sio_inb(sio_data, 0x1a);
+ int cr1b = sio_data->sio_inb(sio_data, 0x1b);
+ int cr24 = sio_data->sio_inb(sio_data, 0x24);
+ int cr2a = sio_data->sio_inb(sio_data, 0x2a);
+ int cr2b = sio_data->sio_inb(sio_data, 0x2b);
+ int cr2f = sio_data->sio_inb(sio_data, 0x2f);
+
+ fan3pin = !(cr2b & 0x10);
+ fan4pin = (cr2b & 0x80) || // pin 1(2)
+ (!(cr2f & 0x10) && (cr1a & 0x04)); // pin 65(66)
+ fan5pin = (cr2b & 0x80) || // pin 126(127)
+ (!(cr1b & 0x03) && (cr2a & 0x02)); // pin 94(96)
+
+ pwm3pin = fan3pin && (cr24 & 0x08);
+ pwm4pin = fan4pin;
+ pwm5pin = fan5pin;
+ } else {
+ /*
+ * NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D,
+ * NCT6797D, NCT6798D
+ */
+ int cr1a = sio_data->sio_inb(sio_data, 0x1a);
+ int cr1b = sio_data->sio_inb(sio_data, 0x1b);
+ int cr1c = sio_data->sio_inb(sio_data, 0x1c);
+ int cr1d = sio_data->sio_inb(sio_data, 0x1d);
+ int cr2a = sio_data->sio_inb(sio_data, 0x2a);
+ int cr2b = sio_data->sio_inb(sio_data, 0x2b);
+ int cr2d = sio_data->sio_inb(sio_data, 0x2d);
+ int cr2f = sio_data->sio_inb(sio_data, 0x2f);
+ bool dsw_en = cr2f & BIT(3);
+ bool ddr4_en = cr2f & BIT(4);
+ int cre0;
+ int creb;
+ int cred;
+
+ sio_data->sio_select(sio_data, NCT6775_LD_12);
+ cre0 = sio_data->sio_inb(sio_data, 0xe0);
+ creb = sio_data->sio_inb(sio_data, 0xeb);
+ cred = sio_data->sio_inb(sio_data, 0xed);
+
+ fan3pin = !(cr1c & BIT(5));
+ fan4pin = !(cr1c & BIT(6));
+ fan5pin = !(cr1c & BIT(7));
+
+ pwm3pin = !(cr1c & BIT(0));
+ pwm4pin = !(cr1c & BIT(1));
+ pwm5pin = !(cr1c & BIT(2));
+
+ switch (data->kind) {
+ case nct6791:
+ fan6pin = cr2d & BIT(1);
+ pwm6pin = cr2d & BIT(0);
+ break;
+ case nct6792:
+ fan6pin = !dsw_en && (cr2d & BIT(1));
+ pwm6pin = !dsw_en && (cr2d & BIT(0));
+ break;
+ case nct6793:
+ fan5pin |= cr1b & BIT(5);
+ fan5pin |= creb & BIT(5);
+
+ fan6pin = !dsw_en && (cr2d & BIT(1));
+ fan6pin |= creb & BIT(3);
+
+ pwm5pin |= cr2d & BIT(7);
+ pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
+
+ pwm6pin = !dsw_en && (cr2d & BIT(0));
+ pwm6pin |= creb & BIT(2);
+ break;
+ case nct6795:
+ fan5pin |= cr1b & BIT(5);
+ fan5pin |= creb & BIT(5);
+
+ fan6pin = (cr2a & BIT(4)) &&
+ (!dsw_en || (cred & BIT(4)));
+ fan6pin |= creb & BIT(3);
+
+ pwm5pin |= cr2d & BIT(7);
+ pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
+
+ pwm6pin = (cr2a & BIT(3)) && (cred & BIT(2));
+ pwm6pin |= creb & BIT(2);
+ break;
+ case nct6796:
+ fan5pin |= cr1b & BIT(5);
+ fan5pin |= (cre0 & BIT(3)) && !(cr1b & BIT(0));
+ fan5pin |= creb & BIT(5);
+
+ fan6pin = (cr2a & BIT(4)) &&
+ (!dsw_en || (cred & BIT(4)));
+ fan6pin |= creb & BIT(3);
+
+ fan7pin = !(cr2b & BIT(2));
+
+ pwm5pin |= cr2d & BIT(7);
+ pwm5pin |= (cre0 & BIT(4)) && !(cr1b & BIT(0));
+ pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
+
+ pwm6pin = (cr2a & BIT(3)) && (cred & BIT(2));
+ pwm6pin |= creb & BIT(2);
+
+ pwm7pin = !(cr1d & (BIT(2) | BIT(3)));
+ break;
+ case nct6797:
+ fan5pin |= !ddr4_en && (cr1b & BIT(5));
+ fan5pin |= creb & BIT(5);
+
+ fan6pin = cr2a & BIT(4);
+ fan6pin |= creb & BIT(3);
+
+ fan7pin = cr1a & BIT(1);
+
+ pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
+ pwm5pin |= !ddr4_en && (cr2d & BIT(7));
+
+ pwm6pin = creb & BIT(2);
+ pwm6pin |= cred & BIT(2);
+
+ pwm7pin = cr1d & BIT(4);
+ break;
+ case nct6798:
+ fan6pin = !(cr1b & BIT(0)) && (cre0 & BIT(3));
+ fan6pin |= cr2a & BIT(4);
+ fan6pin |= creb & BIT(5);
+
+ fan7pin = cr1b & BIT(5);
+ fan7pin |= !(cr2b & BIT(2));
+ fan7pin |= creb & BIT(3);
+
+ pwm6pin = !(cr1b & BIT(0)) && (cre0 & BIT(4));
+ pwm6pin |= !(cred & BIT(2)) && (cr2a & BIT(3));
+ pwm6pin |= (creb & BIT(4)) && !(cr2a & BIT(0));
+
+ pwm7pin = !(cr1d & (BIT(2) | BIT(3)));
+ pwm7pin |= cr2d & BIT(7);
+ pwm7pin |= creb & BIT(2);
+ break;
+ default: /* NCT6779D */
+ break;
+ }
+
+ fan4min = fan4pin;
+ }
+
+ /* fan 1 and 2 (0x03) are always present */
+ data->has_fan = 0x03 | (fan3pin << 2) | (fan4pin << 3) |
+ (fan5pin << 4) | (fan6pin << 5) | (fan7pin << 6);
+ data->has_fan_min = 0x03 | (fan3pin << 2) | (fan4min << 3) |
+ (fan5pin << 4) | (fan6pin << 5) | (fan7pin << 6);
+ data->has_pwm = 0x03 | (pwm3pin << 2) | (pwm4pin << 3) |
+ (pwm5pin << 4) | (pwm6pin << 5) | (pwm7pin << 6);
+}
+
+static ssize_t
+cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
+}
+
+static DEVICE_ATTR_RO(cpu0_vid);
+
+/* Case open detection */
+
+static const u8 NCT6775_REG_CR_CASEOPEN_CLR[] = { 0xe6, 0xee };
+static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 };
+
+static ssize_t
+clear_caseopen(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct nct6775_data *data = dev_get_drvdata(dev);
+ struct nct6775_sio_data *sio_data = data->driver_data;
+ int nr = to_sensor_dev_attr(attr)->index - INTRUSION_ALARM_BASE;
+ unsigned long val;
+ u8 reg;
+ int ret;
+
+ if (kstrtoul(buf, 10, &val) || val != 0)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+
+ /*
+ * Use CR registers to clear caseopen status.
+ * The CR registers are the same for all chips, and not all chips
+ * support clearing the caseopen status through "regular" registers.
+ */
+ ret = sio_data->sio_enter(sio_data);
+ if (ret) {
+ count = ret;
+ goto error;
+ }
+
+ sio_data->sio_select(sio_data, NCT6775_LD_ACPI);
+ reg = sio_data->sio_inb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr]);
+ reg |= NCT6775_CR_CASEOPEN_CLR_MASK[nr];
+ sio_data->sio_outb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg);
+ reg &= ~NCT6775_CR_CASEOPEN_CLR_MASK[nr];
+ sio_data->sio_outb(sio_data, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg);
+ sio_data->sio_exit(sio_data);
+
+ data->valid = false; /* Force cache refresh */
+error:
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static SENSOR_DEVICE_ATTR(intrusion0_alarm, 0644, nct6775_show_alarm,
+ clear_caseopen, INTRUSION_ALARM_BASE);
+static SENSOR_DEVICE_ATTR(intrusion1_alarm, 0644, nct6775_show_alarm,
+ clear_caseopen, INTRUSION_ALARM_BASE + 1);
+static SENSOR_DEVICE_ATTR(intrusion0_beep, 0644, nct6775_show_beep,
+ nct6775_store_beep, INTRUSION_ALARM_BASE);
+static SENSOR_DEVICE_ATTR(intrusion1_beep, 0644, nct6775_show_beep,
+ nct6775_store_beep, INTRUSION_ALARM_BASE + 1);
+static SENSOR_DEVICE_ATTR(beep_enable, 0644, nct6775_show_beep,
+ nct6775_store_beep, BEEP_ENABLE_BASE);
+
+static umode_t nct6775_other_is_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct nct6775_data *data = dev_get_drvdata(dev);
+
+ if (index == 0 && !data->have_vid)
+ return 0;
+
+ if (index == 1 || index == 2) {
+ if (data->ALARM_BITS[INTRUSION_ALARM_BASE + index - 1] < 0)
+ return 0;
+ }
+
+ if (index == 3 || index == 4) {
+ if (data->BEEP_BITS[INTRUSION_ALARM_BASE + index - 3] < 0)
+ return 0;
+ }
+
+ return nct6775_attr_mode(data, attr);
+}
+
+/*
+ * nct6775_other_is_visible uses the index into the following array
+ * to determine if attributes should be created or not.
+ * Any change in order or content must be matched.
+ */
+static struct attribute *nct6775_attributes_other[] = {
+ &dev_attr_cpu0_vid.attr, /* 0 */
+ &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, /* 1 */
+ &sensor_dev_attr_intrusion1_alarm.dev_attr.attr, /* 2 */
+ &sensor_dev_attr_intrusion0_beep.dev_attr.attr, /* 3 */
+ &sensor_dev_attr_intrusion1_beep.dev_attr.attr, /* 4 */
+ &sensor_dev_attr_beep_enable.dev_attr.attr, /* 5 */
+
+ NULL
+};
+
+static const struct attribute_group nct6775_group_other = {
+ .attrs = nct6775_attributes_other,
+ .is_visible = nct6775_other_is_visible,
+};
+
+static int nct6775_platform_probe_init(struct nct6775_data *data)
+{
+ int err;
+ u8 cr2a;
+ struct nct6775_sio_data *sio_data = data->driver_data;
+
+ err = sio_data->sio_enter(sio_data);
+ if (err)
+ return err;
+
+ cr2a = sio_data->sio_inb(sio_data, 0x2a);
+ switch (data->kind) {
+ case nct6775:
+ data->have_vid = (cr2a & 0x40);
+ break;
+ case nct6776:
+ data->have_vid = (cr2a & 0x60) == 0x40;
+ break;
+ case nct6106:
+ case nct6116:
+ case nct6779:
+ case nct6791:
+ case nct6792:
+ case nct6793:
+ case nct6795:
+ case nct6796:
+ case nct6797:
+ case nct6798:
+ break;
+ }
+
+ /*
+ * Read VID value
+ * We can get the VID input values directly at logical device D 0xe3.
+ */
+ if (data->have_vid) {
+ sio_data->sio_select(sio_data, NCT6775_LD_VID);
+ data->vid = sio_data->sio_inb(sio_data, 0xe3);
+ data->vrm = vid_which_vrm();
+ }
+
+ if (fan_debounce) {
+ u8 tmp;
+
+ sio_data->sio_select(sio_data, NCT6775_LD_HWM);
+ tmp = sio_data->sio_inb(sio_data,
+ NCT6775_REG_CR_FAN_DEBOUNCE);
+ switch (data->kind) {
+ case nct6106:
+ case nct6116:
+ tmp |= 0xe0;
+ break;
+ case nct6775:
+ tmp |= 0x1e;
+ break;
+ case nct6776:
+ case nct6779:
+ tmp |= 0x3e;
+ break;
+ case nct6791:
+ case nct6792:
+ case nct6793:
+ case nct6795:
+ case nct6796:
+ case nct6797:
+ case nct6798:
+ tmp |= 0x7e;
+ break;
+ }
+ sio_data->sio_outb(sio_data, NCT6775_REG_CR_FAN_DEBOUNCE,
+ tmp);
+ pr_info("Enabled fan debounce for chip %s\n", data->name);
+ }
+
+ nct6775_check_fan_inputs(data, sio_data);
+
+ sio_data->sio_exit(sio_data);
+
+ return nct6775_add_attr_group(data, &nct6775_group_other);
+}
+
+static const struct regmap_config nct6775_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .reg_read = nct6775_reg_read,
+ .reg_write = nct6775_reg_write,
+};
+
+static const struct regmap_config nct6775_wmi_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .reg_read = nct6775_wmi_reg_read,
+ .reg_write = nct6775_wmi_reg_write,
+};
+
+static int nct6775_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct nct6775_sio_data *sio_data = dev_get_platdata(dev);
+ struct nct6775_data *data;
+ struct resource *res;
+ const struct regmap_config *regmapcfg;
+
+ if (sio_data->access == access_direct) {
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!devm_request_region(&pdev->dev, res->start, IOREGION_LENGTH, DRVNAME))
+ return -EBUSY;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->kind = sio_data->kind;
+ data->sioreg = sio_data->sioreg;
+
+ if (sio_data->access == access_direct) {
+ data->addr = res->start;
+ regmapcfg = &nct6775_regmap_config;
+ } else {
+ regmapcfg = &nct6775_wmi_regmap_config;
+ }
+
+ platform_set_drvdata(pdev, data);
+
+ data->driver_data = sio_data;
+ data->driver_init = nct6775_platform_probe_init;
+
+ return nct6775_probe(&pdev->dev, data, regmapcfg);
+}
+
+static struct platform_driver nct6775_driver = {
+ .driver = {
+ .name = DRVNAME,
+ .pm = &nct6775_dev_pm_ops,
+ },
+ .probe = nct6775_platform_probe,
+};
+
+/* nct6775_find() looks for a '627 in the Super-I/O config space */
+static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
+{
+ u16 val;
+ int err;
+ int addr;
+
+ sio_data->access = access_direct;
+ sio_data->sioreg = sioaddr;
+
+ err = sio_data->sio_enter(sio_data);
+ if (err)
+ return err;
+
+ val = (sio_data->sio_inb(sio_data, SIO_REG_DEVID) << 8) |
+ sio_data->sio_inb(sio_data, SIO_REG_DEVID + 1);
+ if (force_id && val != 0xffff)
+ val = force_id;
+
+ switch (val & SIO_ID_MASK) {
+ case SIO_NCT6106_ID:
+ sio_data->kind = nct6106;
+ break;
+ case SIO_NCT6116_ID:
+ sio_data->kind = nct6116;
+ break;
+ case SIO_NCT6775_ID:
+ sio_data->kind = nct6775;
+ break;
+ case SIO_NCT6776_ID:
+ sio_data->kind = nct6776;
+ break;
+ case SIO_NCT6779_ID:
+ sio_data->kind = nct6779;
+ break;
+ case SIO_NCT6791_ID:
+ sio_data->kind = nct6791;
+ break;
+ case SIO_NCT6792_ID:
+ sio_data->kind = nct6792;
+ break;
+ case SIO_NCT6793_ID:
+ sio_data->kind = nct6793;
+ break;
+ case SIO_NCT6795_ID:
+ sio_data->kind = nct6795;
+ break;
+ case SIO_NCT6796_ID:
+ sio_data->kind = nct6796;
+ break;
+ case SIO_NCT6797_ID:
+ sio_data->kind = nct6797;
+ break;
+ case SIO_NCT6798_ID:
+ sio_data->kind = nct6798;
+ break;
+ default:
+ if (val != 0xffff)
+ pr_debug("unsupported chip ID: 0x%04x\n", val);
+ sio_data->sio_exit(sio_data);
+ return -ENODEV;
+ }
+
+ /* We have a known chip, find the HWM I/O address */
+ sio_data->sio_select(sio_data, NCT6775_LD_HWM);
+ val = (sio_data->sio_inb(sio_data, SIO_REG_ADDR) << 8)
+ | sio_data->sio_inb(sio_data, SIO_REG_ADDR + 1);
+ addr = val & IOREGION_ALIGNMENT;
+ if (addr == 0) {
+ pr_err("Refusing to enable a Super-I/O device with a base I/O port 0\n");
+ sio_data->sio_exit(sio_data);
+ return -ENODEV;
+ }
+
+ /* Activate logical device if needed */
+ val = sio_data->sio_inb(sio_data, SIO_REG_ENABLE);
+ if (!(val & 0x01)) {
+ pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n");
+ sio_data->sio_outb(sio_data, SIO_REG_ENABLE, val | 0x01);
+ }
+
+ if (sio_data->kind == nct6791 || sio_data->kind == nct6792 ||
+ sio_data->kind == nct6793 || sio_data->kind == nct6795 ||
+ sio_data->kind == nct6796 || sio_data->kind == nct6797 ||
+ sio_data->kind == nct6798)
+ nct6791_enable_io_mapping(sio_data);
+
+ sio_data->sio_exit(sio_data);
+ pr_info("Found %s or compatible chip at %#x:%#x\n",
+ nct6775_sio_names[sio_data->kind], sioaddr, addr);
+
+ return addr;
+}
+
+/*
+ * when Super-I/O functions move to a separate file, the Super-I/O
+ * bus will manage the lifetime of the device and this module will only keep
+ * track of the nct6775 driver. But since we use platform_device_alloc(), we
+ * must keep track of the device
+ */
+static struct platform_device *pdev[2];
+
+static const char * const asus_wmi_boards[] = {
+ "PRO H410T",
+ "ProArt X570-CREATOR WIFI",
+ "Pro B550M-C",
+ "Pro WS X570-ACE",
+ "PRIME B360-PLUS",
+ "PRIME B460-PLUS",
+ "PRIME B550-PLUS",
+ "PRIME B550M-A",
+ "PRIME B550M-A (WI-FI)",
+ "PRIME H410M-R",
+ "PRIME X570-P",
+ "PRIME X570-PRO",
+ "ROG CROSSHAIR VIII DARK HERO",
+ "ROG CROSSHAIR VIII FORMULA",
+ "ROG CROSSHAIR VIII HERO",
+ "ROG CROSSHAIR VIII IMPACT",
+ "ROG STRIX B550-A GAMING",
+ "ROG STRIX B550-E GAMING",
+ "ROG STRIX B550-F GAMING",
+ "ROG STRIX B550-F GAMING (WI-FI)",
+ "ROG STRIX B550-F GAMING WIFI II",
+ "ROG STRIX B550-I GAMING",
+ "ROG STRIX B550-XE GAMING (WI-FI)",
+ "ROG STRIX X570-E GAMING",
+ "ROG STRIX X570-E GAMING WIFI II",
+ "ROG STRIX X570-F GAMING",
+ "ROG STRIX X570-I GAMING",
+ "ROG STRIX Z390-E GAMING",
+ "ROG STRIX Z390-F GAMING",
+ "ROG STRIX Z390-H GAMING",
+ "ROG STRIX Z390-I GAMING",
+ "ROG STRIX Z490-A GAMING",
+ "ROG STRIX Z490-E GAMING",
+ "ROG STRIX Z490-F GAMING",
+ "ROG STRIX Z490-G GAMING",
+ "ROG STRIX Z490-G GAMING (WI-FI)",
+ "ROG STRIX Z490-H GAMING",
+ "ROG STRIX Z490-I GAMING",
+ "TUF GAMING B550M-PLUS",
+ "TUF GAMING B550M-PLUS (WI-FI)",
+ "TUF GAMING B550-PLUS",
+ "TUF GAMING B550-PRO",
+ "TUF GAMING X570-PLUS",
+ "TUF GAMING X570-PLUS (WI-FI)",
+ "TUF GAMING X570-PRO (WI-FI)",
+ "TUF GAMING Z490-PLUS",
+ "TUF GAMING Z490-PLUS (WI-FI)",
+};
+
+static int __init sensors_nct6775_platform_init(void)
+{
+ int i, err;
+ bool found = false;
+ int address;
+ struct resource res;
+ struct nct6775_sio_data sio_data;
+ int sioaddr[2] = { 0x2e, 0x4e };
+ enum sensor_access access = access_direct;
+ const char *board_vendor, *board_name;
+ u8 tmp;
+
+ err = platform_driver_register(&nct6775_driver);
+ if (err)
+ return err;
+
+ board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
+ board_name = dmi_get_system_info(DMI_BOARD_NAME);
+
+ if (board_name && board_vendor &&
+ !strcmp(board_vendor, "ASUSTeK COMPUTER INC.")) {
+ err = match_string(asus_wmi_boards, ARRAY_SIZE(asus_wmi_boards),
+ board_name);
+ if (err >= 0) {
+ /* if reading chip id via WMI succeeds, use WMI */
+ if (!nct6775_asuswmi_read(0, NCT6775_PORT_CHIPID, &tmp) && tmp) {
+ pr_info("Using Asus WMI to access %#x chip.\n", tmp);
+ access = access_asuswmi;
+ } else {
+ pr_err("Can't read ChipID by Asus WMI.\n");
+ }
+ }
+ }
+
+ /*
+ * initialize sio_data->kind and sio_data->sioreg.
+ *
+ * when Super-I/O functions move to a separate file, the Super-I/O
+ * driver will probe 0x2e and 0x4e and auto-detect the presence of a
+ * nct6775 hardware monitor, and call probe()
+ */
+ for (i = 0; i < ARRAY_SIZE(pdev); i++) {
+ sio_data.sio_outb = superio_outb;
+ sio_data.sio_inb = superio_inb;
+ sio_data.sio_select = superio_select;
+ sio_data.sio_enter = superio_enter;
+ sio_data.sio_exit = superio_exit;
+
+ address = nct6775_find(sioaddr[i], &sio_data);
+ if (address <= 0)
+ continue;
+
+ found = true;
+
+ sio_data.access = access;
+
+ if (access == access_asuswmi) {
+ sio_data.sio_outb = superio_wmi_outb;
+ sio_data.sio_inb = superio_wmi_inb;
+ sio_data.sio_select = superio_wmi_select;
+ sio_data.sio_enter = superio_wmi_enter;
+ sio_data.sio_exit = superio_wmi_exit;
+ }
+
+ pdev[i] = platform_device_alloc(DRVNAME, address);
+ if (!pdev[i]) {
+ err = -ENOMEM;
+ goto exit_device_unregister;
+ }
+
+ err = platform_device_add_data(pdev[i], &sio_data,
+ sizeof(struct nct6775_sio_data));
+ if (err)
+ goto exit_device_put;
+
+ if (sio_data.access == access_direct) {
+ memset(&res, 0, sizeof(res));
+ res.name = DRVNAME;
+ res.start = address + IOREGION_OFFSET;
+ res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1;
+ res.flags = IORESOURCE_IO;
+
+ err = acpi_check_resource_conflict(&res);
+ if (err) {
+ platform_device_put(pdev[i]);
+ pdev[i] = NULL;
+ continue;
+ }
+
+ err = platform_device_add_resources(pdev[i], &res, 1);
+ if (err)
+ goto exit_device_put;
+ }
+
+ /* platform_device_add calls probe() */
+ err = platform_device_add(pdev[i]);
+ if (err)
+ goto exit_device_put;
+ }
+ if (!found) {
+ err = -ENODEV;
+ goto exit_unregister;
+ }
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(pdev[i]);
+exit_device_unregister:
+ while (--i >= 0) {
+ if (pdev[i])
+ platform_device_unregister(pdev[i]);
+ }
+exit_unregister:
+ platform_driver_unregister(&nct6775_driver);
+ return err;
+}
+
+static void __exit sensors_nct6775_platform_exit(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pdev); i++) {
+ if (pdev[i])
+ platform_device_unregister(pdev[i]);
+ }
+ platform_driver_unregister(&nct6775_driver);
+}
+
+MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+MODULE_DESCRIPTION("Platform driver for NCT6775F and compatible chips");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(HWMON_NCT6775);
+
+module_init(sensors_nct6775_platform_init);
+module_exit(sensors_nct6775_platform_exit);
diff --git a/drivers/hwmon/nct6775.h b/drivers/hwmon/nct6775.h
new file mode 100644
index 000000000000..93f708148e65
--- /dev/null
+++ b/drivers/hwmon/nct6775.h
@@ -0,0 +1,252 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __HWMON_NCT6775_H__
+#define __HWMON_NCT6775_H__
+
+#include <linux/types.h>
+
+enum kinds { nct6106, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792,
+ nct6793, nct6795, nct6796, nct6797, nct6798 };
+enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };
+
+#define NUM_TEMP 10 /* Max number of temp attribute sets w/ limits*/
+#define NUM_TEMP_FIXED 6 /* Max number of fixed temp attribute sets */
+#define NUM_TSI_TEMP 8 /* Max number of TSI temp register pairs */
+
+#define NUM_REG_ALARM 7 /* Max number of alarm registers */
+#define NUM_REG_BEEP 5 /* Max number of beep registers */
+
+#define NUM_FAN 7
+
+struct nct6775_data {
+ int addr; /* IO base of hw monitor block */
+ int sioreg; /* SIO register address */
+ enum kinds kind;
+ const char *name;
+
+ const struct attribute_group *groups[7];
+ u8 num_groups;
+
+ u16 reg_temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst,
+ * 3=temp_crit, 4=temp_lcrit
+ */
+ u8 temp_src[NUM_TEMP];
+ u16 reg_temp_config[NUM_TEMP];
+ const char * const *temp_label;
+ u32 temp_mask;
+ u32 virt_temp_mask;
+
+ u16 REG_CONFIG;
+ u16 REG_VBAT;
+ u16 REG_DIODE;
+ u8 DIODE_MASK;
+
+ const s8 *ALARM_BITS;
+ const s8 *BEEP_BITS;
+
+ const u16 *REG_VIN;
+ const u16 *REG_IN_MINMAX[2];
+
+ const u16 *REG_TARGET;
+ const u16 *REG_FAN;
+ const u16 *REG_FAN_MODE;
+ const u16 *REG_FAN_MIN;
+ const u16 *REG_FAN_PULSES;
+ const u16 *FAN_PULSE_SHIFT;
+ const u16 *REG_FAN_TIME[3];
+
+ const u16 *REG_TOLERANCE_H;
+
+ const u8 *REG_PWM_MODE;
+ const u8 *PWM_MODE_MASK;
+
+ const u16 *REG_PWM[7]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor,
+ * [3]=pwm_max, [4]=pwm_step,
+ * [5]=weight_duty_step, [6]=weight_duty_base
+ */
+ const u16 *REG_PWM_READ;
+
+ const u16 *REG_CRITICAL_PWM_ENABLE;
+ u8 CRITICAL_PWM_ENABLE_MASK;
+ const u16 *REG_CRITICAL_PWM;
+
+ const u16 *REG_AUTO_TEMP;
+ const u16 *REG_AUTO_PWM;
+
+ const u16 *REG_CRITICAL_TEMP;
+ const u16 *REG_CRITICAL_TEMP_TOLERANCE;
+
+ const u16 *REG_TEMP_SOURCE; /* temp register sources */
+ const u16 *REG_TEMP_SEL;
+ const u16 *REG_WEIGHT_TEMP_SEL;
+ const u16 *REG_WEIGHT_TEMP[3]; /* 0=base, 1=tolerance, 2=step */
+
+ const u16 *REG_TEMP_OFFSET;
+
+ const u16 *REG_ALARM;
+ const u16 *REG_BEEP;
+
+ const u16 *REG_TSI_TEMP;
+
+ unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg);
+ unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg);
+
+ struct mutex update_lock;
+ bool valid; /* true if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ /* Register values */
+ u8 bank; /* current register bank */
+ u8 in_num; /* number of in inputs we have */
+ u8 in[15][3]; /* [0]=in, [1]=in_max, [2]=in_min */
+ unsigned int rpm[NUM_FAN];
+ u16 fan_min[NUM_FAN];
+ u8 fan_pulses[NUM_FAN];
+ u8 fan_div[NUM_FAN];
+ u8 has_pwm;
+ u8 has_fan; /* some fan inputs can be disabled */
+ u8 has_fan_min; /* some fans don't have min register */
+ bool has_fan_div;
+
+ u8 num_temp_alarms; /* 2, 3, or 6 */
+ u8 num_temp_beeps; /* 2, 3, or 6 */
+ u8 temp_fixed_num; /* 3 or 6 */
+ u8 temp_type[NUM_TEMP_FIXED];
+ s8 temp_offset[NUM_TEMP_FIXED];
+ s16 temp[5][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst,
+ * 3=temp_crit, 4=temp_lcrit
+ */
+ s16 tsi_temp[NUM_TSI_TEMP];
+ u64 alarms;
+ u64 beeps;
+
+ u8 pwm_num; /* number of pwm */
+ u8 pwm_mode[NUM_FAN]; /* 0->DC variable voltage,
+ * 1->PWM variable duty cycle
+ */
+ enum pwm_enable pwm_enable[NUM_FAN];
+ /* 0->off
+ * 1->manual
+ * 2->thermal cruise mode (also called SmartFan I)
+ * 3->fan speed cruise mode
+ * 4->SmartFan III
+ * 5->enhanced variable thermal cruise (SmartFan IV)
+ */
+ u8 pwm[7][NUM_FAN]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor,
+ * [3]=pwm_max, [4]=pwm_step,
+ * [5]=weight_duty_step, [6]=weight_duty_base
+ */
+
+ u8 target_temp[NUM_FAN];
+ u8 target_temp_mask;
+ u32 target_speed[NUM_FAN];
+ u32 target_speed_tolerance[NUM_FAN];
+ u8 speed_tolerance_limit;
+
+ u8 temp_tolerance[2][NUM_FAN];
+ u8 tolerance_mask;
+
+ u8 fan_time[3][NUM_FAN]; /* 0 = stop_time, 1 = step_up, 2 = step_down */
+
+ /* Automatic fan speed control registers */
+ int auto_pwm_num;
+ u8 auto_pwm[NUM_FAN][7];
+ u8 auto_temp[NUM_FAN][7];
+ u8 pwm_temp_sel[NUM_FAN];
+ u8 pwm_weight_temp_sel[NUM_FAN];
+ u8 weight_temp[3][NUM_FAN]; /* 0->temp_step, 1->temp_step_tol,
+ * 2->temp_base
+ */
+
+ u8 vid;
+ u8 vrm;
+
+ bool have_vid;
+
+ u16 have_temp;
+ u16 have_temp_fixed;
+ u16 have_tsi_temp;
+ u16 have_in;
+
+ /* Remember extra register values over suspend/resume */
+ u8 vbat;
+ u8 fandiv1;
+ u8 fandiv2;
+ u8 sio_reg_enable;
+
+ struct regmap *regmap;
+ bool read_only;
+
+ /* driver-specific (platform, i2c) initialization hook and data */
+ int (*driver_init)(struct nct6775_data *data);
+ void *driver_data;
+};
+
+static inline int nct6775_read_value(struct nct6775_data *data, u16 reg, u16 *value)
+{
+ unsigned int tmp;
+ int ret = regmap_read(data->regmap, reg, &tmp);
+
+ if (!ret)
+ *value = tmp;
+ return ret;
+}
+
+static inline int nct6775_write_value(struct nct6775_data *data, u16 reg, u16 value)
+{
+ return regmap_write(data->regmap, reg, value);
+}
+
+bool nct6775_reg_is_word_sized(struct nct6775_data *data, u16 reg);
+int nct6775_probe(struct device *dev, struct nct6775_data *data,
+ const struct regmap_config *regmapcfg);
+
+ssize_t nct6775_show_alarm(struct device *dev, struct device_attribute *attr, char *buf);
+ssize_t nct6775_show_beep(struct device *dev, struct device_attribute *attr, char *buf);
+ssize_t nct6775_store_beep(struct device *dev, struct device_attribute *attr, const char *buf,
+ size_t count);
+
+static inline int nct6775_write_temp(struct nct6775_data *data, u16 reg, u16 value)
+{
+ if (!nct6775_reg_is_word_sized(data, reg))
+ value >>= 8;
+ return nct6775_write_value(data, reg, value);
+}
+
+static inline umode_t nct6775_attr_mode(struct nct6775_data *data, struct attribute *attr)
+{
+ return data->read_only ? (attr->mode & ~0222) : attr->mode;
+}
+
+static inline int
+nct6775_add_attr_group(struct nct6775_data *data, const struct attribute_group *group)
+{
+ /* Need to leave a NULL terminator at the end of data->groups */
+ if (data->num_groups == ARRAY_SIZE(data->groups) - 1)
+ return -ENOBUFS;
+
+ data->groups[data->num_groups++] = group;
+ return 0;
+}
+
+#define NCT6775_REG_BANK 0x4E
+#define NCT6775_REG_CONFIG 0x40
+
+#define NCT6775_REG_FANDIV1 0x506
+#define NCT6775_REG_FANDIV2 0x507
+
+#define NCT6791_REG_HM_IO_SPACE_LOCK_ENABLE 0x28
+
+#define FAN_ALARM_BASE 16
+#define TEMP_ALARM_BASE 24
+#define INTRUSION_ALARM_BASE 30
+#define BEEP_ENABLE_BASE 15
+
+/*
+ * Not currently used:
+ * REG_MAN_ID has the value 0x5ca3 for all supported chips.
+ * REG_CHIP_ID == 0x88/0xa1/0xc1 depending on chip model.
+ * REG_MAN_ID is at port 0x4f
+ * REG_CHIP_ID is at port 0x58
+ */
+
+#endif /* __HWMON_NCT6775_H__ */
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index f00cd59f1d19..d78f4bebc718 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -1149,44 +1149,75 @@ static void occ_parse_poll_response(struct occ *occ)
sizeof(*header), size + sizeof(*header));
}
-int occ_setup(struct occ *occ, const char *name)
+int occ_active(struct occ *occ, bool active)
{
- int rc;
-
- mutex_init(&occ->lock);
- occ->groups[0] = &occ->group;
+ int rc = mutex_lock_interruptible(&occ->lock);
- /* no need to lock */
- rc = occ_poll(occ);
- if (rc == -ESHUTDOWN) {
- dev_info(occ->bus_dev, "host is not ready\n");
- return rc;
- } else if (rc < 0) {
- dev_err(occ->bus_dev,
- "failed to get OCC poll response=%02x: %d\n",
- occ->resp.return_status, rc);
+ if (rc)
return rc;
- }
- occ->next_update = jiffies + OCC_UPDATE_FREQUENCY;
- occ_parse_poll_response(occ);
+ if (active) {
+ if (occ->active) {
+ rc = -EALREADY;
+ goto unlock;
+ }
- rc = occ_setup_sensor_attrs(occ);
- if (rc) {
- dev_err(occ->bus_dev, "failed to setup sensor attrs: %d\n",
- rc);
- return rc;
- }
+ occ->error_count = 0;
+ occ->last_safe = 0;
- occ->hwmon = devm_hwmon_device_register_with_groups(occ->bus_dev, name,
- occ, occ->groups);
- if (IS_ERR(occ->hwmon)) {
- rc = PTR_ERR(occ->hwmon);
- dev_err(occ->bus_dev, "failed to register hwmon device: %d\n",
- rc);
- return rc;
+ rc = occ_poll(occ);
+ if (rc < 0) {
+ dev_err(occ->bus_dev,
+ "failed to get OCC poll response=%02x: %d\n",
+ occ->resp.return_status, rc);
+ goto unlock;
+ }
+
+ occ->active = true;
+ occ->next_update = jiffies + OCC_UPDATE_FREQUENCY;
+ occ_parse_poll_response(occ);
+
+ rc = occ_setup_sensor_attrs(occ);
+ if (rc) {
+ dev_err(occ->bus_dev,
+ "failed to setup sensor attrs: %d\n", rc);
+ goto unlock;
+ }
+
+ occ->hwmon = hwmon_device_register_with_groups(occ->bus_dev,
+ "occ", occ,
+ occ->groups);
+ if (IS_ERR(occ->hwmon)) {
+ rc = PTR_ERR(occ->hwmon);
+ occ->hwmon = NULL;
+ dev_err(occ->bus_dev,
+ "failed to register hwmon device: %d\n", rc);
+ goto unlock;
+ }
+ } else {
+ if (!occ->active) {
+ rc = -EALREADY;
+ goto unlock;
+ }
+
+ if (occ->hwmon)
+ hwmon_device_unregister(occ->hwmon);
+ occ->active = false;
+ occ->hwmon = NULL;
}
+unlock:
+ mutex_unlock(&occ->lock);
+ return rc;
+}
+
+int occ_setup(struct occ *occ)
+{
+ int rc;
+
+ mutex_init(&occ->lock);
+ occ->groups[0] = &occ->group;
+
rc = occ_setup_sysfs(occ);
if (rc)
dev_err(occ->bus_dev, "failed to setup sysfs: %d\n", rc);
@@ -1195,6 +1226,15 @@ int occ_setup(struct occ *occ, const char *name)
}
EXPORT_SYMBOL_GPL(occ_setup);
+void occ_shutdown(struct occ *occ)
+{
+ occ_shutdown_sysfs(occ);
+
+ if (occ->hwmon)
+ hwmon_device_unregister(occ->hwmon);
+}
+EXPORT_SYMBOL_GPL(occ_shutdown);
+
MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
MODULE_DESCRIPTION("Common OCC hwmon code");
MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h
index 2dd4a4d240c0..64d5ec7e169b 100644
--- a/drivers/hwmon/occ/common.h
+++ b/drivers/hwmon/occ/common.h
@@ -106,6 +106,7 @@ struct occ {
struct attribute_group group;
const struct attribute_group *groups[2];
+ bool active;
int error; /* final transfer error after retry */
int last_error; /* latest transfer error */
unsigned int error_count; /* number of xfr errors observed */
@@ -123,9 +124,11 @@ struct occ {
u8 prev_mode;
};
-int occ_setup(struct occ *occ, const char *name);
+int occ_active(struct occ *occ, bool active);
+int occ_setup(struct occ *occ);
int occ_setup_sysfs(struct occ *occ);
void occ_shutdown(struct occ *occ);
+void occ_shutdown_sysfs(struct occ *occ);
void occ_sysfs_poll_done(struct occ *occ);
int occ_update_response(struct occ *occ);
diff --git a/drivers/hwmon/occ/p8_i2c.c b/drivers/hwmon/occ/p8_i2c.c
index 9e61e1fb5142..da39ea28df31 100644
--- a/drivers/hwmon/occ/p8_i2c.c
+++ b/drivers/hwmon/occ/p8_i2c.c
@@ -223,7 +223,7 @@ static int p8_i2c_occ_probe(struct i2c_client *client)
occ->poll_cmd_data = 0x10; /* P8 OCC poll data */
occ->send_cmd = p8_i2c_occ_send_cmd;
- return occ_setup(occ, "p8_occ");
+ return occ_setup(occ);
}
static int p8_i2c_occ_remove(struct i2c_client *client)
diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c
index 49b13cc01073..42fc7b97bb34 100644
--- a/drivers/hwmon/occ/p9_sbe.c
+++ b/drivers/hwmon/occ/p9_sbe.c
@@ -145,7 +145,7 @@ static int p9_sbe_occ_probe(struct platform_device *pdev)
occ->poll_cmd_data = 0x20; /* P9 OCC poll data */
occ->send_cmd = p9_sbe_occ_send_cmd;
- rc = occ_setup(occ, "p9_occ");
+ rc = occ_setup(occ);
if (rc == -ESHUTDOWN)
rc = -ENODEV; /* Host is shutdown, don't spew errors */
diff --git a/drivers/hwmon/occ/sysfs.c b/drivers/hwmon/occ/sysfs.c
index b2f788a77746..2317301fc1e9 100644
--- a/drivers/hwmon/occ/sysfs.c
+++ b/drivers/hwmon/occ/sysfs.c
@@ -6,13 +6,13 @@
#include <linux/export.h>
#include <linux/hwmon-sysfs.h>
#include <linux/kernel.h>
+#include <linux/kstrtox.h>
#include <linux/sysfs.h>
#include "common.h"
/* OCC status register */
#define OCC_STAT_MASTER BIT(7)
-#define OCC_STAT_ACTIVE BIT(0)
/* OCC extended status register */
#define OCC_EXT_STAT_DVFS_OT BIT(7)
@@ -22,6 +22,25 @@
#define OCC_EXT_STAT_DVFS_VDD BIT(3)
#define OCC_EXT_STAT_GPU_THROTTLE GENMASK(2, 0)
+static ssize_t occ_active_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int rc;
+ bool active;
+ struct occ *occ = dev_get_drvdata(dev);
+
+ rc = kstrtobool(buf, &active);
+ if (rc)
+ return rc;
+
+ rc = occ_active(occ, active);
+ if (rc)
+ return rc;
+
+ return count;
+}
+
static ssize_t occ_sysfs_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -31,54 +50,64 @@ static ssize_t occ_sysfs_show(struct device *dev,
struct occ_poll_response_header *header;
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
- rc = occ_update_response(occ);
- if (rc)
- return rc;
+ if (occ->active) {
+ rc = occ_update_response(occ);
+ if (rc)
+ return rc;
- header = (struct occ_poll_response_header *)occ->resp.data;
-
- switch (sattr->index) {
- case 0:
- val = !!(header->status & OCC_STAT_MASTER);
- break;
- case 1:
- val = !!(header->status & OCC_STAT_ACTIVE);
- break;
- case 2:
- val = !!(header->ext_status & OCC_EXT_STAT_DVFS_OT);
- break;
- case 3:
- val = !!(header->ext_status & OCC_EXT_STAT_DVFS_POWER);
- break;
- case 4:
- val = !!(header->ext_status & OCC_EXT_STAT_MEM_THROTTLE);
- break;
- case 5:
- val = !!(header->ext_status & OCC_EXT_STAT_QUICK_DROP);
- break;
- case 6:
- val = header->occ_state;
- break;
- case 7:
- if (header->status & OCC_STAT_MASTER)
- val = hweight8(header->occs_present);
- else
+ header = (struct occ_poll_response_header *)occ->resp.data;
+
+ switch (sattr->index) {
+ case 0:
+ val = !!(header->status & OCC_STAT_MASTER);
+ break;
+ case 1:
val = 1;
- break;
- case 8:
- val = header->ips_status;
- break;
- case 9:
- val = header->mode;
- break;
- case 10:
- val = !!(header->ext_status & OCC_EXT_STAT_DVFS_VDD);
- break;
- case 11:
- val = header->ext_status & OCC_EXT_STAT_GPU_THROTTLE;
- break;
- default:
- return -EINVAL;
+ break;
+ case 2:
+ val = !!(header->ext_status & OCC_EXT_STAT_DVFS_OT);
+ break;
+ case 3:
+ val = !!(header->ext_status & OCC_EXT_STAT_DVFS_POWER);
+ break;
+ case 4:
+ val = !!(header->ext_status &
+ OCC_EXT_STAT_MEM_THROTTLE);
+ break;
+ case 5:
+ val = !!(header->ext_status & OCC_EXT_STAT_QUICK_DROP);
+ break;
+ case 6:
+ val = header->occ_state;
+ break;
+ case 7:
+ if (header->status & OCC_STAT_MASTER)
+ val = hweight8(header->occs_present);
+ else
+ val = 1;
+ break;
+ case 8:
+ val = header->ips_status;
+ break;
+ case 9:
+ val = header->mode;
+ break;
+ case 10:
+ val = !!(header->ext_status & OCC_EXT_STAT_DVFS_VDD);
+ break;
+ case 11:
+ val = header->ext_status & OCC_EXT_STAT_GPU_THROTTLE;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ if (sattr->index == 1)
+ val = 0;
+ else if (sattr->index <= 11)
+ val = -ENODATA;
+ else
+ return -EINVAL;
}
return sysfs_emit(buf, "%d\n", val);
@@ -95,7 +124,8 @@ static ssize_t occ_error_show(struct device *dev,
}
static SENSOR_DEVICE_ATTR(occ_master, 0444, occ_sysfs_show, NULL, 0);
-static SENSOR_DEVICE_ATTR(occ_active, 0444, occ_sysfs_show, NULL, 1);
+static SENSOR_DEVICE_ATTR(occ_active, 0644, occ_sysfs_show, occ_active_store,
+ 1);
static SENSOR_DEVICE_ATTR(occ_dvfs_overtemp, 0444, occ_sysfs_show, NULL, 2);
static SENSOR_DEVICE_ATTR(occ_dvfs_power, 0444, occ_sysfs_show, NULL, 3);
static SENSOR_DEVICE_ATTR(occ_mem_throttle, 0444, occ_sysfs_show, NULL, 4);
@@ -139,7 +169,7 @@ void occ_sysfs_poll_done(struct occ *occ)
* On the first poll response, we haven't yet created the sysfs
* attributes, so don't make any notify calls.
*/
- if (!occ->hwmon)
+ if (!occ->active)
goto done;
if ((header->status & OCC_STAT_MASTER) !=
@@ -148,12 +178,6 @@ void occ_sysfs_poll_done(struct occ *occ)
sysfs_notify(&occ->bus_dev->kobj, NULL, name);
}
- if ((header->status & OCC_STAT_ACTIVE) !=
- (occ->prev_stat & OCC_STAT_ACTIVE)) {
- name = sensor_dev_attr_occ_active.dev_attr.attr.name;
- sysfs_notify(&occ->bus_dev->kobj, NULL, name);
- }
-
if ((header->ext_status & OCC_EXT_STAT_DVFS_OT) !=
(occ->prev_ext_stat & OCC_EXT_STAT_DVFS_OT)) {
name = sensor_dev_attr_occ_dvfs_overtemp.dev_attr.attr.name;
@@ -227,8 +251,7 @@ int occ_setup_sysfs(struct occ *occ)
return sysfs_create_group(&occ->bus_dev->kobj, &occ_sysfs);
}
-void occ_shutdown(struct occ *occ)
+void occ_shutdown_sysfs(struct occ *occ)
{
sysfs_remove_group(&occ->bus_dev->kobj, &occ_sysfs);
}
-EXPORT_SYMBOL_GPL(occ_shutdown);
diff --git a/drivers/hwmon/peci/cputemp.c b/drivers/hwmon/peci/cputemp.c
index 12156328f5cf..57470fda5f6c 100644
--- a/drivers/hwmon/peci/cputemp.c
+++ b/drivers/hwmon/peci/cputemp.c
@@ -447,29 +447,23 @@ static const struct hwmon_ops peci_cputemp_ops = {
.read = cputemp_read,
};
-static const u32 peci_cputemp_temp_channel_config[] = {
- /* Die temperature */
- HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_CRIT_HYST,
- /* DTS margin */
- HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_CRIT_HYST,
- /* Tcontrol temperature */
- HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT,
- /* Tthrottle temperature */
- HWMON_T_LABEL | HWMON_T_INPUT,
- /* Tjmax temperature */
- HWMON_T_LABEL | HWMON_T_INPUT,
- /* Core temperature - for all core channels */
- [channel_core ... CPUTEMP_CHANNEL_NUMS - 1] = HWMON_T_LABEL | HWMON_T_INPUT,
- 0
-};
-
-static const struct hwmon_channel_info peci_cputemp_temp_channel = {
- .type = hwmon_temp,
- .config = peci_cputemp_temp_channel_config,
-};
-
static const struct hwmon_channel_info *peci_cputemp_info[] = {
- &peci_cputemp_temp_channel,
+ HWMON_CHANNEL_INFO(temp,
+ /* Die temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX |
+ HWMON_T_CRIT | HWMON_T_CRIT_HYST,
+ /* DTS margin */
+ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX |
+ HWMON_T_CRIT | HWMON_T_CRIT_HYST,
+ /* Tcontrol temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT,
+ /* Tthrottle temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT,
+ /* Tjmax temperature */
+ HWMON_T_LABEL | HWMON_T_INPUT,
+ /* Core temperature - for all core channels */
+ [channel_core ... CPUTEMP_CHANNEL_NUMS - 1] =
+ HWMON_T_LABEL | HWMON_T_INPUT),
NULL
};
diff --git a/drivers/hwmon/peci/dimmtemp.c b/drivers/hwmon/peci/dimmtemp.c
index c8222354c005..0a633bda3668 100644
--- a/drivers/hwmon/peci/dimmtemp.c
+++ b/drivers/hwmon/peci/dimmtemp.c
@@ -4,6 +4,7 @@
#include <linux/auxiliary_bus.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
+#include <linux/devm-helpers.h>
#include <linux/hwmon.h>
#include <linux/jiffies.h>
#include <linux/module.h>
@@ -219,7 +220,7 @@ static int check_populated_dimms(struct peci_dimmtemp *priv)
int chan_rank_max = priv->gen_info->chan_rank_max;
int dimm_idx_max = priv->gen_info->dimm_idx_max;
u32 chan_rank_empty = 0;
- u64 dimm_mask = 0;
+ u32 dimm_mask = 0;
int chan_rank, dimm_idx, ret;
u32 pcs;
@@ -278,9 +279,9 @@ static int check_populated_dimms(struct peci_dimmtemp *priv)
return -EAGAIN;
}
- dev_dbg(priv->dev, "Scanned populated DIMMs: %#llx\n", dimm_mask);
+ dev_dbg(priv->dev, "Scanned populated DIMMs: %#x\n", dimm_mask);
- bitmap_from_u64(priv->dimm_mask, dimm_mask);
+ bitmap_from_arr32(priv->dimm_mask, &dimm_mask, DIMM_NUMS_MAX);
return 0;
}
@@ -299,18 +300,10 @@ static int create_dimm_temp_label(struct peci_dimmtemp *priv, int chan)
return 0;
}
-static const u32 peci_dimmtemp_temp_channel_config[] = {
- [0 ... DIMM_NUMS_MAX - 1] = HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT,
- 0
-};
-
-static const struct hwmon_channel_info peci_dimmtemp_temp_channel = {
- .type = hwmon_temp,
- .config = peci_dimmtemp_temp_channel_config,
-};
-
static const struct hwmon_channel_info *peci_dimmtemp_temp_info[] = {
- &peci_dimmtemp_temp_channel,
+ HWMON_CHANNEL_INFO(temp,
+ [0 ... DIMM_NUMS_MAX - 1] = HWMON_T_LABEL |
+ HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT),
NULL
};
@@ -378,13 +371,6 @@ static void create_dimm_temp_info_delayed(struct work_struct *work)
dev_err(priv->dev, "Failed to populate DIMM temp info\n");
}
-static void remove_delayed_work(void *_priv)
-{
- struct peci_dimmtemp *priv = _priv;
-
- cancel_delayed_work_sync(&priv->detect_work);
-}
-
static int peci_dimmtemp_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id)
{
struct device *dev = &adev->dev;
@@ -415,9 +401,8 @@ static int peci_dimmtemp_probe(struct auxiliary_device *adev, const struct auxil
"Unexpected PECI revision %#x, some features may be unavailable\n",
peci_dev->info.peci_revision);
- INIT_DELAYED_WORK(&priv->detect_work, create_dimm_temp_info_delayed);
-
- ret = devm_add_action_or_reset(priv->dev, remove_delayed_work, priv);
+ ret = devm_delayed_work_autocancel(priv->dev, &priv->detect_work,
+ create_dimm_temp_info_delayed);
if (ret)
return ret;
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index a2ea1d5a8765..dfae76db65ae 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -228,10 +228,10 @@ config SENSORS_MAX16064
be called max16064.
config SENSORS_MAX16601
- tristate "Maxim MAX16508, MAX16601"
+ tristate "Maxim MAX16508, MAX16601, MAX16602"
help
If you say yes here you get hardware monitoring support for Maxim
- MAX16508 and MAX16601.
+ MAX16508, MAX16601 and MAX16602.
This driver can also be built as a module. If so, the module will
be called max16601.
@@ -408,6 +408,15 @@ config SENSORS_UCD9200
This driver can also be built as a module. If so, the module will
be called ucd9200.
+config SENSORS_XDPE152
+ tristate "Infineon XDPE152 family"
+ help
+ If you say yes here you get hardware monitoring support for Infineon
+ XDPE15284, XDPE152C4, device.
+
+ This driver can also be built as a module. If so, the module will
+ be called xdpe152c4.
+
config SENSORS_XDPE122
tristate "Infineon XDPE122 family"
help
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index a4a96ac71de7..4678fba5012c 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -43,5 +43,6 @@ obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o
obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
obj-$(CONFIG_SENSORS_XDPE122) += xdpe12284.o
+obj-$(CONFIG_SENSORS_XDPE152) += xdpe152c4.o
obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o
obj-$(CONFIG_SENSORS_PIM4328) += pim4328.o
diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c
index 0127273883f0..531aa674a928 100644
--- a/drivers/hwmon/pmbus/ltc2978.c
+++ b/drivers/hwmon/pmbus/ltc2978.c
@@ -196,6 +196,17 @@ static int ltc_read_byte_data(struct i2c_client *client, int page, int reg)
return pmbus_read_byte_data(client, page, reg);
}
+static int ltc_write_byte_data(struct i2c_client *client, int page, int reg, u8 value)
+{
+ int ret;
+
+ ret = ltc_wait_ready(client);
+ if (ret < 0)
+ return ret;
+
+ return pmbus_write_byte_data(client, page, reg, value);
+}
+
static int ltc_write_byte(struct i2c_client *client, int page, u8 byte)
{
int ret;
@@ -681,6 +692,7 @@ static int ltc2978_probe(struct i2c_client *client)
info = &data->info;
info->write_word_data = ltc2978_write_word_data;
info->write_byte = ltc_write_byte;
+ info->write_byte_data = ltc_write_byte_data;
info->read_word_data = ltc_read_word_data;
info->read_byte_data = ltc_read_byte_data;
diff --git a/drivers/hwmon/pmbus/max16601.c b/drivers/hwmon/pmbus/max16601.c
index 5a226a564776..b628405e6586 100644
--- a/drivers/hwmon/pmbus/max16601.c
+++ b/drivers/hwmon/pmbus/max16601.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Hardware monitoring driver for Maxim MAX16508 and MAX16601.
+ * Hardware monitoring driver for Maxim MAX16508, MAX16601 and MAX16602.
*
* Implementation notes:
*
@@ -31,7 +31,7 @@
#include "pmbus.h"
-enum chips { max16508, max16601 };
+enum chips { max16508, max16601, max16602 };
#define REG_DEFAULT_NUM_POP 0xc4
#define REG_SETPT_DVID 0xd1
@@ -202,7 +202,7 @@ static int max16601_identify(struct i2c_client *client,
else
info->vrm_version[0] = vr12;
- if (data->id != max16601)
+ if (data->id != max16601 && data->id != max16602)
return 0;
reg = i2c_smbus_read_byte_data(client, REG_DEFAULT_NUM_POP);
@@ -264,6 +264,7 @@ static void max16601_remove(void *_data)
static const struct i2c_device_id max16601_id[] = {
{"max16508", max16508},
{"max16601", max16601},
+ {"max16602", max16602},
{}
};
MODULE_DEVICE_TABLE(i2c, max16601_id);
@@ -280,13 +281,15 @@ static int max16601_get_id(struct i2c_client *client)
return -ENODEV;
/*
- * PMBUS_IC_DEVICE_ID is expected to return "MAX16601y.xx"
- * or "MAX16500y.xx".
+ * PMBUS_IC_DEVICE_ID is expected to return "MAX16601y.xx" or
+ * MAX16602y.xx or "MAX16500y.xx".cdxxcccccccccc
*/
if (!strncmp(buf, "MAX16500", 8)) {
id = max16508;
} else if (!strncmp(buf, "MAX16601", 8)) {
id = max16601;
+ } else if (!strncmp(buf, "MAX16602", 8)) {
+ id = max16602;
} else {
buf[ret] = '\0';
dev_err(dev, "Unsupported chip '%s'\n", buf);
diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
index e74b6ef070f3..c031a9700ace 100644
--- a/drivers/hwmon/pmbus/pmbus.h
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -438,6 +438,8 @@ struct pmbus_driver_info {
int (*read_byte_data)(struct i2c_client *client, int page, int reg);
int (*read_word_data)(struct i2c_client *client, int page, int phase,
int reg);
+ int (*write_byte_data)(struct i2c_client *client, int page, int reg,
+ u8 byte);
int (*write_word_data)(struct i2c_client *client, int page, int reg,
u16 word);
int (*write_byte)(struct i2c_client *client, int page, u8 value);
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index d93574d6a1fb..02912022853d 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -19,6 +19,8 @@
#include <linux/pmbus.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
+#include <linux/of.h>
+#include <linux/thermal.h>
#include "pmbus.h"
/*
@@ -276,6 +278,42 @@ static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg,
return pmbus_write_word_data(client, page, reg, word);
}
+/*
+ * _pmbus_write_byte_data() is similar to pmbus_write_byte_data(), but checks if
+ * a device specific mapping function exists and calls it if necessary.
+ */
+static int _pmbus_write_byte_data(struct i2c_client *client, int page, int reg, u8 value)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ const struct pmbus_driver_info *info = data->info;
+ int status;
+
+ if (info->write_byte_data) {
+ status = info->write_byte_data(client, page, reg, value);
+ if (status != -ENODATA)
+ return status;
+ }
+ return pmbus_write_byte_data(client, page, reg, value);
+}
+
+/*
+ * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if
+ * a device specific mapping function exists and calls it if necessary.
+ */
+static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ const struct pmbus_driver_info *info = data->info;
+ int status;
+
+ if (info->read_byte_data) {
+ status = info->read_byte_data(client, page, reg);
+ if (status != -ENODATA)
+ return status;
+ }
+ return pmbus_read_byte_data(client, page, reg);
+}
+
int pmbus_update_fan(struct i2c_client *client, int page, int id,
u8 config, u8 mask, u16 command)
{
@@ -283,14 +321,14 @@ int pmbus_update_fan(struct i2c_client *client, int page, int id,
int rv;
u8 to;
- from = pmbus_read_byte_data(client, page,
+ from = _pmbus_read_byte_data(client, page,
pmbus_fan_config_registers[id]);
if (from < 0)
return from;
to = (from & ~mask) | (config & mask);
if (to != from) {
- rv = pmbus_write_byte_data(client, page,
+ rv = _pmbus_write_byte_data(client, page,
pmbus_fan_config_registers[id], to);
if (rv < 0)
return rv;
@@ -390,37 +428,19 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
unsigned int tmp;
int rv;
- rv = pmbus_read_byte_data(client, page, reg);
+ rv = _pmbus_read_byte_data(client, page, reg);
if (rv < 0)
return rv;
tmp = (rv & ~mask) | (value & mask);
if (tmp != rv)
- rv = pmbus_write_byte_data(client, page, reg, tmp);
+ rv = _pmbus_write_byte_data(client, page, reg, tmp);
return rv;
}
EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS);
-/*
- * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if
- * a device specific mapping function exists and calls it if necessary.
- */
-static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg)
-{
- struct pmbus_data *data = i2c_get_clientdata(client);
- const struct pmbus_driver_info *info = data->info;
- int status;
-
- if (info->read_byte_data) {
- status = info->read_byte_data(client, page, reg);
- if (status != -ENODATA)
- return status;
- }
- return pmbus_read_byte_data(client, page, reg);
-}
-
static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page,
int reg)
{
@@ -455,7 +475,7 @@ static int pmbus_get_fan_rate(struct i2c_client *client, int page, int id,
return s->data;
}
- config = pmbus_read_byte_data(client, page,
+ config = _pmbus_read_byte_data(client, page,
pmbus_fan_config_registers[id]);
if (config < 0)
return config;
@@ -912,7 +932,7 @@ static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b,
regval = status & mask;
if (regval) {
- ret = pmbus_write_byte_data(client, page, reg, regval);
+ ret = _pmbus_write_byte_data(client, page, reg, regval);
if (ret)
goto unlock;
}
@@ -1083,6 +1103,68 @@ static int pmbus_add_boolean(struct pmbus_data *data,
return pmbus_add_attribute(data, &a->dev_attr.attr);
}
+/* of thermal for pmbus temperature sensors */
+struct pmbus_thermal_data {
+ struct pmbus_data *pmbus_data;
+ struct pmbus_sensor *sensor;
+};
+
+static int pmbus_thermal_get_temp(void *data, int *temp)
+{
+ struct pmbus_thermal_data *tdata = data;
+ struct pmbus_sensor *sensor = tdata->sensor;
+ struct pmbus_data *pmbus_data = tdata->pmbus_data;
+ struct i2c_client *client = to_i2c_client(pmbus_data->dev);
+ struct device *dev = pmbus_data->hwmon_dev;
+ int ret = 0;
+
+ if (!dev) {
+ /* May not even get to hwmon yet */
+ *temp = 0;
+ return 0;
+ }
+
+ mutex_lock(&pmbus_data->update_lock);
+ pmbus_update_sensor_data(client, sensor);
+ if (sensor->data < 0)
+ ret = sensor->data;
+ else
+ *temp = (int)pmbus_reg2data(pmbus_data, sensor);
+ mutex_unlock(&pmbus_data->update_lock);
+
+ return ret;
+}
+
+static const struct thermal_zone_of_device_ops pmbus_thermal_ops = {
+ .get_temp = pmbus_thermal_get_temp,
+};
+
+static int pmbus_thermal_add_sensor(struct pmbus_data *pmbus_data,
+ struct pmbus_sensor *sensor, int index)
+{
+ struct device *dev = pmbus_data->dev;
+ struct pmbus_thermal_data *tdata;
+ struct thermal_zone_device *tzd;
+
+ tdata = devm_kzalloc(dev, sizeof(*tdata), GFP_KERNEL);
+ if (!tdata)
+ return -ENOMEM;
+
+ tdata->sensor = sensor;
+ tdata->pmbus_data = pmbus_data;
+
+ tzd = devm_thermal_zone_of_sensor_register(dev, index, tdata,
+ &pmbus_thermal_ops);
+ /*
+ * If CONFIG_THERMAL_OF is disabled, this returns -ENODEV,
+ * so ignore that error but forward any other error.
+ */
+ if (IS_ERR(tzd) && (PTR_ERR(tzd) != -ENODEV))
+ return PTR_ERR(tzd);
+
+ return 0;
+}
+
static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
const char *name, const char *type,
int seq, int page, int phase,
@@ -1126,6 +1208,10 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
sensor->next = data->sensors;
data->sensors = sensor;
+ /* temperature sensors with _input values are registered with thermal */
+ if (class == PSC_TEMPERATURE && strcmp(type, "input") == 0)
+ pmbus_thermal_add_sensor(data, sensor, seq);
+
return sensor;
}
@@ -2309,6 +2395,21 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
int page, ret;
/*
+ * Figure out if PEC is enabled before accessing any other register.
+ * Make sure PEC is disabled, will be enabled later if needed.
+ */
+ client->flags &= ~I2C_CLIENT_PEC;
+
+ /* Enable PEC if the controller and bus supports it */
+ if (!(data->flags & PMBUS_NO_CAPABILITY)) {
+ ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
+ if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) {
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC))
+ client->flags |= I2C_CLIENT_PEC;
+ }
+ }
+
+ /*
* 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.
@@ -2326,19 +2427,6 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
data->has_status_word = true;
}
- /* Make sure PEC is disabled, will be enabled later if needed */
- client->flags &= ~I2C_CLIENT_PEC;
-
- /* Enable PEC if the controller and bus supports it */
- if (!(data->flags & PMBUS_NO_CAPABILITY)) {
- ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
- if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) {
- if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) {
- client->flags |= I2C_CLIENT_PEC;
- }
- }
- }
-
/*
* Check if the chip is write protected. If it is, we can not clear
* faults, and we should not try it. Also, in that case, writes into
@@ -2399,7 +2487,7 @@ static int pmbus_regulator_is_enabled(struct regulator_dev *rdev)
int ret;
mutex_lock(&data->update_lock);
- ret = pmbus_read_byte_data(client, page, PMBUS_OPERATION);
+ ret = _pmbus_read_byte_data(client, page, PMBUS_OPERATION);
mutex_unlock(&data->update_lock);
if (ret < 0)
@@ -2498,7 +2586,7 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
if (!(func & cat->func))
continue;
- status = pmbus_read_byte_data(client, page, cat->reg);
+ status = _pmbus_read_byte_data(client, page, cat->reg);
if (status < 0) {
mutex_unlock(&data->update_lock);
return status;
@@ -2548,11 +2636,78 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
return 0;
}
+static int pmbus_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ struct device *dev = rdev_get_dev(rdev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ struct pmbus_sensor s = {
+ .page = rdev_get_id(rdev),
+ .class = PSC_VOLTAGE_OUT,
+ .convert = true,
+ };
+
+ s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_READ_VOUT);
+ if (s.data < 0)
+ return s.data;
+
+ return (int)pmbus_reg2data(data, &s) * 1000; /* unit is uV */
+}
+
+static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
+ int max_uv, unsigned int *selector)
+{
+ struct device *dev = rdev_get_dev(rdev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct pmbus_data *data = i2c_get_clientdata(client);
+ struct pmbus_sensor s = {
+ .page = rdev_get_id(rdev),
+ .class = PSC_VOLTAGE_OUT,
+ .convert = true,
+ .data = -1,
+ };
+ int val = DIV_ROUND_CLOSEST(min_uv, 1000); /* convert to mV */
+ int low, high;
+
+ *selector = 0;
+
+ if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MIN))
+ s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_MFR_VOUT_MIN);
+ if (s.data < 0) {
+ s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_VOUT_MARGIN_LOW);
+ if (s.data < 0)
+ return s.data;
+ }
+ low = pmbus_reg2data(data, &s);
+
+ s.data = -1;
+ if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MAX))
+ s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_MFR_VOUT_MAX);
+ if (s.data < 0) {
+ s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_VOUT_MARGIN_HIGH);
+ if (s.data < 0)
+ return s.data;
+ }
+ high = pmbus_reg2data(data, &s);
+
+ /* Make sure we are within margins */
+ if (low > val)
+ val = low;
+ if (high < val)
+ val = high;
+
+ val = pmbus_data2reg(data, &s, val);
+
+ return _pmbus_write_word_data(client, s.page, PMBUS_VOUT_COMMAND, (u16)val);
+}
+
const struct regulator_ops pmbus_regulator_ops = {
.enable = pmbus_regulator_enable,
.disable = pmbus_regulator_disable,
.is_enabled = pmbus_regulator_is_enabled,
.get_error_flags = pmbus_regulator_get_error_flags,
+ .get_voltage = pmbus_regulator_get_voltage,
+ .set_voltage = pmbus_regulator_set_voltage,
};
EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS);
diff --git a/drivers/hwmon/pmbus/xdpe152c4.c b/drivers/hwmon/pmbus/xdpe152c4.c
new file mode 100644
index 000000000000..b8a36ef73e45
--- /dev/null
+++ b/drivers/hwmon/pmbus/xdpe152c4.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for Infineon Multi-phase Digital VR Controllers
+ *
+ * Copyright (c) 2022 Infineon Technologies. All rights reserved.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "pmbus.h"
+
+#define XDPE152_PAGE_NUM 2
+
+static struct pmbus_driver_info xdpe152_info = {
+ .pages = XDPE152_PAGE_NUM,
+ .format[PSC_VOLTAGE_IN] = linear,
+ .format[PSC_VOLTAGE_OUT] = linear,
+ .format[PSC_TEMPERATURE] = linear,
+ .format[PSC_CURRENT_IN] = linear,
+ .format[PSC_CURRENT_OUT] = linear,
+ .format[PSC_POWER] = linear,
+ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_STATUS_TEMP |
+ PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT,
+ .func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
+ PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT,
+};
+
+static int xdpe152_probe(struct i2c_client *client)
+{
+ struct pmbus_driver_info *info;
+
+ info = devm_kmemdup(&client->dev, &xdpe152_info, sizeof(*info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ return pmbus_do_probe(client, info);
+}
+
+static const struct i2c_device_id xdpe152_id[] = {
+ {"xdpe152c4", 0},
+ {"xdpe15284", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, xdpe152_id);
+
+static const struct of_device_id __maybe_unused xdpe152_of_match[] = {
+ {.compatible = "infineon,xdpe152c4"},
+ {.compatible = "infineon,xdpe15284"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, xdpe152_of_match);
+
+static struct i2c_driver xdpe152_driver = {
+ .driver = {
+ .name = "xdpe152c4",
+ .of_match_table = of_match_ptr(xdpe152_of_match),
+ },
+ .probe_new = xdpe152_probe,
+ .id_table = xdpe152_id,
+};
+
+module_i2c_driver(xdpe152_driver);
+
+MODULE_AUTHOR("Greg Schwendimann <greg.schwendimann@infineon.com>");
+MODULE_DESCRIPTION("PMBus driver for Infineon XDPE152 family");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index f12b9a28a232..6c08551d8d14 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -49,16 +49,6 @@ struct pwm_fan_ctx {
struct hwmon_channel_info fan_channel;
};
-static const u32 pwm_fan_channel_config_pwm[] = {
- HWMON_PWM_INPUT,
- 0
-};
-
-static const struct hwmon_channel_info pwm_fan_channel_pwm = {
- .type = hwmon_pwm,
- .config = pwm_fan_channel_config_pwm,
-};
-
/* This handler assumes self resetting edge triggered interrupt. */
static irqreturn_t pulse_handler(int irq, void *dev_id)
{
@@ -387,7 +377,7 @@ static int pwm_fan_probe(struct platform_device *pdev)
if (!channels)
return -ENOMEM;
- channels[0] = &pwm_fan_channel_pwm;
+ channels[0] = HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT);
for (i = 0; i < ctx->tach_count; i++) {
struct pwm_fan_tach *tach = &ctx->tachs[i];
diff --git a/drivers/hwmon/sl28cpld-hwmon.c b/drivers/hwmon/sl28cpld-hwmon.c
index e48f58ec5b9c..9ce4899a81a5 100644
--- a/drivers/hwmon/sl28cpld-hwmon.c
+++ b/drivers/hwmon/sl28cpld-hwmon.c
@@ -54,7 +54,7 @@ static int sl28cpld_hwmon_read(struct device *dev,
/*
* The counter period is 1000ms and the sysfs specification
- * says we should asssume 2 pulses per revolution.
+ * says we should assume 2 pulses per revolution.
*/
value *= 60 / 2;
@@ -67,18 +67,8 @@ static int sl28cpld_hwmon_read(struct device *dev,
return 0;
}
-static const u32 sl28cpld_hwmon_fan_config[] = {
- HWMON_F_INPUT,
- 0
-};
-
-static const struct hwmon_channel_info sl28cpld_hwmon_fan = {
- .type = hwmon_fan,
- .config = sl28cpld_hwmon_fan_config,
-};
-
static const struct hwmon_channel_info *sl28cpld_hwmon_info[] = {
- &sl28cpld_hwmon_fan,
+ HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
NULL
};
diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c
index 52c9e7d3f2ae..cc0a1c219b1f 100644
--- a/drivers/hwmon/tmp401.c
+++ b/drivers/hwmon/tmp401.c
@@ -41,6 +41,8 @@ enum chips { tmp401, tmp411, tmp431, tmp432, tmp435 };
#define TMP401_STATUS 0x02
#define TMP401_CONFIG 0x03
#define TMP401_CONVERSION_RATE 0x04
+#define TMP4XX_N_FACTOR_REG 0x18
+#define TMP43X_BETA_RANGE 0x25
#define TMP401_TEMP_CRIT_HYST 0x21
#define TMP401_MANUFACTURER_ID_REG 0xFE
#define TMP401_DEVICE_ID_REG 0xFF
@@ -543,6 +545,8 @@ static int tmp401_init_client(struct tmp401_data *data)
struct regmap *regmap = data->regmap;
u32 config, config_orig;
int ret;
+ u32 val = 0;
+ s32 nfactor = 0;
/* Set conversion rate to 2 Hz */
ret = regmap_write(regmap, TMP401_CONVERSION_RATE, 5);
@@ -557,12 +561,50 @@ static int tmp401_init_client(struct tmp401_data *data)
config_orig = config;
config &= ~TMP401_CONFIG_SHUTDOWN;
+ if (of_property_read_bool(data->client->dev.of_node, "ti,extended-range-enable")) {
+ /* Enable measurement over extended temperature range */
+ config |= TMP401_CONFIG_RANGE;
+ }
+
data->extended_range = !!(config & TMP401_CONFIG_RANGE);
- if (config != config_orig)
+ if (config != config_orig) {
ret = regmap_write(regmap, TMP401_CONFIG, config);
+ if (ret < 0)
+ return ret;
+ }
- return ret;
+ ret = of_property_read_u32(data->client->dev.of_node, "ti,n-factor", &nfactor);
+ if (!ret) {
+ if (data->kind == tmp401) {
+ dev_err(&data->client->dev, "ti,tmp401 does not support n-factor correction\n");
+ return -EINVAL;
+ }
+ if (nfactor < -128 || nfactor > 127) {
+ dev_err(&data->client->dev, "n-factor is invalid (%d)\n", nfactor);
+ return -EINVAL;
+ }
+ ret = regmap_write(regmap, TMP4XX_N_FACTOR_REG, (unsigned int)nfactor);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = of_property_read_u32(data->client->dev.of_node, "ti,beta-compensation", &val);
+ if (!ret) {
+ if (data->kind == tmp401 || data->kind == tmp411) {
+ dev_err(&data->client->dev, "ti,tmp401 or ti,tmp411 does not support beta compensation\n");
+ return -EINVAL;
+ }
+ if (val > 15) {
+ dev_err(&data->client->dev, "beta-compensation is invalid (%u)\n", val);
+ return -EINVAL;
+ }
+ ret = regmap_write(regmap, TMP43X_BETA_RANGE, val);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
}
static int tmp401_detect(struct i2c_client *client,
diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c
index ad03262cca56..09e49ec8b6f4 100644
--- a/drivers/thermal/thermal_hwmon.c
+++ b/drivers/thermal/thermal_hwmon.c
@@ -149,8 +149,8 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
INIT_LIST_HEAD(&hwmon->tz_list);
strlcpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH);
strreplace(hwmon->type, '-', '_');
- hwmon->device = hwmon_device_register_with_info(&tz->device, hwmon->type,
- hwmon, NULL, NULL);
+ hwmon->device = hwmon_device_register_for_thermal(&tz->device,
+ hwmon->type, hwmon);
if (IS_ERR(hwmon->device)) {
result = PTR_ERR(hwmon->device);
goto free_mem;
@@ -277,3 +277,5 @@ int devm_thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
return ret;
}
EXPORT_SYMBOL_GPL(devm_thermal_add_hwmon_sysfs);
+
+MODULE_IMPORT_NS(HWMON_THERMAL);