mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
hwmon fixes for v7.4-rc2
* acpi_power_meter: Check for 32-bit and 64-bit unknown ACPI values, Fix missing locking in sysfs show callbacks and resume, and Prevent spurious power alarm when power is unknown * aquacomputer_d5next: Avoid truncating scaled sensor readings, Check maxcollection when probing AQUAERO devices, and reject short status reports * asus_rog_ryujin: Fix pump control source on the Ryujin III, and Fix timeout when reading 0% duty cycle * asus_wmi_sensors: Avoid global struct mutation * chipcap2: Series of fixes addressing various bugs in the driver * coretemp: Check for NULL platform device in hotplug callbacks, Fix 32-bit jiffies wraparound in show_temp() hnd Set nr_cores only after core_data allocation succeeds * corsair-cpro: Cache fan target only after a successful command * gpd-fan: Reset both fans to automatic on duo boards, and Update the speed cache before dropping the PM reference * hp-wmi-sensors: Avoid global struct mutation, Fix unbounded loops in scale_numeric_sensor(), and Serialize alarm flag read-and-clear with state->lock * lm75: Fix setting polarity mask for chips with 16-bit config register * lm87: Fix drvdata type confusion in vrm_show() and vrm_store() * nct6683: Fix incorrect struct cast in show_pwm() and store_pwm() * peci/dimmtemp: Fix devres leak and UAF on concurrent unbind * pmbus core: Check return value from pmbus_thermal_add_sensor(), Register the thermal sensors with a 0-based index, and Fix return value check from thermal zone registration, and release devres allocated memory after devm_thermal_of_zone_register error * pmbus/q54sj108a2: Add missing NULL pointer check * sht15: Stop deferred work before releasing its resources * sht4x: Convert update_interval to jiffies in pending data check * tmp108: Fix initial conversion delay and wait until ready -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEiHPvMQj9QTOCiqgVyx8mb86fmYEFAmrrtEsACgkQyx8mb86f mYFOtw/7Bzpn9T0daAQtJ/oM5wWeMQFDGyAQCyHlgRavcGhKwhNbU1CF6ESQ14ud 7tIubFVDRuO4wqYrRkiGcf6e65npD2WNaacEbIjNXaFtT5XmNM8xFwF6wQfS8SHz jWUfb79zDo1zw0bFFkCpxyh0xmH3nhRJdVKBpB6Akqcd+0M5c8lX+vF3Vwu1sFby kCxclMun8n40MQ2ypaHKjP1nFhFSU5IlD5ojQsDwGyfJEUV32K074+Davq/MqOC7 0Ev6IupMTq9gp/vZTsxj0bLIeTxz2ee6eIftTCav/1Ifp1RoWRz4hlw6WxxK/nx8 n6v6+v1+fEXCvg0Dubf607r6+PeT4AX46YQsLV/UVz1UmFvziavm7KZvEtypCwTC +fB+W70gXNUTcohnY3gm2SPlmRgnN+32N+Oo6T9CuCLmPZ0eKUnVAjrJpQidHfGc HfbSyx7Cmf5gpRtKpmqZ0Z7ya/FZFY9jq4WmzoBuh2qDMEf2afUHqBrYVO/ZA4Ho cz2UeV47BM0HOQomqVRcWTRe4aehO28QxnKguQnD3bqvABn219mw6Jp5oxhDFHVF iv2MNMVFN2WKfjjDNJmFfJJzw98nzHy+Ip7Z3SE3Gl3sovkBm7vFBJ3/FC1jKxMk eTUlsiZDewox7gQUzm4iChtKtu2eaUYomfsqk9xySq8zC9OcXkw= =ALE6 -----END PGP SIGNATURE----- Merge tag 'hwmon-for-v7.4-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging Pull hwmon fixes from Guenter Roeck: - acpi_power_meter: Check for 32-bit and 64-bit unknown ACPI values, Fix missing locking in sysfs show callbacks and resume, and Prevent spurious power alarm when power is unknown - aquacomputer_d5next: Avoid truncating scaled sensor readings, Check maxcollection when probing AQUAERO devices, and reject short status reports - asus_rog_ryujin: Fix pump control source on the Ryujin III, and Fix timeout when reading 0% duty cycle - asus_wmi_sensors: Avoid global struct mutation - chipcap2: Series of fixes addressing various bugs in the driver - coretemp: Check for NULL platform device in hotplug callbacks, Fix 32-bit jiffies wraparound in show_temp() hnd Set nr_cores only after core_data allocation succeeds - corsair-cpro: Cache fan target only after a successful command - gpd-fan: Reset both fans to automatic on duo boards, and Update the speed cache before dropping the PM reference - hp-wmi-sensors: Avoid global struct mutation, Fix unbounded loops in scale_numeric_sensor(), and Serialize alarm flag read-and-clear with state->lock - lm75: Fix setting polarity mask for chips with 16-bit config register - lm87: Fix drvdata type confusion in vrm_show() and vrm_store() - nct6683: Fix incorrect struct cast in show_pwm() and store_pwm() - peci/dimmtemp: Fix devres leak and UAF on concurrent unbind - pmbus core: Check return value from pmbus_thermal_add_sensor(), Register the thermal sensors with a 0-based index, and Fix return value check from thermal zone registration, and release devres allocated memory after devm_thermal_of_zone_register error - pmbus/q54sj108a2: Add missing NULL pointer check - sht15: Stop deferred work before releasing its resources - sht4x: Convert update_interval to jiffies in pending data check - tmp108: Fix initial conversion delay and wait until ready * tag 'hwmon-for-v7.4-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (41 commits) hwmon: (asus_rog_ryujin) Fix timeout when reading 0% duty cycle hwmon: (hp-wmi-sensors) Serialize alarm flag read-and-clear with state->lock hwmon: (acpi_power_meter) Check for 32-bit and 64-bit unknown ACPI values hwmon: (acpi_power_meter) Prevent spurious power alarm when power is unknown hwmon: (acpi_power_meter) Fix missing locking in sysfs show callbacks and resume hwmon: (aquacomputer_d5next) Avoid truncating scaled sensor readings hwmon: (sht4x) Convert update_interval to jiffies in pending data check hwmon: (coretemp) Fix 32-bit jiffies wraparound in show_temp() hwmon: (coretemp) Set nr_cores only after core_data allocation succeeds hwmon: (coretemp) Check for NULL platform device in hotplug callbacks hwmon: (gpio-fan) Update the speed cache before dropping the PM reference hwmon: (tmp108) Fix initial conversion delay and wait until ready hwmon: (lm75) Fix setting polarity mask for chips with 16-bit config register hwmon: (pmbus) Fix return value check from thermal zone registration hwmon: (pmbus) Release devres allocated memory after devm_thermal_of_zone_register error hwmon: (pmbus) Check return value from pmbus_thermal_add_sensor() hwmon: (pmbus/q54sj108a2) Add missing NULL pointer check hwmon: (peci/dimmtemp) Fix devres leak and UAF on concurrent unbind hwmon: (nct6683) Fix incorrect struct cast in show_pwm() and store_pwm() hwmon: (lm87) Fix drvdata type confusion in vrm_show() and vrm_store() ...
This commit is contained in:
commit
3e516eae69
|
|
@ -34,6 +34,12 @@ Usage notes
|
|||
As these are USB HIDs, the driver can be loaded automatically by the kernel and
|
||||
supports hot swapping.
|
||||
|
||||
The pump of the RYUJIN III also has a 4-pin cable for a motherboard fan header.
|
||||
The cooler either hands the pump to its internal controller, which follows the
|
||||
PWM of the header its 4-pin cable is plugged into, or runs it at the pump duty
|
||||
set over USB. Writing pwm1 selects the duty set over USB. Writing pwm2 sets
|
||||
only the internal fan duty and leaves the pump control as it is.
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
|
|
|
|||
|
|
@ -30,13 +30,6 @@
|
|||
#define POWER_METER_CAN_CAP (1 << 2)
|
||||
#define POWER_METER_CAN_NOTIFY (1 << 3)
|
||||
#define POWER_METER_IS_BATTERY (1 << 8)
|
||||
#define UNKNOWN_HYSTERESIS 0xFFFFFFFF
|
||||
/*
|
||||
* ACPI integers can either be 32-bit or 64-bit wide, so
|
||||
* we have to check for both values.
|
||||
*/
|
||||
#define UNKNOWN_POWER_32 U32_MAX
|
||||
#define UNKNOWN_POWER_64 U64_MAX
|
||||
|
||||
#define METER_NOTIFY_CONFIG 0x80
|
||||
#define METER_NOTIFY_TRIP 0x81
|
||||
|
|
@ -54,6 +47,15 @@ static bool force_cap_on;
|
|||
|
||||
static DEFINE_MUTEX(acpi_notify_lock);
|
||||
|
||||
/*
|
||||
* ACPI integers can either be 32-bit or 64-bit wide, so
|
||||
* we have to check for both values.
|
||||
*/
|
||||
static bool acpi_power_meter_is_unknown(u64 val)
|
||||
{
|
||||
return val == U32_MAX || val == U64_MAX;
|
||||
}
|
||||
|
||||
static int can_cap_in_hardware(void)
|
||||
{
|
||||
return force_cap_on || cap_in_hardware;
|
||||
|
|
@ -179,9 +181,10 @@ static int update_meter(struct acpi_power_meter_resource *resource)
|
|||
acpi_status status;
|
||||
unsigned long local_jiffies = jiffies;
|
||||
|
||||
if (time_before(local_jiffies, resource->sensors_last_updated +
|
||||
if (!acpi_power_meter_is_unknown(resource->caps.sampling_time) &&
|
||||
time_before(local_jiffies, resource->sensors_last_updated +
|
||||
msecs_to_jiffies(resource->caps.sampling_time)) &&
|
||||
resource->sensors_valid)
|
||||
resource->sensors_valid)
|
||||
return 0;
|
||||
|
||||
status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PMM",
|
||||
|
|
@ -377,28 +380,30 @@ static int set_avg_interval(struct acpi_power_meter_resource *resource,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int get_power_alarm_state(struct acpi_power_meter_resource *resource,
|
||||
long *val)
|
||||
static bool get_power_alarm_state(struct acpi_power_meter_resource *resource)
|
||||
{
|
||||
bool alarm = false;
|
||||
int ret;
|
||||
|
||||
if (resource->caps.flags & POWER_METER_CAN_NOTIFY)
|
||||
alarm = resource->power_alarm;
|
||||
resource->power_alarm = false;
|
||||
|
||||
ret = update_meter(resource);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (ret || acpi_power_meter_is_unknown(resource->power))
|
||||
return alarm;
|
||||
|
||||
/* need to update cap if not to support the notification. */
|
||||
if (!(resource->caps.flags & POWER_METER_CAN_NOTIFY)) {
|
||||
ret = update_cap(resource);
|
||||
if (ret)
|
||||
return ret;
|
||||
resource->power_alarm = resource->power > resource->cap;
|
||||
*val = resource->power_alarm;
|
||||
} else {
|
||||
*val = resource->power_alarm || resource->power > resource->cap;
|
||||
resource->power_alarm = resource->power > resource->cap;
|
||||
return false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (acpi_power_meter_is_unknown(resource->cap))
|
||||
return alarm;
|
||||
|
||||
return alarm || resource->power > resource->cap;
|
||||
}
|
||||
|
||||
static umode_t power_meter_is_visible(const void *data,
|
||||
|
|
@ -458,37 +463,47 @@ static int power_meter_read(struct device *dev, enum hwmon_sensor_types type,
|
|||
ret = update_meter(res);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (res->power == UNKNOWN_POWER_32 || res->power == UNKNOWN_POWER_64)
|
||||
if (acpi_power_meter_is_unknown(res->power))
|
||||
return -ENODATA;
|
||||
*val = res->power * 1000;
|
||||
break;
|
||||
case hwmon_power_average_interval_min:
|
||||
if (acpi_power_meter_is_unknown(res->caps.min_avg_interval))
|
||||
return -ENODATA;
|
||||
*val = res->caps.min_avg_interval;
|
||||
break;
|
||||
case hwmon_power_average_interval_max:
|
||||
if (acpi_power_meter_is_unknown(res->caps.max_avg_interval))
|
||||
return -ENODATA;
|
||||
*val = res->caps.max_avg_interval;
|
||||
break;
|
||||
case hwmon_power_average_interval:
|
||||
ret = update_avg_interval(res);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = (res)->avg_interval;
|
||||
if (acpi_power_meter_is_unknown(res->avg_interval))
|
||||
return -ENODATA;
|
||||
*val = res->avg_interval;
|
||||
break;
|
||||
case hwmon_power_cap_min:
|
||||
if (acpi_power_meter_is_unknown(res->caps.min_cap))
|
||||
return -ENODATA;
|
||||
*val = res->caps.min_cap * 1000;
|
||||
break;
|
||||
case hwmon_power_cap_max:
|
||||
if (acpi_power_meter_is_unknown(res->caps.max_cap))
|
||||
return -ENODATA;
|
||||
*val = res->caps.max_cap * 1000;
|
||||
break;
|
||||
case hwmon_power_alarm:
|
||||
ret = get_power_alarm_state(res, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = get_power_alarm_state(res);
|
||||
break;
|
||||
case hwmon_power_cap:
|
||||
ret = update_cap(res);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (acpi_power_meter_is_unknown(res->cap))
|
||||
return -ENODATA;
|
||||
*val = res->cap * 1000;
|
||||
break;
|
||||
default:
|
||||
|
|
@ -584,6 +599,8 @@ static ssize_t power1_average_min_show(struct device *dev,
|
|||
{
|
||||
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
|
||||
|
||||
guard(mutex)(&res->lock);
|
||||
|
||||
if (res->trip[POWER_METER_TRIP_AVERAGE_MIN_IDX] < 0)
|
||||
return sysfs_emit(buf, "unknown\n");
|
||||
|
||||
|
|
@ -596,6 +613,8 @@ static ssize_t power1_average_max_show(struct device *dev,
|
|||
{
|
||||
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
|
||||
|
||||
guard(mutex)(&res->lock);
|
||||
|
||||
if (res->trip[POWER_METER_TRIP_AVERAGE_MAX_IDX] < 0)
|
||||
return sysfs_emit(buf, "unknown\n");
|
||||
|
||||
|
|
@ -608,7 +627,9 @@ static ssize_t power1_cap_hyst_show(struct device *dev,
|
|||
{
|
||||
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
|
||||
|
||||
if (res->caps.hysteresis == UNKNOWN_HYSTERESIS)
|
||||
guard(mutex)(&res->lock);
|
||||
|
||||
if (acpi_power_meter_is_unknown(res->caps.hysteresis))
|
||||
return sysfs_emit(buf, "unknown\n");
|
||||
|
||||
return sysfs_emit(buf, "%llu\n", res->caps.hysteresis * 1000);
|
||||
|
|
@ -619,8 +640,14 @@ static ssize_t power1_accuracy_show(struct device *dev,
|
|||
char *buf)
|
||||
{
|
||||
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
|
||||
unsigned int acc = res->caps.accuracy;
|
||||
unsigned int acc;
|
||||
|
||||
guard(mutex)(&res->lock);
|
||||
|
||||
if (acpi_power_meter_is_unknown(res->caps.accuracy))
|
||||
return sysfs_emit(buf, "unknown\n");
|
||||
|
||||
acc = res->caps.accuracy;
|
||||
return sysfs_emit(buf, "%u.%u%%\n", acc / 1000, acc % 1000);
|
||||
}
|
||||
|
||||
|
|
@ -630,6 +657,8 @@ static ssize_t power1_is_battery_show(struct device *dev,
|
|||
{
|
||||
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
|
||||
|
||||
guard(mutex)(&res->lock);
|
||||
|
||||
return sysfs_emit(buf, "%u\n",
|
||||
res->caps.flags & POWER_METER_IS_BATTERY ? 1 : 0);
|
||||
}
|
||||
|
|
@ -640,6 +669,11 @@ static ssize_t power1_model_number_show(struct device *dev,
|
|||
{
|
||||
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
|
||||
|
||||
guard(mutex)(&res->lock);
|
||||
|
||||
if (!res->model_number)
|
||||
return -ENODATA;
|
||||
|
||||
return sysfs_emit(buf, "%s\n", res->model_number);
|
||||
}
|
||||
|
||||
|
|
@ -649,6 +683,11 @@ static ssize_t power1_oem_info_show(struct device *dev,
|
|||
{
|
||||
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
|
||||
|
||||
guard(mutex)(&res->lock);
|
||||
|
||||
if (!res->oem_info)
|
||||
return -ENODATA;
|
||||
|
||||
return sysfs_emit(buf, "%s\n", res->oem_info);
|
||||
}
|
||||
|
||||
|
|
@ -658,6 +697,11 @@ static ssize_t power1_serial_number_show(struct device *dev,
|
|||
{
|
||||
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
|
||||
|
||||
guard(mutex)(&res->lock);
|
||||
|
||||
if (!res->serial_number)
|
||||
return -ENODATA;
|
||||
|
||||
return sysfs_emit(buf, "%s\n", res->serial_number);
|
||||
}
|
||||
|
||||
|
|
@ -984,6 +1028,8 @@ static int acpi_power_meter_resume(struct device *dev)
|
|||
{
|
||||
struct acpi_power_meter_resource *resource = dev_get_drvdata(dev);
|
||||
|
||||
guard(mutex)(&resource->lock);
|
||||
|
||||
free_capabilities(resource);
|
||||
read_capabilities(resource);
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/hwmon.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/unaligned.h>
|
||||
|
|
@ -132,6 +133,7 @@ static u8 aquaero_secondary_ctrl_report[] = {
|
|||
#define AQUAERO_FAN_CURRENT_OFFSET 0x06
|
||||
#define AQUAERO_FAN_POWER_OFFSET 0x08
|
||||
#define AQUAERO_FAN_SPEED_OFFSET 0x00
|
||||
#define AQUAERO_MIN_STATUS_REPORT_SIZE (0x18B + AQUAERO_FAN_POWER_OFFSET + sizeof(u16))
|
||||
static u16 aquaero_sensor_fan_offsets[] = { 0x167, 0x173, 0x17f, 0x18B };
|
||||
|
||||
/* Control report offsets for the Aquaero fan controllers */
|
||||
|
|
@ -155,6 +157,8 @@ static u16 aquaero_ctrl_fan_offsets[] = { 0x20c, 0x220, 0x234, 0x248 };
|
|||
#define D5NEXT_5V_VOLTAGE 0x39
|
||||
#define D5NEXT_12V_VOLTAGE 0x37
|
||||
#define D5NEXT_VIRTUAL_SENSORS_START 0x3f
|
||||
#define D5NEXT_MIN_STATUS_REPORT_SIZE (D5NEXT_PUMP_OFFSET + AQC_FAN_SPEED_OFFSET + \
|
||||
sizeof(u16))
|
||||
static u16 d5next_sensor_fan_offsets[] = { D5NEXT_PUMP_OFFSET, D5NEXT_FAN_OFFSET };
|
||||
|
||||
/* Control report offsets for the D5 Next pump */
|
||||
|
|
@ -179,11 +183,14 @@ static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 }; /* Pump and fan speed (fr
|
|||
#define AQUASTREAMULT_FAN_CURRENT_OFFSET 0x00
|
||||
#define AQUASTREAMULT_FAN_POWER_OFFSET 0x04
|
||||
#define AQUASTREAMULT_FAN_SPEED_OFFSET 0x06
|
||||
#define AQUASTREAMULT_MIN_STATUS_REPORT_SIZE (AQUASTREAMULT_PRESSURE_OFFSET + sizeof(u16))
|
||||
static u16 aquastreamult_sensor_fan_offsets[] = { AQUASTREAMULT_FAN_OFFSET };
|
||||
|
||||
/* Spec and sensor report offset for the Farbwerk RGB controller */
|
||||
#define FARBWERK_NUM_SENSORS 4
|
||||
#define FARBWERK_SENSOR_START 0x2f
|
||||
#define FARBWERK_MIN_STATUS_REPORT_SIZE (FARBWERK_SENSOR_START + \
|
||||
FARBWERK_NUM_SENSORS * AQC_SENSOR_SIZE)
|
||||
|
||||
/* Specs of the Farbwerk 360 RGB controller */
|
||||
#define FARBWERK360_NUM_SENSORS 4
|
||||
|
|
@ -193,6 +200,8 @@ static u16 aquastreamult_sensor_fan_offsets[] = { AQUASTREAMULT_FAN_OFFSET };
|
|||
/* Sensor report offsets for the Farbwerk 360 */
|
||||
#define FARBWERK360_SENSOR_START 0x32
|
||||
#define FARBWERK360_VIRTUAL_SENSORS_START 0x3a
|
||||
#define FARBWERK360_MIN_STATUS_REPORT_SIZE (FARBWERK360_VIRTUAL_SENSORS_START + \
|
||||
FARBWERK360_NUM_VIRTUAL_SENSORS * AQC_SENSOR_SIZE)
|
||||
|
||||
/* Control report offsets for the Farbwerk 360 */
|
||||
#define FARBWERK360_TEMP_CTRL_OFFSET 0x8
|
||||
|
|
@ -209,6 +218,7 @@ static u16 aquastreamult_sensor_fan_offsets[] = { AQUASTREAMULT_FAN_OFFSET };
|
|||
#define OCTO_SENSOR_START 0x3D
|
||||
#define OCTO_VIRTUAL_SENSORS_START 0x45
|
||||
#define OCTO_FLOW_SENSOR_OFFSET 0x7B
|
||||
#define OCTO_MIN_STATUS_REPORT_SIZE (0xD8 + AQC_FAN_SPEED_OFFSET + sizeof(u16))
|
||||
static u16 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 };
|
||||
|
||||
/* Control report offsets for the Octo */
|
||||
|
|
@ -229,6 +239,7 @@ static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0
|
|||
#define QUADRO_SENSOR_START 0x34
|
||||
#define QUADRO_VIRTUAL_SENSORS_START 0x3c
|
||||
#define QUADRO_FLOW_SENSOR_OFFSET 0x6e
|
||||
#define QUADRO_MIN_STATUS_REPORT_SIZE (0x97 + AQC_FAN_SPEED_OFFSET + sizeof(u16))
|
||||
static u16 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 };
|
||||
|
||||
/* Control report offsets for the Quadro */
|
||||
|
|
@ -248,6 +259,7 @@ static u16 quadro_ctrl_fan_offsets[] = { 0x37, 0x8c, 0xe1, 0x136 }; /* Fan speed
|
|||
#define HIGHFLOWNEXT_CONDUCTIVITY 95
|
||||
#define HIGHFLOWNEXT_5V_VOLTAGE 97
|
||||
#define HIGHFLOWNEXT_5V_VOLTAGE_USB 99
|
||||
#define HIGHFLOWNEXT_MIN_STATUS_REPORT_SIZE (HIGHFLOWNEXT_5V_VOLTAGE_USB + sizeof(u16))
|
||||
|
||||
/* Specs of the Leakshield */
|
||||
#define LEAKSHIELD_NUM_SENSORS 2
|
||||
|
|
@ -263,6 +275,7 @@ static u16 quadro_ctrl_fan_offsets[] = { 0x37, 0x8c, 0xe1, 0x136 }; /* Fan speed
|
|||
#define LEAKSHIELD_FLOW_IN 111
|
||||
#define LEAKSHIELD_RESERVOIR_VOLUME 313
|
||||
#define LEAKSHIELD_RESERVOIR_FILLED 311
|
||||
#define LEAKSHIELD_MIN_STATUS_REPORT_SIZE (LEAKSHIELD_RESERVOIR_VOLUME + sizeof(u16))
|
||||
|
||||
/* Specs of the Aquastream XT pump */
|
||||
#define AQUASTREAMXT_SERIAL_START 0x3a
|
||||
|
|
@ -554,6 +567,7 @@ struct aqc_data {
|
|||
const char *name;
|
||||
|
||||
int status_report_id; /* Used for legacy devices, report is stored in buffer */
|
||||
u16 min_report_size;
|
||||
int ctrl_report_id;
|
||||
int secondary_ctrl_report_id;
|
||||
int secondary_ctrl_report_size;
|
||||
|
|
@ -599,9 +613,9 @@ struct aqc_data {
|
|||
u32 speed_input_min[1];
|
||||
u32 speed_input_target[1];
|
||||
u32 speed_input_max[1];
|
||||
u32 power_input[8];
|
||||
long power_input[8];
|
||||
u16 voltage_input[8];
|
||||
u16 current_input[8];
|
||||
u32 current_input[8];
|
||||
|
||||
/* Label values */
|
||||
const char *const *temp_label;
|
||||
|
|
@ -976,7 +990,7 @@ static int aqc_legacy_read(struct aqc_data *priv)
|
|||
|
||||
/* Calculation derived from linear regression */
|
||||
sensor_value = get_unaligned_le16(priv->buffer + AQUASTREAMXT_PUMP_CURR_OFFSET);
|
||||
priv->current_input[0] = DIV_ROUND_CLOSEST(sensor_value * 176, 100) - 52;
|
||||
priv->current_input[0] = max(DIV_ROUND_CLOSEST(sensor_value * 176, 100) - 52, 0);
|
||||
|
||||
sensor_value = get_unaligned_le16(priv->buffer + AQUASTREAMXT_PUMP_VOLTAGE_OFFSET);
|
||||
priv->voltage_input[0] = DIV_ROUND_CLOSEST(sensor_value * 1000, 61);
|
||||
|
|
@ -1006,6 +1020,7 @@ static int aqc_legacy_read(struct aqc_data *priv)
|
|||
static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
||||
int channel, long *val)
|
||||
{
|
||||
long power;
|
||||
int ret;
|
||||
struct aqc_data *priv = dev_get_drvdata(dev);
|
||||
|
||||
|
|
@ -1070,7 +1085,10 @@ static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
|||
}
|
||||
break;
|
||||
case hwmon_power:
|
||||
*val = priv->power_input[channel];
|
||||
power = READ_ONCE(priv->power_input[channel]);
|
||||
if (power == -ENODATA)
|
||||
return -ENODATA;
|
||||
*val = power;
|
||||
break;
|
||||
case hwmon_pwm:
|
||||
switch (priv->kind) {
|
||||
|
|
@ -1333,6 +1351,8 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
|
|||
return 0;
|
||||
|
||||
priv = hid_get_drvdata(hdev);
|
||||
if (!priv->min_report_size || size < priv->min_report_size)
|
||||
return 0;
|
||||
|
||||
/* Info provided with every report */
|
||||
priv->serial_number[0] = get_unaligned_be16(data + priv->serial_number_start_offset);
|
||||
|
|
@ -1368,9 +1388,9 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
|
|||
priv->speed_input[i] =
|
||||
get_unaligned_be16(data + priv->fan_sensor_offsets[i] +
|
||||
priv->fan_structure->speed);
|
||||
priv->power_input[i] =
|
||||
get_unaligned_be16(data + priv->fan_sensor_offsets[i] +
|
||||
priv->fan_structure->power) * 10000;
|
||||
WRITE_ONCE(priv->power_input[i],
|
||||
get_unaligned_be16(data + priv->fan_sensor_offsets[i] +
|
||||
priv->fan_structure->power) * 10000);
|
||||
priv->voltage_input[i] =
|
||||
get_unaligned_be16(data + priv->fan_sensor_offsets[i] +
|
||||
priv->fan_structure->voltage) * 10;
|
||||
|
|
@ -1410,7 +1430,8 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
|
|||
priv->speed_input[2] = get_unaligned_be16(data + AQUASTREAMULT_PRESSURE_OFFSET);
|
||||
priv->speed_input[3] = get_unaligned_be16(data + AQUASTREAMULT_FLOW_SENSOR_OFFSET);
|
||||
|
||||
priv->power_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_POWER) * 10000;
|
||||
WRITE_ONCE(priv->power_input[1],
|
||||
get_unaligned_be16(data + AQUASTREAMULT_PUMP_POWER) * 10000);
|
||||
|
||||
priv->voltage_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_VOLTAGE) * 10;
|
||||
|
||||
|
|
@ -1423,10 +1444,11 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
|
|||
case highflownext:
|
||||
/* If external temp sensor is not connected, its power reading is also N/A */
|
||||
if (priv->temp_input[1] == -ENODATA)
|
||||
priv->power_input[0] = -ENODATA;
|
||||
WRITE_ONCE(priv->power_input[0], -ENODATA);
|
||||
else
|
||||
priv->power_input[0] =
|
||||
get_unaligned_be16(data + HIGHFLOWNEXT_POWER) * 1000000;
|
||||
WRITE_ONCE(priv->power_input[0],
|
||||
min_t(u64, get_unaligned_be16(data + HIGHFLOWNEXT_POWER) *
|
||||
1000000ULL, LONG_MAX));
|
||||
|
||||
priv->voltage_input[0] = get_unaligned_be16(data + HIGHFLOWNEXT_5V_VOLTAGE) * 10;
|
||||
priv->voltage_input[1] =
|
||||
|
|
@ -1518,6 +1540,7 @@ static void aqc_debugfs_init(struct aqc_data *priv)
|
|||
static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
{
|
||||
struct aqc_data *priv;
|
||||
u16 min_report_size = 0;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
|
|
@ -1554,7 +1577,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
* they present. The two other devices have the type of the second element in
|
||||
* their respective collections set to 1, while the real device has it set to 0.
|
||||
*/
|
||||
if (hdev->collection[1].type != 0) {
|
||||
if (hdev->maxcollection < 2 || hdev->collection[1].type != 0) {
|
||||
ret = -ENODEV;
|
||||
goto fail_and_close;
|
||||
}
|
||||
|
|
@ -1585,6 +1608,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
priv->power_label = label_fan_power;
|
||||
priv->voltage_label = label_fan_voltage;
|
||||
priv->current_label = label_fan_current;
|
||||
min_report_size = AQUAERO_MIN_STATUS_REPORT_SIZE;
|
||||
break;
|
||||
case USB_PRODUCT_ID_D5NEXT:
|
||||
priv->kind = d5next;
|
||||
|
|
@ -1610,6 +1634,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
priv->power_label = label_d5next_power;
|
||||
priv->voltage_label = label_d5next_voltages;
|
||||
priv->current_label = label_d5next_current;
|
||||
min_report_size = D5NEXT_MIN_STATUS_REPORT_SIZE;
|
||||
break;
|
||||
case USB_PRODUCT_ID_FARBWERK:
|
||||
priv->kind = farbwerk;
|
||||
|
|
@ -1620,6 +1645,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
priv->temp_sensor_start_offset = FARBWERK_SENSOR_START;
|
||||
|
||||
priv->temp_label = label_temp_sensors;
|
||||
min_report_size = FARBWERK_MIN_STATUS_REPORT_SIZE;
|
||||
break;
|
||||
case USB_PRODUCT_ID_FARBWERK360:
|
||||
priv->kind = farbwerk360;
|
||||
|
|
@ -1636,6 +1662,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
|
||||
priv->temp_label = label_temp_sensors;
|
||||
priv->virtual_temp_label = label_virtual_temp_sensors;
|
||||
min_report_size = FARBWERK360_MIN_STATUS_REPORT_SIZE;
|
||||
break;
|
||||
case USB_PRODUCT_ID_OCTO:
|
||||
priv->kind = octo;
|
||||
|
|
@ -1665,6 +1692,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
priv->power_label = label_fan_power;
|
||||
priv->voltage_label = label_fan_voltage;
|
||||
priv->current_label = label_fan_current;
|
||||
min_report_size = OCTO_MIN_STATUS_REPORT_SIZE;
|
||||
break;
|
||||
case USB_PRODUCT_ID_QUADRO:
|
||||
priv->kind = quadro;
|
||||
|
|
@ -1694,6 +1722,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
priv->power_label = label_fan_power;
|
||||
priv->voltage_label = label_fan_voltage;
|
||||
priv->current_label = label_fan_current;
|
||||
min_report_size = QUADRO_MIN_STATUS_REPORT_SIZE;
|
||||
break;
|
||||
case USB_PRODUCT_ID_HIGHFLOWNEXT:
|
||||
priv->kind = highflownext;
|
||||
|
|
@ -1711,6 +1740,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
priv->speed_label = label_highflownext_fan_speed;
|
||||
priv->power_label = label_highflownext_power;
|
||||
priv->voltage_label = label_highflownext_voltage;
|
||||
min_report_size = HIGHFLOWNEXT_MIN_STATUS_REPORT_SIZE;
|
||||
break;
|
||||
case USB_PRODUCT_ID_LEAKSHIELD:
|
||||
/*
|
||||
|
|
@ -1730,6 +1760,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
|
||||
priv->temp_label = label_leakshield_temp_sensors;
|
||||
priv->speed_label = label_leakshield_fan_speed;
|
||||
min_report_size = LEAKSHIELD_MIN_STATUS_REPORT_SIZE;
|
||||
break;
|
||||
case USB_PRODUCT_ID_AQUASTREAMXT:
|
||||
priv->kind = aquastreamxt;
|
||||
|
|
@ -1760,6 +1791,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
priv->power_label = label_aquastreamult_power;
|
||||
priv->voltage_label = label_aquastreamult_voltages;
|
||||
priv->current_label = label_aquastreamult_current;
|
||||
min_report_size = AQUASTREAMULT_MIN_STATUS_REPORT_SIZE;
|
||||
break;
|
||||
case USB_PRODUCT_ID_POWERADJUST3:
|
||||
priv->kind = poweradjust3;
|
||||
|
|
@ -1833,6 +1865,8 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||
break;
|
||||
}
|
||||
|
||||
priv->min_report_size = min_report_size;
|
||||
|
||||
if (priv->buffer_size != 0) {
|
||||
priv->checksum_start = 0x01;
|
||||
priv->checksum_length = priv->buffer_size - 3;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ static const struct rog_ryujin_device_info rog_ryujin_iii_info = {
|
|||
#define MAX_REPORT_LENGTH 65
|
||||
|
||||
/* Cooler duty report offsets */
|
||||
#define RYUJIN_PUMP_SOURCE 3
|
||||
#define RYUJIN_PUMP_DUTY 4
|
||||
#define RYUJIN_INTERNAL_FAN_DUTY 5
|
||||
|
||||
|
|
@ -70,10 +71,15 @@ static const u8 get_cooler_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0x9A };
|
|||
static const u8 get_controller_speed_cmd[] = { RYUJIN_CMD_PREFIX, 0xA0 };
|
||||
static const u8 get_controller_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0xA1 };
|
||||
|
||||
#define RYUJIN_SET_COOLER_CHANNEL_OFFSET 2
|
||||
#define RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET 3
|
||||
#define RYUJIN_SET_COOLER_FAN_DUTY_OFFSET 4
|
||||
static const u8 set_cooler_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0x1A, 0x00, 0x00, 0x00 };
|
||||
|
||||
/* Pump control sources in the set cooler duty command and the cooler duty report */
|
||||
#define RYUJIN_SOURCE_HEADER 0
|
||||
#define RYUJIN_SOURCE_USB 1
|
||||
|
||||
#define RYUJIN_SET_CONTROLLER_FAN_DUTY_OFFSET 4
|
||||
static const u8 set_controller_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0x21, 0x00, 0x00, 0x00 };
|
||||
|
||||
|
|
@ -117,6 +123,7 @@ struct rog_ryujin_data {
|
|||
s32 temp_input[1];
|
||||
u16 speed_input[6]; /* Pump, internal fan and four controller fan speeds in RPM */
|
||||
u8 duty_input[3]; /* Pump, internal fan and controller fan duty in PWM */
|
||||
u8 pump_source; /* Pump control source from the cooler duty report */
|
||||
|
||||
u8 *buffer;
|
||||
unsigned long updated; /* jiffies */
|
||||
|
|
@ -200,16 +207,23 @@ static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, i
|
|||
/* Send command for getting data */
|
||||
ret = rog_ryujin_write_expanded(priv, cmd, cmd_length);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto out_complete;
|
||||
|
||||
ret = wait_for_completion_interruptible_timeout(status_completion,
|
||||
msecs_to_jiffies(STATUS_VALIDITY));
|
||||
if (ret == 0)
|
||||
return -ETIMEDOUT;
|
||||
else if (ret < 0)
|
||||
return ret;
|
||||
ret = -ETIMEDOUT;
|
||||
if (ret < 0)
|
||||
goto out_complete;
|
||||
|
||||
return 0;
|
||||
|
||||
out_complete:
|
||||
spin_lock_irqsave(&priv->status_report_request_lock, flags);
|
||||
if (!completion_done(status_completion))
|
||||
complete_all(status_completion);
|
||||
spin_unlock_irqrestore(&priv->status_report_request_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rog_ryujin_get_status(struct rog_ryujin_data *priv)
|
||||
|
|
@ -306,6 +320,44 @@ static int rog_ryujin_read_string(struct device *dev, enum hwmon_sensor_types ty
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* On devices with a non-zero duty_channel (the RYUJIN III), the channel byte of
|
||||
* the set cooler duty command selects the pump control source.
|
||||
* RYUJIN_SOURCE_HEADER hands the pump to its internal controller, which follows
|
||||
* the PWM of the header its 4-pin cable is plugged into. RYUJIN_SOURCE_USB
|
||||
* makes the pump follow the pump duty in the command. The cooler duty report
|
||||
* returns the current source.
|
||||
*
|
||||
* A fan duty write sends the reported source back, so that it leaves the pump
|
||||
* control source as it is. A pump duty write selects USB. The cooler does not
|
||||
* pass on a pump duty equal to the one it reports, and after power-on it
|
||||
* reports 40% while a pump handed over at that duty runs at full speed. So
|
||||
* when a pump duty write asks for the reported duty, write a duty 1% away
|
||||
* first.
|
||||
*/
|
||||
static int rog_ryujin_select_pump_source(struct rog_ryujin_data *priv, u8 *set_cmd,
|
||||
int channel)
|
||||
{
|
||||
u8 duty = set_cmd[RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET];
|
||||
int ret;
|
||||
|
||||
if (channel == 1) {
|
||||
set_cmd[RYUJIN_SET_COOLER_CHANNEL_OFFSET] = priv->pump_source;
|
||||
return 0;
|
||||
}
|
||||
|
||||
set_cmd[RYUJIN_SET_COOLER_CHANNEL_OFFSET] = RYUJIN_SOURCE_USB;
|
||||
|
||||
if (duty != rog_ryujin_pwm_to_percent(priv->duty_input[0]))
|
||||
return 0;
|
||||
|
||||
set_cmd[RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET] = duty < 100 ? duty + 1 : duty - 1;
|
||||
ret = rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH, &priv->cooler_duty_set);
|
||||
set_cmd[RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET] = duty;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rog_ryujin_write_fixed_duty(struct rog_ryujin_data *priv, int channel, int val)
|
||||
{
|
||||
u8 set_cmd[SET_CMD_LENGTH];
|
||||
|
|
@ -323,7 +375,7 @@ static int rog_ryujin_write_fixed_duty(struct rog_ryujin_data *priv, int channel
|
|||
return ret;
|
||||
|
||||
memcpy(set_cmd, set_cooler_duty_cmd, SET_CMD_LENGTH);
|
||||
set_cmd[2] = priv->info->duty_channel;
|
||||
set_cmd[RYUJIN_SET_COOLER_CHANNEL_OFFSET] = priv->info->duty_channel;
|
||||
|
||||
/* Cooler duties are set as 0-100% */
|
||||
val = rog_ryujin_pwm_to_percent(val);
|
||||
|
|
@ -340,6 +392,12 @@ static int rog_ryujin_write_fixed_duty(struct rog_ryujin_data *priv, int channel
|
|||
set_cmd[RYUJIN_SET_COOLER_FAN_DUTY_OFFSET] = val;
|
||||
}
|
||||
|
||||
if (priv->info->duty_channel) {
|
||||
ret = rog_ryujin_select_pump_source(priv, set_cmd, channel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH, &priv->cooler_duty_set);
|
||||
} else {
|
||||
/*
|
||||
|
|
@ -482,6 +540,7 @@ static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *repo
|
|||
goto unlock;
|
||||
}
|
||||
read_cooler_duty:
|
||||
priv->pump_source = data[RYUJIN_PUMP_SOURCE];
|
||||
priv->duty_input[0] = rog_ryujin_percent_to_pwm(data[RYUJIN_PUMP_DUTY]);
|
||||
priv->duty_input[1] = rog_ryujin_percent_to_pwm(data[RYUJIN_INTERNAL_FAN_DUTY]);
|
||||
|
||||
|
|
@ -578,6 +637,18 @@ static int rog_ryujin_probe(struct hid_device *hdev, const struct hid_device_id
|
|||
init_completion(&priv->cooler_duty_set);
|
||||
init_completion(&priv->controller_duty_set);
|
||||
|
||||
/*
|
||||
* Start completions in the completed state so rog_ryujin_raw_event()
|
||||
* only sees !completion_done() while rog_ryujin_execute_cmd() is
|
||||
* awaiting a response.
|
||||
*/
|
||||
complete_all(&priv->cooler_status_received);
|
||||
complete_all(&priv->controller_status_received);
|
||||
complete_all(&priv->cooler_duty_received);
|
||||
complete_all(&priv->controller_duty_received);
|
||||
complete_all(&priv->cooler_duty_set);
|
||||
complete_all(&priv->controller_duty_set);
|
||||
|
||||
priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "rog_ryujin",
|
||||
priv, &rog_ryujin_chip_info, NULL);
|
||||
if (IS_ERR(priv->hwmon_dev)) {
|
||||
|
|
|
|||
|
|
@ -164,6 +164,7 @@ struct asus_wmi_wmi_info {
|
|||
|
||||
struct asus_wmi_sensors {
|
||||
struct asus_wmi_wmi_info wmi;
|
||||
struct hwmon_chip_info chip_info;
|
||||
/* lock access to internal cache */
|
||||
struct mutex lock;
|
||||
};
|
||||
|
|
@ -496,11 +497,6 @@ static const struct hwmon_ops asus_wmi_hwmon_ops = {
|
|||
.read_string = asus_wmi_hwmon_read_string,
|
||||
};
|
||||
|
||||
static struct hwmon_chip_info asus_wmi_chip_info = {
|
||||
.ops = &asus_wmi_hwmon_ops,
|
||||
.info = NULL,
|
||||
};
|
||||
|
||||
static int asus_wmi_configure_sensor_setup(struct device *dev,
|
||||
struct asus_wmi_sensors *sensor_data)
|
||||
{
|
||||
|
|
@ -508,7 +504,7 @@ static int asus_wmi_configure_sensor_setup(struct device *dev,
|
|||
struct hwmon_channel_info *asus_wmi_hwmon_chan;
|
||||
int nr_count[hwmon_max] = {}, nr_types = 0;
|
||||
struct asus_wmi_sensor_info *temp_sensor;
|
||||
const struct hwmon_chip_info *chip_info;
|
||||
struct hwmon_chip_info *chip_info;
|
||||
enum hwmon_sensor_types type;
|
||||
struct device *hwdev;
|
||||
int i, idx;
|
||||
|
|
@ -549,8 +545,9 @@ static int asus_wmi_configure_sensor_setup(struct device *dev,
|
|||
if (!ptr_asus_wmi_ci)
|
||||
return -ENOMEM;
|
||||
|
||||
asus_wmi_chip_info.info = ptr_asus_wmi_ci;
|
||||
chip_info = &asus_wmi_chip_info;
|
||||
chip_info = &sensor_data->chip_info;
|
||||
chip_info->ops = &asus_wmi_hwmon_ops;
|
||||
chip_info->info = ptr_asus_wmi_ci;
|
||||
|
||||
sensor_data->wmi.info_by_id = devm_kcalloc(dev, sensor_data->wmi.sensor_count,
|
||||
sizeof(*sensor_data->wmi.info_by_id),
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/unaligned.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/property.h>
|
||||
|
|
@ -90,6 +92,9 @@ struct cc2_data {
|
|||
int irq_low;
|
||||
int irq_high;
|
||||
bool process_irqs;
|
||||
/* protects process_irqs and the alarm indicators */
|
||||
struct mutex irq_lock;
|
||||
bool regulator_enabled;
|
||||
};
|
||||
|
||||
/* %RH as a per cent mille from a register value */
|
||||
|
|
@ -118,20 +123,33 @@ static int cc2_enable(struct cc2_data *data)
|
|||
{
|
||||
int ret;
|
||||
|
||||
/* exclusive regulator, check in case a disable failed */
|
||||
if (regulator_is_enabled(data->regulator))
|
||||
return 0;
|
||||
if (data->irq_ready > 0) {
|
||||
scoped_guard(mutex, &data->irq_lock)
|
||||
data->process_irqs = false;
|
||||
|
||||
/* clear any pending completion */
|
||||
try_wait_for_completion(&data->complete);
|
||||
synchronize_irq(data->irq_ready);
|
||||
|
||||
scoped_guard(mutex, &data->irq_lock)
|
||||
reinit_completion(&data->complete);
|
||||
}
|
||||
|
||||
/* exclusive regulator, check in case a disable failed */
|
||||
if (data->regulator_enabled) {
|
||||
scoped_guard(mutex, &data->irq_lock)
|
||||
data->process_irqs = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = regulator_enable(data->regulator);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->regulator_enabled = true;
|
||||
|
||||
usleep_range(CC2_STARTUP_TIME_US, CC2_STARTUP_TIME_US + 125);
|
||||
|
||||
data->process_irqs = true;
|
||||
scoped_guard(mutex, &data->irq_lock)
|
||||
data->process_irqs = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -140,15 +158,36 @@ static void cc2_disable(struct cc2_data *data)
|
|||
{
|
||||
int err;
|
||||
|
||||
/* let the alarms already raised by the last measurement be processed */
|
||||
if (data->irq_low > 0)
|
||||
synchronize_irq(data->irq_low);
|
||||
|
||||
if (data->irq_high > 0)
|
||||
synchronize_irq(data->irq_high);
|
||||
|
||||
/* ignore alarms triggered by voltage toggling when powering up */
|
||||
data->process_irqs = false;
|
||||
scoped_guard(mutex, &data->irq_lock)
|
||||
data->process_irqs = false;
|
||||
|
||||
if (data->irq_ready > 0)
|
||||
synchronize_irq(data->irq_ready);
|
||||
|
||||
/* exclusive regulator, check in case an enable failed */
|
||||
if (regulator_is_enabled(data->regulator)) {
|
||||
err = regulator_disable(data->regulator);
|
||||
if (err)
|
||||
dev_dbg(&data->client->dev, "Failed to disable device");
|
||||
if (!data->regulator_enabled)
|
||||
return;
|
||||
|
||||
err = regulator_disable(data->regulator);
|
||||
if (err) {
|
||||
dev_dbg(&data->client->dev, "Failed to disable device");
|
||||
return;
|
||||
}
|
||||
|
||||
data->regulator_enabled = false;
|
||||
}
|
||||
|
||||
static void cc2_disable_action(void *data)
|
||||
{
|
||||
cc2_disable(data);
|
||||
}
|
||||
|
||||
static int cc2_cmd_response_diagnostic(struct device *dev, u8 status)
|
||||
|
|
@ -167,13 +206,13 @@ static int cc2_cmd_response_diagnostic(struct device *dev, u8 status)
|
|||
case CC2_RESPONSE_BUSY:
|
||||
return -EBUSY;
|
||||
case CC2_RESPONSE_NACK:
|
||||
if (resp & CC2_ERR_CORR_EEPROM)
|
||||
if (status & CC2_ERR_CORR_EEPROM)
|
||||
dev_dbg(dev, "Command failed: corrected EEPROM\n");
|
||||
if (resp & CC2_ERR_UNCORR_EEPROM)
|
||||
if (status & CC2_ERR_UNCORR_EEPROM)
|
||||
dev_dbg(dev, "Command failed: uncorrected EEPROM\n");
|
||||
if (resp & CC2_ERR_RAM_PARITY)
|
||||
if (status & CC2_ERR_RAM_PARITY)
|
||||
dev_dbg(dev, "Command failed: RAM parity\n");
|
||||
if (resp & CC2_ERR_RAM_PARITY)
|
||||
if (status & CC2_ERR_CONFIG_LOAD)
|
||||
dev_dbg(dev, "Command failed: configuration error\n");
|
||||
return -ENODATA;
|
||||
default:
|
||||
|
|
@ -226,7 +265,7 @@ static int cc2_command_mode_start(struct cc2_data *data)
|
|||
2 * CC2_RESP_START_CM_US);
|
||||
}
|
||||
ret = cc2_read_command_status(data->client);
|
||||
if (ret != -ETIMEDOUT || i == CC2_CM_RETRIES)
|
||||
if (ret != -ETIMEDOUT || i == CC2_CM_RETRIES - 1)
|
||||
break;
|
||||
|
||||
/* command window missed, prepare for a retry */
|
||||
|
|
@ -260,8 +299,7 @@ static int cc2_write_reg(struct cc2_data *data, u8 reg, u16 val)
|
|||
if (ret < 0)
|
||||
goto disable;
|
||||
|
||||
cpu_to_be16s(&val);
|
||||
ret = i2c_smbus_write_word_data(data->client, reg, val);
|
||||
ret = i2c_smbus_write_word_swapped(data->client, reg, val);
|
||||
if (ret < 0)
|
||||
goto disable;
|
||||
|
||||
|
|
@ -311,7 +349,7 @@ static int cc2_read_reg(struct cc2_data *data, u8 reg, u16 *val)
|
|||
if (ret != CC2_EEPROM_DATA_LEN)
|
||||
return ret < 0 ? ret : -EIO;
|
||||
|
||||
*val = be16_to_cpup((__be16 *)&buf[1]);
|
||||
*val = get_unaligned_be16(&buf[1]);
|
||||
|
||||
return cc2_read_command_status(data->client);
|
||||
}
|
||||
|
|
@ -351,10 +389,10 @@ static int cc2_data_fetch(struct i2c_client *client,
|
|||
|
||||
switch (type) {
|
||||
case hwmon_humidity:
|
||||
*val = cc2_rh_convert(be16_to_cpup((__be16 *)&data[0]));
|
||||
*val = cc2_rh_convert(get_unaligned_be16(&data[0]));
|
||||
break;
|
||||
case hwmon_temp:
|
||||
*val = cc2_temp_convert(be16_to_cpup((__be16 *)&data[2]));
|
||||
*val = cc2_temp_convert(get_unaligned_be16(&data[2]));
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
|
@ -482,8 +520,10 @@ static irqreturn_t cc2_ready_interrupt(int irq, void *data)
|
|||
{
|
||||
struct cc2_data *cc2 = data;
|
||||
|
||||
if (cc2->process_irqs)
|
||||
complete(&cc2->complete);
|
||||
scoped_guard(mutex, &cc2->irq_lock) {
|
||||
if (cc2->process_irqs)
|
||||
complete(&cc2->complete);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
|
@ -492,10 +532,12 @@ static irqreturn_t cc2_low_interrupt(int irq, void *data)
|
|||
{
|
||||
struct cc2_data *cc2 = data;
|
||||
|
||||
if (cc2->process_irqs) {
|
||||
hwmon_notify_event(cc2->hwmon, hwmon_humidity,
|
||||
hwmon_humidity_min_alarm, 0);
|
||||
cc2->rh_alarm.low_alarm = true;
|
||||
scoped_guard(mutex, &cc2->irq_lock) {
|
||||
if (cc2->process_irqs) {
|
||||
hwmon_notify_event(cc2->hwmon, hwmon_humidity,
|
||||
hwmon_humidity_min_alarm, 0);
|
||||
cc2->rh_alarm.low_alarm = true;
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
|
@ -505,10 +547,12 @@ static irqreturn_t cc2_high_interrupt(int irq, void *data)
|
|||
{
|
||||
struct cc2_data *cc2 = data;
|
||||
|
||||
if (cc2->process_irqs) {
|
||||
hwmon_notify_event(cc2->hwmon, hwmon_humidity,
|
||||
hwmon_humidity_max_alarm, 0);
|
||||
cc2->rh_alarm.high_alarm = true;
|
||||
scoped_guard(mutex, &cc2->irq_lock) {
|
||||
if (cc2->process_irqs) {
|
||||
hwmon_notify_event(cc2->hwmon, hwmon_humidity,
|
||||
hwmon_humidity_max_alarm, 0);
|
||||
cc2->rh_alarm.high_alarm = true;
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
|
@ -524,11 +568,13 @@ static int cc2_humidity_min_alarm_status(struct cc2_data *data, long *val)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (data->rh_alarm.low_alarm) {
|
||||
*val = (measurement < min_hyst) ? 1 : 0;
|
||||
data->rh_alarm.low_alarm = *val;
|
||||
} else {
|
||||
*val = 0;
|
||||
scoped_guard(mutex, &data->irq_lock) {
|
||||
if (data->rh_alarm.low_alarm) {
|
||||
*val = (measurement < min_hyst) ? 1 : 0;
|
||||
data->rh_alarm.low_alarm = *val;
|
||||
} else {
|
||||
*val = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -544,11 +590,13 @@ static int cc2_humidity_max_alarm_status(struct cc2_data *data, long *val)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (data->rh_alarm.high_alarm) {
|
||||
*val = (measurement > max_hyst) ? 1 : 0;
|
||||
data->rh_alarm.high_alarm = *val;
|
||||
} else {
|
||||
*val = 0;
|
||||
scoped_guard(mutex, &data->irq_lock) {
|
||||
if (data->rh_alarm.high_alarm) {
|
||||
*val = (measurement > max_hyst) ? 1 : 0;
|
||||
data->rh_alarm.high_alarm = *val;
|
||||
} else {
|
||||
*val = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -631,28 +679,47 @@ static int cc2_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
|||
}
|
||||
}
|
||||
|
||||
static int cc2_request_ready_irq(struct cc2_data *data, struct device *dev)
|
||||
static int cc2_get_irqs(struct cc2_data *data, struct device *dev)
|
||||
{
|
||||
data->irq_ready = fwnode_irq_get_byname(dev_fwnode(dev), "ready");
|
||||
if (data->irq_ready == -EPROBE_DEFER)
|
||||
return data->irq_ready;
|
||||
if (data->irq_ready > 0)
|
||||
init_completion(&data->complete);
|
||||
|
||||
data->irq_low = fwnode_irq_get_byname(dev_fwnode(dev), "low");
|
||||
if (data->irq_low == -EPROBE_DEFER)
|
||||
return data->irq_low;
|
||||
if (data->irq_low > 0)
|
||||
data->rh_alarm.low_alarm_visible = true;
|
||||
|
||||
data->irq_high = fwnode_irq_get_byname(dev_fwnode(dev), "high");
|
||||
if (data->irq_high == -EPROBE_DEFER)
|
||||
return data->irq_high;
|
||||
if (data->irq_high > 0)
|
||||
data->rh_alarm.high_alarm_visible = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cc2_request_data_ready(struct cc2_data *data, struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
data->irq_ready = fwnode_irq_get_byname(dev_fwnode(dev), "ready");
|
||||
if (data->irq_ready > 0) {
|
||||
init_completion(&data->complete);
|
||||
if (data->irq_ready > 0)
|
||||
ret = devm_request_threaded_irq(dev, data->irq_ready, NULL,
|
||||
cc2_ready_interrupt,
|
||||
IRQF_ONESHOT |
|
||||
IRQF_TRIGGER_RISING,
|
||||
dev_name(dev), data);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cc2_request_alarm_irqs(struct cc2_data *data, struct device *dev)
|
||||
static int cc2_request_alarms(struct cc2_data *data, struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
data->irq_low = fwnode_irq_get_byname(dev_fwnode(dev), "low");
|
||||
if (data->irq_low > 0) {
|
||||
ret = devm_request_threaded_irq(dev, data->irq_low, NULL,
|
||||
cc2_low_interrupt,
|
||||
|
|
@ -661,11 +728,8 @@ static int cc2_request_alarm_irqs(struct cc2_data *data, struct device *dev)
|
|||
dev_name(dev), data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->rh_alarm.low_alarm_visible = true;
|
||||
}
|
||||
|
||||
data->irq_high = fwnode_irq_get_byname(dev_fwnode(dev), "high");
|
||||
if (data->irq_high > 0) {
|
||||
ret = devm_request_threaded_irq(dev, data->irq_high, NULL,
|
||||
cc2_high_interrupt,
|
||||
|
|
@ -674,8 +738,6 @@ static int cc2_request_alarm_irqs(struct cc2_data *data, struct device *dev)
|
|||
dev_name(dev), data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->rh_alarm.high_alarm_visible = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -717,6 +779,8 @@ static int cc2_probe(struct i2c_client *client)
|
|||
|
||||
i2c_set_clientdata(client, data);
|
||||
|
||||
mutex_init(&data->irq_lock);
|
||||
|
||||
data->client = client;
|
||||
|
||||
data->regulator = devm_regulator_get_exclusive(dev, "vdd");
|
||||
|
|
@ -726,14 +790,18 @@ static int cc2_probe(struct i2c_client *client)
|
|||
|
||||
device_property_read_string(dev, "label", &data->label);
|
||||
|
||||
ret = cc2_request_ready_irq(data, dev);
|
||||
ret = cc2_get_irqs(data, dev);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to get IRQs\n");
|
||||
|
||||
ret = devm_add_action_or_reset(dev, cc2_disable_action, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cc2_request_data_ready(data, dev);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to request ready irq\n");
|
||||
|
||||
ret = cc2_request_alarm_irqs(data, dev);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to request alarm irqs\n");
|
||||
|
||||
data->hwmon = devm_hwmon_device_register_with_info(dev, client->name,
|
||||
data, &cc2_chip_info,
|
||||
NULL);
|
||||
|
|
@ -741,16 +809,13 @@ static int cc2_probe(struct i2c_client *client)
|
|||
return dev_err_probe(dev, PTR_ERR(data->hwmon),
|
||||
"Failed to register hwmon device\n");
|
||||
|
||||
ret = cc2_request_alarms(data, dev);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to request alarm irqs\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cc2_remove(struct i2c_client *client)
|
||||
{
|
||||
struct cc2_data *data = i2c_get_clientdata(client);
|
||||
|
||||
cc2_disable(data);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id cc2_id[] = {
|
||||
{ .name = "cc2d23" },
|
||||
{ .name = "cc2d23s" },
|
||||
|
|
@ -783,7 +848,6 @@ static struct i2c_driver cc2_driver = {
|
|||
.of_match_table = cc2_of_match,
|
||||
},
|
||||
.probe = cc2_probe,
|
||||
.remove = cc2_remove,
|
||||
.id_table = cc2_id,
|
||||
};
|
||||
module_i2c_driver(cc2_driver);
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ struct temp_data {
|
|||
int temp;
|
||||
bool temp_valid;
|
||||
int tjmax;
|
||||
unsigned long last_updated;
|
||||
u64 last_updated;
|
||||
unsigned int cpu;
|
||||
int index;
|
||||
u32 cpu_core_id;
|
||||
|
|
@ -407,7 +407,7 @@ static ssize_t show_temp(struct device *dev,
|
|||
mutex_lock(&tdata->update_lock);
|
||||
|
||||
/* Check whether the time interval has elapsed */
|
||||
if (time_after(jiffies, tdata->last_updated + HZ)) {
|
||||
if (time_after64(get_jiffies_64(), tdata->last_updated + HZ)) {
|
||||
tjmax = get_tjmax(tdata, dev);
|
||||
rdmsrq_on_cpu(tdata->cpu, tdata->status_reg, &val.q);
|
||||
/*
|
||||
|
|
@ -417,7 +417,7 @@ static ssize_t show_temp(struct device *dev,
|
|||
*/
|
||||
tdata->temp_valid = (val.l >> 31) & 1;
|
||||
tdata->temp = tjmax - ((val.l >> 16) & 0xff) * 1000;
|
||||
tdata->last_updated = jiffies;
|
||||
tdata->last_updated = get_jiffies_64();
|
||||
}
|
||||
|
||||
valid = tdata->temp_valid;
|
||||
|
|
@ -491,11 +491,12 @@ init_temp_data(struct platform_data *pdata, unsigned int cpu, int pkg_flag)
|
|||
struct temp_data *tdata;
|
||||
|
||||
if (!pdata->core_data) {
|
||||
pdata->nr_cores = topology_num_cores_per_package();
|
||||
pdata->core_data = kzalloc_objs(struct temp_data *,
|
||||
pdata->nr_cores);
|
||||
int nr_cores = topology_num_cores_per_package();
|
||||
|
||||
pdata->core_data = kzalloc_objs(struct temp_data *, nr_cores);
|
||||
if (!pdata->core_data)
|
||||
return NULL;
|
||||
pdata->nr_cores = nr_cores;
|
||||
}
|
||||
|
||||
tdata = kzalloc_obj(struct temp_data);
|
||||
|
|
@ -520,11 +521,8 @@ init_temp_data(struct platform_data *pdata, unsigned int cpu, int pkg_flag)
|
|||
tdata->cpu = cpu;
|
||||
tdata->cpu_core_id = topology_core_id(cpu);
|
||||
tdata->attr_size = MAX_CORE_ATTRS;
|
||||
/*
|
||||
* A zero timestamp does not look stale on 32-bit, where jiffies
|
||||
* starts just short of wrapping. Backdate it instead.
|
||||
*/
|
||||
tdata->last_updated = jiffies - HZ - 1;
|
||||
/* Backdate to force a refresh on the first read */
|
||||
tdata->last_updated = get_jiffies_64() - HZ - 1;
|
||||
mutex_init(&tdata->update_lock);
|
||||
return tdata;
|
||||
}
|
||||
|
|
@ -695,6 +693,9 @@ static int coretemp_cpu_online(unsigned int cpu)
|
|||
if (!housekeeping_cpu(cpu, HK_TYPE_MISC))
|
||||
return 0;
|
||||
|
||||
if (!pdev)
|
||||
return 0;
|
||||
|
||||
pdata = platform_get_drvdata(pdev);
|
||||
if (!pdata->hwmon_dev) {
|
||||
struct device *hwmon;
|
||||
|
|
@ -746,6 +747,9 @@ static int coretemp_cpu_offline(unsigned int cpu)
|
|||
return 0;
|
||||
|
||||
/* If the physical CPU device does not exist, just return */
|
||||
if (!pdev)
|
||||
return 0;
|
||||
|
||||
pd = platform_get_drvdata(pdev);
|
||||
if (!pd->hwmon_dev)
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -215,10 +215,11 @@ static int set_target(struct ccp_device *ccp, int channel, long val)
|
|||
int ret;
|
||||
|
||||
val = clamp_val(val, 0, 0xFFFF);
|
||||
ccp->target[channel] = val;
|
||||
|
||||
mutex_lock(&ccp->mutex);
|
||||
ret = send_usb_cmd(ccp, CTL_SET_FAN_TARGET, channel, val >> 8, val);
|
||||
if (!ret)
|
||||
ccp->target[channel] = val;
|
||||
|
||||
mutex_unlock(&ccp->mutex);
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -444,6 +444,7 @@ static void gpd_duo_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABL
|
|||
break;
|
||||
case AUTOMATIC:
|
||||
gpd_ecram_write(data->drvdata, data->drvdata->pwm_write, 0);
|
||||
gpd_ecram_write(data->drvdata, data->drvdata->pwm_write + 1, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,7 +150,10 @@ static int set_fan_speed(struct gpio_fan_data *fan_data, int speed_index)
|
|||
|
||||
__set_fan_ctrl(fan_data, fan_data->speed[speed_index].ctrl_val);
|
||||
|
||||
if (fan_data->speed_index > 0 && speed_index == 0) {
|
||||
/* The GPIOs have changed, and a PM put drops the reference on error. */
|
||||
fan_data->speed_index = speed_index;
|
||||
|
||||
if (speed_index == 0) {
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_put_sync(fan_data->dev);
|
||||
|
|
@ -158,8 +161,6 @@ static int set_fan_speed(struct gpio_fan_data *fan_data, int speed_index)
|
|||
return ret;
|
||||
}
|
||||
|
||||
fan_data->speed_index = speed_index;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -390,6 +390,7 @@ struct hp_wmi_info {
|
|||
*/
|
||||
struct hp_wmi_sensors {
|
||||
struct wmi_device *wdev;
|
||||
struct hwmon_chip_info chip_info;
|
||||
struct hp_wmi_info **info_map[hwmon_max];
|
||||
u8 channel_count[hwmon_max];
|
||||
bool has_intrusion;
|
||||
|
|
@ -738,15 +739,21 @@ static long scale_numeric_sensor(const struct hp_wmi_numeric_sensor *nsensor)
|
|||
|
||||
val = current_reading;
|
||||
|
||||
for (; unit_modifier < target_modifier; unit_modifier++)
|
||||
for (; val && unit_modifier < target_modifier; unit_modifier++)
|
||||
val = DIV_ROUND_CLOSEST(val, 10);
|
||||
|
||||
for (; unit_modifier > target_modifier; unit_modifier--) {
|
||||
if (val > LONG_MAX / 10) {
|
||||
val = LONG_MAX;
|
||||
break;
|
||||
if (val) {
|
||||
for (; unit_modifier > target_modifier; unit_modifier--) {
|
||||
if (val > LONG_MAX / 10) {
|
||||
val = LONG_MAX;
|
||||
break;
|
||||
}
|
||||
if (val < LONG_MIN / 10) {
|
||||
val = LONG_MIN;
|
||||
break;
|
||||
}
|
||||
val *= 10;
|
||||
}
|
||||
val *= 10;
|
||||
}
|
||||
|
||||
if (sensor_type == HP_WMI_TYPE_TEMPERATURE) {
|
||||
|
|
@ -1473,8 +1480,10 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
|
|||
|
||||
if ((type == hwmon_temp && attr == hwmon_temp_alarm) ||
|
||||
(type == hwmon_fan && attr == hwmon_fan_alarm)) {
|
||||
mutex_lock(&state->lock);
|
||||
*out_val = info->alarm ? 1 : 0;
|
||||
info->alarm = false;
|
||||
mutex_unlock(&state->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1531,11 +1540,6 @@ static const struct hwmon_ops hp_wmi_hwmon_ops = {
|
|||
.write = hp_wmi_hwmon_write,
|
||||
};
|
||||
|
||||
static struct hwmon_chip_info hp_wmi_chip_info = {
|
||||
.ops = &hp_wmi_hwmon_ops,
|
||||
.info = NULL,
|
||||
};
|
||||
|
||||
static struct hp_wmi_info *match_fan_event(struct hp_wmi_sensors *state,
|
||||
const char *event_description)
|
||||
{
|
||||
|
|
@ -1962,7 +1966,8 @@ static int make_chip_info(struct hp_wmi_sensors *state, bool has_events)
|
|||
if (!ptr_channel_info)
|
||||
return -ENOMEM;
|
||||
|
||||
hp_wmi_chip_info.info = ptr_channel_info;
|
||||
state->chip_info.ops = &hp_wmi_hwmon_ops;
|
||||
state->chip_info.info = ptr_channel_info;
|
||||
|
||||
for (type = hwmon_chip; type < hwmon_max; type++) {
|
||||
count = channel_count[type];
|
||||
|
|
@ -2053,7 +2058,7 @@ static int hp_wmi_sensors_init(struct hp_wmi_sensors *state)
|
|||
return err;
|
||||
|
||||
hwdev = devm_hwmon_device_register_with_info(dev, "hp_wmi_sensors",
|
||||
state, &hp_wmi_chip_info,
|
||||
state, &state->chip_info,
|
||||
NULL);
|
||||
return PTR_ERR_OR_ZERO(hwdev);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,8 +123,7 @@ struct lm75_data {
|
|||
|
||||
static const u8 lm75_sample_set_masks[] = { 0 << 5, 1 << 5, 2 << 5, 3 << 5 };
|
||||
|
||||
#define LM75_ALERT_POLARITY_HIGH_8_BIT (BIT(2))
|
||||
#define LM75_ALERT_POLARITY_HIGH_16_BIT (BIT(2) << 8)
|
||||
#define LM75_ALERT_POLARITY_HIGH_BIT BIT(2)
|
||||
#define LM75_SAMPLE_CLEAR_MASK (3 << 5)
|
||||
|
||||
/* The structure below stores the configuration values of the supported devices.
|
||||
|
|
@ -770,11 +769,7 @@ static int lm75_generic_probe(struct device *dev, const char *name,
|
|||
data->orig_conf = status;
|
||||
|
||||
/* Enforce polarity active-low (default) or active-high (devicetree) */
|
||||
if (!data->params->config_reg_16bits)
|
||||
pol_mask = LM75_ALERT_POLARITY_HIGH_8_BIT;
|
||||
else
|
||||
pol_mask = LM75_ALERT_POLARITY_HIGH_16_BIT;
|
||||
|
||||
pol_mask = LM75_ALERT_POLARITY_HIGH_BIT;
|
||||
clr_mask = data->params->clr_mask | pol_mask;
|
||||
set_mask = data->params->set_mask & ~pol_mask;
|
||||
if (device_property_read_bool(dev, "ti,alert-polarity-active-high"))
|
||||
|
|
|
|||
|
|
@ -606,13 +606,16 @@ static DEVICE_ATTR_RO(cpu0_vid);
|
|||
static ssize_t vrm_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct lm87_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = dev_get_drvdata(dev);
|
||||
struct lm87_data *data = i2c_get_clientdata(client);
|
||||
|
||||
return sprintf(buf, "%d\n", data->vrm);
|
||||
}
|
||||
static ssize_t vrm_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct lm87_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = dev_get_drvdata(dev);
|
||||
struct lm87_data *data = i2c_get_clientdata(client);
|
||||
unsigned long val;
|
||||
int err;
|
||||
|
||||
|
|
|
|||
|
|
@ -920,7 +920,7 @@ static ssize_t
|
|||
show_pwm(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct nct6683_data *data = nct6683_update_device(dev);
|
||||
struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
|
||||
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
|
||||
int index = sattr->index;
|
||||
|
||||
return sprintf(buf, "%d\n", data->pwm[index]);
|
||||
|
|
@ -930,7 +930,7 @@ static ssize_t
|
|||
store_pwm(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 sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
|
||||
struct nct6683_data *data = dev_get_drvdata(dev);
|
||||
int index = sattr->index;
|
||||
unsigned long val;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
#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>
|
||||
|
|
@ -326,11 +325,13 @@ static int create_dimm_temp_info(struct peci_dimmtemp *priv)
|
|||
if (ret == -ENODEV) {
|
||||
dev_dbg(priv->dev, "No DIMMs found\n");
|
||||
return 0;
|
||||
} else if (ret) {
|
||||
}
|
||||
if (ret == -EAGAIN) {
|
||||
schedule_delayed_work(&priv->detect_work, DIMM_MASK_CHECK_DELAY_JIFFIES);
|
||||
dev_dbg(priv->dev, "Deferred populating DIMM temp info\n");
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
channels = priv->gen_info->chan_rank_max * priv->gen_info->dimm_idx_max;
|
||||
|
||||
|
|
@ -398,10 +399,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);
|
||||
|
||||
ret = devm_delayed_work_autocancel(priv->dev, &priv->detect_work,
|
||||
create_dimm_temp_info_delayed);
|
||||
if (ret)
|
||||
return ret;
|
||||
auxiliary_set_drvdata(adev, priv);
|
||||
INIT_DELAYED_WORK(&priv->detect_work, create_dimm_temp_info_delayed);
|
||||
|
||||
ret = create_dimm_temp_info(priv);
|
||||
if (ret && ret != -EAGAIN) {
|
||||
|
|
@ -412,6 +411,13 @@ static int peci_dimmtemp_probe(struct auxiliary_device *adev, const struct auxil
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void peci_dimmtemp_remove(struct auxiliary_device *adev)
|
||||
{
|
||||
struct peci_dimmtemp *priv = auxiliary_get_drvdata(adev);
|
||||
|
||||
cancel_delayed_work_sync(&priv->detect_work);
|
||||
}
|
||||
|
||||
static int
|
||||
read_thresholds_hsx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data)
|
||||
{
|
||||
|
|
@ -665,6 +671,7 @@ MODULE_DEVICE_TABLE(auxiliary, peci_dimmtemp_ids);
|
|||
|
||||
static struct auxiliary_driver peci_dimmtemp_driver = {
|
||||
.probe = peci_dimmtemp_probe,
|
||||
.remove = peci_dimmtemp_remove,
|
||||
.id_table = peci_dimmtemp_ids,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1524,14 +1524,22 @@ static int pmbus_thermal_add_sensor(struct pmbus_data *pmbus_data,
|
|||
tdata->sensor = sensor;
|
||||
tdata->pmbus_data = pmbus_data;
|
||||
|
||||
tzd = devm_thermal_of_zone_register(dev, index, tdata,
|
||||
/*
|
||||
* The PMBus core numbers its temperature sensors from 1, as the
|
||||
* hwmon tempN attributes, while the thermal core expects a 0-based
|
||||
* sensor index.
|
||||
*/
|
||||
tzd = devm_thermal_of_zone_register(dev, index - 1, tdata,
|
||||
&pmbus_thermal_ops);
|
||||
/*
|
||||
* If CONFIG_THERMAL_OF is disabled, this returns -ENODEV,
|
||||
* so ignore that error but forward any other error.
|
||||
* If CONFIG_THERMAL_OF is disabled, devm_thermal_of_zone_register()
|
||||
* returns -ENOTSUPP. If no thermal zone is configured for the sensor,
|
||||
* it returns -ENODEV. Ignore those errors but forward any other error.
|
||||
*/
|
||||
if (IS_ERR(tzd) && (PTR_ERR(tzd) != -ENODEV))
|
||||
if (IS_ERR(tzd) && PTR_ERR(tzd) != -ENODEV && PTR_ERR(tzd) != -ENOTSUPP) {
|
||||
devm_kfree(dev, tdata);
|
||||
return PTR_ERR(tzd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1546,10 +1554,11 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
|
|||
{
|
||||
struct sensor_device_attribute *a;
|
||||
struct pmbus_sensor *sensor;
|
||||
int err;
|
||||
|
||||
sensor = devm_kzalloc(data->dev, sizeof(*sensor), GFP_KERNEL);
|
||||
if (!sensor)
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
a = &sensor->attribute;
|
||||
|
||||
if (type)
|
||||
|
|
@ -1574,14 +1583,17 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
|
|||
pmbus_set_sensor, -1);
|
||||
|
||||
if (pmbus_add_attribute(data, &a->dev_attr.attr))
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
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);
|
||||
if (class == PSC_TEMPERATURE && strcmp(type, "input") == 0) {
|
||||
err = pmbus_thermal_add_sensor(data, sensor, seq);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
return sensor;
|
||||
}
|
||||
|
|
@ -1682,8 +1694,8 @@ static int pmbus_add_limit_attrs(struct i2c_client *client,
|
|||
page, 0xff, l->reg, attr->class,
|
||||
attr->update || l->update,
|
||||
l->readonly, l->writeonly, true);
|
||||
if (!curr)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(curr))
|
||||
return PTR_ERR(curr);
|
||||
if (l->sbit && (info->func[page] & attr->sfunc)) {
|
||||
ret = pmbus_add_boolean(data, name,
|
||||
l->alarm, index,
|
||||
|
|
@ -1722,8 +1734,8 @@ static int pmbus_add_sensor_attrs_one(struct i2c_client *client,
|
|||
}
|
||||
base = pmbus_add_sensor(data, name, "input", index, page, phase,
|
||||
attr->reg, attr->class, true, true, false, true);
|
||||
if (!base)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
/* No limit and alarm attributes for phase specific sensors */
|
||||
if (attr->sfunc && phase == 0xff) {
|
||||
ret = pmbus_add_limit_attrs(client, data, info, name,
|
||||
|
|
@ -2396,8 +2408,8 @@ static int pmbus_add_fan_ctrl(struct i2c_client *client,
|
|||
0xff, PMBUS_VIRT_FAN_TARGET_1 + id, PSC_FAN,
|
||||
false, false, false, true);
|
||||
|
||||
if (!sensor)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(sensor))
|
||||
return PTR_ERR(sensor);
|
||||
|
||||
if (!((data->info->func[page] & PMBUS_HAVE_PWM12) ||
|
||||
(data->info->func[page] & PMBUS_HAVE_PWM34)))
|
||||
|
|
@ -2407,15 +2419,15 @@ static int pmbus_add_fan_ctrl(struct i2c_client *client,
|
|||
0xff, PMBUS_VIRT_PWM_1 + id, PSC_PWM,
|
||||
false, false, false, true);
|
||||
|
||||
if (!sensor)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(sensor))
|
||||
return PTR_ERR(sensor);
|
||||
|
||||
sensor = pmbus_add_sensor(data, "pwm", "enable", index, page,
|
||||
0xff, PMBUS_VIRT_PWM_ENABLE_1 + id, PSC_PWM,
|
||||
true, false, false, false);
|
||||
|
||||
if (!sensor)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(sensor))
|
||||
return PTR_ERR(sensor);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2432,6 +2444,7 @@ static int pmbus_add_fan_attributes(struct i2c_client *client,
|
|||
int f;
|
||||
|
||||
for (f = 0; f < ARRAY_SIZE(pmbus_fan_registers); f++) {
|
||||
struct pmbus_sensor *sensor;
|
||||
int regval;
|
||||
|
||||
if (!(info->func[page] & pmbus_fan_flags[f]))
|
||||
|
|
@ -2452,10 +2465,11 @@ static int pmbus_add_fan_attributes(struct i2c_client *client,
|
|||
(!(regval & (PB_FAN_1_INSTALLED >> ((f & 1) * 4)))))
|
||||
continue;
|
||||
|
||||
if (pmbus_add_sensor(data, "fan", "input", index,
|
||||
page, 0xff, pmbus_fan_registers[f],
|
||||
PSC_FAN, true, true, false, true) == NULL)
|
||||
return -ENOMEM;
|
||||
sensor = pmbus_add_sensor(data, "fan", "input", index,
|
||||
page, 0xff, pmbus_fan_registers[f],
|
||||
PSC_FAN, true, true, false, true);
|
||||
if (IS_ERR(sensor))
|
||||
return PTR_ERR(sensor);
|
||||
|
||||
/* Fan control */
|
||||
if (pmbus_check_word_register(client, page,
|
||||
|
|
|
|||
|
|
@ -351,6 +351,8 @@ static int q54sj108a2_probe(struct i2c_client *client)
|
|||
psu->client = client;
|
||||
|
||||
debugfs = pmbus_get_debugfs_dir(client);
|
||||
if (!debugfs)
|
||||
return 0;
|
||||
|
||||
q54sj108a2_dir = debugfs_create_dir(client->name, debugfs);
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include <linux/bitrev.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
/* Commands */
|
||||
#define SHT15_MEASURE_TEMP 0x03
|
||||
|
|
@ -976,25 +977,22 @@ static int sht15_probe(struct platform_device *pdev)
|
|||
goto err_release_reg;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, gpiod_to_irq(data->data),
|
||||
sht15_interrupt_fired,
|
||||
IRQF_TRIGGER_FALLING,
|
||||
"sht15 data",
|
||||
data);
|
||||
ret = request_irq(gpiod_to_irq(data->data), sht15_interrupt_fired,
|
||||
IRQF_TRIGGER_FALLING, "sht15 data", data);
|
||||
if (ret)
|
||||
goto err_release_reg;
|
||||
disable_irq_nosync(gpiod_to_irq(data->data));
|
||||
ret = sht15_connection_reset(data);
|
||||
if (ret)
|
||||
goto err_release_reg;
|
||||
goto err_free_irq;
|
||||
ret = sht15_soft_reset(data);
|
||||
if (ret)
|
||||
goto err_release_reg;
|
||||
goto err_free_irq;
|
||||
|
||||
ret = sysfs_create_group(&pdev->dev.kobj, &sht15_attr_group);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "sysfs create failed\n");
|
||||
goto err_release_reg;
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(data->dev);
|
||||
|
|
@ -1007,9 +1005,13 @@ static int sht15_probe(struct platform_device *pdev)
|
|||
|
||||
err_release_sysfs_group:
|
||||
sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group);
|
||||
err_free_irq:
|
||||
disable_work_sync(&data->read_work);
|
||||
free_irq(gpiod_to_irq(data->data), data);
|
||||
err_release_reg:
|
||||
if (!IS_ERR(data->reg)) {
|
||||
regulator_unregister_notifier(data->reg, &data->nb);
|
||||
cancel_work_sync(&data->update_supply_work);
|
||||
regulator_disable(data->reg);
|
||||
}
|
||||
return ret;
|
||||
|
|
@ -1023,12 +1025,16 @@ static void sht15_remove(struct platform_device *pdev)
|
|||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group);
|
||||
|
||||
disable_work_sync(&data->read_work);
|
||||
free_irq(gpiod_to_irq(data->data), data);
|
||||
|
||||
ret = sht15_soft_reset(data);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "Failed to reset device (%pe)\n", ERR_PTR(ret));
|
||||
|
||||
if (!IS_ERR(data->reg)) {
|
||||
regulator_unregister_notifier(data->reg, &data->nb);
|
||||
cancel_work_sync(&data->update_supply_work);
|
||||
regulator_disable(data->reg);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,7 +111,9 @@ static int sht4x_read_values(struct sht4x_data *data)
|
|||
}
|
||||
|
||||
if (data->data_pending &&
|
||||
time_before64(get_jiffies_64(), data->heating_complete + data->update_interval)) {
|
||||
time_before64(get_jiffies_64(),
|
||||
data->heating_complete +
|
||||
msecs_to_jiffies(data->update_interval))) {
|
||||
data->data_pending = false;
|
||||
} else {
|
||||
next_update = data->last_updated +
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/i3c/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
@ -78,7 +78,7 @@
|
|||
struct tmp108 {
|
||||
struct regmap *regmap;
|
||||
u16 orig_config;
|
||||
u64 ready_time;
|
||||
ktime_t ready_time;
|
||||
const struct tmp108_params *params;
|
||||
};
|
||||
|
||||
|
|
@ -121,6 +121,7 @@ static int tmp108_read(struct device *dev, enum hwmon_sensor_types type,
|
|||
struct tmp108 *tmp108 = dev_get_drvdata(dev);
|
||||
unsigned int regval;
|
||||
int err, hyst;
|
||||
s64 delay;
|
||||
|
||||
if (type == hwmon_chip) {
|
||||
if (attr == hwmon_chip_update_interval) {
|
||||
|
|
@ -137,12 +138,10 @@ static int tmp108_read(struct device *dev, enum hwmon_sensor_types type,
|
|||
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
/* Is it too early to return a conversion ? */
|
||||
if (time_before64(get_jiffies_64(), tmp108->ready_time)) {
|
||||
dev_dbg(dev, "%s: Conversion not ready yet..\n",
|
||||
__func__);
|
||||
return -EAGAIN;
|
||||
}
|
||||
/* Wait if it is too early to return a conversion */
|
||||
delay = ktime_us_delta(tmp108->ready_time, ktime_get());
|
||||
if (delay > 0)
|
||||
usleep_range(delay, delay + 1000);
|
||||
err = regmap_read(tmp108->regmap, TMP108_REG_TEMP, ®val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
|
@ -477,11 +476,8 @@ static int tmp108_common_probe(struct device *dev, struct regmap *regmap, char *
|
|||
return err;
|
||||
}
|
||||
|
||||
tmp108->ready_time = get_jiffies_64();
|
||||
if ((tmp108->orig_config & TMP108_CONF_MODE_MASK) ==
|
||||
TMP108_MODE_SHUTDOWN)
|
||||
tmp108->ready_time +=
|
||||
msecs_to_jiffies(TMP108_CONVERSION_TIME_MS);
|
||||
tmp108->ready_time = ktime_add_ms(ktime_get(),
|
||||
TMP108_CONVERSION_TIME_MS);
|
||||
|
||||
err = devm_add_action_or_reset(dev, tmp108_restore_config, tmp108);
|
||||
if (err) {
|
||||
|
|
@ -528,8 +524,8 @@ static int tmp108_resume(struct device *dev)
|
|||
|
||||
err = regmap_update_bits(tmp108->regmap, TMP108_REG_CONF,
|
||||
TMP108_CONF_MODE_MASK, TMP108_MODE_CONTINUOUS);
|
||||
tmp108->ready_time = get_jiffies_64() +
|
||||
msecs_to_jiffies(TMP108_CONVERSION_TIME_MS);
|
||||
tmp108->ready_time = ktime_add_ms(ktime_get(),
|
||||
TMP108_CONVERSION_TIME_MS);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user