summaryrefslogtreecommitdiff
path: root/drivers/hwmon
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/applesmc.c7
-rw-r--r--drivers/hwmon/aspeed-pwm-tacho.c4
-rw-r--r--drivers/hwmon/asus_rog_ryujin.c30
-rw-r--r--drivers/hwmon/cgbc-hwmon.c129
-rw-r--r--drivers/hwmon/chipcap2.c9
-rw-r--r--drivers/hwmon/corsair-cpro.c24
-rw-r--r--drivers/hwmon/gpio-fan.c17
-rw-r--r--drivers/hwmon/hp-wmi-sensors.c32
-rw-r--r--drivers/hwmon/hwmon.c31
-rw-r--r--drivers/hwmon/ina2xx.c163
-rw-r--r--drivers/hwmon/k10temp.c2
-rw-r--r--drivers/hwmon/ltc4282.c2
-rw-r--r--drivers/hwmon/mcp9982.c2
-rw-r--r--drivers/hwmon/nct6694-hwmon.c35
-rw-r--r--drivers/hwmon/pmbus/pmbus.h3
-rw-r--r--drivers/hwmon/pmbus/pmbus_core.c4
-rw-r--r--drivers/hwmon/pmbus/tps53679.c11
-rw-r--r--drivers/hwmon/pwm-fan.c13
-rw-r--r--drivers/hwmon/sht4x.c8
-rw-r--r--drivers/hwmon/w83791d.c1
-rw-r--r--drivers/hwmon/w83793.c4
-rw-r--r--drivers/hwmon/yogafan.c2
22 files changed, 386 insertions, 147 deletions
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index 00e603b5e401..d0baa10502f7 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -1128,12 +1128,17 @@ static void applesmc_release_light_sensor(void)
static int applesmc_create_key_backlight(void)
{
+ int ret;
+
if (!smcreg.has_key_backlight)
return 0;
applesmc_led_wq = create_singlethread_workqueue("applesmc-led");
if (!applesmc_led_wq)
return -ENOMEM;
- return led_classdev_register(&pdev->dev, &applesmc_backlight);
+ ret = led_classdev_register(&pdev->dev, &applesmc_backlight);
+ if (ret)
+ destroy_workqueue(applesmc_led_wq);
+ return ret;
}
static void applesmc_release_key_backlight(void)
diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c
index 1c5945d4ba37..bfce589c3fb1 100644
--- a/drivers/hwmon/aspeed-pwm-tacho.c
+++ b/drivers/hwmon/aspeed-pwm-tacho.c
@@ -934,7 +934,9 @@ static int aspeed_pwm_tacho_probe(struct platform_device *pdev)
"missing or invalid reset controller device tree entry");
return PTR_ERR(priv->rst);
}
- reset_control_deassert(priv->rst);
+ ret = reset_control_deassert(priv->rst);
+ if (ret)
+ return ret;
ret = devm_add_action_or_reset(dev, aspeed_pwm_tacho_remove, priv);
if (ret)
diff --git a/drivers/hwmon/asus_rog_ryujin.c b/drivers/hwmon/asus_rog_ryujin.c
index 702edb831394..e297557ca346 100644
--- a/drivers/hwmon/asus_rog_ryujin.c
+++ b/drivers/hwmon/asus_rog_ryujin.c
@@ -184,6 +184,7 @@ static int rog_ryujin_write_expanded(struct rog_ryujin_data *priv, const u8 *cmd
static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, int cmd_length,
struct completion *status_completion)
{
+ unsigned long flags;
int ret;
/*
@@ -191,9 +192,9 @@ static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, i
* completion. Reinit is done because hidraw could have triggered
* the raw event parsing and marked the passed in completion as done.
*/
- spin_lock_bh(&priv->status_report_request_lock);
+ spin_lock_irqsave(&priv->status_report_request_lock, flags);
reinit_completion(status_completion);
- spin_unlock_bh(&priv->status_report_request_lock);
+ spin_unlock_irqrestore(&priv->status_report_request_lock, flags);
/* Send command for getting data */
ret = rog_ryujin_write_expanded(priv, cmd, cmd_length);
@@ -421,11 +422,19 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo
int size)
{
struct rog_ryujin_data *priv = hid_get_drvdata(hdev);
+ unsigned long flags;
- if (data[0] != RYUJIN_CMD_PREFIX)
+ if (size < 2 || data[0] != RYUJIN_CMD_PREFIX)
return 0;
+ spin_lock_irqsave(&priv->status_report_request_lock, flags);
+
if (data[1] == RYUJIN_GET_COOLER_STATUS_CMD_RESPONSE) {
+ if (size <= priv->info->temp_offset + 1 ||
+ size <= priv->info->pump_speed_offset + 1 ||
+ size <= priv->info->fan_speed_offset + 1)
+ goto unlock;
+
/* Received coolant temp and speeds of pump and internal fan */
priv->temp_input[0] = data[priv->info->temp_offset] * 1000 +
data[priv->info->temp_offset + 1] * 100;
@@ -437,6 +446,9 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo
if (!completion_done(&priv->cooler_status_received))
complete_all(&priv->cooler_status_received);
} else if (data[1] == RYUJIN_GET_CONTROLLER_SPEED_CMD_RESPONSE) {
+ if (size <= RYUJIN_CONTROLLER_SPEED_3 + 1)
+ goto unlock;
+
/* Received speeds of four fans attached to the controller */
priv->speed_input[2] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_1);
priv->speed_input[3] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_2);
@@ -446,6 +458,9 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo
if (!completion_done(&priv->controller_status_received))
complete_all(&priv->controller_status_received);
} else if (data[1] == RYUJIN_GET_COOLER_DUTY_CMD_RESPONSE) {
+ if (size <= RYUJIN_INTERNAL_FAN_DUTY)
+ goto unlock;
+
/* Received report for pump and internal fan duties (in %) */
if (data[RYUJIN_PUMP_DUTY] == 0 && data[RYUJIN_INTERNAL_FAN_DUTY] == 0) {
/*
@@ -463,7 +478,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo
* We're expecting a report, so parse it.
*/
goto read_cooler_duty;
- return 0;
+ goto unlock;
}
read_cooler_duty:
priv->duty_input[0] = rog_ryujin_percent_to_pwm(data[RYUJIN_PUMP_DUTY]);
@@ -472,6 +487,9 @@ read_cooler_duty:
if (!completion_done(&priv->cooler_duty_received))
complete_all(&priv->cooler_duty_received);
} else if (data[1] == RYUJIN_GET_CONTROLLER_DUTY_CMD_RESPONSE) {
+ if (size <= RYUJIN_CONTROLLER_DUTY)
+ goto unlock;
+
/* Received report for controller duty for fans (in PWM) */
if (data[RYUJIN_CONTROLLER_DUTY] == 0) {
/*
@@ -489,7 +507,7 @@ read_cooler_duty:
* We're expecting a report, so parse it.
*/
goto read_controller_duty;
- return 0;
+ goto unlock;
}
read_controller_duty:
priv->duty_input[2] = data[RYUJIN_CONTROLLER_DUTY];
@@ -498,6 +516,8 @@ read_controller_duty:
complete_all(&priv->controller_duty_received);
}
+unlock:
+ spin_unlock_irqrestore(&priv->status_report_request_lock, flags);
return 0;
}
diff --git a/drivers/hwmon/cgbc-hwmon.c b/drivers/hwmon/cgbc-hwmon.c
index 3aff4e092132..230062a46907 100644
--- a/drivers/hwmon/cgbc-hwmon.c
+++ b/drivers/hwmon/cgbc-hwmon.c
@@ -41,45 +41,60 @@ enum cgbc_sensor_types {
static const char * const cgbc_hwmon_labels_temp[] = {
"CPU Temperature",
- "Box Temperature",
+ "Case Temperature",
"Ambient Temperature",
- "Board Temperature",
- "Carrier Temperature",
- "Chipset Temperature",
- "Video Temperature",
+ "CPU Board Temperature",
+ "Carrier Board Temperature",
+ "System Chipset Temperature",
+ "Video Controller/Board Temperature",
"Other Temperature",
- "TOPDIM Temperature",
- "BOTTOMDIM Temperature",
+ "Top DIMM 0 Temperature",
+ "Bottom DIMM 0 Temperature",
+ "Alternate Board Temperature",
+ "Top DIMM 1 Temperature",
+ "Top DIMM 2 Temperature",
+ "Top DIMM 3 Temperature",
+ "Top DIMM 4 Temperature",
+ "Top DIMM 5 Temperature",
+ "Top DIMM 6 Temperature",
+ "Top DIMM 7 Temperature",
+ "Bottom DIMM 1 Temperature",
};
+static const char * const cgbc_hwmon_labels_in[] = {
+ "CPU Core Voltage",
+ "DC Runtime Voltage",
+ "DC Standby Voltage",
+ "CMOS Battery Voltage",
+ "Battery Supply Voltage",
+ "AC Voltage",
+ "Other Voltage",
+ "5V Runtime Voltage",
+ "5V Standby Voltage",
+ "3V3 Runtime Voltage",
+ "3V3 Standby Voltage",
+ "VCore A Voltage",
+ "VCore B Voltage",
+ "12V Runtime Voltage",
+ "12V Standby Voltage",
+};
+
+/*
+ * Current sensors are a bit special, they don't have consecutive IDs like
+ * other types of sensors. So they need to be defined explicitly.
+ */
static const struct {
- enum hwmon_sensor_types type;
const char *label;
-} cgbc_hwmon_labels_in[] = {
- { hwmon_in, "CPU Voltage" },
- { hwmon_in, "DC Runtime Voltage" },
- { hwmon_in, "DC Standby Voltage" },
- { hwmon_in, "CMOS Battery Voltage" },
- { hwmon_in, "Battery Voltage" },
- { hwmon_in, "AC Voltage" },
- { hwmon_in, "Other Voltage" },
- { hwmon_in, "5V Voltage" },
- { hwmon_in, "5V Standby Voltage" },
- { hwmon_in, "3V3 Voltage" },
- { hwmon_in, "3V3 Standby Voltage" },
- { hwmon_in, "VCore A Voltage" },
- { hwmon_in, "VCore B Voltage" },
- { hwmon_in, "12V Voltage" },
- { hwmon_curr, "DC Current" },
- { hwmon_curr, "5V Current" },
- { hwmon_curr, "12V Current" },
+ int id;
+} cgbc_hwmon_labels_curr[] = {
+ { "DC Runtime Current", 0x12 },
+ { "5V Runtime Current", 0x18 },
+ { "12V Runtime Current", 0x1E },
};
-#define CGBC_HWMON_NB_IN_SENSORS 14
-
static const char * const cgbc_hwmon_labels_fan[] = {
"CPU Fan",
- "Box Fan",
+ "Case Fan",
"Ambient Fan",
"Chipset Fan",
"Video Fan",
@@ -114,7 +129,8 @@ static int cgbc_hwmon_probe_sensors(struct device *dev, struct cgbc_hwmon_data *
for (i = 0; i < nb_sensors; i++) {
enum cgbc_sensor_types type;
- unsigned int channel;
+ unsigned int channel, id;
+ int j;
/*
* No need to request data for the first sensor.
@@ -128,32 +144,49 @@ static int cgbc_hwmon_probe_sensors(struct device *dev, struct cgbc_hwmon_data *
}
type = FIELD_GET(CGBC_HWMON_TYPE_MASK, data[1]);
- channel = FIELD_GET(CGBC_HWMON_ID_MASK, data[1]) - 1;
+ id = FIELD_GET(CGBC_HWMON_ID_MASK, data[1]);
+ channel = id - 1;
if (type == CGBC_HWMON_TYPE_TEMP && channel < ARRAY_SIZE(cgbc_hwmon_labels_temp)) {
sensor->type = hwmon_temp;
sensor->label = cgbc_hwmon_labels_temp[channel];
- } else if (type == CGBC_HWMON_TYPE_IN &&
- channel < ARRAY_SIZE(cgbc_hwmon_labels_in)) {
+ } else if (type == CGBC_HWMON_TYPE_IN) {
/*
* The Board Controller doesn't differentiate current and voltage sensors.
- * Get the sensor type from cgbc_hwmon_labels_in[channel].type instead.
+ * First check if it is a current sensor.
*/
- sensor->type = cgbc_hwmon_labels_in[channel].type;
- sensor->label = cgbc_hwmon_labels_in[channel].label;
+ for (j = 0; j < ARRAY_SIZE(cgbc_hwmon_labels_curr); j++) {
+ if (id == cgbc_hwmon_labels_curr[j].id) {
+ sensor->type = hwmon_curr;
+ sensor->label = cgbc_hwmon_labels_curr[j].label;
+ channel = j;
+ }
+ }
+
+ /* If it's not a current sensor, it may be a voltage sensor. */
+ if (!sensor->label && channel < ARRAY_SIZE(cgbc_hwmon_labels_in)) {
+ sensor->type = hwmon_in;
+ sensor->label = cgbc_hwmon_labels_in[channel];
+ }
} else if (type == CGBC_HWMON_TYPE_FAN &&
channel < ARRAY_SIZE(cgbc_hwmon_labels_fan)) {
sensor->type = hwmon_fan;
sensor->label = cgbc_hwmon_labels_fan[channel];
- } else {
- dev_warn(dev, "Board Controller returned an unknown sensor (type=%d, channel=%d), ignore it",
- type, channel);
+ }
+
+ if (!sensor->label) {
+ dev_warn(dev, "Board Controller returned an unknown sensor (bc_type=%d, bc_id=%d), ignore it",
+ type, id);
continue;
}
sensor->active = FIELD_GET(CGBC_HWMON_ACTIVE_BIT, data[1]);
sensor->channel = channel;
sensor->index = i;
+
+ dev_dbg(dev, "Found sensor: bc_type=%d, bc_id=%d, hwmon_type=%d, hwmon_channel=%d, hwmon_label='%s', active=%d\n",
+ type, id, sensor->type, sensor->channel, sensor->label, sensor->active);
+
sensor++;
hwmon->nb_sensors++;
}
@@ -167,14 +200,6 @@ static struct cgbc_hwmon_sensor *cgbc_hwmon_find_sensor(struct cgbc_hwmon_data *
struct cgbc_hwmon_sensor *sensor = NULL;
int i;
- /*
- * The Board Controller doesn't differentiate current and voltage sensors.
- * The channel value (from the Board Controller point of view) shall be computed for current
- * sensors.
- */
- if (type == hwmon_curr)
- channel += CGBC_HWMON_NB_IN_SENSORS;
-
for (i = 0; i < hwmon->nb_sensors; i++) {
if (hwmon->sensors[i].type == type && hwmon->sensors[i].channel == channel) {
sensor = &hwmon->sensors[i];
@@ -240,7 +265,12 @@ static const struct hwmon_channel_info * const cgbc_hwmon_info[] = {
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
- HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL),
+ HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL),
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,
@@ -248,7 +278,8 @@ static const struct hwmon_channel_info * const cgbc_hwmon_info[] = {
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_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),
diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c
index 086571d556b7..9bef767b589e 100644
--- a/drivers/hwmon/chipcap2.c
+++ b/drivers/hwmon/chipcap2.c
@@ -92,11 +92,6 @@ struct cc2_data {
bool process_irqs;
};
-enum cc2_chan_addr {
- CC2_CHAN_TEMP = 0,
- CC2_CHAN_HUMIDITY,
-};
-
/* %RH as a per cent mille from a register value */
static long cc2_rh_convert(u16 data)
{
@@ -499,7 +494,7 @@ static irqreturn_t cc2_low_interrupt(int irq, void *data)
if (cc2->process_irqs) {
hwmon_notify_event(cc2->hwmon, hwmon_humidity,
- hwmon_humidity_min_alarm, CC2_CHAN_HUMIDITY);
+ hwmon_humidity_min_alarm, 0);
cc2->rh_alarm.low_alarm = true;
}
@@ -512,7 +507,7 @@ static irqreturn_t cc2_high_interrupt(int irq, void *data)
if (cc2->process_irqs) {
hwmon_notify_event(cc2->hwmon, hwmon_humidity,
- hwmon_humidity_max_alarm, CC2_CHAN_HUMIDITY);
+ hwmon_humidity_max_alarm, 0);
cc2->rh_alarm.high_alarm = true;
}
diff --git a/drivers/hwmon/corsair-cpro.c b/drivers/hwmon/corsair-cpro.c
index 8354a002f4c5..c09645152613 100644
--- a/drivers/hwmon/corsair-cpro.c
+++ b/drivers/hwmon/corsair-cpro.c
@@ -566,21 +566,18 @@ static int bootloader_show(struct seq_file *seqf, void *unused)
}
DEFINE_SHOW_ATTRIBUTE(bootloader);
-static void ccp_debugfs_init(struct ccp_device *ccp)
+static void ccp_debugfs_init(struct ccp_device *ccp, bool fw_valid, bool bl_valid)
{
char name[32];
- int ret;
scnprintf(name, sizeof(name), "corsaircpro-%s", dev_name(&ccp->hdev->dev));
ccp->debugfs = debugfs_create_dir(name, NULL);
- ret = get_fw_version(ccp);
- if (!ret)
+ if (fw_valid)
debugfs_create_file("firmware_version", 0444,
ccp->debugfs, ccp, &firmware_fops);
- ret = get_bl_version(ccp);
- if (!ret)
+ if (bl_valid)
debugfs_create_file("bootloader_version", 0444,
ccp->debugfs, ccp, &bootloader_fops);
}
@@ -588,6 +585,7 @@ static void ccp_debugfs_init(struct ccp_device *ccp)
static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct ccp_device *ccp;
+ bool fw_valid, bl_valid;
int ret;
ccp = devm_kzalloc(&hdev->dev, sizeof(*ccp), GFP_KERNEL);
@@ -632,17 +630,27 @@ static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret)
goto out_hw_close;
- ccp_debugfs_init(ccp);
+ /*
+ * Query the versions before registering the hwmon device: they send
+ * USB commands without holding ccp->mutex, which is only safe while
+ * nothing else can call send_usb_cmd().
+ */
+ fw_valid = !get_fw_version(ccp);
+ bl_valid = !get_bl_version(ccp);
ccp->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsaircpro",
ccp, &ccp_chip_info, NULL);
if (IS_ERR(ccp->hwmon_dev)) {
ret = PTR_ERR(ccp->hwmon_dev);
- goto out_hw_close;
+ goto out_debugfs_remove;
}
+ ccp_debugfs_init(ccp, fw_valid, bl_valid);
+
return 0;
+out_debugfs_remove:
+ debugfs_remove_recursive(ccp->debugfs);
out_hw_close:
hid_hw_close(hdev);
hid_device_io_stop(hdev);
diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c
index 084828e1e281..3f78375eeb44 100644
--- a/drivers/hwmon/gpio-fan.c
+++ b/drivers/hwmon/gpio-fan.c
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/devm-helpers.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/kstrtox.h>
@@ -67,7 +68,7 @@ static irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id)
schedule_work(&fan_data->alarm_work);
- return IRQ_NONE;
+ return IRQ_HANDLED;
}
static ssize_t fan1_alarm_show(struct device *dev,
@@ -84,6 +85,7 @@ static DEVICE_ATTR_RO(fan1_alarm);
static int fan_alarm_init(struct gpio_fan_data *fan_data)
{
int alarm_irq;
+ int err;
struct device *dev = fan_data->dev;
/*
@@ -94,10 +96,14 @@ static int fan_alarm_init(struct gpio_fan_data *fan_data)
if (alarm_irq <= 0)
return 0;
- INIT_WORK(&fan_data->alarm_work, fan_alarm_notify);
+ err = devm_work_autocancel(dev, &fan_data->alarm_work,
+ fan_alarm_notify);
+ if (err)
+ return err;
+
irq_set_irq_type(alarm_irq, IRQ_TYPE_EDGE_BOTH);
return devm_request_irq(dev, alarm_irq, fan_alarm_irq_handler,
- IRQF_SHARED, "GPIO fan alarm", fan_data);
+ 0, "GPIO fan alarm", fan_data);
}
/*
@@ -606,8 +612,11 @@ static void gpio_fan_shutdown(struct platform_device *pdev)
{
struct gpio_fan_data *fan_data = platform_get_drvdata(pdev);
- if (fan_data->gpios)
+ if (fan_data->gpios) {
+ mutex_lock(&fan_data->lock);
set_fan_speed(fan_data, 0);
+ mutex_unlock(&fan_data->lock);
+ }
}
static int gpio_fan_runtime_suspend(struct device *dev)
diff --git a/drivers/hwmon/hp-wmi-sensors.c b/drivers/hwmon/hp-wmi-sensors.c
index 03c684ba83bd..cfa0d6ad0fb3 100644
--- a/drivers/hwmon/hp-wmi-sensors.c
+++ b/drivers/hwmon/hp-wmi-sensors.c
@@ -526,14 +526,12 @@ static int check_wobj(const union acpi_object *wobj,
for (prop = 0; prop <= last_prop; prop++) {
type = elements[prop].type;
valid_type = property_map[prop];
- if (type != valid_type) {
- if (type == ACPI_TYPE_BUFFER &&
- valid_type == ACPI_TYPE_STRING &&
- is_raw_wmi_string(elements[prop].buffer.pointer,
- elements[prop].buffer.length))
- continue;
+ if (type == ACPI_TYPE_BUFFER &&
+ is_raw_wmi_string(elements[prop].buffer.pointer,
+ elements[prop].buffer.length))
+ type = ACPI_TYPE_STRING;
+ if (type != valid_type)
return -EINVAL;
- }
}
return 0;
@@ -579,6 +577,7 @@ static int check_numeric_sensor_wobj(const union acpi_object *wobj,
int prop = HP_WMI_PROPERTY_NAME;
acpi_object_type valid_type;
union acpi_object *elements;
+ union acpi_object *element;
u32 elem_count;
int last_prop;
bool is_new;
@@ -602,13 +601,20 @@ static int check_numeric_sensor_wobj(const union acpi_object *wobj,
elem_count > HP_WMI_MAX_PROPERTIES)
return -EINVAL;
- type = elements[HP_WMI_PROPERTY_SIZE].type;
+ element = &elements[HP_WMI_PROPERTY_SIZE];
+ type = element->type;
switch (type) {
case ACPI_TYPE_INTEGER:
is_new = true;
last_prop = HP_WMI_PROPERTY_RATE_UNITS;
break;
+ case ACPI_TYPE_BUFFER:
+ if (!is_raw_wmi_string(element->buffer.pointer,
+ element->buffer.length))
+ return -EINVAL;
+ fallthrough;
+
case ACPI_TYPE_STRING:
is_new = false;
last_prop = HP_WMI_PROPERTY_CURRENT_READING;
@@ -631,6 +637,10 @@ static int check_numeric_sensor_wobj(const union acpi_object *wobj,
for (i = 0; i < elem_count && prop <= last_prop; i++, prop++) {
type = elements[i].type;
valid_type = hp_wmi_property_map[prop];
+ if (type == ACPI_TYPE_BUFFER &&
+ is_raw_wmi_string(elements[i].buffer.pointer,
+ elements[i].buffer.length))
+ type = ACPI_TYPE_STRING;
if (type != valid_type)
return -EINVAL;
@@ -651,6 +661,10 @@ static int check_numeric_sensor_wobj(const union acpi_object *wobj,
/* PossibleStates[0] has already been type-checked. */
for (j = 0; i + 1 < elem_count && j + 1 < count; j++) {
type = elements[++i].type;
+ if (type == ACPI_TYPE_BUFFER &&
+ is_raw_wmi_string(elements[i].buffer.pointer,
+ elements[i].buffer.length))
+ type = ACPI_TYPE_STRING;
if (type != valid_type)
return -EINVAL;
}
@@ -1247,7 +1261,9 @@ static int fungible_show(struct seq_file *seqf, enum hp_wmi_property prop)
break;
case HP_WMI_PROPERTY_CURRENT_STATE:
+ mutex_lock(&state->lock);
seq_printf(seqf, "%s\n", nsensor->current_state);
+ mutex_unlock(&state->lock);
break;
case HP_WMI_PROPERTY_UNIT_MODIFIER:
diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
index 41755910a25a..10d2df3efdfa 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -318,6 +318,11 @@ static int hwmon_attr_base(enum hwmon_sensor_types type)
return 1;
}
+static bool is_hwmon_device(struct device *dev)
+{
+ return dev->class == &hwmon_class;
+}
+
#if IS_REACHABLE(CONFIG_I2C)
/*
@@ -338,7 +343,7 @@ static int hwmon_attr_base(enum hwmon_sensor_types type)
static int hwmon_match_device(struct device *dev, const void *data)
{
- return dev->class == &hwmon_class;
+ return is_hwmon_device(dev);
}
static ssize_t pec_show(struct device *dev, const struct device_attribute *dummy,
@@ -371,18 +376,17 @@ static ssize_t pec_store(struct device *dev, const struct device_attribute *deva
* handling is not required.
*/
hwdev = to_hwmon_device(hdev);
- guard(mutex)(&hwdev->lock);
- if (hwdev->chip->ops->write) {
- err = hwdev->chip->ops->write(hdev, hwmon_chip, hwmon_chip_pec, 0, val);
- if (err && err != -EOPNOTSUPP)
- goto put;
+ scoped_guard(mutex, &hwdev->lock) {
+ if (hwdev->chip->ops->write) {
+ err = hwdev->chip->ops->write(hdev, hwmon_chip, hwmon_chip_pec, 0, val);
+ if (err && err != -EOPNOTSUPP)
+ goto put;
+ }
+ if (!val)
+ client->flags &= ~I2C_CLIENT_PEC;
+ else
+ client->flags |= I2C_CLIENT_PEC;
}
-
- if (!val)
- client->flags &= ~I2C_CLIENT_PEC;
- else
- client->flags |= I2C_CLIENT_PEC;
-
err = count;
put:
put_device(hdev);
@@ -782,6 +786,9 @@ int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type,
const char *template;
int base;
+ if (WARN(!is_hwmon_device(dev), "%s is not a hardware monitoring device\n",
+ dev_name(dev)))
+ return -EINVAL;
if (type >= ARRAY_SIZE(__templates))
return -EINVAL;
if (attr >= __templates_size[type])
diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
index 5c6dc2c370d8..19b35f3bf3a3 100644
--- a/drivers/hwmon/ina2xx.c
+++ b/drivers/hwmon/ina2xx.c
@@ -8,6 +8,7 @@
*/
#include <linux/bitfield.h>
+#include <linux/bitops.h>
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/device.h>
@@ -129,6 +130,17 @@ enum ina2xx_ids {
sy24655
};
+enum ina2xx_alert_type {
+ INA2XX_ALERT_NONE,
+ INA2XX_ALERT_CURRENT_LOW,
+ INA2XX_ALERT_CURRENT_HIGH,
+ INA2XX_ALERT_POWER_HIGH,
+ INA2XX_ALERT_BUS_VOLTAGE_LOW,
+ INA2XX_ALERT_BUS_VOLTAGE_HIGH,
+ INA2XX_ALERT_SHUNT_VOLTAGE_LOW,
+ INA2XX_ALERT_SHUNT_VOLTAGE_HIGH,
+};
+
struct ina2xx_config {
u16 config_default;
bool has_alerts; /* chip supports alerts and limits */
@@ -148,6 +160,7 @@ struct ina2xx_data {
const struct ina2xx_config *config;
enum ina2xx_ids chip;
+ enum ina2xx_alert_type active_alert;
long rshunt;
long current_lsb_uA;
long power_lsb_uW;
@@ -428,16 +441,78 @@ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val)
}
}
-static int ina226_alert_limit_read(struct ina2xx_data *data, u32 mask, int reg, long *val)
+static u32 ina2xx_alert_type_to_mask(enum ina2xx_alert_type alert)
+{
+ switch (alert) {
+ case INA2XX_ALERT_CURRENT_LOW:
+ case INA2XX_ALERT_SHUNT_VOLTAGE_LOW:
+ return INA226_SHUNT_UNDER_VOLTAGE_MASK;
+ case INA2XX_ALERT_CURRENT_HIGH:
+ case INA2XX_ALERT_SHUNT_VOLTAGE_HIGH:
+ return INA226_SHUNT_OVER_VOLTAGE_MASK;
+ case INA2XX_ALERT_BUS_VOLTAGE_LOW:
+ return INA226_BUS_UNDER_VOLTAGE_MASK;
+ case INA2XX_ALERT_BUS_VOLTAGE_HIGH:
+ return INA226_BUS_OVER_VOLTAGE_MASK;
+ case INA2XX_ALERT_POWER_HIGH:
+ return INA226_POWER_OVER_LIMIT_MASK;
+ case INA2XX_ALERT_NONE:
+ return 0;
+ default:
+ /* programmer error */
+ WARN_ON_ONCE(1);
+ return 0;
+ }
+}
+
+static enum ina2xx_alert_type ina2xx_mask_to_alert_type(u32 mask)
+{
+ int top_bit = fls(mask & INA226_ALERT_CONFIG_MASK);
+
+ if (!top_bit)
+ return INA2XX_ALERT_NONE;
+
+ /*
+ * Multiple bits may be set, with the highest-set function taking
+ * precedence according to the datasheet. Shunt voltage masks are
+ * assumed to map to voltage monitoring rather than current monitoring,
+ * since the latter isn't directly implemented in the hardware.
+ */
+ switch (BIT(top_bit - 1)) {
+ case INA226_SHUNT_OVER_VOLTAGE_MASK:
+ return INA2XX_ALERT_SHUNT_VOLTAGE_HIGH;
+ case INA226_SHUNT_UNDER_VOLTAGE_MASK:
+ return INA2XX_ALERT_SHUNT_VOLTAGE_LOW;
+ case INA226_BUS_OVER_VOLTAGE_MASK:
+ return INA2XX_ALERT_BUS_VOLTAGE_HIGH;
+ case INA226_BUS_UNDER_VOLTAGE_MASK:
+ return INA2XX_ALERT_BUS_VOLTAGE_LOW;
+ case INA226_POWER_OVER_LIMIT_MASK:
+ return INA2XX_ALERT_POWER_HIGH;
+ default:
+ return INA2XX_ALERT_NONE;
+ }
+}
+
+static int ina226_alert_limit_read(struct ina2xx_data *data, enum ina2xx_alert_type alert,
+ int reg, long *val)
{
struct regmap *regmap = data->regmap;
int regval;
+ u32 mask;
int ret;
+ /* Avoid nonzero reads from inactive alerts caused by shared limit register */
+ if (data->active_alert != alert) {
+ *val = 0;
+ return 0;
+ }
+
ret = regmap_read(regmap, INA226_MASK_ENABLE, &regval);
if (ret)
return ret;
+ mask = ina2xx_alert_type_to_mask(alert);
if (regval & mask) {
ret = regmap_read(regmap, INA226_ALERT_LIMIT, &regval);
if (ret)
@@ -449,9 +524,11 @@ static int ina226_alert_limit_read(struct ina2xx_data *data, u32 mask, int reg,
return 0;
}
-static int ina226_alert_limit_write(struct ina2xx_data *data, u32 mask, int reg, long val)
+static int ina226_alert_limit_write(struct ina2xx_data *data, enum ina2xx_alert_type alert,
+ int reg, long val)
{
struct regmap *regmap = data->regmap;
+ u32 mask;
int ret;
if (val < 0)
@@ -466,15 +543,22 @@ static int ina226_alert_limit_write(struct ina2xx_data *data, u32 mask, int reg,
INA226_ALERT_CONFIG_MASK, 0);
if (ret < 0)
return ret;
+ data->active_alert = INA2XX_ALERT_NONE;
ret = regmap_write(regmap, INA226_ALERT_LIMIT,
ina226_alert_to_reg(data, reg, val));
if (ret < 0)
return ret;
- if (val)
- return regmap_update_bits(regmap, INA226_MASK_ENABLE,
- INA226_ALERT_CONFIG_MASK, mask);
+ if (val) {
+ mask = ina2xx_alert_type_to_mask(alert);
+ ret = regmap_update_bits(regmap, INA226_MASK_ENABLE,
+ INA226_ALERT_CONFIG_MASK, mask);
+ if (ret < 0)
+ return ret;
+ data->active_alert = alert;
+ }
+
return 0;
}
@@ -498,15 +582,26 @@ static int ina2xx_chip_read(struct device *dev, u32 attr, long *val)
return 0;
}
-static int ina226_alert_read(struct regmap *regmap, u32 mask, long *val)
+static int ina226_alert_read(struct ina2xx_data *data, enum ina2xx_alert_type alert, long *val)
{
unsigned int regval;
+ u32 mask;
int ret;
- ret = regmap_read_bypassed(regmap, INA226_MASK_ENABLE, &regval);
+ /*
+ * With alert latching, reading alerts from hardware also clears the
+ * alert, so return early if the alert is inactive.
+ */
+ if (data->active_alert != alert) {
+ *val = 0;
+ return 0;
+ }
+
+ ret = regmap_read_bypassed(data->regmap, INA226_MASK_ENABLE, &regval);
if (ret)
return ret;
+ mask = ina2xx_alert_type_to_mask(alert);
*val = (regval & mask) && (regval & INA226_ALERT_FUNCTION_FLAG);
return 0;
@@ -515,10 +610,10 @@ static int ina226_alert_read(struct regmap *regmap, u32 mask, long *val)
static int ina2xx_in_read(struct device *dev, u32 attr, int channel, long *val)
{
int voltage_reg = channel ? INA2XX_BUS_VOLTAGE : INA2XX_SHUNT_VOLTAGE;
- u32 under_voltage_mask = channel ? INA226_BUS_UNDER_VOLTAGE_MASK
- : INA226_SHUNT_UNDER_VOLTAGE_MASK;
- u32 over_voltage_mask = channel ? INA226_BUS_OVER_VOLTAGE_MASK
- : INA226_SHUNT_OVER_VOLTAGE_MASK;
+ enum ina2xx_alert_type under_voltage_alert = channel ? INA2XX_ALERT_BUS_VOLTAGE_LOW
+ : INA2XX_ALERT_SHUNT_VOLTAGE_LOW;
+ enum ina2xx_alert_type over_voltage_alert = channel ? INA2XX_ALERT_BUS_VOLTAGE_HIGH
+ : INA2XX_ALERT_SHUNT_VOLTAGE_HIGH;
struct ina2xx_data *data = dev_get_drvdata(dev);
struct regmap *regmap = data->regmap;
unsigned int regval;
@@ -532,15 +627,15 @@ static int ina2xx_in_read(struct device *dev, u32 attr, int channel, long *val)
*val = ina2xx_get_value(data, voltage_reg, regval);
break;
case hwmon_in_lcrit:
- return ina226_alert_limit_read(data, under_voltage_mask,
+ return ina226_alert_limit_read(data, under_voltage_alert,
voltage_reg, val);
case hwmon_in_crit:
- return ina226_alert_limit_read(data, over_voltage_mask,
+ return ina226_alert_limit_read(data, over_voltage_alert,
voltage_reg, val);
case hwmon_in_lcrit_alarm:
- return ina226_alert_read(regmap, under_voltage_mask, val);
+ return ina226_alert_read(data, under_voltage_alert, val);
case hwmon_in_crit_alarm:
- return ina226_alert_read(regmap, over_voltage_mask, val);
+ return ina226_alert_read(data, over_voltage_alert, val);
default:
return -EOPNOTSUPP;
}
@@ -594,10 +689,10 @@ static int ina2xx_power_read(struct device *dev, u32 attr, long *val)
case hwmon_power_average:
return sy24655_average_power_read(data, SY24655_EIN, val);
case hwmon_power_crit:
- return ina226_alert_limit_read(data, INA226_POWER_OVER_LIMIT_MASK,
+ return ina226_alert_limit_read(data, INA2XX_ALERT_POWER_HIGH,
INA2XX_POWER, val);
case hwmon_power_crit_alarm:
- return ina226_alert_read(data->regmap, INA226_POWER_OVER_LIMIT_MASK, val);
+ return ina226_alert_read(data, INA2XX_ALERT_POWER_HIGH, val);
default:
return -EOPNOTSUPP;
}
@@ -633,15 +728,15 @@ static int ina2xx_curr_read(struct device *dev, u32 attr, long *val)
*val = ina2xx_get_value(data, INA2XX_CURRENT, regval);
return 0;
case hwmon_curr_lcrit:
- return ina226_alert_limit_read(data, INA226_SHUNT_UNDER_VOLTAGE_MASK,
+ return ina226_alert_limit_read(data, INA2XX_ALERT_CURRENT_LOW,
INA2XX_CURRENT, val);
case hwmon_curr_crit:
- return ina226_alert_limit_read(data, INA226_SHUNT_OVER_VOLTAGE_MASK,
+ return ina226_alert_limit_read(data, INA2XX_ALERT_CURRENT_HIGH,
INA2XX_CURRENT, val);
case hwmon_curr_lcrit_alarm:
- return ina226_alert_read(regmap, INA226_SHUNT_UNDER_VOLTAGE_MASK, val);
+ return ina226_alert_read(data, INA2XX_ALERT_CURRENT_LOW, val);
case hwmon_curr_crit_alarm:
- return ina226_alert_read(regmap, INA226_SHUNT_OVER_VOLTAGE_MASK, val);
+ return ina226_alert_read(data, INA2XX_ALERT_CURRENT_HIGH, val);
default:
return -EOPNOTSUPP;
}
@@ -685,12 +780,12 @@ static int ina2xx_in_write(struct device *dev, u32 attr, int channel, long val)
switch (attr) {
case hwmon_in_lcrit:
return ina226_alert_limit_write(data,
- channel ? INA226_BUS_UNDER_VOLTAGE_MASK : INA226_SHUNT_UNDER_VOLTAGE_MASK,
+ channel ? INA2XX_ALERT_BUS_VOLTAGE_LOW : INA2XX_ALERT_SHUNT_VOLTAGE_LOW,
channel ? INA2XX_BUS_VOLTAGE : INA2XX_SHUNT_VOLTAGE,
val);
case hwmon_in_crit:
return ina226_alert_limit_write(data,
- channel ? INA226_BUS_OVER_VOLTAGE_MASK : INA226_SHUNT_OVER_VOLTAGE_MASK,
+ channel ? INA2XX_ALERT_BUS_VOLTAGE_HIGH : INA2XX_ALERT_SHUNT_VOLTAGE_HIGH,
channel ? INA2XX_BUS_VOLTAGE : INA2XX_SHUNT_VOLTAGE,
val);
default:
@@ -705,7 +800,7 @@ static int ina2xx_power_write(struct device *dev, u32 attr, long val)
switch (attr) {
case hwmon_power_crit:
- return ina226_alert_limit_write(data, INA226_POWER_OVER_LIMIT_MASK,
+ return ina226_alert_limit_write(data, INA2XX_ALERT_POWER_HIGH,
INA2XX_POWER, val);
default:
return -EOPNOTSUPP;
@@ -719,10 +814,10 @@ static int ina2xx_curr_write(struct device *dev, u32 attr, long val)
switch (attr) {
case hwmon_curr_lcrit:
- return ina226_alert_limit_write(data, INA226_SHUNT_UNDER_VOLTAGE_MASK,
+ return ina226_alert_limit_write(data, INA2XX_ALERT_CURRENT_LOW,
INA2XX_CURRENT, val);
case hwmon_curr_crit:
- return ina226_alert_limit_write(data, INA226_SHUNT_OVER_VOLTAGE_MASK,
+ return ina226_alert_limit_write(data, INA2XX_ALERT_CURRENT_HIGH,
INA2XX_CURRENT, val);
default:
return -EOPNOTSUPP;
@@ -883,8 +978,12 @@ static ssize_t shunt_resistor_show(struct device *dev,
struct device_attribute *da, char *buf)
{
struct ina2xx_data *data = dev_get_drvdata(dev);
+ long rshunt;
- return sysfs_emit(buf, "%li\n", data->rshunt);
+ scoped_guard(hwmon_lock, dev) {
+ rshunt = data->rshunt;
+ }
+ return sysfs_emit(buf, "%li\n", rshunt);
}
static ssize_t shunt_resistor_store(struct device *dev,
@@ -940,6 +1039,16 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data)
if (data->config->has_alerts) {
bool active_high = device_property_read_bool(dev, "ti,alert-polarity-active-high");
+ unsigned int mask_enable;
+
+ /*
+ * Infer active alert from MASK_ENABLE in case it's already
+ * configured (e.g., by a past probe or firmware)
+ */
+ ret = regmap_read(regmap, INA226_MASK_ENABLE, &mask_enable);
+ if (ret < 0)
+ return ret;
+ data->active_alert = ina2xx_mask_to_alert_type(mask_enable);
regmap_update_bits(regmap, INA226_MASK_ENABLE,
INA226_ALERT_LATCH_ENABLE | INA226_ALERT_POLARITY,
diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c
index 75a45010d687..3e7e63edc6a3 100644
--- a/drivers/hwmon/k10temp.c
+++ b/drivers/hwmon/k10temp.c
@@ -523,7 +523,7 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
}
} else if (boot_cpu_data.x86 == 0x1a) {
switch (boot_cpu_data.x86_model) {
- case 0x00 ... 0x2f: /* Zen5 Turin */
+ case 0x00 ... 0x1f: /* Zen5 Turin */
data->ccd_offset = 0x1F0;
k10temp_get_ccd_support(data, 16);
break;
diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c
index b1675dc5b3c7..54ba4b8542e9 100644
--- a/drivers/hwmon/ltc4282.c
+++ b/drivers/hwmon/ltc4282.c
@@ -1106,7 +1106,7 @@ static const struct clk_ops ltc4282_ops = {
static int ltc428_clk_provider_setup(struct ltc4282_state *st,
struct device *dev)
{
- struct clk_init_data init;
+ struct clk_init_data init = {};
int ret;
if (!IS_ENABLED(CONFIG_COMMON_CLK))
diff --git a/drivers/hwmon/mcp9982.c b/drivers/hwmon/mcp9982.c
index 9e19e2697e25..3918dc36c946 100644
--- a/drivers/hwmon/mcp9982.c
+++ b/drivers/hwmon/mcp9982.c
@@ -395,6 +395,8 @@ static int mcp9982_read(struct device *dev, enum hwmon_sensor_types type, u32 at
reg_status, !(reg_status & MCP9982_STATUS_BUSY),
MCP9982_WAKE_UP_TIME_US,
MCP9982_WAKE_UP_TIME_US * 10);
+ if (ret)
+ return ret;
break;
}
break;
diff --git a/drivers/hwmon/nct6694-hwmon.c b/drivers/hwmon/nct6694-hwmon.c
index 6dcf22ca5018..9a9a4db434c4 100644
--- a/drivers/hwmon/nct6694-hwmon.c
+++ b/drivers/hwmon/nct6694-hwmon.c
@@ -159,6 +159,9 @@ static inline s8 temp_to_reg(long val)
#define NCT6694_HWMON_TEMP_CONFIG (HWMON_T_INPUT | HWMON_T_ENABLE | \
HWMON_T_MAX | HWMON_T_MAX_HYST | \
HWMON_T_MAX_ALARM)
+#define NCT6694_HWMON_DTIN_CONFIG (HWMON_T_INPUT | \
+ HWMON_T_MAX | HWMON_T_MAX_HYST | \
+ HWMON_T_MAX_ALARM)
#define NCT6694_HWMON_FAN_CONFIG (HWMON_F_INPUT | HWMON_F_ENABLE | \
HWMON_F_MIN | HWMON_F_MIN_ALARM)
#define NCT6694_HWMON_PWM_CONFIG (HWMON_PWM_INPUT | HWMON_PWM_ENABLE | \
@@ -193,22 +196,22 @@ static const struct hwmon_channel_info *nct6694_info[] = {
NCT6694_HWMON_TEMP_CONFIG, /* TDP2 */
NCT6694_HWMON_TEMP_CONFIG, /* TDP3 */
NCT6694_HWMON_TEMP_CONFIG, /* TDP4 */
- NCT6694_HWMON_TEMP_CONFIG, /* DTIN0 */
- NCT6694_HWMON_TEMP_CONFIG, /* DTIN1 */
- NCT6694_HWMON_TEMP_CONFIG, /* DTIN2 */
- NCT6694_HWMON_TEMP_CONFIG, /* DTIN3 */
- NCT6694_HWMON_TEMP_CONFIG, /* DTIN4 */
- NCT6694_HWMON_TEMP_CONFIG, /* DTIN5 */
- NCT6694_HWMON_TEMP_CONFIG, /* DTIN6 */
- NCT6694_HWMON_TEMP_CONFIG, /* DTIN7 */
- NCT6694_HWMON_TEMP_CONFIG, /* DTIN8 */
- NCT6694_HWMON_TEMP_CONFIG, /* DTIN9 */
- NCT6694_HWMON_TEMP_CONFIG, /* DTIN10 */
- NCT6694_HWMON_TEMP_CONFIG, /* DTIN11 */
- NCT6694_HWMON_TEMP_CONFIG, /* DTIN12 */
- NCT6694_HWMON_TEMP_CONFIG, /* DTIN13 */
- NCT6694_HWMON_TEMP_CONFIG, /* DTIN14 */
- NCT6694_HWMON_TEMP_CONFIG), /* DTIN15 */
+ NCT6694_HWMON_DTIN_CONFIG, /* DTIN0 */
+ NCT6694_HWMON_DTIN_CONFIG, /* DTIN1 */
+ NCT6694_HWMON_DTIN_CONFIG, /* DTIN2 */
+ NCT6694_HWMON_DTIN_CONFIG, /* DTIN3 */
+ NCT6694_HWMON_DTIN_CONFIG, /* DTIN4 */
+ NCT6694_HWMON_DTIN_CONFIG, /* DTIN5 */
+ NCT6694_HWMON_DTIN_CONFIG, /* DTIN6 */
+ NCT6694_HWMON_DTIN_CONFIG, /* DTIN7 */
+ NCT6694_HWMON_DTIN_CONFIG, /* DTIN8 */
+ NCT6694_HWMON_DTIN_CONFIG, /* DTIN9 */
+ NCT6694_HWMON_DTIN_CONFIG, /* DTIN10 */
+ NCT6694_HWMON_DTIN_CONFIG, /* DTIN11 */
+ NCT6694_HWMON_DTIN_CONFIG, /* DTIN12 */
+ NCT6694_HWMON_DTIN_CONFIG, /* DTIN13 */
+ NCT6694_HWMON_DTIN_CONFIG, /* DTIN14 */
+ NCT6694_HWMON_DTIN_CONFIG), /* DTIN15 */
HWMON_CHANNEL_INFO(fan,
NCT6694_HWMON_FAN_CONFIG, /* FIN0 */
diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
index 2cd3216b3cd9..920c1102ab6d 100644
--- a/drivers/hwmon/pmbus/pmbus.h
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -242,6 +242,7 @@ enum pmbus_regs {
/*
* OPERATION
*/
+#define PB_OPERATION_CONTROL_V_SRC GENMASK(5, 4)
#define PB_OPERATION_CONTROL_ON BIT(7)
/*
@@ -386,7 +387,7 @@ enum pmbus_sensor_classes {
};
#define PMBUS_PAGES 32 /* Per PMBus specification */
-#define PMBUS_PHASES 10 /* Maximum number of phases per page */
+#define PMBUS_PHASES 16 /* Maximum number of phases per page */
/* Functionality bit mask */
#define PMBUS_HAVE_VIN BIT(0)
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 806c9a4913bb..5f69c1420b4e 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -1275,7 +1275,9 @@ static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b,
regval = status & mask;
if (regval) {
- if (data->revision >= PMBUS_REV_12) {
+ /* Generic STATUS_WORD alarms are not individually clearable. */
+ if (data->revision >= PMBUS_REV_12 &&
+ reg != PMBUS_STATUS_WORD) {
ret = _pmbus_write_byte_data(client, page, reg, regval);
if (ret)
return ret;
diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c
index 31e54608b3c9..6c25701b36fc 100644
--- a/drivers/hwmon/pmbus/tps53679.c
+++ b/drivers/hwmon/pmbus/tps53679.c
@@ -187,7 +187,7 @@ static int tps53676_identify(struct i2c_client *client,
return -EIO;
for (i = 0; i < 2 * TPS53676_MAX_PHASES; i += 2) {
if (buf[i + 1] & 0x80) {
- if (buf[i] & 0x08)
+ if (buf[i] & BIT(4))
phases_b++;
else
phases_a++;
@@ -200,6 +200,15 @@ static int tps53676_identify(struct i2c_client *client,
if (phases_b > 0) {
info->pages = 2;
info->phases[1] = phases_b;
+ } else {
+ /*
+ * pmbus_set_page() does not update the PAGE register on
+ * single-page devices, so select page 0 explicitly in case
+ * the boot firmware left the device on another page.
+ */
+ ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
+ if (ret < 0)
+ return ret;
}
return 0;
}
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 3b87f65bae05..c633d7f6464c 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -483,7 +483,6 @@ static void pwm_fan_cleanup(void *__ctx)
{
struct pwm_fan_ctx *ctx = __ctx;
- timer_delete_sync(&ctx->rpm_timer);
if (ctx->pwm_shutdown) {
ctx->enable_mode = pwm_enable_reg_enable;
__set_pwm(ctx, ctx->pwm_shutdown);
@@ -494,6 +493,13 @@ static void pwm_fan_cleanup(void *__ctx)
}
}
+static void pwm_fan_timer_cleanup(void *__ctx)
+{
+ struct pwm_fan_ctx *ctx = __ctx;
+
+ timer_shutdown_sync(&ctx->rpm_timer);
+}
+
static int pwm_fan_probe(struct platform_device *pdev)
{
struct thermal_cooling_device *cdev;
@@ -644,6 +650,10 @@ static int pwm_fan_probe(struct platform_device *pdev)
}
if (ctx->tach_count > 0) {
+ ret = devm_add_action_or_reset(dev, pwm_fan_timer_cleanup, ctx);
+ if (ret)
+ return ret;
+
ctx->sample_start = ktime_get();
mod_timer(&ctx->rpm_timer, jiffies + HZ);
@@ -700,6 +710,7 @@ static void pwm_fan_shutdown(struct platform_device *pdev)
{
struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev);
+ pwm_fan_timer_cleanup(ctx);
pwm_fan_cleanup(ctx);
}
diff --git a/drivers/hwmon/sht4x.c b/drivers/hwmon/sht4x.c
index 9cace0e8acda..a97dda9e92dc 100644
--- a/drivers/hwmon/sht4x.c
+++ b/drivers/hwmon/sht4x.c
@@ -277,6 +277,8 @@ static ssize_t heater_enable_store(struct device *dev,
heating_time_bound = 1100;
}
+ guard(hwmon_lock)(dev);
+
if (time_before(jiffies, data->heating_complete))
return -EBUSY;
@@ -286,7 +288,7 @@ static ssize_t heater_enable_store(struct device *dev,
data->heating_complete = jiffies + msecs_to_jiffies(heating_time_bound);
data->data_pending = true;
- return 0;
+ return count;
}
static ssize_t heater_power_show(struct device *dev,
@@ -314,6 +316,8 @@ static ssize_t heater_power_store(struct device *dev,
if (power != 20 && power != 110 && power != 200)
return -EINVAL;
+ guard(hwmon_lock)(dev);
+
data->heater_power = power;
return count;
@@ -344,6 +348,8 @@ static ssize_t heater_time_store(struct device *dev,
if (time != 100 && time != 1000)
return -EINVAL;
+ guard(hwmon_lock)(dev);
+
data->heater_time = time;
return count;
diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c
index 4a777430af5c..4b07a25ae59e 100644
--- a/drivers/hwmon/w83791d.c
+++ b/drivers/hwmon/w83791d.c
@@ -1415,6 +1415,7 @@ static void w83791d_remove(struct i2c_client *client)
struct w83791d_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&client->dev.kobj, &w83791d_group_fanpwm45);
sysfs_remove_group(&client->dev.kobj, &w83791d_group);
}
diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c
index a548586369e1..c6ef04c69856 100644
--- a/drivers/hwmon/w83793.c
+++ b/drivers/hwmon/w83793.c
@@ -1928,7 +1928,9 @@ exit_remove:
for (i = 0; i < ARRAY_SIZE(w83793_temp); i++)
device_remove_file(dev, &w83793_temp[i].dev_attr);
free_mem:
- kfree(data);
+ mutex_lock(&watchdog_data_mutex);
+ kref_put(&data->kref, w83793_release_resources);
+ mutex_unlock(&watchdog_data_mutex);
exit:
return err;
}
diff --git a/drivers/hwmon/yogafan.c b/drivers/hwmon/yogafan.c
index 48fa5148d9e2..278cb089b0fd 100644
--- a/drivers/hwmon/yogafan.c
+++ b/drivers/hwmon/yogafan.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/**
+/*
* yoga_fan.c - Lenovo Yoga/Legion Fan Hardware Monitoring Driver
*
* Provides fan speed monitoring for Lenovo Yoga, Legion, and IdeaPad