summaryrefslogtreecommitdiff
path: root/drivers/hwmon
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/Kconfig75
-rw-r--r--drivers/hwmon/Makefile6
-rw-r--r--drivers/hwmon/acpi_power_meter.c856
-rw-r--r--drivers/hwmon/ad7314.c10
-rw-r--r--drivers/hwmon/aht10.c16
-rw-r--r--drivers/hwmon/amc6821.c50
-rw-r--r--drivers/hwmon/asus-ec-sensors.c63
-rw-r--r--drivers/hwmon/cgbc-hwmon.c304
-rw-r--r--drivers/hwmon/dell-smm-hwmon.c12
-rw-r--r--drivers/hwmon/emc2305.c38
-rw-r--r--drivers/hwmon/fam15h_power.c6
-rw-r--r--drivers/hwmon/ftsteutates.c9
-rw-r--r--drivers/hwmon/gpio-fan.c119
-rw-r--r--drivers/hwmon/gsc-hwmon.c1
-rw-r--r--drivers/hwmon/hp-wmi-sensors.c4
-rw-r--r--drivers/hwmon/htu31.c350
-rw-r--r--drivers/hwmon/hwmon-vid.c4
-rw-r--r--drivers/hwmon/hwmon.c4
-rw-r--r--drivers/hwmon/ina238.c214
-rw-r--r--drivers/hwmon/ina2xx.c8
-rw-r--r--drivers/hwmon/ina3221.c9
-rw-r--r--drivers/hwmon/isl28022.c52
-rw-r--r--drivers/hwmon/k10temp.c11
-rw-r--r--drivers/hwmon/kbatt.c147
-rw-r--r--drivers/hwmon/kfan.c246
-rw-r--r--drivers/hwmon/lm75.c2
-rw-r--r--drivers/hwmon/lm90.c84
-rw-r--r--drivers/hwmon/ltc2992.c30
-rw-r--r--drivers/hwmon/ltc4282.c51
-rw-r--r--drivers/hwmon/max6639.c16
-rw-r--r--drivers/hwmon/max77705-hwmon.c221
-rw-r--r--drivers/hwmon/nct6683.c3
-rw-r--r--drivers/hwmon/nct6775-core.c4
-rw-r--r--drivers/hwmon/nct7363.c2
-rw-r--r--drivers/hwmon/npcm750-pwm-fan.c2
-rw-r--r--drivers/hwmon/ntc_thermistor.c81
-rw-r--r--drivers/hwmon/occ/common.c240
-rw-r--r--drivers/hwmon/oxp-sensors.c716
-rw-r--r--drivers/hwmon/peci/dimmtemp.c10
-rw-r--r--drivers/hwmon/pmbus/Kconfig33
-rw-r--r--drivers/hwmon/pmbus/Makefile2
-rw-r--r--drivers/hwmon/pmbus/ina233.c191
-rw-r--r--drivers/hwmon/pmbus/lm25066.c2
-rw-r--r--drivers/hwmon/pmbus/lt3074.c122
-rw-r--r--drivers/hwmon/pmbus/ltc2978.c69
-rw-r--r--drivers/hwmon/pmbus/max34440.c119
-rw-r--r--drivers/hwmon/pmbus/mpq7932.c4
-rw-r--r--drivers/hwmon/pmbus/mpq8785.c91
-rw-r--r--drivers/hwmon/pmbus/pmbus.c2
-rw-r--r--drivers/hwmon/pmbus/pmbus.h19
-rw-r--r--drivers/hwmon/pmbus/pmbus_core.c436
-rw-r--r--drivers/hwmon/pmbus/tda38640.c2
-rw-r--r--drivers/hwmon/pmbus/tps25990.c2
-rw-r--r--drivers/hwmon/pmbus/ucd9000.c16
-rw-r--r--drivers/hwmon/pt5161l.c46
-rw-r--r--drivers/hwmon/pwm-fan.c8
-rw-r--r--drivers/hwmon/qnap-mcu-hwmon.c1
-rw-r--r--drivers/hwmon/sbrmi.c357
-rw-r--r--drivers/hwmon/sg2042-mcu.c42
-rw-r--r--drivers/hwmon/sht3x.c67
-rw-r--r--drivers/hwmon/spd5118.c357
-rw-r--r--drivers/hwmon/tmp102.c5
-rw-r--r--drivers/hwmon/tps23861.c31
-rw-r--r--drivers/hwmon/xgene-hwmon.c41
64 files changed, 3563 insertions, 2578 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 4cbaba15d86e..079620dd4286 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -335,6 +335,26 @@ config SENSORS_K10TEMP
This driver can also be built as a module. If so, the module
will be called k10temp.
+config SENSORS_KBATT
+ tristate "KEBA battery controller support"
+ depends on KEBA_CP500
+ help
+ This driver supports the battery monitoring controller found in
+ KEBA system FPGA devices.
+
+ This driver can also be built as a module. If so, the module
+ will be called kbatt.
+
+config SENSORS_KFAN
+ tristate "KEBA fan controller support"
+ depends on KEBA_CP500
+ help
+ This driver supports the fan controller found in KEBA system
+ FPGA devices.
+
+ This driver can also be built as a module. If so, the module
+ will be called kfan.
+
config SENSORS_FAM15H_POWER
tristate "AMD Family 15h processor power"
depends on X86 && PCI && CPU_SUP_AMD
@@ -463,6 +483,16 @@ config SENSORS_BT1_PVT_ALARMS
the data conversion will be periodically performed and the data will be
saved in the internal driver cache.
+config SENSORS_CGBC
+ tristate "Congatec Board Controller Sensors"
+ depends on MFD_CGBC
+ help
+ Enable sensors support for the Congatec Board Controller. It has
+ temperature, voltage, current and fan sensors.
+
+ This driver can also be built as a module. If so, the module will be
+ called cgbc-hwmon.
+
config SENSORS_CHIPCAP2
tristate "Amphenol ChipCap 2 relative humidity and temperature sensor"
depends on I2C
@@ -789,6 +819,17 @@ config SENSORS_HS3001
This driver can also be built as a module. If so, the module
will be called hs3001.
+config SENSORS_HTU31
+ tristate "Measurement Specialties HTU31 humidity and temperature sensor"
+ depends on I2C
+ select CRC8
+ help
+ If you say yes here you get support for the HTU31 humidity
+ and temperature sensors.
+
+ This driver can also be built as a module. If so, the module
+ will be called htu31.
+
config SENSORS_IBMAEM
tristate "IBM Active Energy Manager temperature/power sensors and control"
select IPMI_SI
@@ -1287,6 +1328,15 @@ config SENSORS_MAX31790
This driver can also be built as a module. If so, the module
will be called max31790.
+config SENSORS_MAX77705
+ tristate "MAX77705 current and voltage sensor"
+ depends on MFD_MAX77705
+ help
+ If you say yes here you get support for MAX77705 sensors connected with I2C.
+
+ This driver can also be built as a module. If so, the module
+ will be called max77705-hwmon.
+
config SENSORS_MC34VR500
tristate "NXP MC34VR500 hardware monitoring driver"
depends on I2C
@@ -1519,7 +1569,7 @@ config SENSORS_LM90
MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695,
MAX6696,
ON Semiconductor NCT1008, NCT210, NCT72, NCT214, NCT218,
- Winbond/Nuvoton W83L771W/G/AWG/ASG,
+ Winbond/Nuvoton W83L771W/G/AWG/ASG, NCT7716, NCT7717 and NCT7718,
Philips NE1618, SA56004, GMT G781, Texas Instruments TMP451 and TMP461
sensor chips.
@@ -1625,7 +1675,7 @@ config SENSORS_NTC_THERMISTOR
B57891S0103 from EPCOS.
This driver can also be built as a module. If so, the module
- will be called ntc-thermistor.
+ will be called ntc_thermistor.
config SENSORS_NCT6683
tristate "Nuvoton NCT6683D"
@@ -1774,17 +1824,6 @@ config SENSORS_NZXT_SMART2
source "drivers/hwmon/occ/Kconfig"
-config SENSORS_OXP
- tristate "OneXPlayer EC fan control"
- depends on ACPI_EC
- depends on X86
- help
- If you say yes here you get support for fan readings and control over
- OneXPlayer handheld devices. Only OneXPlayer mini AMD handheld variant
- boards are supported.
-
- Can also be built as a module. In that case it will be called oxp-sensors.
-
config SENSORS_PCF8591
tristate "Philips PCF8591 ADC/DAC"
depends on I2C
@@ -1866,16 +1905,6 @@ config SENSORS_SBTSI
This driver can also be built as a module. If so, the module will
be called sbtsi_temp.
-config SENSORS_SBRMI
- tristate "Emulated SB-RMI sensor"
- depends on I2C
- help
- If you say yes here you get support for emulated RMI
- sensors on AMD SoCs with APML interface connected to a BMC device.
-
- This driver can also be built as a module. If so, the module will
- be called sbrmi.
-
config SENSORS_SHT15
tristate "Sensiron humidity and temperature sensors. SHT15 and compat."
depends on GPIOLIB || COMPILE_TEST
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index b7ef0f0562d3..48e5866c0c9a 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -59,6 +59,7 @@ obj-$(CONFIG_SENSORS_ASUS_ROG_RYUJIN) += asus_rog_ryujin.o
obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
obj-$(CONFIG_SENSORS_AXI_FAN_CONTROL) += axi-fan-control.o
obj-$(CONFIG_SENSORS_BT1_PVT) += bt1-pvt.o
+obj-$(CONFIG_SENSORS_CGBC) += cgbc-hwmon.o
obj-$(CONFIG_SENSORS_CHIPCAP2) += chipcap2.o
obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
obj-$(CONFIG_SENSORS_CORSAIR_CPRO) += corsair-cpro.o
@@ -91,6 +92,7 @@ obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o
obj-$(CONFIG_SENSORS_GXP_FAN_CTRL) += gxp-fan-ctrl.o
obj-$(CONFIG_SENSORS_HIH6130) += hih6130.o
obj-$(CONFIG_SENSORS_HS3001) += hs3001.o
+obj-$(CONFIG_SENSORS_HTU31) += htu31.o
obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o
obj-$(CONFIG_SENSORS_I5500) += i5500_temp.o
obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o
@@ -108,6 +110,8 @@ obj-$(CONFIG_SENSORS_IT87) += it87.o
obj-$(CONFIG_SENSORS_JC42) += jc42.o
obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o
obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o
+obj-$(CONFIG_SENSORS_KBATT) += kbatt.o
+obj-$(CONFIG_SENSORS_KFAN) += kfan.o
obj-$(CONFIG_SENSORS_LAN966X) += lan966x-hwmon.o
obj-$(CONFIG_SENSORS_LENOVO_EC) += lenovo-ec-sensors.o
obj-$(CONFIG_SENSORS_LINEAGE) += lineage-pem.o
@@ -159,6 +163,7 @@ obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
obj-$(CONFIG_SENSORS_MAX6697) += max6697.o
obj-$(CONFIG_SENSORS_MAX31790) += max31790.o
obj-$(CONFIG_MAX31827) += max31827.o
+obj-$(CONFIG_SENSORS_MAX77705) += max77705-hwmon.o
obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
obj-$(CONFIG_SENSORS_MC34VR500) += mc34vr500.o
obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
@@ -181,7 +186,6 @@ obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
obj-$(CONFIG_SENSORS_NZXT_KRAKEN2) += nzxt-kraken2.o
obj-$(CONFIG_SENSORS_NZXT_KRAKEN3) += nzxt-kraken3.o
obj-$(CONFIG_SENSORS_NZXT_SMART2) += nzxt-smart2.o
-obj-$(CONFIG_SENSORS_OXP) += oxp-sensors.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c
index 44afb07409a4..29ccdc2fb7ff 100644
--- a/drivers/hwmon/acpi_power_meter.c
+++ b/drivers/hwmon/acpi_power_meter.c
@@ -87,25 +87,14 @@ struct acpi_power_meter_resource {
bool power_alarm;
int sensors_valid;
unsigned long sensors_last_updated;
- struct sensor_device_attribute sensors[NUM_SENSORS];
- int num_sensors;
+#define POWER_METER_TRIP_AVERAGE_MIN_IDX 0
+#define POWER_METER_TRIP_AVERAGE_MAX_IDX 1
s64 trip[2];
int num_domain_devices;
struct acpi_device **domain_devices;
struct kobject *holders_dir;
};
-struct sensor_template {
- char *label;
- ssize_t (*show)(struct device *dev,
- struct device_attribute *devattr,
- char *buf);
- ssize_t (*set)(struct device *dev,
- struct device_attribute *devattr,
- const char *buf, size_t count);
- int index;
-};
-
/* Averaging interval */
static int update_avg_interval(struct acpi_power_meter_resource *resource)
{
@@ -124,62 +113,6 @@ static int update_avg_interval(struct acpi_power_meter_resource *resource)
return 0;
}
-static ssize_t show_avg_interval(struct device *dev,
- struct device_attribute *devattr,
- char *buf)
-{
- struct acpi_device *acpi_dev = to_acpi_device(dev);
- struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
-
- mutex_lock(&resource->lock);
- update_avg_interval(resource);
- mutex_unlock(&resource->lock);
-
- return sprintf(buf, "%llu\n", resource->avg_interval);
-}
-
-static ssize_t set_avg_interval(struct device *dev,
- struct device_attribute *devattr,
- const char *buf, size_t count)
-{
- struct acpi_device *acpi_dev = to_acpi_device(dev);
- struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
- union acpi_object arg0 = { ACPI_TYPE_INTEGER };
- struct acpi_object_list args = { 1, &arg0 };
- int res;
- unsigned long temp;
- unsigned long long data;
- acpi_status status;
-
- res = kstrtoul(buf, 10, &temp);
- if (res)
- return res;
-
- if (temp > resource->caps.max_avg_interval ||
- temp < resource->caps.min_avg_interval)
- return -EINVAL;
- arg0.integer.value = temp;
-
- mutex_lock(&resource->lock);
- status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PAI",
- &args, &data);
- if (ACPI_SUCCESS(status))
- resource->avg_interval = temp;
- mutex_unlock(&resource->lock);
-
- if (ACPI_FAILURE(status)) {
- acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_PAI",
- status);
- return -EINVAL;
- }
-
- /* _PAI returns 0 on success, nonzero otherwise */
- if (data)
- return -EINVAL;
-
- return count;
-}
-
/* Cap functions */
static int update_cap(struct acpi_power_meter_resource *resource)
{
@@ -198,61 +131,6 @@ static int update_cap(struct acpi_power_meter_resource *resource)
return 0;
}
-static ssize_t show_cap(struct device *dev,
- struct device_attribute *devattr,
- char *buf)
-{
- struct acpi_device *acpi_dev = to_acpi_device(dev);
- struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
-
- mutex_lock(&resource->lock);
- update_cap(resource);
- mutex_unlock(&resource->lock);
-
- return sprintf(buf, "%llu\n", resource->cap * 1000);
-}
-
-static ssize_t set_cap(struct device *dev, struct device_attribute *devattr,
- const char *buf, size_t count)
-{
- struct acpi_device *acpi_dev = to_acpi_device(dev);
- struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
- union acpi_object arg0 = { ACPI_TYPE_INTEGER };
- struct acpi_object_list args = { 1, &arg0 };
- int res;
- unsigned long temp;
- unsigned long long data;
- acpi_status status;
-
- res = kstrtoul(buf, 10, &temp);
- if (res)
- return res;
-
- temp = DIV_ROUND_CLOSEST(temp, 1000);
- if (temp > resource->caps.max_cap || temp < resource->caps.min_cap)
- return -EINVAL;
- arg0.integer.value = temp;
-
- mutex_lock(&resource->lock);
- status = acpi_evaluate_integer(resource->acpi_dev->handle, "_SHL",
- &args, &data);
- if (ACPI_SUCCESS(status))
- resource->cap = temp;
- mutex_unlock(&resource->lock);
-
- if (ACPI_FAILURE(status)) {
- acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_SHL",
- status);
- return -EINVAL;
- }
-
- /* _SHL returns 0 on success, nonzero otherwise */
- if (data)
- return -EINVAL;
-
- return count;
-}
-
/* Power meter trip points */
static int set_acpi_trip(struct acpi_power_meter_resource *resource)
{
@@ -287,34 +165,6 @@ static int set_acpi_trip(struct acpi_power_meter_resource *resource)
return 0;
}
-static ssize_t set_trip(struct device *dev, struct device_attribute *devattr,
- const char *buf, size_t count)
-{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct acpi_device *acpi_dev = to_acpi_device(dev);
- struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
- unsigned long temp, trip_bk;
- int res;
-
- res = kstrtoul(buf, 10, &temp);
- if (res)
- return res;
-
- temp = DIV_ROUND_CLOSEST(temp, 1000);
-
- guard(mutex)(&resource->lock);
-
- trip_bk = resource->trip[attr->index - 7];
- resource->trip[attr->index - 7] = temp;
- res = set_acpi_trip(resource);
- if (res) {
- resource->trip[attr->index - 7] = trip_bk;
- return res;
- }
-
- return count;
-}
-
/* Power meter */
static int update_meter(struct acpi_power_meter_resource *resource)
{
@@ -341,202 +191,6 @@ static int update_meter(struct acpi_power_meter_resource *resource)
return 0;
}
-static ssize_t show_power(struct device *dev,
- struct device_attribute *devattr,
- char *buf)
-{
- struct acpi_device *acpi_dev = to_acpi_device(dev);
- struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
-
- mutex_lock(&resource->lock);
- update_meter(resource);
- mutex_unlock(&resource->lock);
-
- if (resource->power == UNKNOWN_POWER)
- return -ENODATA;
-
- return sprintf(buf, "%llu\n", resource->power * 1000);
-}
-
-/* Miscellaneous */
-static ssize_t show_str(struct device *dev,
- struct device_attribute *devattr,
- char *buf)
-{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct acpi_device *acpi_dev = to_acpi_device(dev);
- struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
- acpi_string val;
- int ret;
-
- mutex_lock(&resource->lock);
- switch (attr->index) {
- case 0:
- val = resource->model_number;
- break;
- case 1:
- val = resource->serial_number;
- break;
- case 2:
- val = resource->oem_info;
- break;
- default:
- WARN(1, "Implementation error: unexpected attribute index %d\n",
- attr->index);
- val = "";
- break;
- }
- ret = sprintf(buf, "%s\n", val);
- mutex_unlock(&resource->lock);
- return ret;
-}
-
-static ssize_t show_val(struct device *dev,
- struct device_attribute *devattr,
- char *buf)
-{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct acpi_device *acpi_dev = to_acpi_device(dev);
- struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
- u64 val = 0;
- int ret;
-
- guard(mutex)(&resource->lock);
-
- switch (attr->index) {
- case 0:
- val = resource->caps.min_avg_interval;
- break;
- case 1:
- val = resource->caps.max_avg_interval;
- break;
- case 2:
- val = resource->caps.min_cap * 1000;
- break;
- case 3:
- val = resource->caps.max_cap * 1000;
- break;
- case 4:
- if (resource->caps.hysteresis == UNKNOWN_HYSTERESIS)
- return sprintf(buf, "unknown\n");
-
- val = resource->caps.hysteresis * 1000;
- break;
- case 5:
- if (resource->caps.flags & POWER_METER_IS_BATTERY)
- val = 1;
- else
- val = 0;
- break;
- case 6:
- ret = update_meter(resource);
- if (ret)
- return ret;
- /* 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;
- }
- val = resource->power_alarm || resource->power > resource->cap;
- resource->power_alarm = resource->power > resource->cap;
- break;
- case 7:
- case 8:
- if (resource->trip[attr->index - 7] < 0)
- return sprintf(buf, "unknown\n");
-
- val = resource->trip[attr->index - 7] * 1000;
- break;
- default:
- WARN(1, "Implementation error: unexpected attribute index %d\n",
- attr->index);
- break;
- }
-
- return sprintf(buf, "%llu\n", val);
-}
-
-static ssize_t show_accuracy(struct device *dev,
- struct device_attribute *devattr,
- char *buf)
-{
- struct acpi_device *acpi_dev = to_acpi_device(dev);
- struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
- unsigned int acc = resource->caps.accuracy;
-
- return sprintf(buf, "%u.%u%%\n", acc / 1000, acc % 1000);
-}
-
-static ssize_t show_name(struct device *dev,
- struct device_attribute *devattr,
- char *buf)
-{
- return sprintf(buf, "%s\n", ACPI_POWER_METER_NAME);
-}
-
-#define RO_SENSOR_TEMPLATE(_label, _show, _index) \
- { \
- .label = _label, \
- .show = _show, \
- .index = _index, \
- }
-
-#define RW_SENSOR_TEMPLATE(_label, _show, _set, _index) \
- { \
- .label = _label, \
- .show = _show, \
- .set = _set, \
- .index = _index, \
- }
-
-/* Sensor descriptions. If you add a sensor, update NUM_SENSORS above! */
-static struct sensor_template meter_attrs[] = {
- RO_SENSOR_TEMPLATE(POWER_AVERAGE_NAME, show_power, 0),
- RO_SENSOR_TEMPLATE("power1_accuracy", show_accuracy, 0),
- RO_SENSOR_TEMPLATE("power1_average_interval_min", show_val, 0),
- RO_SENSOR_TEMPLATE("power1_average_interval_max", show_val, 1),
- RO_SENSOR_TEMPLATE("power1_is_battery", show_val, 5),
- RW_SENSOR_TEMPLATE(POWER_AVG_INTERVAL_NAME, show_avg_interval,
- set_avg_interval, 0),
- {},
-};
-
-static struct sensor_template misc_cap_attrs[] = {
- RO_SENSOR_TEMPLATE("power1_cap_min", show_val, 2),
- RO_SENSOR_TEMPLATE("power1_cap_max", show_val, 3),
- RO_SENSOR_TEMPLATE("power1_cap_hyst", show_val, 4),
- RO_SENSOR_TEMPLATE(POWER_ALARM_NAME, show_val, 6),
- {},
-};
-
-static struct sensor_template ro_cap_attrs[] = {
- RO_SENSOR_TEMPLATE(POWER_CAP_NAME, show_cap, 0),
- {},
-};
-
-static struct sensor_template rw_cap_attrs[] = {
- RW_SENSOR_TEMPLATE(POWER_CAP_NAME, show_cap, set_cap, 0),
- {},
-};
-
-static struct sensor_template trip_attrs[] = {
- RW_SENSOR_TEMPLATE("power1_average_min", show_val, set_trip, 7),
- RW_SENSOR_TEMPLATE("power1_average_max", show_val, set_trip, 8),
- {},
-};
-
-static struct sensor_template misc_attrs[] = {
- RO_SENSOR_TEMPLATE("name", show_name, 0),
- RO_SENSOR_TEMPLATE("power1_model_number", show_str, 0),
- RO_SENSOR_TEMPLATE("power1_oem_info", show_str, 2),
- RO_SENSOR_TEMPLATE("power1_serial_number", show_str, 1),
- {},
-};
-
-#undef RO_SENSOR_TEMPLATE
-#undef RW_SENSOR_TEMPLATE
-
/* Read power domain data */
static void remove_domain_devices(struct acpi_power_meter_resource *resource)
{
@@ -638,109 +292,434 @@ end:
return res;
}
-/* Registration and deregistration */
-static int register_attrs(struct acpi_power_meter_resource *resource,
- struct sensor_template *attrs)
+static int set_trip(struct acpi_power_meter_resource *resource, u16 trip_idx,
+ unsigned long trip)
{
- struct device *dev = &resource->acpi_dev->dev;
- struct sensor_device_attribute *sensors =
- &resource->sensors[resource->num_sensors];
- int res = 0;
+ unsigned long trip_bk;
+ int ret;
- while (attrs->label) {
- sensors->dev_attr.attr.name = attrs->label;
- sensors->dev_attr.attr.mode = 0444;
- sensors->dev_attr.show = attrs->show;
- sensors->index = attrs->index;
+ trip = DIV_ROUND_CLOSEST(trip, 1000);
+ trip_bk = resource->trip[trip_idx];
- if (attrs->set) {
- sensors->dev_attr.attr.mode |= 0200;
- sensors->dev_attr.store = attrs->set;
- }
+ resource->trip[trip_idx] = trip;
+ ret = set_acpi_trip(resource);
+ if (ret) {
+ dev_err(&resource->acpi_dev->dev, "set %s failed.\n",
+ (trip_idx == POWER_METER_TRIP_AVERAGE_MIN_IDX) ?
+ "power1_average_min" : "power1_average_max");
+ resource->trip[trip_idx] = trip_bk;
+ }
- sysfs_attr_init(&sensors->dev_attr.attr);
- res = device_create_file(dev, &sensors->dev_attr);
- if (res) {
- sensors->dev_attr.attr.name = NULL;
- goto error;
- }
- sensors++;
- resource->num_sensors++;
- attrs++;
+ return ret;
+}
+
+static int set_cap(struct acpi_power_meter_resource *resource,
+ unsigned long cap)
+{
+ union acpi_object arg0 = { ACPI_TYPE_INTEGER };
+ struct acpi_object_list args = { 1, &arg0 };
+ unsigned long long data;
+ acpi_status status;
+
+ cap = DIV_ROUND_CLOSEST(cap, 1000);
+ if (cap > resource->caps.max_cap || cap < resource->caps.min_cap)
+ return -EINVAL;
+
+ arg0.integer.value = cap;
+ status = acpi_evaluate_integer(resource->acpi_dev->handle, "_SHL",
+ &args, &data);
+ if (ACPI_FAILURE(status)) {
+ acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_SHL",
+ status);
+ return -EINVAL;
}
+ resource->cap = cap;
-error:
- return res;
+ /* _SHL returns 0 on success, nonzero otherwise */
+ if (data)
+ return -EINVAL;
+
+ return 0;
}
-static void remove_attrs(struct acpi_power_meter_resource *resource)
+static int set_avg_interval(struct acpi_power_meter_resource *resource,
+ unsigned long val)
{
- int i;
+ union acpi_object arg0 = { ACPI_TYPE_INTEGER };
+ struct acpi_object_list args = { 1, &arg0 };
+ unsigned long long data;
+ acpi_status status;
- for (i = 0; i < resource->num_sensors; i++) {
- if (!resource->sensors[i].dev_attr.attr.name)
- continue;
- device_remove_file(&resource->acpi_dev->dev,
- &resource->sensors[i].dev_attr);
+ if (val > resource->caps.max_avg_interval ||
+ val < resource->caps.min_avg_interval)
+ return -EINVAL;
+
+ arg0.integer.value = val;
+ status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PAI",
+ &args, &data);
+ if (ACPI_FAILURE(status)) {
+ acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_PAI",
+ status);
+ return -EINVAL;
}
+ resource->avg_interval = val;
- remove_domain_devices(resource);
+ /* _PAI returns 0 on success, nonzero otherwise */
+ if (data)
+ return -EINVAL;
- resource->num_sensors = 0;
+ return 0;
}
-static int setup_attrs(struct acpi_power_meter_resource *resource)
+static int get_power_alarm_state(struct acpi_power_meter_resource *resource,
+ long *val)
{
- int res = 0;
+ int ret;
- /* _PMD method is optional. */
- res = read_domain_devices(resource);
- if (res && res != -ENODEV)
- return res;
+ ret = update_meter(resource);
+ if (ret)
+ return ret;
- if (resource->caps.flags & POWER_METER_CAN_MEASURE) {
- res = register_attrs(resource, meter_attrs);
- if (res)
- goto error;
+ /* 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;
}
- if (resource->caps.flags & POWER_METER_CAN_CAP) {
- if (!can_cap_in_hardware()) {
- dev_warn(&resource->acpi_dev->dev,
- "Ignoring unsafe software power cap!\n");
- goto skip_unsafe_cap;
+ return 0;
+}
+
+static umode_t power_meter_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ const struct acpi_power_meter_resource *res = data;
+
+ if (type != hwmon_power)
+ return 0;
+
+ switch (attr) {
+ case hwmon_power_average:
+ case hwmon_power_average_interval_min:
+ case hwmon_power_average_interval_max:
+ if (res->caps.flags & POWER_METER_CAN_MEASURE)
+ return 0444;
+ break;
+ case hwmon_power_average_interval:
+ if (res->caps.flags & POWER_METER_CAN_MEASURE)
+ return 0644;
+ break;
+ case hwmon_power_cap_min:
+ case hwmon_power_cap_max:
+ case hwmon_power_alarm:
+ if (res->caps.flags & POWER_METER_CAN_CAP && can_cap_in_hardware())
+ return 0444;
+ break;
+ case hwmon_power_cap:
+ if (res->caps.flags & POWER_METER_CAN_CAP && can_cap_in_hardware()) {
+ if (res->caps.configurable_cap)
+ return 0644;
+ else
+ return 0444;
}
+ break;
+ default:
+ break;
+ }
- if (resource->caps.configurable_cap)
- res = register_attrs(resource, rw_cap_attrs);
- else
- res = register_attrs(resource, ro_cap_attrs);
+ return 0;
+}
- if (res)
- goto error;
+static int power_meter_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
+ int ret = 0;
- res = register_attrs(resource, misc_cap_attrs);
- if (res)
- goto error;
+ if (type != hwmon_power)
+ return -EINVAL;
+
+ guard(mutex)(&res->lock);
+
+ switch (attr) {
+ case hwmon_power_average:
+ ret = update_meter(res);
+ if (ret)
+ return ret;
+ if (res->power == UNKNOWN_POWER)
+ return -ENODATA;
+ *val = res->power * 1000;
+ break;
+ case hwmon_power_average_interval_min:
+ *val = res->caps.min_avg_interval;
+ break;
+ case hwmon_power_average_interval_max:
+ *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;
+ break;
+ case hwmon_power_cap_min:
+ *val = res->caps.min_cap * 1000;
+ break;
+ case hwmon_power_cap_max:
+ *val = res->caps.max_cap * 1000;
+ break;
+ case hwmon_power_alarm:
+ ret = get_power_alarm_state(res, val);
+ if (ret)
+ return ret;
+ break;
+ case hwmon_power_cap:
+ ret = update_cap(res);
+ if (ret)
+ return ret;
+ *val = res->cap * 1000;
+ break;
+ default:
+ break;
}
-skip_unsafe_cap:
- if (resource->caps.flags & POWER_METER_CAN_TRIP) {
- res = register_attrs(resource, trip_attrs);
- if (res)
- goto error;
+ return 0;
+}
+
+static int power_meter_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
+ int ret;
+
+ if (type != hwmon_power)
+ return -EINVAL;
+
+ guard(mutex)(&res->lock);
+ switch (attr) {
+ case hwmon_power_cap:
+ ret = set_cap(res, val);
+ break;
+ case hwmon_power_average_interval:
+ ret = set_avg_interval(res, val);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
}
- res = register_attrs(resource, misc_attrs);
- if (res)
- goto error;
+ return ret;
+}
- return res;
-error:
- remove_attrs(resource);
- return res;
+static const struct hwmon_channel_info * const power_meter_info[] = {
+ HWMON_CHANNEL_INFO(power, HWMON_P_AVERAGE |
+ HWMON_P_AVERAGE_INTERVAL | HWMON_P_AVERAGE_INTERVAL_MIN |
+ HWMON_P_AVERAGE_INTERVAL_MAX | HWMON_P_CAP | HWMON_P_CAP_MIN |
+ HWMON_P_CAP_MAX | HWMON_P_ALARM),
+ NULL
+};
+
+static const struct hwmon_ops power_meter_ops = {
+ .is_visible = power_meter_is_visible,
+ .read = power_meter_read,
+ .write = power_meter_write,
+};
+
+static const struct hwmon_chip_info power_meter_chip_info = {
+ .ops = &power_meter_ops,
+ .info = power_meter_info,
+};
+
+static ssize_t power1_average_max_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
+ unsigned long trip;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &trip);
+ if (ret)
+ return ret;
+
+ mutex_lock(&res->lock);
+ ret = set_trip(res, POWER_METER_TRIP_AVERAGE_MAX_IDX, trip);
+ mutex_unlock(&res->lock);
+
+ return ret == 0 ? count : ret;
+}
+
+static ssize_t power1_average_min_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
+ unsigned long trip;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &trip);
+ if (ret)
+ return ret;
+
+ mutex_lock(&res->lock);
+ ret = set_trip(res, POWER_METER_TRIP_AVERAGE_MIN_IDX, trip);
+ mutex_unlock(&res->lock);
+
+ return ret == 0 ? count : ret;
+}
+
+static ssize_t power1_average_min_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
+
+ if (res->trip[POWER_METER_TRIP_AVERAGE_MIN_IDX] < 0)
+ return sysfs_emit(buf, "unknown\n");
+
+ return sysfs_emit(buf, "%lld\n",
+ res->trip[POWER_METER_TRIP_AVERAGE_MIN_IDX] * 1000);
+}
+
+static ssize_t power1_average_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
+
+ if (res->trip[POWER_METER_TRIP_AVERAGE_MAX_IDX] < 0)
+ return sysfs_emit(buf, "unknown\n");
+
+ return sysfs_emit(buf, "%lld\n",
+ res->trip[POWER_METER_TRIP_AVERAGE_MAX_IDX] * 1000);
+}
+
+static ssize_t power1_cap_hyst_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
+
+ if (res->caps.hysteresis == UNKNOWN_HYSTERESIS)
+ return sysfs_emit(buf, "unknown\n");
+
+ return sysfs_emit(buf, "%llu\n", res->caps.hysteresis * 1000);
+}
+
+static ssize_t power1_accuracy_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
+ unsigned int acc = res->caps.accuracy;
+
+ return sysfs_emit(buf, "%u.%u%%\n", acc / 1000, acc % 1000);
+}
+
+static ssize_t power1_is_battery_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%u\n",
+ res->caps.flags & POWER_METER_IS_BATTERY ? 1 : 0);
+}
+
+static ssize_t power1_model_number_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%s\n", res->model_number);
+}
+
+static ssize_t power1_oem_info_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%s\n", res->oem_info);
+}
+
+static ssize_t power1_serial_number_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%s\n", res->serial_number);
}
+/* depend on POWER_METER_CAN_TRIP */
+static DEVICE_ATTR_RW(power1_average_max);
+static DEVICE_ATTR_RW(power1_average_min);
+
+/* depend on POWER_METER_CAN_CAP */
+static DEVICE_ATTR_RO(power1_cap_hyst);
+
+/* depend on POWER_METER_CAN_MEASURE */
+static DEVICE_ATTR_RO(power1_accuracy);
+static DEVICE_ATTR_RO(power1_is_battery);
+
+static DEVICE_ATTR_RO(power1_model_number);
+static DEVICE_ATTR_RO(power1_oem_info);
+static DEVICE_ATTR_RO(power1_serial_number);
+
+static umode_t power_extra_is_visible(struct kobject *kobj,
+ struct attribute *attr, int idx)
+{
+ struct device *dev = kobj_to_dev(kobj);
+ struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
+
+ if (attr == &dev_attr_power1_is_battery.attr ||
+ attr == &dev_attr_power1_accuracy.attr) {
+ if ((res->caps.flags & POWER_METER_CAN_MEASURE) == 0)
+ return 0;
+ }
+
+ if (attr == &dev_attr_power1_cap_hyst.attr) {
+ if ((res->caps.flags & POWER_METER_CAN_CAP) == 0) {
+ return 0;
+ } else if (!can_cap_in_hardware()) {
+ dev_warn(&res->acpi_dev->dev,
+ "Ignoring unsafe software power cap!\n");
+ return 0;
+ }
+ }
+
+ if (attr == &dev_attr_power1_average_max.attr ||
+ attr == &dev_attr_power1_average_min.attr) {
+ if ((res->caps.flags & POWER_METER_CAN_TRIP) == 0)
+ return 0;
+ }
+
+ return attr->mode;
+}
+
+static struct attribute *power_extra_attrs[] = {
+ &dev_attr_power1_average_max.attr,
+ &dev_attr_power1_average_min.attr,
+ &dev_attr_power1_cap_hyst.attr,
+ &dev_attr_power1_accuracy.attr,
+ &dev_attr_power1_is_battery.attr,
+ &dev_attr_power1_model_number.attr,
+ &dev_attr_power1_oem_info.attr,
+ &dev_attr_power1_serial_number.attr,
+ NULL
+};
+
+static const struct attribute_group power_extra_group = {
+ .attrs = power_extra_attrs,
+ .is_visible = power_extra_is_visible,
+};
+
+__ATTRIBUTE_GROUPS(power_extra);
+
static void free_capabilities(struct acpi_power_meter_resource *resource)
{
acpi_string *str;
@@ -848,13 +827,23 @@ static void acpi_power_meter_notify(struct acpi_device *device, u32 event)
case METER_NOTIFY_CONFIG:
mutex_lock(&resource->lock);
free_capabilities(resource);
+ remove_domain_devices(resource);
+ hwmon_device_unregister(resource->hwmon_dev);
res = read_capabilities(resource);
- mutex_unlock(&resource->lock);
if (res)
- break;
-
- remove_attrs(resource);
- setup_attrs(resource);
+ dev_err_once(&device->dev, "read capabilities failed.\n");
+ res = read_domain_devices(resource);
+ if (res && res != -ENODEV)
+ dev_err_once(&device->dev, "read domain devices failed.\n");
+ resource->hwmon_dev =
+ hwmon_device_register_with_info(&device->dev,
+ ACPI_POWER_METER_NAME,
+ resource,
+ &power_meter_chip_info,
+ power_extra_groups);
+ if (IS_ERR(resource->hwmon_dev))
+ dev_err_once(&device->dev, "register hwmon device failed.\n");
+ mutex_unlock(&resource->lock);
break;
case METER_NOTIFY_TRIP:
sysfs_notify(&device->dev.kobj, NULL, POWER_AVERAGE_NAME);
@@ -916,7 +905,7 @@ static int acpi_power_meter_add(struct acpi_device *device)
struct acpi_device *ipi_device = acpi_dev_get_first_match_dev("IPI0001", NULL, -1);
if (ipi_device && acpi_wait_for_acpi_ipmi())
- dev_warn(&device->dev, "Waiting for ACPI IPMI timeout");
+ dev_warn(&device->dev, "Waiting for ACPI IPMI timeout");
acpi_dev_put(ipi_device);
}
#endif
@@ -928,11 +917,16 @@ static int acpi_power_meter_add(struct acpi_device *device)
resource->trip[0] = -1;
resource->trip[1] = -1;
- res = setup_attrs(resource);
- if (res)
+ /* _PMD method is optional. */
+ res = read_domain_devices(resource);
+ if (res && res != -ENODEV)
goto exit_free_capability;
- resource->hwmon_dev = hwmon_device_register(&device->dev);
+ resource->hwmon_dev =
+ hwmon_device_register_with_info(&device->dev,
+ ACPI_POWER_METER_NAME, resource,
+ &power_meter_chip_info,
+ power_extra_groups);
if (IS_ERR(resource->hwmon_dev)) {
res = PTR_ERR(resource->hwmon_dev);
goto exit_remove;
@@ -942,7 +936,7 @@ static int acpi_power_meter_add(struct acpi_device *device)
goto exit;
exit_remove:
- remove_attrs(resource);
+ remove_domain_devices(resource);
exit_free_capability:
free_capabilities(resource);
exit_free:
@@ -961,7 +955,7 @@ static void acpi_power_meter_remove(struct acpi_device *device)
resource = acpi_driver_data(device);
hwmon_device_unregister(resource->hwmon_dev);
- remove_attrs(resource);
+ remove_domain_devices(resource);
free_capabilities(resource);
kfree(resource);
diff --git a/drivers/hwmon/ad7314.c b/drivers/hwmon/ad7314.c
index 7802bbf5f958..59424103f634 100644
--- a/drivers/hwmon/ad7314.c
+++ b/drivers/hwmon/ad7314.c
@@ -22,11 +22,13 @@
*/
#define AD7314_TEMP_MASK 0x7FE0
#define AD7314_TEMP_SHIFT 5
+#define AD7314_LEADING_ZEROS_MASK BIT(15)
/*
* ADT7301 and ADT7302 temperature masks
*/
#define ADT7301_TEMP_MASK 0x3FFF
+#define ADT7301_LEADING_ZEROS_MASK (BIT(15) | BIT(14))
enum ad7314_variant {
adt7301,
@@ -65,12 +67,20 @@ static ssize_t ad7314_temperature_show(struct device *dev,
return ret;
switch (spi_get_device_id(chip->spi_dev)->driver_data) {
case ad7314:
+ if (ret & AD7314_LEADING_ZEROS_MASK) {
+ /* Invalid read-out, leading zero part is missing */
+ return -EIO;
+ }
data = (ret & AD7314_TEMP_MASK) >> AD7314_TEMP_SHIFT;
data = sign_extend32(data, 9);
return sprintf(buf, "%d\n", 250 * data);
case adt7301:
case adt7302:
+ if (ret & ADT7301_LEADING_ZEROS_MASK) {
+ /* Invalid read-out, leading zero part is missing */
+ return -EIO;
+ }
/*
* Documented as a 13 bit twos complement register
* with a sign bit - which is a 14 bit 2's complement
diff --git a/drivers/hwmon/aht10.c b/drivers/hwmon/aht10.c
index 312ef3e98754..d1c55e2eb479 100644
--- a/drivers/hwmon/aht10.c
+++ b/drivers/hwmon/aht10.c
@@ -94,7 +94,7 @@ struct aht10_data {
unsigned int meas_size;
};
-/**
+/*
* aht10_init() - Initialize an AHT10/AHT20 chip
* @data: the data associated with this AHT10/AHT20 chip
* Return: 0 if successful, 1 if not
@@ -124,7 +124,7 @@ static int aht10_init(struct aht10_data *data)
return 0;
}
-/**
+/*
* aht10_polltime_expired() - check if the minimum poll interval has
* expired
* @data: the data containing the time to compare
@@ -140,7 +140,7 @@ static int aht10_polltime_expired(struct aht10_data *data)
DECLARE_CRC8_TABLE(crc8_table);
-/**
+/*
* crc8_check() - check crc of the sensor's measurements
* @raw_data: data frame received from sensor(including crc as the last byte)
* @count: size of the data frame
@@ -155,7 +155,7 @@ static int crc8_check(u8 *raw_data, int count)
return crc8(crc8_table, raw_data, count, CRC8_INIT_VALUE);
}
-/**
+/*
* aht10_read_values() - read and parse the raw data from the AHT10/AHT20
* @data: the struct aht10_data to use for the lock
* Return: 0 if successful, 1 if not
@@ -214,7 +214,7 @@ static int aht10_read_values(struct aht10_data *data)
return 0;
}
-/**
+/*
* aht10_interval_write() - store the given minimum poll interval.
* Return: 0 on success, -EINVAL if a value lower than the
* AHT10_MIN_POLL_INTERVAL is given
@@ -226,7 +226,7 @@ static ssize_t aht10_interval_write(struct aht10_data *data,
return 0;
}
-/**
+/*
* aht10_interval_read() - read the minimum poll interval
* in milliseconds
*/
@@ -237,7 +237,7 @@ static ssize_t aht10_interval_read(struct aht10_data *data,
return 0;
}
-/**
+/*
* aht10_temperature1_read() - read the temperature in millidegrees
*/
static int aht10_temperature1_read(struct aht10_data *data, long *val)
@@ -252,7 +252,7 @@ static int aht10_temperature1_read(struct aht10_data *data, long *val)
return 0;
}
-/**
+/*
* aht10_humidity1_read() - read the relative humidity in millipercent
*/
static int aht10_humidity1_read(struct aht10_data *data, long *val)
diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c
index 1e3c6acd8974..13a789cc85d2 100644
--- a/drivers/hwmon/amc6821.c
+++ b/drivers/hwmon/amc6821.c
@@ -23,9 +23,12 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_platform.h>
+#include <linux/pwm.h>
#include <linux/regmap.h>
#include <linux/slab.h>
+#include <dt-bindings/pwm/pwm.h>
+
/*
* Addresses to scan.
*/
@@ -37,7 +40,7 @@ static const unsigned short normal_i2c[] = {0x18, 0x19, 0x1a, 0x2c, 0x2d, 0x2e,
* Insmod parameters
*/
-static int pwminv; /*Inverted PWM output. */
+static int pwminv = -1; /*Inverted PWM output. */
module_param(pwminv, int, 0444);
static int init = 1; /*Power-on initialization.*/
@@ -845,9 +848,43 @@ static int amc6821_detect(struct i2c_client *client, struct i2c_board_info *info
return 0;
}
-static int amc6821_init_client(struct amc6821_data *data)
+static enum pwm_polarity amc6821_pwm_polarity(struct i2c_client *client)
+{
+ enum pwm_polarity polarity = PWM_POLARITY_NORMAL;
+ struct of_phandle_args args;
+ struct device_node *fan_np;
+
+ /*
+ * For backward compatibility, the pwminv module parameter takes
+ * always the precedence over any other device description
+ */
+ if (pwminv == 0)
+ return PWM_POLARITY_NORMAL;
+ if (pwminv > 0)
+ return PWM_POLARITY_INVERSED;
+
+ fan_np = of_get_child_by_name(client->dev.of_node, "fan");
+ if (!fan_np)
+ return PWM_POLARITY_NORMAL;
+
+ if (of_parse_phandle_with_args(fan_np, "pwms", "#pwm-cells", 0, &args))
+ goto out;
+ of_node_put(args.np);
+
+ if (args.args_count != 2)
+ goto out;
+
+ if (args.args[1] & PWM_POLARITY_INVERTED)
+ polarity = PWM_POLARITY_INVERSED;
+out:
+ of_node_put(fan_np);
+ return polarity;
+}
+
+static int amc6821_init_client(struct i2c_client *client, struct amc6821_data *data)
{
struct regmap *regmap = data->regmap;
+ u32 regval;
int err;
if (init) {
@@ -864,11 +901,14 @@ static int amc6821_init_client(struct amc6821_data *data)
if (err)
return err;
+ regval = AMC6821_CONF1_START;
+ if (amc6821_pwm_polarity(client) == PWM_POLARITY_INVERSED)
+ regval |= AMC6821_CONF1_PWMINV;
+
err = regmap_update_bits(regmap, AMC6821_REG_CONF1,
AMC6821_CONF1_THERMOVIE | AMC6821_CONF1_FANIE |
AMC6821_CONF1_START | AMC6821_CONF1_PWMINV,
- AMC6821_CONF1_START |
- (pwminv ? AMC6821_CONF1_PWMINV : 0));
+ regval);
if (err)
return err;
}
@@ -916,7 +956,7 @@ static int amc6821_probe(struct i2c_client *client)
"Failed to initialize regmap\n");
data->regmap = regmap;
- err = amc6821_init_client(data);
+ err = amc6821_init_client(client, data);
if (err)
return err;
diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c
index 43e54dc513da..e0a95197c71b 100644
--- a/drivers/hwmon/asus-ec-sensors.c
+++ b/drivers/hwmon/asus-ec-sensors.c
@@ -169,7 +169,11 @@ enum board_family {
family_intel_600_series
};
-/* All the known sensors for ASUS EC controllers */
+/*
+ * All the known sensors for ASUS EC controllers. These arrays have to be sorted
+ * by the full ((bank << 8) + index) register index (see asus_ec_block_read() as
+ * to why).
+ */
static const struct ec_sensor_info sensors_family_amd_400[] = {
[ec_sensor_temp_chipset] =
EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a),
@@ -183,10 +187,10 @@ static const struct ec_sensor_info sensors_family_amd_400[] = {
EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e),
[ec_sensor_in_cpu_core] =
EC_SENSOR("CPU Core", hwmon_in, 2, 0x00, 0xa2),
- [ec_sensor_fan_cpu_opt] =
- EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xbc),
[ec_sensor_fan_vrm_hs] =
EC_SENSOR("VRM HS", hwmon_fan, 2, 0x00, 0xb2),
+ [ec_sensor_fan_cpu_opt] =
+ EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xbc),
[ec_sensor_fan_chipset] =
/* no chipset fans in this generation */
EC_SENSOR("Chipset", hwmon_fan, 0, 0x00, 0x00),
@@ -194,10 +198,10 @@ static const struct ec_sensor_info sensors_family_amd_400[] = {
EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xb4),
[ec_sensor_curr_cpu] =
EC_SENSOR("CPU", hwmon_curr, 1, 0x00, 0xf4),
- [ec_sensor_temp_water_in] =
- EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x0d),
[ec_sensor_temp_water_out] =
EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x0b),
+ [ec_sensor_temp_water_in] =
+ EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x0d),
};
static const struct ec_sensor_info sensors_family_amd_500[] = {
@@ -239,19 +243,20 @@ static const struct ec_sensor_info sensors_family_amd_500[] = {
static const struct ec_sensor_info sensors_family_amd_600[] = {
[ec_sensor_temp_cpu] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x30),
- [ec_sensor_temp_cpu_package] = EC_SENSOR("CPU Package", hwmon_temp, 1, 0x00, 0x31),
+ [ec_sensor_temp_cpu_package] =
+ EC_SENSOR("CPU Package", hwmon_temp, 1, 0x00, 0x31),
[ec_sensor_temp_mb] =
EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x32),
[ec_sensor_temp_vrm] =
EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x33),
[ec_sensor_temp_t_sensor] =
EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x36),
+ [ec_sensor_fan_cpu_opt] =
+ EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0),
[ec_sensor_temp_water_in] =
EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00),
[ec_sensor_temp_water_out] =
EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01),
- [ec_sensor_fan_cpu_opt] =
- EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0),
};
static const struct ec_sensor_info sensors_family_intel_300[] = {
@@ -278,6 +283,14 @@ static const struct ec_sensor_info sensors_family_intel_600[] = {
[ec_sensor_temp_t_sensor] =
EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d),
[ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e),
+ [ec_sensor_fan_water_flow] =
+ EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbe),
+ [ec_sensor_temp_water_in] =
+ EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00),
+ [ec_sensor_temp_water_out] =
+ EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01),
+ [ec_sensor_temp_water_block_in] =
+ EC_SENSOR("Water_Block_In", hwmon_temp, 1, 0x01, 0x02),
};
/* Shortcuts for common combinations */
@@ -300,6 +313,15 @@ struct ec_board_info {
enum board_family family;
};
+static const struct ec_board_info board_info_maximus_vi_hero = {
+ .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
+ SENSOR_TEMP_T_SENSOR |
+ SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
+ SENSOR_FAN_CPU_OPT | SENSOR_FAN_WATER_FLOW,
+ .mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH,
+ .family = family_intel_300_series,
+};
+
static const struct ec_board_info board_info_prime_x470_pro = {
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
@@ -316,6 +338,14 @@ static const struct ec_board_info board_info_prime_x570_pro = {
.family = family_amd_500_series,
};
+static const struct ec_board_info board_info_prime_x670e_pro_wifi = {
+ .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE |
+ SENSOR_TEMP_MB | SENSOR_TEMP_VRM |
+ SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT,
+ .mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH,
+ .family = family_amd_600_series,
+};
+
static const struct ec_board_info board_info_pro_art_x570_creator_wifi = {
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM |
SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT |
@@ -394,6 +424,13 @@ static const struct ec_board_info board_info_maximus_xi_hero = {
.family = family_intel_300_series,
};
+static const struct ec_board_info board_info_maximus_z690_formula = {
+ .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
+ SENSOR_SET_TEMP_WATER | SENSOR_FAN_WATER_FLOW,
+ .mutex_path = ASUS_HW_ACCESS_MUTEX_RMTW_ASMX,
+ .family = family_intel_600_series,
+};
+
static const struct ec_board_info board_info_crosshair_viii_impact = {
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
@@ -499,10 +536,14 @@ static const struct ec_board_info board_info_tuf_gaming_x670e_plus = {
}
static const struct dmi_system_id dmi_table[] = {
+ DMI_EXACT_MATCH_ASUS_BOARD_NAME("MAXIMUS VI HERO",
+ &board_info_maximus_vi_hero),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X470-PRO",
&board_info_prime_x470_pro),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X570-PRO",
&board_info_prime_x570_pro),
+ DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X670E-PRO WIFI",
+ &board_info_prime_x670e_pro_wifi),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt X570-CREATOR WIFI",
&board_info_pro_art_x570_creator_wifi),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt X670E-CREATOR WIFI",
@@ -527,6 +568,8 @@ static const struct dmi_system_id dmi_table[] = {
&board_info_maximus_xi_hero),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS XI HERO (WI-FI)",
&board_info_maximus_xi_hero),
+ DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS Z690 FORMULA",
+ &board_info_maximus_z690_formula),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII IMPACT",
&board_info_crosshair_viii_impact),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-E GAMING",
@@ -923,6 +966,10 @@ static int asus_ec_hwmon_read_string(struct device *dev,
{
struct ec_sensors_data *state = dev_get_drvdata(dev);
int sensor_index = find_ec_sensor_index(state, type, channel);
+
+ if (sensor_index < 0)
+ return sensor_index;
+
*str = get_sensor_info(state, sensor_index)->label;
return 0;
diff --git a/drivers/hwmon/cgbc-hwmon.c b/drivers/hwmon/cgbc-hwmon.c
new file mode 100644
index 000000000000..772f44d56ccf
--- /dev/null
+++ b/drivers/hwmon/cgbc-hwmon.c
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * cgbc-hwmon - Congatec Board Controller hardware monitoring driver
+ *
+ * Copyright (C) 2024 Thomas Richard <thomas.richard@bootlin.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/hwmon.h>
+#include <linux/mfd/cgbc.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define CGBC_HWMON_CMD_SENSOR 0x77
+#define CGBC_HWMON_CMD_SENSOR_DATA_SIZE 0x05
+
+#define CGBC_HWMON_TYPE_MASK GENMASK(6, 5)
+#define CGBC_HWMON_ID_MASK GENMASK(4, 0)
+#define CGBC_HWMON_ACTIVE_BIT BIT(7)
+
+struct cgbc_hwmon_sensor {
+ enum hwmon_sensor_types type;
+ bool active;
+ unsigned int index;
+ unsigned int channel;
+ const char *label;
+};
+
+struct cgbc_hwmon_data {
+ struct cgbc_device_data *cgbc;
+ unsigned int nb_sensors;
+ struct cgbc_hwmon_sensor *sensors;
+};
+
+enum cgbc_sensor_types {
+ CGBC_HWMON_TYPE_TEMP = 1,
+ CGBC_HWMON_TYPE_IN,
+ CGBC_HWMON_TYPE_FAN
+};
+
+static const char * const cgbc_hwmon_labels_temp[] = {
+ "CPU Temperature",
+ "Box Temperature",
+ "Ambient Temperature",
+ "Board Temperature",
+ "Carrier Temperature",
+ "Chipset Temperature",
+ "Video Temperature",
+ "Other Temperature",
+ "TOPDIM Temperature",
+ "BOTTOMDIM Temperature",
+};
+
+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" },
+};
+
+#define CGBC_HWMON_NB_IN_SENSORS 14
+
+static const char * const cgbc_hwmon_labels_fan[] = {
+ "CPU Fan",
+ "Box Fan",
+ "Ambient Fan",
+ "Chipset Fan",
+ "Video Fan",
+ "Other Fan",
+};
+
+static int cgbc_hwmon_cmd(struct cgbc_device_data *cgbc, u8 index, u8 *data)
+{
+ u8 cmd[2] = {CGBC_HWMON_CMD_SENSOR, index};
+
+ return cgbc_command(cgbc, cmd, sizeof(cmd), data, CGBC_HWMON_CMD_SENSOR_DATA_SIZE, NULL);
+}
+
+static int cgbc_hwmon_probe_sensors(struct device *dev, struct cgbc_hwmon_data *hwmon)
+{
+ struct cgbc_device_data *cgbc = hwmon->cgbc;
+ struct cgbc_hwmon_sensor *sensor = hwmon->sensors;
+ u8 data[CGBC_HWMON_CMD_SENSOR_DATA_SIZE], nb_sensors, i;
+ int ret;
+
+ ret = cgbc_hwmon_cmd(cgbc, 0, &data[0]);
+ if (ret)
+ return ret;
+
+ nb_sensors = data[0];
+
+ hwmon->sensors = devm_kzalloc(dev, sizeof(*hwmon->sensors) * nb_sensors, GFP_KERNEL);
+ sensor = hwmon->sensors;
+
+ for (i = 0; i < nb_sensors; i++) {
+ enum cgbc_sensor_types type;
+ unsigned int channel;
+
+ /*
+ * No need to request data for the first sensor.
+ * We got data for the first sensor when we ask the number of sensors to the Board
+ * Controller.
+ */
+ if (i) {
+ ret = cgbc_hwmon_cmd(cgbc, i, &data[0]);
+ if (ret)
+ return ret;
+ }
+
+ type = FIELD_GET(CGBC_HWMON_TYPE_MASK, data[1]);
+ channel = FIELD_GET(CGBC_HWMON_ID_MASK, data[1]) - 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)) {
+ /*
+ * The Board Controller doesn't differentiate current and voltage sensors.
+ * Get the sensor type from cgbc_hwmon_labels_in[channel].type instead.
+ */
+ sensor->type = cgbc_hwmon_labels_in[channel].type;
+ sensor->label = cgbc_hwmon_labels_in[channel].label;
+ } 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);
+ continue;
+ }
+
+ sensor->active = FIELD_GET(CGBC_HWMON_ACTIVE_BIT, data[1]);
+ sensor->channel = channel;
+ sensor->index = i;
+ sensor++;
+ hwmon->nb_sensors++;
+ }
+
+ return 0;
+}
+
+static struct cgbc_hwmon_sensor *cgbc_hwmon_find_sensor(struct cgbc_hwmon_data *hwmon,
+ enum hwmon_sensor_types type, int channel)
+{
+ 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];
+ break;
+ }
+ }
+
+ return sensor;
+}
+
+static int cgbc_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
+ long *val)
+{
+ struct cgbc_hwmon_data *hwmon = dev_get_drvdata(dev);
+ struct cgbc_hwmon_sensor *sensor = cgbc_hwmon_find_sensor(hwmon, type, channel);
+ struct cgbc_device_data *cgbc = hwmon->cgbc;
+ u8 data[CGBC_HWMON_CMD_SENSOR_DATA_SIZE];
+ int ret;
+
+ ret = cgbc_hwmon_cmd(cgbc, sensor->index, &data[0]);
+ if (ret)
+ return ret;
+
+ *val = (data[3] << 8) | data[2];
+
+ /*
+ * For the Board Controller 1lsb = 0.1 degree centigrade.
+ * Other units are as expected.
+ */
+ if (sensor->type == hwmon_temp)
+ *val *= 100;
+
+ return 0;
+}
+
+static umode_t cgbc_hwmon_is_visible(const void *_data, enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ struct cgbc_hwmon_data *data = (struct cgbc_hwmon_data *)_data;
+ struct cgbc_hwmon_sensor *sensor;
+
+ sensor = cgbc_hwmon_find_sensor(data, type, channel);
+ if (!sensor)
+ return 0;
+
+ return sensor->active ? 0444 : 0;
+}
+
+static int cgbc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+ int channel, const char **str)
+{
+ struct cgbc_hwmon_data *hwmon = dev_get_drvdata(dev);
+ struct cgbc_hwmon_sensor *sensor = cgbc_hwmon_find_sensor(hwmon, type, channel);
+
+ *str = sensor->label;
+
+ return 0;
+}
+
+static const struct hwmon_channel_info * const cgbc_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(temp,
+ 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,
+ 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),
+ HWMON_CHANNEL_INFO(fan,
+ HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL,
+ HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL,
+ HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL),
+ NULL
+};
+
+static const struct hwmon_ops cgbc_hwmon_ops = {
+ .is_visible = cgbc_hwmon_is_visible,
+ .read = cgbc_hwmon_read,
+ .read_string = cgbc_hwmon_read_string,
+};
+
+static const struct hwmon_chip_info cgbc_chip_info = {
+ .ops = &cgbc_hwmon_ops,
+ .info = cgbc_hwmon_info,
+};
+
+static int cgbc_hwmon_probe(struct platform_device *pdev)
+{
+ struct cgbc_device_data *cgbc = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct cgbc_hwmon_data *data;
+ struct device *hwmon_dev;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->cgbc = cgbc;
+
+ ret = cgbc_hwmon_probe_sensors(dev, data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to probe sensors");
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, "cgbc_hwmon", data, &cgbc_chip_info,
+ NULL);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static struct platform_driver cgbc_hwmon_driver = {
+ .driver = {
+ .name = "cgbc-hwmon",
+ },
+ .probe = cgbc_hwmon_probe,
+};
+
+module_platform_driver(cgbc_hwmon_driver);
+
+MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>");
+MODULE_DESCRIPTION("Congatec Board Controller Hardware Monitoring Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c
index cd00adaad1b4..1e2c8e284001 100644
--- a/drivers/hwmon/dell-smm-hwmon.c
+++ b/drivers/hwmon/dell-smm-hwmon.c
@@ -73,7 +73,7 @@
#define DELL_SMM_LEGACY_EXECUTE 0x1
#define DELL_SMM_NO_TEMP 10
-#define DELL_SMM_NO_FANS 3
+#define DELL_SMM_NO_FANS 4
struct smm_regs {
unsigned int eax;
@@ -1074,11 +1074,14 @@ static const struct hwmon_channel_info * const dell_smm_info[] = {
HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX |
HWMON_F_TARGET,
HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX |
+ HWMON_F_TARGET,
+ HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX |
HWMON_F_TARGET
),
HWMON_CHANNEL_INFO(pwm,
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
HWMON_PWM_INPUT,
+ HWMON_PWM_INPUT,
HWMON_PWM_INPUT
),
NULL
@@ -1271,6 +1274,13 @@ static const struct dmi_system_id i8k_dmi_table[] __initconst = {
},
},
{
+ .ident = "Dell OptiPlex 7050",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "OptiPlex 7050"),
+ },
+ },
+ {
.ident = "Dell Precision",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
diff --git a/drivers/hwmon/emc2305.c b/drivers/hwmon/emc2305.c
index 4d39fbd83769..234c54956a4b 100644
--- a/drivers/hwmon/emc2305.c
+++ b/drivers/hwmon/emc2305.c
@@ -112,8 +112,6 @@ static char *emc2305_fan_name[] = {
"emc2305_fan5",
};
-static void emc2305_unset_tz(struct device *dev);
-
static int emc2305_get_max_channel(const struct emc2305_data *data)
{
return data->pwm_num;
@@ -293,8 +291,9 @@ static int emc2305_set_single_tz(struct device *dev, int idx)
pwm = data->pwm_min[cdev_idx];
data->cdev_data[cdev_idx].cdev =
- thermal_cooling_device_register(emc2305_fan_name[idx], data,
- &emc2305_cooling_ops);
+ devm_thermal_of_cooling_device_register(dev, dev->of_node,
+ emc2305_fan_name[idx], data,
+ &emc2305_cooling_ops);
if (IS_ERR(data->cdev_data[cdev_idx].cdev)) {
dev_err(dev, "Failed to register cooling device %s\n", emc2305_fan_name[idx]);
@@ -332,24 +331,9 @@ static int emc2305_set_tz(struct device *dev)
for (i = 0; i < data->pwm_num; i++) {
ret = emc2305_set_single_tz(dev, i + 1);
if (ret)
- goto thermal_cooling_device_register_fail;
+ return ret;
}
return 0;
-
-thermal_cooling_device_register_fail:
- emc2305_unset_tz(dev);
- return ret;
-}
-
-static void emc2305_unset_tz(struct device *dev)
-{
- struct emc2305_data *data = dev_get_drvdata(dev);
- int i;
-
- /* Unregister cooling device. */
- for (i = 0; i < EMC2305_PWM_MAX; i++)
- if (data->cdev_data[i].cdev)
- thermal_cooling_device_unregister(data->cdev_data[i].cdev);
}
static umode_t
@@ -599,20 +583,18 @@ static int emc2305_probe(struct i2c_client *client)
return 0;
}
-static void emc2305_remove(struct i2c_client *client)
-{
- struct device *dev = &client->dev;
-
- if (IS_REACHABLE(CONFIG_THERMAL))
- emc2305_unset_tz(dev);
-}
+static const struct of_device_id of_emc2305_match_table[] = {
+ { .compatible = "microchip,emc2305", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, of_emc2305_match_table);
static struct i2c_driver emc2305_driver = {
.driver = {
.name = "emc2305",
+ .of_match_table = of_emc2305_match_table,
},
.probe = emc2305_probe,
- .remove = emc2305_remove,
.id_table = emc2305_ids,
};
diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
index 9ed2c4b6734e..8ecebea53651 100644
--- a/drivers/hwmon/fam15h_power.c
+++ b/drivers/hwmon/fam15h_power.c
@@ -143,8 +143,8 @@ static void do_read_registers_on_cu(void *_data)
*/
cu = topology_core_id(smp_processor_id());
- rdmsrl_safe(MSR_F15H_CU_PWR_ACCUMULATOR, &data->cu_acc_power[cu]);
- rdmsrl_safe(MSR_F15H_PTSC, &data->cpu_sw_pwr_ptsc[cu]);
+ rdmsrq_safe(MSR_F15H_CU_PWR_ACCUMULATOR, &data->cu_acc_power[cu]);
+ rdmsrq_safe(MSR_F15H_PTSC, &data->cpu_sw_pwr_ptsc[cu]);
data->cu_on[cu] = 1;
}
@@ -424,7 +424,7 @@ static int fam15h_power_init_data(struct pci_dev *f4,
*/
data->cpu_pwr_sample_ratio = cpuid_ecx(0x80000007);
- if (rdmsrl_safe(MSR_F15H_CU_MAX_PWR_ACCUMULATOR, &tmp)) {
+ if (rdmsrq_safe(MSR_F15H_CU_MAX_PWR_ACCUMULATOR, &tmp)) {
pr_err("Failed to read max compute unit power accumulator MSR\n");
return -ENODEV;
}
diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c
index a3a07662e491..8aeec16a7a90 100644
--- a/drivers/hwmon/ftsteutates.c
+++ b/drivers/hwmon/ftsteutates.c
@@ -423,13 +423,16 @@ static int fts_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
break;
case hwmon_pwm:
switch (attr) {
- case hwmon_pwm_auto_channels_temp:
- if (data->fan_source[channel] == FTS_FAN_SOURCE_INVALID)
+ case hwmon_pwm_auto_channels_temp: {
+ u8 fan_source = data->fan_source[channel];
+
+ if (fan_source == FTS_FAN_SOURCE_INVALID || fan_source >= BITS_PER_LONG)
*val = 0;
else
- *val = BIT(data->fan_source[channel]);
+ *val = BIT(fan_source);
return 0;
+ }
default:
break;
}
diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c
index d92c536be9af..516c34bb61c9 100644
--- a/drivers/hwmon/gpio-fan.c
+++ b/drivers/hwmon/gpio-fan.c
@@ -20,6 +20,9 @@
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/of_platform.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
#include <linux/thermal.h>
struct gpio_fan_speed {
@@ -42,6 +45,7 @@ struct gpio_fan_data {
bool pwm_enable;
struct gpio_desc *alarm_gpio;
struct work_struct alarm_work;
+ struct regulator *supply;
};
/*
@@ -125,13 +129,32 @@ static int __get_fan_ctrl(struct gpio_fan_data *fan_data)
}
/* Must be called with fan_data->lock held, except during initialization. */
-static void set_fan_speed(struct gpio_fan_data *fan_data, int speed_index)
+static int set_fan_speed(struct gpio_fan_data *fan_data, int speed_index)
{
if (fan_data->speed_index == speed_index)
- return;
+ return 0;
+
+ if (fan_data->speed_index == 0 && speed_index > 0) {
+ int ret;
+
+ ret = pm_runtime_resume_and_get(fan_data->dev);
+ if (ret < 0)
+ return ret;
+ }
__set_fan_ctrl(fan_data, fan_data->speed[speed_index].ctrl_val);
+
+ if (fan_data->speed_index > 0 && speed_index == 0) {
+ int ret;
+
+ ret = pm_runtime_put_sync(fan_data->dev);
+ if (ret < 0)
+ return ret;
+ }
+
fan_data->speed_index = speed_index;
+
+ return 0;
}
static int get_fan_speed_index(struct gpio_fan_data *fan_data)
@@ -176,7 +199,7 @@ static ssize_t pwm1_store(struct device *dev, struct device_attribute *attr,
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
unsigned long pwm;
int speed_index;
- int ret = count;
+ int ret;
if (kstrtoul(buf, 10, &pwm) || pwm > 255)
return -EINVAL;
@@ -189,12 +212,12 @@ static ssize_t pwm1_store(struct device *dev, struct device_attribute *attr,
}
speed_index = DIV_ROUND_UP(pwm * (fan_data->num_speed - 1), 255);
- set_fan_speed(fan_data, speed_index);
+ ret = set_fan_speed(fan_data, speed_index);
exit_unlock:
mutex_unlock(&fan_data->lock);
- return ret;
+ return ret ? ret : count;
}
static ssize_t pwm1_enable_show(struct device *dev,
@@ -211,6 +234,7 @@ static ssize_t pwm1_enable_store(struct device *dev,
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
unsigned long val;
+ int ret = 0;
if (kstrtoul(buf, 10, &val) || val > 1)
return -EINVAL;
@@ -224,11 +248,11 @@ static ssize_t pwm1_enable_store(struct device *dev,
/* Disable manual control mode: set fan at full speed. */
if (val == 0)
- set_fan_speed(fan_data, fan_data->num_speed - 1);
+ ret = set_fan_speed(fan_data, fan_data->num_speed - 1);
mutex_unlock(&fan_data->lock);
- return count;
+ return ret ? ret : count;
}
static ssize_t pwm1_mode_show(struct device *dev,
@@ -279,7 +303,7 @@ static ssize_t set_rpm(struct device *dev, struct device_attribute *attr,
goto exit_unlock;
}
- set_fan_speed(fan_data, rpm_to_speed_index(fan_data, rpm));
+ ret = set_fan_speed(fan_data, rpm_to_speed_index(fan_data, rpm));
exit_unlock:
mutex_unlock(&fan_data->lock);
@@ -386,6 +410,7 @@ static int gpio_fan_set_cur_state(struct thermal_cooling_device *cdev,
unsigned long state)
{
struct gpio_fan_data *fan_data = cdev->devdata;
+ int ret;
if (!fan_data)
return -EINVAL;
@@ -393,8 +418,13 @@ static int gpio_fan_set_cur_state(struct thermal_cooling_device *cdev,
if (state >= fan_data->num_speed)
return -EINVAL;
- set_fan_speed(fan_data, state);
- return 0;
+ mutex_lock(&fan_data->lock);
+
+ ret = set_fan_speed(fan_data, state);
+
+ mutex_unlock(&fan_data->lock);
+
+ return ret;
}
static const struct thermal_cooling_device_ops gpio_fan_cool_ops = {
@@ -489,7 +519,13 @@ MODULE_DEVICE_TABLE(of, of_gpio_fan_match);
static void gpio_fan_stop(void *data)
{
+ struct gpio_fan_data *fan_data = data;
+
+ mutex_lock(&fan_data->lock);
set_fan_speed(data, 0);
+ mutex_unlock(&fan_data->lock);
+
+ pm_runtime_disable(fan_data->dev);
}
static int gpio_fan_probe(struct platform_device *pdev)
@@ -512,6 +548,11 @@ static int gpio_fan_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, fan_data);
mutex_init(&fan_data->lock);
+ fan_data->supply = devm_regulator_get(dev, "fan");
+ if (IS_ERR(fan_data->supply))
+ return dev_err_probe(dev, PTR_ERR(fan_data->supply),
+ "Failed to get fan-supply");
+
/* Configure control GPIOs if available. */
if (fan_data->gpios && fan_data->num_gpios > 0) {
if (!fan_data->speed || fan_data->num_speed <= 1)
@@ -539,6 +580,17 @@ static int gpio_fan_probe(struct platform_device *pdev)
return err;
}
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ /* If current GPIO state is active, mark RPM as active as well */
+ if (fan_data->speed_index > 0) {
+ int ret;
+
+ ret = pm_runtime_resume_and_get(&pdev->dev);
+ if (ret)
+ return ret;
+ }
+
/* Optional cooling device register for Device tree platforms */
fan_data->cdev = devm_thermal_of_cooling_device_register(dev, np,
"gpio-fan", fan_data, &gpio_fan_cool_ops);
@@ -556,36 +608,69 @@ static void gpio_fan_shutdown(struct platform_device *pdev)
set_fan_speed(fan_data, 0);
}
+static int gpio_fan_runtime_suspend(struct device *dev)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (fan_data->supply)
+ ret = regulator_disable(fan_data->supply);
+
+ return ret;
+}
+
+static int gpio_fan_runtime_resume(struct device *dev)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (fan_data->supply)
+ ret = regulator_enable(fan_data->supply);
+
+ return ret;
+}
+
static int gpio_fan_suspend(struct device *dev)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+ int ret = 0;
if (fan_data->gpios) {
fan_data->resume_speed = fan_data->speed_index;
- set_fan_speed(fan_data, 0);
+ mutex_lock(&fan_data->lock);
+ ret = set_fan_speed(fan_data, 0);
+ mutex_unlock(&fan_data->lock);
}
- return 0;
+ return ret;
}
static int gpio_fan_resume(struct device *dev)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+ int ret = 0;
- if (fan_data->gpios)
- set_fan_speed(fan_data, fan_data->resume_speed);
+ if (fan_data->gpios) {
+ mutex_lock(&fan_data->lock);
+ ret = set_fan_speed(fan_data, fan_data->resume_speed);
+ mutex_unlock(&fan_data->lock);
+ }
- return 0;
+ return ret;
}
-static DEFINE_SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume);
+static const struct dev_pm_ops gpio_fan_pm = {
+ RUNTIME_PM_OPS(gpio_fan_runtime_suspend,
+ gpio_fan_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(gpio_fan_suspend, gpio_fan_resume)
+};
static struct platform_driver gpio_fan_driver = {
.probe = gpio_fan_probe,
.shutdown = gpio_fan_shutdown,
.driver = {
.name = "gpio-fan",
- .pm = pm_sleep_ptr(&gpio_fan_pm),
+ .pm = pm_ptr(&gpio_fan_pm),
.of_match_table = of_gpio_fan_match,
},
};
diff --git a/drivers/hwmon/gsc-hwmon.c b/drivers/hwmon/gsc-hwmon.c
index 14a6385cd7cc..0f9af82cebec 100644
--- a/drivers/hwmon/gsc-hwmon.c
+++ b/drivers/hwmon/gsc-hwmon.c
@@ -47,7 +47,6 @@ static const struct regmap_bus gsc_hwmon_regmap_bus = {
static const struct regmap_config gsc_hwmon_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
- .cache_type = REGCACHE_NONE,
};
static ssize_t pwm_auto_point_temp_show(struct device *dev,
diff --git a/drivers/hwmon/hp-wmi-sensors.c b/drivers/hwmon/hp-wmi-sensors.c
index d6bdad26feb1..03c684ba83bd 100644
--- a/drivers/hwmon/hp-wmi-sensors.c
+++ b/drivers/hwmon/hp-wmi-sensors.c
@@ -1197,7 +1197,7 @@ static int hp_wmi_update_info(struct hp_wmi_sensors *state,
if (time_after(jiffies, info->last_updated + HZ)) {
mutex_lock(&state->lock);
- wobj = hp_wmi_get_wobj(HP_WMI_NUMERIC_SENSOR_GUID, instance);
+ wobj = wmidev_block_query(state->wdev, instance);
if (!wobj) {
ret = -EIO;
goto out_unlock;
@@ -1745,7 +1745,7 @@ static int init_numeric_sensors(struct hp_wmi_sensors *state,
return -ENOMEM;
for (i = 0, info = info_arr; i < icount; i++, info++) {
- wobj = hp_wmi_get_wobj(HP_WMI_NUMERIC_SENSOR_GUID, i);
+ wobj = wmidev_block_query(state->wdev, i);
if (!wobj)
return -EIO;
diff --git a/drivers/hwmon/htu31.c b/drivers/hwmon/htu31.c
new file mode 100644
index 000000000000..7521a371aa6c
--- /dev/null
+++ b/drivers/hwmon/htu31.c
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * The driver for Measurement Specialties HTU31 Temperature and Humidity sensor.
+ *
+ * Copyright (C) 2025
+ * Author: Andrei Lalaev <andrey.lalaev@gmail.com>
+ */
+
+#include <linux/array_size.h>
+#include <linux/cleanup.h>
+#include <linux/crc8.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+#define HTU31_READ_TEMP_HUM_CMD 0x00
+#define HTU31_READ_SERIAL_CMD 0x0a
+#define HTU31_CONVERSION_CMD 0x5e
+#define HTU31_HEATER_OFF_CMD 0x02
+#define HTU31_HEATER_ON_CMD 0x04
+
+#define HTU31_TEMP_HUM_LEN 6
+
+/* Conversion time for the highest resolution */
+#define HTU31_HUMIDITY_CONV_TIME 10000 /* us */
+#define HTU31_TEMPERATURE_CONV_TIME 15000 /* us */
+
+#define HTU31_SERIAL_NUMBER_LEN 3
+#define HTU31_SERIAL_NUMBER_CRC_LEN 1
+#define HTU31_SERIAL_NUMBER_CRC_OFFSET 3
+
+#define HTU31_CRC8_INIT_VAL 0
+#define HTU31_CRC8_POLYNOMIAL 0x31
+DECLARE_CRC8_TABLE(htu31_crc8_table);
+
+/**
+ * struct htu31_data - all the data required to operate a HTU31 chip
+ * @client: the i2c client associated with the HTU31
+ * @lock: a mutex to prevent parallel access to the data
+ * @wait_time: the time needed by sensor to convert values
+ * @temperature: the latest temperature value in millidegrees
+ * @humidity: the latest relative humidity value in millipercent
+ * @serial_number: the serial number of the sensor
+ * @heater_enable: the internal state of the heater
+ */
+struct htu31_data {
+ struct i2c_client *client;
+ struct mutex lock; /* Used to protect against parallel data updates */
+ long wait_time;
+ long temperature;
+ long humidity;
+ u8 serial_number[HTU31_SERIAL_NUMBER_LEN];
+ bool heater_enable;
+};
+
+static long htu31_temp_to_millicelsius(u16 val)
+{
+ return -40000 + DIV_ROUND_CLOSEST_ULL(165000ULL * val, 65535);
+}
+
+static long htu31_relative_humidity(u16 val)
+{
+ return DIV_ROUND_CLOSEST_ULL(100000ULL * val, 65535);
+}
+
+static int htu31_data_fetch_command(struct htu31_data *data)
+{
+ struct i2c_client *client = data->client;
+ u8 conversion_on = HTU31_CONVERSION_CMD;
+ u8 read_data_cmd = HTU31_READ_TEMP_HUM_CMD;
+ u8 t_h_buf[HTU31_TEMP_HUM_LEN] = {};
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &read_data_cmd,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(t_h_buf),
+ .buf = t_h_buf,
+ },
+ };
+ int ret;
+ u8 crc;
+
+ guard(mutex)(&data->lock);
+
+ ret = i2c_master_send(client, &conversion_on, 1);
+ if (ret != 1) {
+ ret = ret < 0 ? ret : -EIO;
+ dev_err(&client->dev,
+ "Conversion command is failed. Error code: %d\n", ret);
+ return ret;
+ }
+
+ fsleep(data->wait_time);
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs)) {
+ ret = ret < 0 ? ret : -EIO;
+ dev_err(&client->dev,
+ "T&H command is failed. Error code: %d\n", ret);
+ return ret;
+ }
+
+ crc = crc8(htu31_crc8_table, &t_h_buf[0], 2, HTU31_CRC8_INIT_VAL);
+ if (crc != t_h_buf[2]) {
+ dev_err(&client->dev, "Temperature CRC mismatch\n");
+ return -EIO;
+ }
+
+ crc = crc8(htu31_crc8_table, &t_h_buf[3], 2, HTU31_CRC8_INIT_VAL);
+ if (crc != t_h_buf[5]) {
+ dev_err(&client->dev, "Humidity CRC mismatch\n");
+ return -EIO;
+ }
+
+ data->temperature = htu31_temp_to_millicelsius(be16_to_cpup((__be16 *)&t_h_buf[0]));
+ data->humidity = htu31_relative_humidity(be16_to_cpup((__be16 *)&t_h_buf[3]));
+
+ return 0;
+}
+
+static umode_t htu31_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_temp:
+ case hwmon_humidity:
+ return 0444;
+ default:
+ return 0;
+ }
+}
+
+static int htu31_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct htu31_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ ret = htu31_data_fetch_command(data);
+ if (ret < 0)
+ return ret;
+
+ switch (type) {
+ case hwmon_temp:
+ if (attr != hwmon_temp_input)
+ return -EINVAL;
+
+ *val = data->temperature;
+ break;
+ case hwmon_humidity:
+ if (attr != hwmon_humidity_input)
+ return -EINVAL;
+
+ *val = data->humidity;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int htu31_read_serial_number(struct htu31_data *data)
+{
+ struct i2c_client *client = data->client;
+ u8 read_sn_cmd = HTU31_READ_SERIAL_CMD;
+ u8 sn_buf[HTU31_SERIAL_NUMBER_LEN + HTU31_SERIAL_NUMBER_CRC_LEN];
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &read_sn_cmd,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(sn_buf),
+ .buf = sn_buf,
+ },
+ };
+ int ret;
+ u8 crc;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
+ return ret;
+
+ crc = crc8(htu31_crc8_table, sn_buf, HTU31_SERIAL_NUMBER_LEN, HTU31_CRC8_INIT_VAL);
+ if (crc != sn_buf[HTU31_SERIAL_NUMBER_CRC_OFFSET]) {
+ dev_err(&client->dev, "Serial number CRC mismatch\n");
+ return -EIO;
+ }
+
+ memcpy(data->serial_number, sn_buf, HTU31_SERIAL_NUMBER_LEN);
+
+ return 0;
+}
+
+static ssize_t heater_enable_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct htu31_data *data = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%d\n", data->heater_enable);
+}
+
+static ssize_t heater_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct htu31_data *data = dev_get_drvdata(dev);
+ u8 heater_cmd;
+ bool status;
+ int ret;
+
+ ret = kstrtobool(buf, &status);
+ if (ret)
+ return ret;
+
+ heater_cmd = status ? HTU31_HEATER_ON_CMD : HTU31_HEATER_OFF_CMD;
+
+ guard(mutex)(&data->lock);
+
+ ret = i2c_master_send(data->client, &heater_cmd, 1);
+ if (ret < 0)
+ return ret;
+
+ data->heater_enable = status;
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(heater_enable);
+
+static int serial_number_show(struct seq_file *seq_file,
+ void *unused)
+{
+ struct htu31_data *data = seq_file->private;
+
+ seq_printf(seq_file, "%X%X%X\n", data->serial_number[0],
+ data->serial_number[1], data->serial_number[2]);
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(serial_number);
+
+static struct attribute *htu31_attrs[] = {
+ &dev_attr_heater_enable.attr,
+ NULL
+};
+
+ATTRIBUTE_GROUPS(htu31);
+
+static const struct hwmon_channel_info * const htu31_info[] = {
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
+ HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT),
+ NULL
+};
+
+static const struct hwmon_ops htu31_hwmon_ops = {
+ .is_visible = htu31_is_visible,
+ .read = htu31_read,
+};
+
+static const struct hwmon_chip_info htu31_chip_info = {
+ .info = htu31_info,
+ .ops = &htu31_hwmon_ops,
+};
+
+static int htu31_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct htu31_data *data;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ data->wait_time = HTU31_TEMPERATURE_CONV_TIME + HTU31_HUMIDITY_CONV_TIME;
+
+ ret = devm_mutex_init(dev, &data->lock);
+ if (ret)
+ return ret;
+
+ crc8_populate_msb(htu31_crc8_table, HTU31_CRC8_POLYNOMIAL);
+
+ ret = htu31_read_serial_number(data);
+ if (ret) {
+ dev_err(dev, "Failed to read serial number\n");
+ return ret;
+ }
+
+ debugfs_create_file("serial_number",
+ 0444,
+ client->debugfs,
+ data,
+ &serial_number_fops);
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev,
+ client->name,
+ data,
+ &htu31_chip_info,
+ htu31_groups);
+
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct i2c_device_id htu31_id[] = {
+ { "htu31" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, htu31_id);
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id htu31_of_match[] = {
+ { .compatible = "meas,htu31" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, htu31_of_match);
+#endif
+
+static struct i2c_driver htu31_driver = {
+ .driver = {
+ .name = "htu31",
+ .of_match_table = of_match_ptr(htu31_of_match),
+ },
+ .probe = htu31_probe,
+ .id_table = htu31_id,
+};
+module_i2c_driver(htu31_driver);
+
+MODULE_AUTHOR("Andrei Lalaev <andrey.lalaev@gmail.com>");
+MODULE_DESCRIPTION("HTU31 Temperature and Humidity sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/hwmon-vid.c b/drivers/hwmon/hwmon-vid.c
index 6d1175a51832..2df4956296ed 100644
--- a/drivers/hwmon/hwmon-vid.c
+++ b/drivers/hwmon/hwmon-vid.c
@@ -15,6 +15,10 @@
#include <linux/kernel.h>
#include <linux/hwmon-vid.h>
+#ifdef CONFIG_X86
+#include <asm/msr.h>
+#endif
+
/*
* Common code for decoding VID pins.
*
diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
index 9703d60e9bbf..1688c210888a 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -646,8 +646,8 @@ static const char * const hwmon_power_attr_templates[] = {
[hwmon_power_enable] = "power%d_enable",
[hwmon_power_average] = "power%d_average",
[hwmon_power_average_interval] = "power%d_average_interval",
- [hwmon_power_average_interval_max] = "power%d_interval_max",
- [hwmon_power_average_interval_min] = "power%d_interval_min",
+ [hwmon_power_average_interval_max] = "power%d_average_interval_max",
+ [hwmon_power_average_interval_min] = "power%d_average_interval_min",
[hwmon_power_average_highest] = "power%d_average_highest",
[hwmon_power_average_lowest] = "power%d_average_lowest",
[hwmon_power_average_max] = "power%d_average_max",
diff --git a/drivers/hwmon/ina238.c b/drivers/hwmon/ina238.c
index 2d9f12f68d50..a4a41742786b 100644
--- a/drivers/hwmon/ina238.c
+++ b/drivers/hwmon/ina238.c
@@ -21,11 +21,14 @@
#define INA238_CONFIG 0x0
#define INA238_ADC_CONFIG 0x1
#define INA238_SHUNT_CALIBRATION 0x2
+#define SQ52206_SHUNT_TEMPCO 0x3
#define INA238_SHUNT_VOLTAGE 0x4
#define INA238_BUS_VOLTAGE 0x5
#define INA238_DIE_TEMP 0x6
#define INA238_CURRENT 0x7
#define INA238_POWER 0x8
+#define SQ52206_ENERGY 0x9
+#define SQ52206_CHARGE 0xa
#define INA238_DIAG_ALERT 0xb
#define INA238_SHUNT_OVER_VOLTAGE 0xc
#define INA238_SHUNT_UNDER_VOLTAGE 0xd
@@ -33,9 +36,12 @@
#define INA238_BUS_UNDER_VOLTAGE 0xf
#define INA238_TEMP_LIMIT 0x10
#define INA238_POWER_LIMIT 0x11
+#define SQ52206_POWER_PEAK 0x20
#define INA238_DEVICE_ID 0x3f /* not available on INA237 */
#define INA238_CONFIG_ADCRANGE BIT(4)
+#define SQ52206_CONFIG_ADCRANGE_HIGH BIT(4)
+#define SQ52206_CONFIG_ADCRANGE_LOW BIT(3)
#define INA238_DIAG_ALERT_TMPOL BIT(7)
#define INA238_DIAG_ALERT_SHNTOL BIT(6)
@@ -44,12 +50,13 @@
#define INA238_DIAG_ALERT_BUSUL BIT(3)
#define INA238_DIAG_ALERT_POL BIT(2)
-#define INA238_REGISTERS 0x11
+#define INA238_REGISTERS 0x20
#define INA238_RSHUNT_DEFAULT 10000 /* uOhm */
/* Default configuration of device on reset. */
#define INA238_CONFIG_DEFAULT 0
+#define SQ52206_CONFIG_DEFAULT 0x0005
/* 16 sample averaging, 1052us conversion time, continuous mode */
#define INA238_ADC_CONFIG_DEFAULT 0xfb6a
/* Configure alerts to be based on averaged value (SLOWALERT) */
@@ -87,14 +94,19 @@
* shunt = 0x4000 / (819.2 * 10^6) / 0.001 = 20000 uOhms (with 1mA/lsb)
*
* Current (mA) = register value * 20000 / rshunt / 4 * gain
- * Power (W) = 0.2 * register value * 20000 / rshunt / 4 * gain
+ * Power (mW) = 0.2 * register value * 20000 / rshunt / 4 * gain
+ * (Specific for SQ52206)
+ * Power (mW) = 0.24 * register value * 20000 / rshunt / 4 * gain
+ * Energy (mJ) = 16 * 0.24 * register value * 20000 / rshunt / 4 * gain
*/
#define INA238_CALIBRATION_VALUE 16384
#define INA238_FIXED_SHUNT 20000
#define INA238_SHUNT_VOLTAGE_LSB 5 /* 5 uV/lsb */
#define INA238_BUS_VOLTAGE_LSB 3125 /* 3.125 mV/lsb */
-#define INA238_DIE_TEMP_LSB 125 /* 125 mC/lsb */
+#define INA238_DIE_TEMP_LSB 1250000 /* 125.0000 mC/lsb */
+#define SQ52206_BUS_VOLTAGE_LSB 3750 /* 3.75 mV/lsb */
+#define SQ52206_DIE_TEMP_LSB 78125 /* 7.8125 mC/lsb */
static const struct regmap_config ina238_regmap_config = {
.max_register = INA238_REGISTERS,
@@ -102,7 +114,20 @@ static const struct regmap_config ina238_regmap_config = {
.val_bits = 16,
};
+enum ina238_ids { ina238, ina237, sq52206 };
+
+struct ina238_config {
+ bool has_power_highest; /* chip detection power peak */
+ bool has_energy; /* chip detection energy */
+ u8 temp_shift; /* fixed parameters for temp calculate */
+ u32 power_calculate_factor; /* fixed parameters for power calculate */
+ u16 config_default; /* Power-on default state */
+ int bus_voltage_lsb; /* use for temperature calculate, uV/lsb */
+ int temp_lsb; /* use for temperature calculate */
+};
+
struct ina238_data {
+ const struct ina238_config *config;
struct i2c_client *client;
struct mutex config_lock;
struct regmap *regmap;
@@ -110,6 +135,36 @@ struct ina238_data {
int gain;
};
+static const struct ina238_config ina238_config[] = {
+ [ina238] = {
+ .has_energy = false,
+ .has_power_highest = false,
+ .temp_shift = 4,
+ .power_calculate_factor = 20,
+ .config_default = INA238_CONFIG_DEFAULT,
+ .bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB,
+ .temp_lsb = INA238_DIE_TEMP_LSB,
+ },
+ [ina237] = {
+ .has_energy = false,
+ .has_power_highest = false,
+ .temp_shift = 4,
+ .power_calculate_factor = 20,
+ .config_default = INA238_CONFIG_DEFAULT,
+ .bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB,
+ .temp_lsb = INA238_DIE_TEMP_LSB,
+ },
+ [sq52206] = {
+ .has_energy = true,
+ .has_power_highest = true,
+ .temp_shift = 0,
+ .power_calculate_factor = 24,
+ .config_default = SQ52206_CONFIG_DEFAULT,
+ .bus_voltage_lsb = SQ52206_BUS_VOLTAGE_LSB,
+ .temp_lsb = SQ52206_DIE_TEMP_LSB,
+ },
+};
+
static int ina238_read_reg24(const struct i2c_client *client, u8 reg, u32 *val)
{
u8 data[3];
@@ -126,6 +181,24 @@ static int ina238_read_reg24(const struct i2c_client *client, u8 reg, u32 *val)
return 0;
}
+static int ina238_read_reg40(const struct i2c_client *client, u8 reg, u64 *val)
+{
+ u8 data[5];
+ u32 low;
+ int err;
+
+ /* 40-bit register read */
+ err = i2c_smbus_read_i2c_block_data(client, reg, 5, data);
+ if (err < 0)
+ return err;
+ if (err != 5)
+ return -EIO;
+ low = (data[1] << 24) | (data[2] << 16) | (data[3] << 8) | data[4];
+ *val = ((long long)data[0] << 32) | low;
+
+ return 0;
+}
+
static int ina238_read_in(struct device *dev, u32 attr, int channel,
long *val)
{
@@ -197,10 +270,10 @@ static int ina238_read_in(struct device *dev, u32 attr, int channel,
regval = (s16)regval;
if (channel == 0)
/* gain of 1 -> LSB / 4 */
- *val = (regval * INA238_SHUNT_VOLTAGE_LSB) /
- (1000 * (4 - data->gain + 1));
+ *val = (regval * INA238_SHUNT_VOLTAGE_LSB) *
+ data->gain / (1000 * 4);
else
- *val = (regval * INA238_BUS_VOLTAGE_LSB) / 1000;
+ *val = (regval * data->config->bus_voltage_lsb) / 1000;
break;
case hwmon_in_max_alarm:
case hwmon_in_min_alarm:
@@ -225,8 +298,8 @@ static int ina238_write_in(struct device *dev, u32 attr, int channel,
case 0:
/* signed value, clamp to max range +/-163 mV */
regval = clamp_val(val, -163, 163);
- regval = (regval * 1000 * (4 - data->gain + 1)) /
- INA238_SHUNT_VOLTAGE_LSB;
+ regval = (regval * 1000 * 4) /
+ (INA238_SHUNT_VOLTAGE_LSB * data->gain);
regval = clamp_val(regval, S16_MIN, S16_MAX);
switch (attr) {
@@ -242,7 +315,7 @@ static int ina238_write_in(struct device *dev, u32 attr, int channel,
case 1:
/* signed value, positive values only. Clamp to max 102.396 V */
regval = clamp_val(val, 0, 102396);
- regval = (regval * 1000) / INA238_BUS_VOLTAGE_LSB;
+ regval = (regval * 1000) / data->config->bus_voltage_lsb;
regval = clamp_val(regval, 0, S16_MAX);
switch (attr) {
@@ -297,8 +370,19 @@ static int ina238_read_power(struct device *dev, u32 attr, long *val)
return err;
/* Fixed 1mA lsb, scaled by 1000000 to have result in uW */
- power = div_u64(regval * 1000ULL * INA238_FIXED_SHUNT *
- data->gain, 20 * data->rshunt);
+ power = div_u64(regval * 1000ULL * INA238_FIXED_SHUNT * data->gain *
+ data->config->power_calculate_factor, 4 * 100 * data->rshunt);
+ /* Clamp value to maximum value of long */
+ *val = clamp_val(power, 0, LONG_MAX);
+ break;
+ case hwmon_power_input_highest:
+ err = ina238_read_reg24(data->client, SQ52206_POWER_PEAK, &regval);
+ if (err)
+ return err;
+
+ /* Fixed 1mA lsb, scaled by 1000000 to have result in uW */
+ power = div_u64(regval * 1000ULL * INA238_FIXED_SHUNT * data->gain *
+ data->config->power_calculate_factor, 4 * 100 * data->rshunt);
/* Clamp value to maximum value of long */
*val = clamp_val(power, 0, LONG_MAX);
break;
@@ -311,8 +395,8 @@ static int ina238_read_power(struct device *dev, u32 attr, long *val)
* Truncated 24-bit compare register, lower 8-bits are
* truncated. Same conversion to/from uW as POWER register.
*/
- power = div_u64((regval << 8) * 1000ULL * INA238_FIXED_SHUNT *
- data->gain, 20 * data->rshunt);
+ power = div_u64((regval << 8) * 1000ULL * INA238_FIXED_SHUNT * data->gain *
+ data->config->power_calculate_factor, 4 * 100 * data->rshunt);
/* Clamp value to maximum value of long */
*val = clamp_val(power, 0, LONG_MAX);
break;
@@ -344,8 +428,8 @@ static int ina238_write_power(struct device *dev, u32 attr, long val)
* register.
*/
regval = clamp_val(val, 0, LONG_MAX);
- regval = div_u64(val * 20ULL * data->rshunt,
- 1000ULL * INA238_FIXED_SHUNT * data->gain);
+ regval = div_u64(val * 4 * 100 * data->rshunt, data->config->power_calculate_factor *
+ 1000ULL * INA238_FIXED_SHUNT * data->gain);
regval = clamp_val(regval >> 8, 0, U16_MAX);
return regmap_write(data->regmap, INA238_POWER_LIMIT, regval);
@@ -362,17 +446,17 @@ static int ina238_read_temp(struct device *dev, u32 attr, long *val)
err = regmap_read(data->regmap, INA238_DIE_TEMP, &regval);
if (err)
return err;
-
- /* Signed, bits 15-4 of register, result in mC */
- *val = ((s16)regval >> 4) * INA238_DIE_TEMP_LSB;
+ /* Signed, result in mC */
+ *val = div_s64(((s64)((s16)regval) >> data->config->temp_shift) *
+ (s64)data->config->temp_lsb, 10000);
break;
case hwmon_temp_max:
err = regmap_read(data->regmap, INA238_TEMP_LIMIT, &regval);
if (err)
return err;
-
- /* Signed, bits 15-4 of register, result in mC */
- *val = ((s16)regval >> 4) * INA238_DIE_TEMP_LSB;
+ /* Signed, result in mC */
+ *val = div_s64(((s64)((s16)regval) >> data->config->temp_shift) *
+ (s64)data->config->temp_lsb, 10000);
break;
case hwmon_temp_max_alarm:
err = regmap_read(data->regmap, INA238_DIAG_ALERT, &regval);
@@ -396,13 +480,33 @@ static int ina238_write_temp(struct device *dev, u32 attr, long val)
if (attr != hwmon_temp_max)
return -EOPNOTSUPP;
- /* Signed, bits 15-4 of register */
- regval = (val / INA238_DIE_TEMP_LSB) << 4;
- regval = clamp_val(regval, S16_MIN, S16_MAX) & 0xfff0;
+ /* Signed */
+ regval = clamp_val(val, -40000, 125000);
+ regval = div_s64(val * 10000, data->config->temp_lsb) << data->config->temp_shift;
+ regval = clamp_val(regval, S16_MIN, S16_MAX) & (0xffff << data->config->temp_shift);
return regmap_write(data->regmap, INA238_TEMP_LIMIT, regval);
}
+static ssize_t energy1_input_show(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct ina238_data *data = dev_get_drvdata(dev);
+ int ret;
+ u64 regval;
+ u64 energy;
+
+ ret = ina238_read_reg40(data->client, SQ52206_ENERGY, &regval);
+ if (ret)
+ return ret;
+
+ /* result in mJ */
+ energy = div_u64(regval * INA238_FIXED_SHUNT * data->gain * 16 *
+ data->config->power_calculate_factor, 4 * 100 * data->rshunt);
+
+ return sysfs_emit(buf, "%llu\n", energy);
+}
+
static int ina238_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
@@ -422,7 +526,7 @@ static int ina238_read(struct device *dev, enum hwmon_sensor_types type,
}
static int ina238_write(struct device *dev, enum hwmon_sensor_types type,
- u32 attr, int channel, long val)
+ u32 attr, int channel, long val)
{
struct ina238_data *data = dev_get_drvdata(dev);
int err;
@@ -452,6 +556,9 @@ static umode_t ina238_is_visible(const void *drvdata,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
+ const struct ina238_data *data = drvdata;
+ bool has_power_highest = data->config->has_power_highest;
+
switch (type) {
case hwmon_in:
switch (attr) {
@@ -479,6 +586,10 @@ static umode_t ina238_is_visible(const void *drvdata,
return 0444;
case hwmon_power_max:
return 0644;
+ case hwmon_power_input_highest:
+ if (has_power_highest)
+ return 0444;
+ return 0;
default:
return 0;
}
@@ -512,7 +623,8 @@ static const struct hwmon_channel_info * const ina238_info[] = {
HWMON_C_INPUT),
HWMON_CHANNEL_INFO(power,
/* 0: power */
- HWMON_P_INPUT | HWMON_P_MAX | HWMON_P_MAX_ALARM),
+ HWMON_P_INPUT | HWMON_P_MAX |
+ HWMON_P_MAX_ALARM | HWMON_P_INPUT_HIGHEST),
HWMON_CHANNEL_INFO(temp,
/* 0: die temperature */
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_ALARM),
@@ -530,20 +642,35 @@ static const struct hwmon_chip_info ina238_chip_info = {
.info = ina238_info,
};
+/* energy attributes are 5 bytes wide so we need u64 */
+static DEVICE_ATTR_RO(energy1_input);
+
+static struct attribute *ina238_attrs[] = {
+ &dev_attr_energy1_input.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(ina238);
+
static int ina238_probe(struct i2c_client *client)
{
struct ina2xx_platform_data *pdata = dev_get_platdata(&client->dev);
struct device *dev = &client->dev;
struct device *hwmon_dev;
struct ina238_data *data;
+ enum ina238_ids chip;
int config;
int ret;
+ chip = (uintptr_t)i2c_get_match_data(client);
+
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->client = client;
+ /* set the device type */
+ data->config = &ina238_config[chip];
+
mutex_init(&data->config_lock);
data->regmap = devm_regmap_init_i2c(client, &ina238_regmap_config);
@@ -564,15 +691,21 @@ static int ina238_probe(struct i2c_client *client)
/* load shunt gain value */
if (device_property_read_u32(dev, "ti,shunt-gain", &data->gain) < 0)
data->gain = 4; /* Default of ADCRANGE = 0 */
- if (data->gain != 1 && data->gain != 4) {
+ if (data->gain != 1 && data->gain != 2 && data->gain != 4) {
dev_err(dev, "invalid shunt gain value %u\n", data->gain);
return -EINVAL;
}
/* Setup CONFIG register */
- config = INA238_CONFIG_DEFAULT;
- if (data->gain == 1)
+ config = data->config->config_default;
+ if (chip == sq52206) {
+ if (data->gain == 1)
+ config |= SQ52206_CONFIG_ADCRANGE_HIGH; /* ADCRANGE = 10/11 is /1 */
+ else if (data->gain == 2)
+ config |= SQ52206_CONFIG_ADCRANGE_LOW; /* ADCRANGE = 01 is /2 */
+ } else if (data->gain == 1) {
config |= INA238_CONFIG_ADCRANGE; /* ADCRANGE = 1 is /1 */
+ }
ret = regmap_write(data->regmap, INA238_CONFIG, config);
if (ret < 0) {
dev_err(dev, "error configuring the device: %d\n", ret);
@@ -605,7 +738,8 @@ static int ina238_probe(struct i2c_client *client)
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
&ina238_chip_info,
- NULL);
+ data->config->has_energy ?
+ ina238_groups : NULL);
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
@@ -616,15 +750,27 @@ static int ina238_probe(struct i2c_client *client)
}
static const struct i2c_device_id ina238_id[] = {
- { "ina238" },
+ { "ina237", ina237 },
+ { "ina238", ina238 },
+ { "sq52206", sq52206 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ina238_id);
static const struct of_device_id __maybe_unused ina238_of_match[] = {
- { .compatible = "ti,ina237" },
- { .compatible = "ti,ina238" },
- { },
+ {
+ .compatible = "ti,ina237",
+ .data = (void *)ina237
+ },
+ {
+ .compatible = "ti,ina238",
+ .data = (void *)ina238
+ },
+ {
+ .compatible = "silergy,sq52206",
+ .data = (void *)sq52206
+ },
+ { }
};
MODULE_DEVICE_TABLE(of, ina238_of_match);
diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
index 345fe7db9de9..bc3c1f7314b3 100644
--- a/drivers/hwmon/ina2xx.c
+++ b/drivers/hwmon/ina2xx.c
@@ -959,8 +959,12 @@ static int ina2xx_probe(struct i2c_client *client)
return PTR_ERR(data->regmap);
}
- ret = devm_regulator_get_enable(dev, "vs");
- if (ret)
+ /*
+ * Regulator core returns -ENODEV if the 'vs' is not available.
+ * Hence the check for -ENODEV return code is necessary.
+ */
+ ret = devm_regulator_get_enable_optional(dev, "vs");
+ if (ret < 0 && ret != -ENODEV)
return dev_err_probe(dev, ret, "failed to enable vs regulator\n");
ret = ina2xx_init(dev, data);
diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c
index 1bf479a0f793..ce0e3f214f5b 100644
--- a/drivers/hwmon/ina3221.c
+++ b/drivers/hwmon/ina3221.c
@@ -116,7 +116,6 @@ struct ina3221_input {
* @fields: Register fields of the device
* @inputs: Array of channel input source specific structures
* @lock: mutex lock to serialize sysfs attribute accesses
- * @debugfs: Pointer to debugfs entry for device
* @reg_config: Register value of INA3221_CONFIG
* @summation_shunt_resistor: equivalent shunt resistor value for summation
* @summation_channel_control: Value written to SCC field in INA3221_MASK_ENABLE
@@ -128,7 +127,6 @@ struct ina3221_data {
struct regmap_field *fields[F_MAX_FIELDS];
struct ina3221_input inputs[INA3221_NUM_CHANNELS];
struct mutex lock;
- struct dentry *debugfs;
u32 reg_config;
int summation_shunt_resistor;
u32 summation_channel_control;
@@ -913,12 +911,9 @@ static int ina3221_probe(struct i2c_client *client)
goto fail;
}
- scnprintf(name, sizeof(name), "%s-%s", INA3221_DRIVER_NAME, dev_name(dev));
- ina->debugfs = debugfs_create_dir(name, NULL);
-
for (i = 0; i < INA3221_NUM_CHANNELS; i++) {
scnprintf(name, sizeof(name), "in%d_summation_disable", i);
- debugfs_create_bool(name, 0400, ina->debugfs,
+ debugfs_create_bool(name, 0400, client->debugfs,
&ina->inputs[i].summation_disable);
}
@@ -940,8 +935,6 @@ static void ina3221_remove(struct i2c_client *client)
struct ina3221_data *ina = dev_get_drvdata(&client->dev);
int i;
- debugfs_remove_recursive(ina->debugfs);
-
pm_runtime_disable(ina->pm_dev);
pm_runtime_set_suspended(ina->pm_dev);
diff --git a/drivers/hwmon/isl28022.c b/drivers/hwmon/isl28022.c
index 3f9b4520b53e..c2e559dde63f 100644
--- a/drivers/hwmon/isl28022.c
+++ b/drivers/hwmon/isl28022.c
@@ -154,6 +154,7 @@ static int isl28022_read_current(struct device *dev, u32 attr, long *val)
struct isl28022_data *data = dev_get_drvdata(dev);
unsigned int regval;
int err;
+ u16 sign_bit;
switch (attr) {
case hwmon_curr_input:
@@ -161,8 +162,9 @@ static int isl28022_read_current(struct device *dev, u32 attr, long *val)
ISL28022_REG_CURRENT, &regval);
if (err < 0)
return err;
- *val = ((long)regval * 1250L * (long)data->gain) /
- (long)data->shunt;
+ sign_bit = (regval >> 15) & 0x01;
+ *val = (((long)(((u16)regval) & 0x7FFF) - (sign_bit * 32768)) *
+ 1250L * (long)data->gain) / (long)data->shunt;
break;
default:
return -EOPNOTSUPP;
@@ -301,7 +303,7 @@ static const struct regmap_config isl28022_regmap_config = {
.writeable_reg = isl28022_is_writeable_reg,
.volatile_reg = isl28022_is_volatile_reg,
.val_format_endian = REGMAP_ENDIAN_BIG,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
};
@@ -324,26 +326,6 @@ static int shunt_voltage_show(struct seq_file *seqf, void *unused)
}
DEFINE_SHOW_ATTRIBUTE(shunt_voltage);
-static struct dentry *isl28022_debugfs_root;
-
-static void isl28022_debugfs_remove(void *res)
-{
- debugfs_remove_recursive(res);
-}
-
-static void isl28022_debugfs_init(struct i2c_client *client, struct isl28022_data *data)
-{
- char name[16];
- struct dentry *debugfs;
-
- scnprintf(name, sizeof(name), "%d-%04hx", client->adapter->nr, client->addr);
-
- debugfs = debugfs_create_dir(name, isl28022_debugfs_root);
- debugfs_create_file("shunt_voltage", 0444, debugfs, data, &shunt_voltage_fops);
-
- devm_add_action_or_reset(&client->dev, isl28022_debugfs_remove, debugfs);
-}
-
/*
* read property values and make consistency checks.
*
@@ -475,7 +457,7 @@ static int isl28022_probe(struct i2c_client *client)
if (err)
return err;
- isl28022_debugfs_init(client, data);
+ debugfs_create_file("shunt_voltage", 0444, client->debugfs, data, &shunt_voltage_fops);
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
data, &isl28022_chip_info, NULL);
@@ -505,27 +487,7 @@ static struct i2c_driver isl28022_driver = {
.probe = isl28022_probe,
.id_table = isl28022_ids,
};
-
-static int __init isl28022_init(void)
-{
- int err;
-
- isl28022_debugfs_root = debugfs_create_dir("isl28022", NULL);
- err = i2c_add_driver(&isl28022_driver);
- if (!err)
- return 0;
-
- debugfs_remove_recursive(isl28022_debugfs_root);
- return err;
-}
-module_init(isl28022_init);
-
-static void __exit isl28022_exit(void)
-{
- i2c_del_driver(&isl28022_driver);
- debugfs_remove_recursive(isl28022_debugfs_root);
-}
-module_exit(isl28022_exit);
+module_i2c_driver(isl28022_driver);
MODULE_AUTHOR("Carsten SpieĂŸ <mail@carsten-spiess.de>");
MODULE_DESCRIPTION("ISL28022 driver");
diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c
index d0b4cc9a5011..babf2413d666 100644
--- a/drivers/hwmon/k10temp.c
+++ b/drivers/hwmon/k10temp.c
@@ -20,7 +20,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
-#include <asm/amd_node.h>
+#include <asm/amd/node.h>
#include <asm/processor.h>
MODULE_DESCRIPTION("AMD Family 10h+ CPU core temperature monitor");
@@ -467,6 +467,7 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
k10temp_get_ccd_support(data, 4);
break;
case 0x31: /* Zen2 Threadripper */
+ case 0x47: /* Cyan Skillfish */
case 0x60: /* Renoir */
case 0x68: /* Lucienne */
case 0x71: /* Zen2 */
@@ -502,6 +503,13 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
k10temp_get_ccd_support(data, 12);
break;
}
+ } else if (boot_cpu_data.x86 == 0x1a) {
+ switch (boot_cpu_data.x86_model) {
+ case 0x40 ... 0x4f: /* Zen5 Ryzen Desktop */
+ data->ccd_offset = 0x308;
+ k10temp_get_ccd_support(data, 8);
+ break;
+ }
}
for (i = 0; i < ARRAY_SIZE(tctl_offset_table); i++) {
@@ -535,6 +543,7 @@ static const struct pci_device_id k10temp_id_table[] = {
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M30H_DF_F3) },
+ { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M40H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M60H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_MA0H_DF_F3) },
diff --git a/drivers/hwmon/kbatt.c b/drivers/hwmon/kbatt.c
new file mode 100644
index 000000000000..501b8f4ded33
--- /dev/null
+++ b/drivers/hwmon/kbatt.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 KEBA Industrial Automation GmbH
+ *
+ * Driver for KEBA battery monitoring controller FPGA IP core
+ */
+
+#include <linux/hwmon.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/auxiliary_bus.h>
+#include <linux/misc/keba.h>
+#include <linux/mutex.h>
+
+#define KBATT "kbatt"
+
+#define KBATT_CONTROL_REG 0x4
+#define KBATT_CONTROL_BAT_TEST 0x01
+
+#define KBATT_STATUS_REG 0x8
+#define KBATT_STATUS_BAT_OK 0x01
+
+#define KBATT_MAX_UPD_INTERVAL (10 * HZ)
+#define KBATT_SETTLE_TIME_US (100 * USEC_PER_MSEC)
+
+struct kbatt {
+ /* update lock */
+ struct mutex lock;
+ void __iomem *base;
+
+ unsigned long next_update; /* in jiffies */
+ bool alarm;
+};
+
+static bool kbatt_alarm(struct kbatt *kbatt)
+{
+ mutex_lock(&kbatt->lock);
+
+ if (!kbatt->next_update || time_after(jiffies, kbatt->next_update)) {
+ /* switch load on */
+ iowrite8(KBATT_CONTROL_BAT_TEST,
+ kbatt->base + KBATT_CONTROL_REG);
+
+ /* wait some time to let things settle */
+ fsleep(KBATT_SETTLE_TIME_US);
+
+ /* check battery state */
+ if (ioread8(kbatt->base + KBATT_STATUS_REG) &
+ KBATT_STATUS_BAT_OK)
+ kbatt->alarm = false;
+ else
+ kbatt->alarm = true;
+
+ /* switch load off */
+ iowrite8(0, kbatt->base + KBATT_CONTROL_REG);
+
+ kbatt->next_update = jiffies + KBATT_MAX_UPD_INTERVAL;
+ }
+
+ mutex_unlock(&kbatt->lock);
+
+ return kbatt->alarm;
+}
+
+static int kbatt_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct kbatt *kbatt = dev_get_drvdata(dev);
+
+ *val = kbatt_alarm(kbatt) ? 1 : 0;
+
+ return 0;
+}
+
+static umode_t kbatt_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ if (channel == 0 && attr == hwmon_in_min_alarm)
+ return 0444;
+
+ return 0;
+}
+
+static const struct hwmon_channel_info *kbatt_info[] = {
+ HWMON_CHANNEL_INFO(in,
+ /* 0: input minimum alarm channel */
+ HWMON_I_MIN_ALARM),
+ NULL
+};
+
+static const struct hwmon_ops kbatt_hwmon_ops = {
+ .is_visible = kbatt_is_visible,
+ .read = kbatt_read,
+};
+
+static const struct hwmon_chip_info kbatt_chip_info = {
+ .ops = &kbatt_hwmon_ops,
+ .info = kbatt_info,
+};
+
+static int kbatt_probe(struct auxiliary_device *auxdev,
+ const struct auxiliary_device_id *id)
+{
+ struct keba_batt_auxdev *kbatt_auxdev =
+ container_of(auxdev, struct keba_batt_auxdev, auxdev);
+ struct device *dev = &auxdev->dev;
+ struct device *hwmon_dev;
+ struct kbatt *kbatt;
+ int retval;
+
+ kbatt = devm_kzalloc(dev, sizeof(*kbatt), GFP_KERNEL);
+ if (!kbatt)
+ return -ENOMEM;
+
+ retval = devm_mutex_init(dev, &kbatt->lock);
+ if (retval)
+ return retval;
+
+ kbatt->base = devm_ioremap_resource(dev, &kbatt_auxdev->io);
+ if (IS_ERR(kbatt->base))
+ return PTR_ERR(kbatt->base);
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, KBATT, kbatt,
+ &kbatt_chip_info,
+ NULL);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct auxiliary_device_id kbatt_devtype_aux[] = {
+ { .name = "keba.batt" },
+ {}
+};
+MODULE_DEVICE_TABLE(auxiliary, kbatt_devtype_aux);
+
+static struct auxiliary_driver kbatt_driver_aux = {
+ .name = KBATT,
+ .id_table = kbatt_devtype_aux,
+ .probe = kbatt_probe,
+};
+module_auxiliary_driver(kbatt_driver_aux);
+
+MODULE_AUTHOR("Petar Bojanic <boja@keba.com>");
+MODULE_AUTHOR("Gerhard Engleder <eg@keba.com>");
+MODULE_DESCRIPTION("KEBA battery monitoring controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/kfan.c b/drivers/hwmon/kfan.c
new file mode 100644
index 000000000000..f353acb66749
--- /dev/null
+++ b/drivers/hwmon/kfan.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 KEBA Industrial Automation GmbH
+ *
+ * Driver for KEBA fan controller FPGA IP core
+ *
+ */
+
+#include <linux/hwmon.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/auxiliary_bus.h>
+#include <linux/misc/keba.h>
+
+#define KFAN "kfan"
+
+#define KFAN_CONTROL_REG 0x04
+
+#define KFAN_STATUS_REG 0x08
+#define KFAN_STATUS_PRESENT 0x01
+#define KFAN_STATUS_REGULABLE 0x02
+#define KFAN_STATUS_TACHO 0x04
+#define KFAN_STATUS_BLOCKED 0x08
+
+#define KFAN_TACHO_REG 0x0c
+
+#define KFAN_DEFAULT_DIV 2
+
+struct kfan {
+ void __iomem *base;
+ bool tacho;
+ bool regulable;
+
+ /* hwmon API configuration */
+ u32 fan_channel_config[2];
+ struct hwmon_channel_info fan_info;
+ u32 pwm_channel_config[2];
+ struct hwmon_channel_info pwm_info;
+ const struct hwmon_channel_info *info[3];
+ struct hwmon_chip_info chip;
+};
+
+static bool kfan_get_fault(struct kfan *kfan)
+{
+ u8 status = ioread8(kfan->base + KFAN_STATUS_REG);
+
+ if (!(status & KFAN_STATUS_PRESENT))
+ return true;
+
+ if (!kfan->tacho && (status & KFAN_STATUS_BLOCKED))
+ return true;
+
+ return false;
+}
+
+static unsigned int kfan_count_to_rpm(u16 count)
+{
+ if (count == 0 || count == 0xffff)
+ return 0;
+
+ return 5000000UL / (KFAN_DEFAULT_DIV * count);
+}
+
+static unsigned int kfan_get_rpm(struct kfan *kfan)
+{
+ unsigned int rpm;
+ u16 count;
+
+ count = ioread16(kfan->base + KFAN_TACHO_REG);
+ rpm = kfan_count_to_rpm(count);
+
+ return rpm;
+}
+
+static unsigned int kfan_get_pwm(struct kfan *kfan)
+{
+ return ioread8(kfan->base + KFAN_CONTROL_REG);
+}
+
+static int kfan_set_pwm(struct kfan *kfan, long val)
+{
+ if (val < 0 || val > 0xff)
+ return -EINVAL;
+
+ /* if none-regulable, then only 0 or 0xff can be written */
+ if (!kfan->regulable && val > 0)
+ val = 0xff;
+
+ iowrite8(val, kfan->base + KFAN_CONTROL_REG);
+
+ return 0;
+}
+
+static int kfan_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ struct kfan *kfan = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_pwm:
+ switch (attr) {
+ case hwmon_pwm_input:
+ return kfan_set_pwm(kfan, val);
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int kfan_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct kfan *kfan = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_fan:
+ switch (attr) {
+ case hwmon_fan_fault:
+ *val = kfan_get_fault(kfan);
+ return 0;
+ case hwmon_fan_input:
+ *val = kfan_get_rpm(kfan);
+ return 0;
+ default:
+ break;
+ }
+ break;
+ case hwmon_pwm:
+ switch (attr) {
+ case hwmon_pwm_input:
+ *val = kfan_get_pwm(kfan);
+ return 0;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static umode_t kfan_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_fan:
+ switch (attr) {
+ case hwmon_fan_input:
+ return 0444;
+ case hwmon_fan_fault:
+ return 0444;
+ default:
+ break;
+ }
+ break;
+ case hwmon_pwm:
+ switch (attr) {
+ case hwmon_pwm_input:
+ return 0644;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct hwmon_ops kfan_hwmon_ops = {
+ .is_visible = kfan_is_visible,
+ .read = kfan_read,
+ .write = kfan_write,
+};
+
+static int kfan_probe(struct auxiliary_device *auxdev,
+ const struct auxiliary_device_id *id)
+{
+ struct keba_fan_auxdev *kfan_auxdev =
+ container_of(auxdev, struct keba_fan_auxdev, auxdev);
+ struct device *dev = &auxdev->dev;
+ struct device *hwmon_dev;
+ struct kfan *kfan;
+ u8 status;
+
+ kfan = devm_kzalloc(dev, sizeof(*kfan), GFP_KERNEL);
+ if (!kfan)
+ return -ENOMEM;
+
+ kfan->base = devm_ioremap_resource(dev, &kfan_auxdev->io);
+ if (IS_ERR(kfan->base))
+ return PTR_ERR(kfan->base);
+
+ status = ioread8(kfan->base + KFAN_STATUS_REG);
+ if (status & KFAN_STATUS_REGULABLE)
+ kfan->regulable = true;
+ if (status & KFAN_STATUS_TACHO)
+ kfan->tacho = true;
+
+ /* fan */
+ kfan->fan_channel_config[0] = HWMON_F_FAULT;
+ if (kfan->tacho)
+ kfan->fan_channel_config[0] |= HWMON_F_INPUT;
+ kfan->fan_info.type = hwmon_fan;
+ kfan->fan_info.config = kfan->fan_channel_config;
+ kfan->info[0] = &kfan->fan_info;
+
+ /* PWM */
+ kfan->pwm_channel_config[0] = HWMON_PWM_INPUT;
+ kfan->pwm_info.type = hwmon_pwm;
+ kfan->pwm_info.config = kfan->pwm_channel_config;
+ kfan->info[1] = &kfan->pwm_info;
+
+ kfan->chip.ops = &kfan_hwmon_ops;
+ kfan->chip.info = kfan->info;
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, KFAN, kfan,
+ &kfan->chip, NULL);
+ return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static const struct auxiliary_device_id kfan_devtype_aux[] = {
+ { .name = "keba.fan" },
+ {}
+};
+MODULE_DEVICE_TABLE(auxiliary, kfan_devtype_aux);
+
+static struct auxiliary_driver kfan_driver_aux = {
+ .name = KFAN,
+ .id_table = kfan_devtype_aux,
+ .probe = kfan_probe,
+};
+module_auxiliary_driver(kfan_driver_aux);
+
+MODULE_AUTHOR("Petar Bojanic <boja@keba.com>");
+MODULE_AUTHOR("Gerhard Engleder <eg@keba.com>");
+MODULE_DESCRIPTION("KEBA fan controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
index d95a3c6c245c..9b4875e2fd8d 100644
--- a/drivers/hwmon/lm75.c
+++ b/drivers/hwmon/lm75.c
@@ -622,7 +622,7 @@ static int lm75_i3c_reg_read(void *context, unsigned int reg, unsigned int *val)
{
.rnw = true,
.len = 2,
- .data.out = data->val_buf,
+ .data.in = data->val_buf,
},
};
int ret;
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 511d95a0efb3..c1f528e292f3 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -90,6 +90,9 @@
* This driver also supports NE1618 from Philips. It is similar to NE1617
* but supports 11 bit external temperature values.
*
+ * This driver also supports NCT7716, NCT7717 and NCT7718 from Nuvoton.
+ * The NCT7716 is similar to NCT7717 but has one more address support.
+ *
* Since the LM90 was the first chipset supported by this driver, most
* comments will refer to this chipset, but are actually general and
* concern all supported chipsets, unless mentioned otherwise.
@@ -119,13 +122,15 @@
* Address is fully defined internally and cannot be changed except for
* MAX6659, MAX6680 and MAX6681.
* LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, ADT7461A, MAX6649,
- * MAX6657, MAX6658, NCT1008 and W83L771 have address 0x4c.
+ * MAX6657, MAX6658, NCT1008, NCT7718 and W83L771 have address 0x4c.
* ADM1032-2, ADT7461-2, ADT7461A-2, LM89-1, LM99-1, MAX6646, and NCT1008D
* have address 0x4d.
* MAX6647 has address 0x4e.
* MAX6659 can have address 0x4c, 0x4d or 0x4e.
* MAX6654, MAX6680, and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29,
* 0x2a, 0x2b, 0x4c, 0x4d or 0x4e.
+ * NCT7716 can have address 0x48 or 0x49.
+ * NCT7717 has address 0x48.
* SA56004 can have address 0x48 through 0x4F.
*/
@@ -136,7 +141,7 @@ static const unsigned short normal_i2c[] = {
enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481,
g781, lm84, lm90, lm99,
max1617, max6642, max6646, max6648, max6654, max6657, max6659, max6680, max6696,
- nct210, nct72, ne1618, sa56004, tmp451, tmp461, w83l771,
+ nct210, nct72, nct7716, nct7717, nct7718, ne1618, sa56004, tmp451, tmp461, w83l771,
};
/*
@@ -191,6 +196,9 @@ enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481,
#define ADT7481_REG_MAN_ID 0x3e
#define ADT7481_REG_CHIP_ID 0x3d
+/* NCT7716/7717/7718 registers */
+#define NCT7716_REG_CHIP_ID 0xFD
+
/* Device features */
#define LM90_HAVE_EXTENDED_TEMP BIT(0) /* extended temperature support */
#define LM90_HAVE_OFFSET BIT(1) /* temperature offset register */
@@ -275,6 +283,9 @@ static const struct i2c_device_id lm90_id[] = {
{ "nct214", nct72 },
{ "nct218", nct72 },
{ "nct72", nct72 },
+ { "nct7716", nct7716 },
+ { "nct7717", nct7717 },
+ { "nct7718", nct7718 },
{ "ne1618", ne1618 },
{ "w83l771", w83l771 },
{ "sa56004", sa56004 },
@@ -383,6 +394,18 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = {
.data = (void *)nct72
},
{
+ .compatible = "nuvoton,nct7716",
+ .data = (void *)nct7716
+ },
+ {
+ .compatible = "nuvoton,nct7717",
+ .data = (void *)nct7717
+ },
+ {
+ .compatible = "nuvoton,nct7718",
+ .data = (void *)nct7718
+ },
+ {
.compatible = "winbond,w83l771",
.data = (void *)w83l771
},
@@ -601,6 +624,26 @@ static const struct lm90_params lm90_params[] = {
.resolution = 11,
.max_convrate = 7,
},
+ [nct7716] = {
+ .flags = LM90_HAVE_ALARMS | LM90_HAVE_CONVRATE,
+ .alert_alarms = 0x40,
+ .resolution = 8,
+ .max_convrate = 8,
+ },
+ [nct7717] = {
+ .flags = LM90_HAVE_ALARMS | LM90_HAVE_CONVRATE,
+ .alert_alarms = 0x40,
+ .resolution = 8,
+ .max_convrate = 8,
+ },
+ [nct7718] = {
+ .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT
+ | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
+ | LM90_HAVE_REMOTE_EXT,
+ .alert_alarms = 0x7c,
+ .resolution = 11,
+ .max_convrate = 8,
+ },
[ne1618] = {
.flags = LM90_PAUSE_FOR_CONFIG | LM90_HAVE_BROKEN_ALERT
| LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
@@ -1192,7 +1235,7 @@ static int lm90_update_alarms(struct lm90_data *data, bool force)
static void lm90_alert_work(struct work_struct *__work)
{
- struct delayed_work *delayed_work = container_of(__work, struct delayed_work, work);
+ struct delayed_work *delayed_work = to_delayed_work(__work);
struct lm90_data *data = container_of(delayed_work, struct lm90_data, alert_work);
/* Nothing to do if alerts are enabled */
@@ -2300,6 +2343,38 @@ static const char *lm90_detect_nuvoton(struct i2c_client *client, int chip_id,
return name;
}
+static const char *lm90_detect_nuvoton_50(struct i2c_client *client, int chip_id,
+ int config1, int convrate)
+{
+ int chip_id2 = i2c_smbus_read_byte_data(client, NCT7716_REG_CHIP_ID);
+ int config2 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG2);
+ int address = client->addr;
+ const char *name = NULL;
+
+ if (chip_id2 < 0 || config2 < 0)
+ return NULL;
+
+ if (chip_id2 != 0x50 || convrate > 0x08)
+ return NULL;
+
+ switch (chip_id) {
+ case 0x90:
+ if (address == 0x48 && !(config1 & 0x3e) && !(config2 & 0xfe))
+ name = "nct7717";
+ break;
+ case 0x91:
+ if ((address == 0x48 || address == 0x49) && !(config1 & 0x3e) &&
+ !(config2 & 0xfe))
+ name = "nct7716";
+ else if (address == 0x4c && !(config1 & 0x38) && !(config2 & 0xf8))
+ name = "nct7718";
+ break;
+ default:
+ break;
+ }
+ return name;
+}
+
static const char *lm90_detect_nxp(struct i2c_client *client, bool common_address,
int chip_id, int config1, int convrate)
{
@@ -2484,6 +2559,9 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info)
name = lm90_detect_maxim(client, common_address, chip_id,
config1, convrate);
break;
+ case 0x50:
+ name = lm90_detect_nuvoton_50(client, chip_id, config1, convrate);
+ break;
case 0x54: /* ON MC1066, Microchip TC1068, TCM1617 (originally TelCom) */
if (common_address && !(config1 & 0x3f) && !(convrate & 0xf8))
name = "mc1066";
diff --git a/drivers/hwmon/ltc2992.c b/drivers/hwmon/ltc2992.c
index 541fa09dc6e7..a07e2eb93c71 100644
--- a/drivers/hwmon/ltc2992.c
+++ b/drivers/hwmon/ltc2992.c
@@ -256,33 +256,38 @@ static int ltc2992_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask
return 0;
}
-static void ltc2992_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
+static int ltc2992_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
struct ltc2992_state *st = gpiochip_get_data(chip);
unsigned long gpio_ctrl;
- int reg;
+ int reg, ret;
mutex_lock(&st->gpio_mutex);
reg = ltc2992_read_reg(st, ltc2992_gpio_addr_map[offset].ctrl, 1);
if (reg < 0) {
mutex_unlock(&st->gpio_mutex);
- return;
+ return reg;
}
gpio_ctrl = reg;
assign_bit(ltc2992_gpio_addr_map[offset].ctrl_bit, &gpio_ctrl, value);
- ltc2992_write_reg(st, ltc2992_gpio_addr_map[offset].ctrl, 1, gpio_ctrl);
+ ret = ltc2992_write_reg(st, ltc2992_gpio_addr_map[offset].ctrl, 1,
+ gpio_ctrl);
mutex_unlock(&st->gpio_mutex);
+
+ return ret;
}
-static void ltc2992_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask,
- unsigned long *bits)
+static int ltc2992_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask,
+ unsigned long *bits)
{
struct ltc2992_state *st = gpiochip_get_data(chip);
unsigned long gpio_ctrl_io = 0;
unsigned long gpio_ctrl = 0;
unsigned int gpio_nr;
+ int ret;
for_each_set_bit(gpio_nr, mask, LTC2992_GPIO_NR) {
if (gpio_nr < 3)
@@ -293,9 +298,14 @@ static void ltc2992_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mas
}
mutex_lock(&st->gpio_mutex);
- ltc2992_write_reg(st, LTC2992_GPIO_IO_CTRL, 1, gpio_ctrl_io);
- ltc2992_write_reg(st, LTC2992_GPIO_CTRL, 1, gpio_ctrl);
+ ret = ltc2992_write_reg(st, LTC2992_GPIO_IO_CTRL, 1, gpio_ctrl_io);
+ if (ret)
+ goto out;
+
+ ret = ltc2992_write_reg(st, LTC2992_GPIO_CTRL, 1, gpio_ctrl);
+out:
mutex_unlock(&st->gpio_mutex);
+ return ret;
}
static int ltc2992_config_gpio(struct ltc2992_state *st)
@@ -329,8 +339,8 @@ static int ltc2992_config_gpio(struct ltc2992_state *st)
st->gc.ngpio = ARRAY_SIZE(st->gpio_names);
st->gc.get = ltc2992_gpio_get;
st->gc.get_multiple = ltc2992_gpio_get_multiple;
- st->gc.set = ltc2992_gpio_set;
- st->gc.set_multiple = ltc2992_gpio_set_multiple;
+ st->gc.set_rv = ltc2992_gpio_set;
+ st->gc.set_multiple_rv = ltc2992_gpio_set_multiple;
ret = devm_gpiochip_add_data(&st->client->dev, &st->gc, st);
if (ret)
diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c
index 4f608a3790fb..f607fe8f7937 100644
--- a/drivers/hwmon/ltc4282.c
+++ b/drivers/hwmon/ltc4282.c
@@ -1512,13 +1512,6 @@ static int ltc4282_setup(struct ltc4282_state *st, struct device *dev)
}
if (device_property_read_bool(dev, "adi,fault-log-enable")) {
- ret = regmap_set_bits(st->map, LTC4282_ADC_CTRL,
- LTC4282_FAULT_LOG_EN_MASK);
- if (ret)
- return ret;
- }
-
- if (device_property_read_bool(dev, "adi,fault-log-enable")) {
ret = regmap_set_bits(st->map, LTC4282_ADC_CTRL, LTC4282_FAULT_LOG_EN_MASK);
if (ret)
return ret;
@@ -1674,47 +1667,19 @@ static int ltc4282_show_power1_bad_fault_log(void *arg, u64 *val)
DEFINE_DEBUGFS_ATTRIBUTE(ltc4282_power1_bad_fault_log,
ltc4282_show_power1_bad_fault_log, NULL, "%llu\n");
-static void ltc4282_debugfs_remove(void *dir)
-{
- debugfs_remove_recursive(dir);
-}
-
-static void ltc4282_debugfs_init(struct ltc4282_state *st,
- struct i2c_client *i2c,
- const struct device *hwmon)
+static void ltc4282_debugfs_init(struct ltc4282_state *st, struct i2c_client *i2c)
{
- const char *debugfs_name;
- struct dentry *dentry;
- int ret;
-
- if (!IS_ENABLED(CONFIG_DEBUG_FS))
- return;
-
- debugfs_name = devm_kasprintf(&i2c->dev, GFP_KERNEL, "ltc4282-%s",
- dev_name(hwmon));
- if (!debugfs_name)
- return;
-
- dentry = debugfs_create_dir(debugfs_name, NULL);
- if (IS_ERR(dentry))
- return;
-
- ret = devm_add_action_or_reset(&i2c->dev, ltc4282_debugfs_remove,
- dentry);
- if (ret)
- return;
-
- debugfs_create_file_unsafe("power1_bad_fault_log", 0400, dentry, st,
+ debugfs_create_file_unsafe("power1_bad_fault_log", 0400, i2c->debugfs, st,
&ltc4282_power1_bad_fault_log);
- debugfs_create_file_unsafe("in0_fet_short_fault_log", 0400, dentry, st,
+ debugfs_create_file_unsafe("in0_fet_short_fault_log", 0400, i2c->debugfs, st,
&ltc4282_fet_short_fault_log);
- debugfs_create_file_unsafe("in0_fet_bad_fault_log", 0400, dentry, st,
+ debugfs_create_file_unsafe("in0_fet_bad_fault_log", 0400, i2c->debugfs, st,
&ltc4282_fet_bad_fault_log);
- debugfs_create_file_unsafe("in1_crit_fault_log", 0400, dentry, st,
+ debugfs_create_file_unsafe("in1_crit_fault_log", 0400, i2c->debugfs, st,
&ltc4282_in1_crit_fault_log);
- debugfs_create_file_unsafe("in1_lcrit_fault_log", 0400, dentry, st,
+ debugfs_create_file_unsafe("in1_lcrit_fault_log", 0400, i2c->debugfs, st,
&ltc4282_in1_lcrit_fault_log);
- debugfs_create_file_unsafe("curr1_crit_fault_log", 0400, dentry, st,
+ debugfs_create_file_unsafe("curr1_crit_fault_log", 0400, i2c->debugfs, st,
&ltc4282_curr1_crit_fault_log);
}
@@ -1757,7 +1722,7 @@ static int ltc4282_probe(struct i2c_client *i2c)
if (IS_ERR(hwmon))
return PTR_ERR(hwmon);
- ltc4282_debugfs_init(st, i2c, hwmon);
+ ltc4282_debugfs_init(st, i2c);
return 0;
}
diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c
index 32b4d54b2076..a06346496e1d 100644
--- a/drivers/hwmon/max6639.c
+++ b/drivers/hwmon/max6639.c
@@ -80,6 +80,7 @@ struct max6639_data {
/* Register values initialized only once */
u8 ppr[MAX6639_NUM_CHANNELS]; /* Pulses per rotation 0..3 for 1..4 ppr */
u8 rpm_range[MAX6639_NUM_CHANNELS]; /* Index in above rpm_ranges table */
+ u32 target_rpm[MAX6639_NUM_CHANNELS];
/* Optional regulator for FAN supply */
struct regulator *reg;
@@ -563,6 +564,10 @@ static int max6639_probe_child_from_dt(struct i2c_client *client,
if (!err)
data->rpm_range[i] = rpm_range_to_reg(val);
+ err = of_property_read_u32(child, "target-rpm", &val);
+ if (!err)
+ data->target_rpm[i] = val;
+
return 0;
}
@@ -573,6 +578,7 @@ static int max6639_init_client(struct i2c_client *client,
const struct device_node *np = dev->of_node;
struct device_node *child;
int i, err;
+ u8 target_duty;
/* Reset chip to default values, see below for GCONFIG setup */
err = regmap_write(data->regmap, MAX6639_REG_GCONFIG, MAX6639_GCONFIG_POR);
@@ -586,6 +592,8 @@ static int max6639_init_client(struct i2c_client *client,
/* default: 4000 RPM */
data->rpm_range[0] = 1;
data->rpm_range[1] = 1;
+ data->target_rpm[0] = 4000;
+ data->target_rpm[1] = 4000;
for_each_child_of_node(np, child) {
if (strcmp(child->name, "fan"))
@@ -639,8 +647,12 @@ static int max6639_init_client(struct i2c_client *client,
if (err)
return err;
- /* PWM 120/120 (i.e. 100%) */
- err = regmap_write(data->regmap, MAX6639_REG_TARGTDUTY(i), 120);
+ /* Set PWM based on target RPM if specified */
+ if (data->target_rpm[i] > rpm_ranges[data->rpm_range[i]])
+ data->target_rpm[i] = rpm_ranges[data->rpm_range[i]];
+
+ target_duty = 120 * data->target_rpm[i] / rpm_ranges[data->rpm_range[i]];
+ err = regmap_write(data->regmap, MAX6639_REG_TARGTDUTY(i), target_duty);
if (err)
return err;
}
diff --git a/drivers/hwmon/max77705-hwmon.c b/drivers/hwmon/max77705-hwmon.c
new file mode 100644
index 000000000000..990023e6474e
--- /dev/null
+++ b/drivers/hwmon/max77705-hwmon.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MAX77705 voltage and current hwmon driver.
+ *
+ * Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.com>
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/kernel.h>
+#include <linux/mfd/max77705-private.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+struct channel_desc {
+ u8 reg;
+ u8 avg_reg;
+ const char *const label;
+ // register resolution. nano Volts for voltage, nano Amperes for current
+ u32 resolution;
+};
+
+static const struct channel_desc current_channel_desc[] = {
+ {
+ .reg = IIN_REG,
+ .label = "IIN_REG",
+ .resolution = 125000
+ },
+ {
+ .reg = ISYS_REG,
+ .avg_reg = AVGISYS_REG,
+ .label = "ISYS_REG",
+ .resolution = 312500
+ }
+};
+
+static const struct channel_desc voltage_channel_desc[] = {
+ {
+ .reg = VBYP_REG,
+ .label = "VBYP_REG",
+ .resolution = 427246
+ },
+ {
+ .reg = VSYS_REG,
+ .label = "VSYS_REG",
+ .resolution = 156250
+ }
+};
+
+static int max77705_read_and_convert(struct regmap *regmap, u8 reg, u32 res,
+ bool is_signed, long *val)
+{
+ int ret;
+ u32 regval;
+
+ ret = regmap_read(regmap, reg, &regval);
+ if (ret < 0)
+ return ret;
+
+ if (is_signed)
+ *val = mult_frac((long)sign_extend32(regval, 15), res, 1000000);
+ else
+ *val = mult_frac((long)regval, res, 1000000);
+
+ return 0;
+}
+
+static umode_t max77705_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_in:
+ switch (attr) {
+ case hwmon_in_input:
+ case hwmon_in_label:
+ return 0444;
+ default:
+ break;
+ }
+ break;
+ case hwmon_curr:
+ switch (attr) {
+ case hwmon_curr_input:
+ case hwmon_in_label:
+ return 0444;
+ case hwmon_curr_average:
+ if (current_channel_desc[channel].avg_reg)
+ return 0444;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int max77705_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+ int channel, const char **buf)
+{
+ switch (type) {
+ case hwmon_curr:
+ switch (attr) {
+ case hwmon_in_label:
+ *buf = current_channel_desc[channel].label;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ case hwmon_in:
+ switch (attr) {
+ case hwmon_in_label:
+ *buf = voltage_channel_desc[channel].label;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int max77705_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct regmap *regmap = dev_get_drvdata(dev);
+ u8 reg;
+ u32 res;
+
+ switch (type) {
+ case hwmon_curr:
+ switch (attr) {
+ case hwmon_curr_input:
+ reg = current_channel_desc[channel].reg;
+ res = current_channel_desc[channel].resolution;
+
+ return max77705_read_and_convert(regmap, reg, res, true, val);
+ case hwmon_curr_average:
+ reg = current_channel_desc[channel].avg_reg;
+ res = current_channel_desc[channel].resolution;
+
+ return max77705_read_and_convert(regmap, reg, res, true, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ case hwmon_in:
+ switch (attr) {
+ case hwmon_in_input:
+ reg = voltage_channel_desc[channel].reg;
+ res = voltage_channel_desc[channel].resolution;
+
+ return max77705_read_and_convert(regmap, reg, res, false, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static const struct hwmon_ops max77705_hwmon_ops = {
+ .is_visible = max77705_is_visible,
+ .read = max77705_read,
+ .read_string = max77705_read_string,
+};
+
+static const struct hwmon_channel_info *max77705_info[] = {
+ HWMON_CHANNEL_INFO(in,
+ 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_AVERAGE | HWMON_C_LABEL
+ ),
+ NULL
+};
+
+static const struct hwmon_chip_info max77705_chip_info = {
+ .ops = &max77705_hwmon_ops,
+ .info = max77705_info,
+};
+
+static int max77705_hwmon_probe(struct platform_device *pdev)
+{
+ struct device *hwmon_dev;
+ struct regmap *regmap;
+
+ regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!regmap)
+ return -ENODEV;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, "max77705", regmap,
+ &max77705_chip_info, NULL);
+ if (IS_ERR(hwmon_dev))
+ return dev_err_probe(&pdev->dev, PTR_ERR(hwmon_dev),
+ "Unable to register hwmon device\n");
+
+ return 0;
+};
+
+static struct platform_driver max77705_hwmon_driver = {
+ .driver = {
+ .name = "max77705-hwmon",
+ },
+ .probe = max77705_hwmon_probe,
+};
+
+module_platform_driver(max77705_hwmon_driver);
+
+MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>");
+MODULE_DESCRIPTION("MAX77705 monitor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c
index 416ac02e9f74..6cda35388b24 100644
--- a/drivers/hwmon/nct6683.c
+++ b/drivers/hwmon/nct6683.c
@@ -176,6 +176,7 @@ superio_exit(int ioreg)
#define NCT6683_CUSTOMER_ID_MSI2 0x200
#define NCT6683_CUSTOMER_ID_MSI3 0x207
#define NCT6683_CUSTOMER_ID_MSI4 0x20d
+#define NCT6683_CUSTOMER_ID_AMD 0x162b
#define NCT6683_CUSTOMER_ID_ASROCK 0xe2c
#define NCT6683_CUSTOMER_ID_ASROCK2 0xe1b
#define NCT6683_CUSTOMER_ID_ASROCK3 0x1631
@@ -1231,6 +1232,8 @@ static int nct6683_probe(struct platform_device *pdev)
break;
case NCT6683_CUSTOMER_ID_MSI4:
break;
+ case NCT6683_CUSTOMER_ID_AMD:
+ break;
case NCT6683_CUSTOMER_ID_ASROCK:
break;
case NCT6683_CUSTOMER_ID_ASROCK2:
diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c
index fa3351351825..79bc67ffb998 100644
--- a/drivers/hwmon/nct6775-core.c
+++ b/drivers/hwmon/nct6775-core.c
@@ -273,8 +273,8 @@ static const s8 NCT6776_BEEP_BITS[NUM_BEEP_BITS] = {
static const u16 NCT6776_REG_TOLERANCE_H[] = {
0x10c, 0x20c, 0x30c, 0x80c, 0x90c, 0xa0c, 0xb0c };
-static const u8 NCT6776_REG_PWM_MODE[] = { 0x04, 0, 0, 0, 0, 0 };
-static const u8 NCT6776_PWM_MODE_MASK[] = { 0x01, 0, 0, 0, 0, 0 };
+static const u8 NCT6776_REG_PWM_MODE[] = { 0x04, 0, 0, 0, 0, 0, 0 };
+static const u8 NCT6776_PWM_MODE_MASK[] = { 0x01, 0, 0, 0, 0, 0, 0 };
static const u16 NCT6776_REG_FAN_MIN[] = {
0x63a, 0x63c, 0x63e, 0x640, 0x642, 0x64a, 0x64c };
diff --git a/drivers/hwmon/nct7363.c b/drivers/hwmon/nct7363.c
index be7bf32f6e68..e13ab918b1ab 100644
--- a/drivers/hwmon/nct7363.c
+++ b/drivers/hwmon/nct7363.c
@@ -391,7 +391,7 @@ static const struct regmap_config nct7363_regmap_config = {
.val_bits = 8,
.use_single_read = true,
.use_single_write = true,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = nct7363_regmap_is_volatile,
};
diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c
index db3b551828eb..802c73def428 100644
--- a/drivers/hwmon/npcm750-pwm-fan.c
+++ b/drivers/hwmon/npcm750-pwm-fan.c
@@ -331,7 +331,7 @@ static void npcm7xx_fan_polling(struct timer_list *t)
struct npcm7xx_pwm_fan_data *data;
int i;
- data = from_timer(data, t, fan_timer);
+ data = timer_container_of(data, t, fan_timer);
/*
* Polling two module per one round,
diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c
index b5352900463f..d21f7266c411 100644
--- a/drivers/hwmon/ntc_thermistor.c
+++ b/drivers/hwmon/ntc_thermistor.c
@@ -181,40 +181,40 @@ static const struct ntc_compensation ncpXXwf104[] = {
};
static const struct ntc_compensation ncpXXxh103[] = {
- { .temp_c = -40, .ohm = 247565 },
- { .temp_c = -35, .ohm = 181742 },
- { .temp_c = -30, .ohm = 135128 },
- { .temp_c = -25, .ohm = 101678 },
- { .temp_c = -20, .ohm = 77373 },
- { .temp_c = -15, .ohm = 59504 },
- { .temp_c = -10, .ohm = 46222 },
- { .temp_c = -5, .ohm = 36244 },
- { .temp_c = 0, .ohm = 28674 },
- { .temp_c = 5, .ohm = 22878 },
- { .temp_c = 10, .ohm = 18399 },
- { .temp_c = 15, .ohm = 14910 },
- { .temp_c = 20, .ohm = 12169 },
+ { .temp_c = -40, .ohm = 195652 },
+ { .temp_c = -35, .ohm = 148171 },
+ { .temp_c = -30, .ohm = 113347 },
+ { .temp_c = -25, .ohm = 87559 },
+ { .temp_c = -20, .ohm = 68237 },
+ { .temp_c = -15, .ohm = 53650 },
+ { .temp_c = -10, .ohm = 42506 },
+ { .temp_c = -5, .ohm = 33892 },
+ { .temp_c = 0, .ohm = 27219 },
+ { .temp_c = 5, .ohm = 22021 },
+ { .temp_c = 10, .ohm = 17926 },
+ { .temp_c = 15, .ohm = 14674 },
+ { .temp_c = 20, .ohm = 12081 },
{ .temp_c = 25, .ohm = 10000 },
- { .temp_c = 30, .ohm = 8271 },
- { .temp_c = 35, .ohm = 6883 },
- { .temp_c = 40, .ohm = 5762 },
- { .temp_c = 45, .ohm = 4851 },
- { .temp_c = 50, .ohm = 4105 },
- { .temp_c = 55, .ohm = 3492 },
- { .temp_c = 60, .ohm = 2985 },
- { .temp_c = 65, .ohm = 2563 },
- { .temp_c = 70, .ohm = 2211 },
- { .temp_c = 75, .ohm = 1915 },
- { .temp_c = 80, .ohm = 1666 },
- { .temp_c = 85, .ohm = 1454 },
- { .temp_c = 90, .ohm = 1275 },
- { .temp_c = 95, .ohm = 1121 },
- { .temp_c = 100, .ohm = 990 },
- { .temp_c = 105, .ohm = 876 },
- { .temp_c = 110, .ohm = 779 },
- { .temp_c = 115, .ohm = 694 },
- { .temp_c = 120, .ohm = 620 },
- { .temp_c = 125, .ohm = 556 },
+ { .temp_c = 30, .ohm = 8315 },
+ { .temp_c = 35, .ohm = 6948 },
+ { .temp_c = 40, .ohm = 5834 },
+ { .temp_c = 45, .ohm = 4917 },
+ { .temp_c = 50, .ohm = 4161 },
+ { .temp_c = 55, .ohm = 3535 },
+ { .temp_c = 60, .ohm = 3014 },
+ { .temp_c = 65, .ohm = 2586 },
+ { .temp_c = 70, .ohm = 2228 },
+ { .temp_c = 75, .ohm = 1925 },
+ { .temp_c = 80, .ohm = 1669 },
+ { .temp_c = 85, .ohm = 1452 },
+ { .temp_c = 90, .ohm = 1268 },
+ { .temp_c = 95, .ohm = 1110 },
+ { .temp_c = 100, .ohm = 974 },
+ { .temp_c = 105, .ohm = 858 },
+ { .temp_c = 110, .ohm = 758 },
+ { .temp_c = 115, .ohm = 672 },
+ { .temp_c = 120, .ohm = 596 },
+ { .temp_c = 125, .ohm = 531 },
};
/*
@@ -387,12 +387,9 @@ static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uv)
puo = data->pullup_ohm;
pdo = data->pulldown_ohm;
- if (uv == 0)
- return (data->connect == NTC_CONNECTED_POSITIVE) ?
- INT_MAX : 0;
- if (uv >= puv)
- return (data->connect == NTC_CONNECTED_POSITIVE) ?
- 0 : INT_MAX;
+ /* faulty adc value */
+ if (uv == 0 || uv >= puv)
+ return -ENODATA;
if (data->connect == NTC_CONNECTED_POSITIVE && puo == 0)
n = div_u64(pdo * (puv - uv), uv);
@@ -404,8 +401,10 @@ static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uv)
else
n = div64_u64_safe(pdo * puo * uv, pdo * (puv - uv) - puo * uv);
- if (n > INT_MAX)
- n = INT_MAX;
+ /* sensor out of bounds */
+ if (n > data->comp[0].ohm || n < data->comp[data->n_comp - 1].ohm)
+ return -ENODATA;
+
return n;
}
diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c
index 9486db249c64..b3694a4209b9 100644
--- a/drivers/hwmon/occ/common.c
+++ b/drivers/hwmon/occ/common.c
@@ -459,12 +459,10 @@ static ssize_t occ_show_power_1(struct device *dev,
return sysfs_emit(buf, "%llu\n", val);
}
-static u64 occ_get_powr_avg(u64 *accum, u32 *samples)
+static u64 occ_get_powr_avg(u64 accum, u32 samples)
{
- u64 divisor = get_unaligned_be32(samples);
-
- return (divisor == 0) ? 0 :
- div64_u64(get_unaligned_be64(accum) * 1000000ULL, divisor);
+ return (samples == 0) ? 0 :
+ mul_u64_u32_div(accum, 1000000UL, samples);
}
static ssize_t occ_show_power_2(struct device *dev,
@@ -489,8 +487,8 @@ static ssize_t occ_show_power_2(struct device *dev,
get_unaligned_be32(&power->sensor_id),
power->function_id, power->apss_channel);
case 1:
- val = occ_get_powr_avg(&power->accumulator,
- &power->update_tag);
+ val = occ_get_powr_avg(get_unaligned_be64(&power->accumulator),
+ get_unaligned_be32(&power->update_tag));
break;
case 2:
val = (u64)get_unaligned_be32(&power->update_tag) *
@@ -527,8 +525,8 @@ static ssize_t occ_show_power_a0(struct device *dev,
return sysfs_emit(buf, "%u_system\n",
get_unaligned_be32(&power->sensor_id));
case 1:
- val = occ_get_powr_avg(&power->system.accumulator,
- &power->system.update_tag);
+ val = occ_get_powr_avg(get_unaligned_be64(&power->system.accumulator),
+ get_unaligned_be32(&power->system.update_tag));
break;
case 2:
val = (u64)get_unaligned_be32(&power->system.update_tag) *
@@ -541,8 +539,8 @@ static ssize_t occ_show_power_a0(struct device *dev,
return sysfs_emit(buf, "%u_proc\n",
get_unaligned_be32(&power->sensor_id));
case 5:
- val = occ_get_powr_avg(&power->proc.accumulator,
- &power->proc.update_tag);
+ val = occ_get_powr_avg(get_unaligned_be64(&power->proc.accumulator),
+ get_unaligned_be32(&power->proc.update_tag));
break;
case 6:
val = (u64)get_unaligned_be32(&power->proc.update_tag) *
@@ -555,8 +553,8 @@ static ssize_t occ_show_power_a0(struct device *dev,
return sysfs_emit(buf, "%u_vdd\n",
get_unaligned_be32(&power->sensor_id));
case 9:
- val = occ_get_powr_avg(&power->vdd.accumulator,
- &power->vdd.update_tag);
+ val = occ_get_powr_avg(get_unaligned_be64(&power->vdd.accumulator),
+ get_unaligned_be32(&power->vdd.update_tag));
break;
case 10:
val = (u64)get_unaligned_be32(&power->vdd.update_tag) *
@@ -569,8 +567,8 @@ static ssize_t occ_show_power_a0(struct device *dev,
return sysfs_emit(buf, "%u_vdn\n",
get_unaligned_be32(&power->sensor_id));
case 13:
- val = occ_get_powr_avg(&power->vdn.accumulator,
- &power->vdn.update_tag);
+ val = occ_get_powr_avg(get_unaligned_be64(&power->vdn.accumulator),
+ get_unaligned_be32(&power->vdn.update_tag));
break;
case 14:
val = (u64)get_unaligned_be32(&power->vdn.update_tag) *
@@ -747,29 +745,30 @@ static ssize_t occ_show_extended(struct device *dev,
}
/*
- * Some helper macros to make it easier to define an occ_attribute. Since these
- * are dynamically allocated, we shouldn't use the existing kernel macros which
+ * A helper to make it easier to define an occ_attribute. Since these
+ * are dynamically allocated, we cannot use the existing kernel macros which
* stringify the name argument.
*/
-#define ATTR_OCC(_name, _mode, _show, _store) { \
- .attr = { \
- .name = _name, \
- .mode = VERIFY_OCTAL_PERMISSIONS(_mode), \
- }, \
- .show = _show, \
- .store = _store, \
-}
-
-#define SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index) { \
- .dev_attr = ATTR_OCC(_name, _mode, _show, _store), \
- .index = _index, \
- .nr = _nr, \
+static void occ_init_attribute(struct occ_attribute *attr, int mode,
+ ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf),
+ ssize_t (*store)(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count),
+ int nr, int index, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vsnprintf(attr->name, sizeof(attr->name), fmt, args);
+ va_end(args);
+
+ attr->sensor.dev_attr.attr.name = attr->name;
+ attr->sensor.dev_attr.attr.mode = mode;
+ attr->sensor.dev_attr.show = show;
+ attr->sensor.dev_attr.store = store;
+ attr->sensor.index = index;
+ attr->sensor.nr = nr;
}
-#define OCC_INIT_ATTR(_name, _mode, _show, _store, _nr, _index) \
- ((struct sensor_device_attribute_2) \
- SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index))
-
/*
* Allocate and instatiate sensor_device_attribute_2s. It's most efficient to
* use our own instead of the built-in hwmon attribute types.
@@ -855,14 +854,15 @@ static int occ_setup_sensor_attrs(struct occ *occ)
sensors->extended.num_sensors = 0;
}
- occ->attrs = devm_kzalloc(dev, sizeof(*occ->attrs) * num_attrs,
+ occ->attrs = devm_kcalloc(dev, num_attrs, sizeof(*occ->attrs),
GFP_KERNEL);
if (!occ->attrs)
return -ENOMEM;
/* null-terminated list */
- occ->group.attrs = devm_kzalloc(dev, sizeof(*occ->group.attrs) *
- num_attrs + 1, GFP_KERNEL);
+ occ->group.attrs = devm_kcalloc(dev, num_attrs + 1,
+ sizeof(*occ->group.attrs),
+ GFP_KERNEL);
if (!occ->group.attrs)
return -ENOMEM;
@@ -872,43 +872,33 @@ static int occ_setup_sensor_attrs(struct occ *occ)
s = i + 1;
temp = ((struct temp_sensor_2 *)sensors->temp.data) + i;
- snprintf(attr->name, sizeof(attr->name), "temp%d_label", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_temp, NULL,
- 0, i);
+ occ_init_attribute(attr, 0444, show_temp, NULL,
+ 0, i, "temp%d_label", s);
attr++;
if (sensors->temp.version == 2 &&
temp->fru_type == OCC_FRU_TYPE_VRM) {
- snprintf(attr->name, sizeof(attr->name),
- "temp%d_alarm", s);
+ occ_init_attribute(attr, 0444, show_temp, NULL,
+ 1, i, "temp%d_alarm", s);
} else {
- snprintf(attr->name, sizeof(attr->name),
- "temp%d_input", s);
+ occ_init_attribute(attr, 0444, show_temp, NULL,
+ 1, i, "temp%d_input", s);
}
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_temp, NULL,
- 1, i);
attr++;
if (sensors->temp.version > 1) {
- snprintf(attr->name, sizeof(attr->name),
- "temp%d_fru_type", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
- show_temp, NULL, 2, i);
+ occ_init_attribute(attr, 0444, show_temp, NULL,
+ 2, i, "temp%d_fru_type", s);
attr++;
- snprintf(attr->name, sizeof(attr->name),
- "temp%d_fault", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
- show_temp, NULL, 3, i);
+ occ_init_attribute(attr, 0444, show_temp, NULL,
+ 3, i, "temp%d_fault", s);
attr++;
if (sensors->temp.version == 0x10) {
- snprintf(attr->name, sizeof(attr->name),
- "temp%d_max", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
- show_temp, NULL,
- 4, i);
+ occ_init_attribute(attr, 0444, show_temp, NULL,
+ 4, i, "temp%d_max", s);
attr++;
}
}
@@ -917,14 +907,12 @@ static int occ_setup_sensor_attrs(struct occ *occ)
for (i = 0; i < sensors->freq.num_sensors; ++i) {
s = i + 1;
- snprintf(attr->name, sizeof(attr->name), "freq%d_label", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_freq, NULL,
- 0, i);
+ occ_init_attribute(attr, 0444, show_freq, NULL,
+ 0, i, "freq%d_label", s);
attr++;
- snprintf(attr->name, sizeof(attr->name), "freq%d_input", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_freq, NULL,
- 1, i);
+ occ_init_attribute(attr, 0444, show_freq, NULL,
+ 1, i, "freq%d_input", s);
attr++;
}
@@ -940,32 +928,24 @@ static int occ_setup_sensor_attrs(struct occ *occ)
s = (i * 4) + 1;
for (j = 0; j < 4; ++j) {
- snprintf(attr->name, sizeof(attr->name),
- "power%d_label", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
- show_power, NULL,
- nr++, i);
+ occ_init_attribute(attr, 0444, show_power,
+ NULL, nr++, i,
+ "power%d_label", s);
attr++;
- snprintf(attr->name, sizeof(attr->name),
- "power%d_average", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
- show_power, NULL,
- nr++, i);
+ occ_init_attribute(attr, 0444, show_power,
+ NULL, nr++, i,
+ "power%d_average", s);
attr++;
- snprintf(attr->name, sizeof(attr->name),
- "power%d_average_interval", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
- show_power, NULL,
- nr++, i);
+ occ_init_attribute(attr, 0444, show_power,
+ NULL, nr++, i,
+ "power%d_average_interval", s);
attr++;
- snprintf(attr->name, sizeof(attr->name),
- "power%d_input", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
- show_power, NULL,
- nr++, i);
+ occ_init_attribute(attr, 0444, show_power,
+ NULL, nr++, i,
+ "power%d_input", s);
attr++;
s++;
@@ -977,28 +957,20 @@ static int occ_setup_sensor_attrs(struct occ *occ)
for (i = 0; i < sensors->power.num_sensors; ++i) {
s = i + 1;
- snprintf(attr->name, sizeof(attr->name),
- "power%d_label", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
- show_power, NULL, 0, i);
+ occ_init_attribute(attr, 0444, show_power, NULL,
+ 0, i, "power%d_label", s);
attr++;
- snprintf(attr->name, sizeof(attr->name),
- "power%d_average", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
- show_power, NULL, 1, i);
+ occ_init_attribute(attr, 0444, show_power, NULL,
+ 1, i, "power%d_average", s);
attr++;
- snprintf(attr->name, sizeof(attr->name),
- "power%d_average_interval", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
- show_power, NULL, 2, i);
+ occ_init_attribute(attr, 0444, show_power, NULL,
+ 2, i, "power%d_average_interval", s);
attr++;
- snprintf(attr->name, sizeof(attr->name),
- "power%d_input", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
- show_power, NULL, 3, i);
+ occ_init_attribute(attr, 0444, show_power, NULL,
+ 3, i, "power%d_input", s);
attr++;
}
@@ -1006,56 +978,43 @@ static int occ_setup_sensor_attrs(struct occ *occ)
}
if (sensors->caps.num_sensors >= 1) {
- snprintf(attr->name, sizeof(attr->name), "power%d_label", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
- 0, 0);
+ occ_init_attribute(attr, 0444, show_caps, NULL,
+ 0, 0, "power%d_label", s);
attr++;
- snprintf(attr->name, sizeof(attr->name), "power%d_cap", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
- 1, 0);
+ occ_init_attribute(attr, 0444, show_caps, NULL,
+ 1, 0, "power%d_cap", s);
attr++;
- snprintf(attr->name, sizeof(attr->name), "power%d_input", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
- 2, 0);
+ occ_init_attribute(attr, 0444, show_caps, NULL,
+ 2, 0, "power%d_input", s);
attr++;
- snprintf(attr->name, sizeof(attr->name),
- "power%d_cap_not_redundant", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
- 3, 0);
+ occ_init_attribute(attr, 0444, show_caps, NULL,
+ 3, 0, "power%d_cap_not_redundant", s);
attr++;
- snprintf(attr->name, sizeof(attr->name), "power%d_cap_max", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
- 4, 0);
+ occ_init_attribute(attr, 0444, show_caps, NULL,
+ 4, 0, "power%d_cap_max", s);
attr++;
- snprintf(attr->name, sizeof(attr->name), "power%d_cap_min", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
- 5, 0);
+ occ_init_attribute(attr, 0444, show_caps, NULL,
+ 5, 0, "power%d_cap_min", s);
attr++;
- snprintf(attr->name, sizeof(attr->name), "power%d_cap_user",
- s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0644, show_caps,
- occ_store_caps_user, 6, 0);
+ occ_init_attribute(attr, 0644, show_caps, occ_store_caps_user,
+ 6, 0, "power%d_cap_user", s);
attr++;
if (sensors->caps.version > 1) {
- snprintf(attr->name, sizeof(attr->name),
- "power%d_cap_user_source", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
- show_caps, NULL, 7, 0);
+ occ_init_attribute(attr, 0444, show_caps, NULL,
+ 7, 0, "power%d_cap_user_source", s);
attr++;
if (sensors->caps.version > 2) {
- snprintf(attr->name, sizeof(attr->name),
- "power%d_cap_min_soft", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
- show_caps, NULL,
- 8, 0);
+ occ_init_attribute(attr, 0444, show_caps, NULL,
+ 8, 0,
+ "power%d_cap_min_soft", s);
attr++;
}
}
@@ -1064,19 +1023,16 @@ static int occ_setup_sensor_attrs(struct occ *occ)
for (i = 0; i < sensors->extended.num_sensors; ++i) {
s = i + 1;
- snprintf(attr->name, sizeof(attr->name), "extn%d_label", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
- occ_show_extended, NULL, 0, i);
+ occ_init_attribute(attr, 0444, occ_show_extended, NULL,
+ 0, i, "extn%d_label", s);
attr++;
- snprintf(attr->name, sizeof(attr->name), "extn%d_flags", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
- occ_show_extended, NULL, 1, i);
+ occ_init_attribute(attr, 0444, occ_show_extended, NULL,
+ 1, i, "extn%d_flags", s);
attr++;
- snprintf(attr->name, sizeof(attr->name), "extn%d_input", s);
- attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
- occ_show_extended, NULL, 2, i);
+ occ_init_attribute(attr, 0444, occ_show_extended, NULL,
+ 2, i, "extn%d_input", s);
attr++;
}
diff --git a/drivers/hwmon/oxp-sensors.c b/drivers/hwmon/oxp-sensors.c
deleted file mode 100644
index 83730d931824..000000000000
--- a/drivers/hwmon/oxp-sensors.c
+++ /dev/null
@@ -1,716 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Platform driver for OneXPlayer, AOKZOE, AYANEO, and OrangePi Handhelds
- * that expose fan reading and control via hwmon sysfs.
- *
- * Old OXP boards have the same DMI strings and they are told apart by
- * the boot cpu vendor (Intel/AMD). Of these older models only AMD is
- * supported.
- *
- * Fan control is provided via pwm interface in the range [0-255].
- * Old AMD boards use [0-100] as range in the EC, the written value is
- * scaled to accommodate for that. Newer boards like the mini PRO and
- * AOKZOE are not scaled but have the same EC layout. Newer models
- * like the 2 and X1 are [0-184] and are scaled to 0-255. OrangePi
- * are [1-244] and scaled to 0-255.
- *
- * Copyright (C) 2022 JoaquĂ­n I. AramendĂ­a <samsagax@gmail.com>
- * Copyright (C) 2024 Derek J. Clark <derekjohn.clark@gmail.com>
- */
-
-#include <linux/acpi.h>
-#include <linux/dmi.h>
-#include <linux/hwmon.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/processor.h>
-
-/* Handle ACPI lock mechanism */
-static u32 oxp_mutex;
-
-#define ACPI_LOCK_DELAY_MS 500
-
-static bool lock_global_acpi_lock(void)
-{
- return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS, &oxp_mutex));
-}
-
-static bool unlock_global_acpi_lock(void)
-{
- return ACPI_SUCCESS(acpi_release_global_lock(oxp_mutex));
-}
-
-enum oxp_board {
- aok_zoe_a1 = 1,
- aya_neo_2,
- aya_neo_air,
- aya_neo_air_1s,
- aya_neo_air_plus_mendo,
- aya_neo_air_pro,
- aya_neo_flip,
- aya_neo_geek,
- aya_neo_kun,
- orange_pi_neo,
- oxp_2,
- oxp_fly,
- oxp_mini_amd,
- oxp_mini_amd_a07,
- oxp_mini_amd_pro,
- oxp_x1,
-};
-
-static enum oxp_board board;
-
-/* Fan reading and PWM */
-#define OXP_SENSOR_FAN_REG 0x76 /* Fan reading is 2 registers long */
-#define OXP_2_SENSOR_FAN_REG 0x58 /* Fan reading is 2 registers long */
-#define OXP_SENSOR_PWM_ENABLE_REG 0x4A /* PWM enable is 1 register long */
-#define OXP_SENSOR_PWM_REG 0x4B /* PWM reading is 1 register long */
-#define PWM_MODE_AUTO 0x00
-#define PWM_MODE_MANUAL 0x01
-
-/* OrangePi fan reading and PWM */
-#define ORANGEPI_SENSOR_FAN_REG 0x78 /* Fan reading is 2 registers long */
-#define ORANGEPI_SENSOR_PWM_ENABLE_REG 0x40 /* PWM enable is 1 register long */
-#define ORANGEPI_SENSOR_PWM_REG 0x38 /* PWM reading is 1 register long */
-
-/* Turbo button takeover function
- * Different boards have different values and EC registers
- * for the same function
- */
-#define OXP_TURBO_SWITCH_REG 0xF1 /* Mini Pro, OneXFly, AOKZOE */
-#define OXP_2_TURBO_SWITCH_REG 0xEB /* OXP2 and X1 */
-#define OXP_MINI_TURBO_SWITCH_REG 0x1E /* Mini AO7 */
-
-#define OXP_MINI_TURBO_TAKE_VAL 0x01 /* Mini AO7 */
-#define OXP_TURBO_TAKE_VAL 0x40 /* All other models */
-
-#define OXP_TURBO_RETURN_VAL 0x00 /* Common return val */
-
-static const struct dmi_system_id dmi_table[] = {
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),
- DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 AR07"),
- },
- .driver_data = (void *)aok_zoe_a1,
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),
- DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 Pro"),
- },
- .driver_data = (void *)aok_zoe_a1,
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
- DMI_MATCH(DMI_BOARD_NAME, "AYANEO 2"),
- },
- .driver_data = (void *)aya_neo_2,
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
- DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR"),
- },
- .driver_data = (void *)aya_neo_air,
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
- DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR 1S"),
- },
- .driver_data = (void *)aya_neo_air_1s,
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
- DMI_EXACT_MATCH(DMI_BOARD_NAME, "AB05-Mendocino"),
- },
- .driver_data = (void *)aya_neo_air_plus_mendo,
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
- DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR Pro"),
- },
- .driver_data = (void *)aya_neo_air_pro,
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
- DMI_MATCH(DMI_BOARD_NAME, "FLIP"),
- },
- .driver_data = (void *)aya_neo_flip,
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
- DMI_MATCH(DMI_BOARD_NAME, "GEEK"),
- },
- .driver_data = (void *)aya_neo_geek,
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
- DMI_EXACT_MATCH(DMI_BOARD_NAME, "KUN"),
- },
- .driver_data = (void *)aya_neo_kun,
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "OrangePi"),
- DMI_EXACT_MATCH(DMI_BOARD_NAME, "NEO-01"),
- },
- .driver_data = (void *)orange_pi_neo,
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
- DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONE XPLAYER"),
- },
- .driver_data = (void *)oxp_mini_amd,
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
- DMI_MATCH(DMI_BOARD_NAME, "ONEXPLAYER 2"),
- },
- .driver_data = (void *)oxp_2,
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
- DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1"),
- },
- .driver_data = (void *)oxp_fly,
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
- DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER mini A07"),
- },
- .driver_data = (void *)oxp_mini_amd_a07,
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
- DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER Mini Pro"),
- },
- .driver_data = (void *)oxp_mini_amd_pro,
- },
- {
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
- DMI_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1"),
- },
- .driver_data = (void *)oxp_x1,
- },
- {},
-};
-
-/* Helper functions to handle EC read/write */
-static int read_from_ec(u8 reg, int size, long *val)
-{
- int i;
- int ret;
- u8 buffer;
-
- if (!lock_global_acpi_lock())
- return -EBUSY;
-
- *val = 0;
- for (i = 0; i < size; i++) {
- ret = ec_read(reg + i, &buffer);
- if (ret)
- return ret;
- *val <<= i * 8;
- *val += buffer;
- }
-
- if (!unlock_global_acpi_lock())
- return -EBUSY;
-
- return 0;
-}
-
-static int write_to_ec(u8 reg, u8 value)
-{
- int ret;
-
- if (!lock_global_acpi_lock())
- return -EBUSY;
-
- ret = ec_write(reg, value);
-
- if (!unlock_global_acpi_lock())
- return -EBUSY;
-
- return ret;
-}
-
-/* Turbo button toggle functions */
-static int tt_toggle_enable(void)
-{
- u8 reg;
- u8 val;
-
- switch (board) {
- case oxp_mini_amd_a07:
- reg = OXP_MINI_TURBO_SWITCH_REG;
- val = OXP_MINI_TURBO_TAKE_VAL;
- break;
- case aok_zoe_a1:
- case oxp_fly:
- case oxp_mini_amd_pro:
- reg = OXP_TURBO_SWITCH_REG;
- val = OXP_TURBO_TAKE_VAL;
- break;
- case oxp_2:
- case oxp_x1:
- reg = OXP_2_TURBO_SWITCH_REG;
- val = OXP_TURBO_TAKE_VAL;
- break;
- default:
- return -EINVAL;
- }
- return write_to_ec(reg, val);
-}
-
-static int tt_toggle_disable(void)
-{
- u8 reg;
- u8 val;
-
- switch (board) {
- case oxp_mini_amd_a07:
- reg = OXP_MINI_TURBO_SWITCH_REG;
- val = OXP_TURBO_RETURN_VAL;
- break;
- case aok_zoe_a1:
- case oxp_fly:
- case oxp_mini_amd_pro:
- reg = OXP_TURBO_SWITCH_REG;
- val = OXP_TURBO_RETURN_VAL;
- break;
- case oxp_2:
- case oxp_x1:
- reg = OXP_2_TURBO_SWITCH_REG;
- val = OXP_TURBO_RETURN_VAL;
- break;
- default:
- return -EINVAL;
- }
- return write_to_ec(reg, val);
-}
-
-/* Callbacks for turbo toggle attribute */
-static umode_t tt_toggle_is_visible(struct kobject *kobj,
- struct attribute *attr, int n)
-{
- switch (board) {
- case aok_zoe_a1:
- case oxp_2:
- case oxp_fly:
- case oxp_mini_amd_a07:
- case oxp_mini_amd_pro:
- case oxp_x1:
- return attr->mode;
- default:
- break;
- }
- return 0;
-}
-
-static ssize_t tt_toggle_store(struct device *dev,
- struct device_attribute *attr, const char *buf,
- size_t count)
-{
- int rval;
- bool value;
-
- rval = kstrtobool(buf, &value);
- if (rval)
- return rval;
-
- if (value) {
- rval = tt_toggle_enable();
- } else {
- rval = tt_toggle_disable();
- }
- if (rval)
- return rval;
-
- return count;
-}
-
-static ssize_t tt_toggle_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- int retval;
- u8 reg;
- long val;
-
- switch (board) {
- case oxp_mini_amd_a07:
- reg = OXP_MINI_TURBO_SWITCH_REG;
- break;
- case aok_zoe_a1:
- case oxp_fly:
- case oxp_mini_amd_pro:
- reg = OXP_TURBO_SWITCH_REG;
- break;
- case oxp_2:
- case oxp_x1:
- reg = OXP_2_TURBO_SWITCH_REG;
- break;
- default:
- return -EINVAL;
- }
-
- retval = read_from_ec(reg, 1, &val);
- if (retval)
- return retval;
-
- return sysfs_emit(buf, "%d\n", !!val);
-}
-
-static DEVICE_ATTR_RW(tt_toggle);
-
-/* PWM enable/disable functions */
-static int oxp_pwm_enable(void)
-{
- switch (board) {
- case orange_pi_neo:
- return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL);
- case aok_zoe_a1:
- case aya_neo_2:
- case aya_neo_air:
- case aya_neo_air_plus_mendo:
- case aya_neo_air_pro:
- case aya_neo_flip:
- case aya_neo_geek:
- case aya_neo_kun:
- case oxp_2:
- case oxp_fly:
- case oxp_mini_amd:
- case oxp_mini_amd_a07:
- case oxp_mini_amd_pro:
- case oxp_x1:
- return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL);
- default:
- return -EINVAL;
- }
-}
-
-static int oxp_pwm_disable(void)
-{
- switch (board) {
- case orange_pi_neo:
- return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO);
- case aok_zoe_a1:
- case aya_neo_2:
- case aya_neo_air:
- case aya_neo_air_1s:
- case aya_neo_air_plus_mendo:
- case aya_neo_air_pro:
- case aya_neo_flip:
- case aya_neo_geek:
- case aya_neo_kun:
- case oxp_2:
- case oxp_fly:
- case oxp_mini_amd:
- case oxp_mini_amd_a07:
- case oxp_mini_amd_pro:
- case oxp_x1:
- return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO);
- default:
- return -EINVAL;
- }
-}
-
-/* Callbacks for hwmon interface */
-static umode_t oxp_ec_hwmon_is_visible(const void *drvdata,
- enum hwmon_sensor_types type, u32 attr, int channel)
-{
- switch (type) {
- case hwmon_fan:
- return 0444;
- case hwmon_pwm:
- return 0644;
- default:
- return 0;
- }
-}
-
-static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type,
- u32 attr, int channel, long *val)
-{
- int ret;
-
- switch (type) {
- case hwmon_fan:
- switch (attr) {
- case hwmon_fan_input:
- switch (board) {
- case orange_pi_neo:
- return read_from_ec(ORANGEPI_SENSOR_FAN_REG, 2, val);
- case oxp_2:
- case oxp_x1:
- return read_from_ec(OXP_2_SENSOR_FAN_REG, 2, val);
- case aok_zoe_a1:
- case aya_neo_2:
- case aya_neo_air:
- case aya_neo_air_1s:
- case aya_neo_air_plus_mendo:
- case aya_neo_air_pro:
- case aya_neo_flip:
- case aya_neo_geek:
- case aya_neo_kun:
- case oxp_fly:
- case oxp_mini_amd:
- case oxp_mini_amd_a07:
- case oxp_mini_amd_pro:
- return read_from_ec(OXP_SENSOR_FAN_REG, 2, val);
- default:
- break;
- }
- break;
- default:
- break;
- }
- break;
- case hwmon_pwm:
- switch (attr) {
- case hwmon_pwm_input:
- switch (board) {
- case orange_pi_neo:
- ret = read_from_ec(ORANGEPI_SENSOR_PWM_REG, 1, val);
- if (ret)
- return ret;
- /* scale from range [1-244] */
- *val = ((*val - 1) * 254 / 243) + 1;
- break;
- case oxp_2:
- case oxp_x1:
- ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
- if (ret)
- return ret;
- /* scale from range [0-184] */
- *val = (*val * 255) / 184;
- break;
- case aya_neo_2:
- case aya_neo_air:
- case aya_neo_air_1s:
- case aya_neo_air_plus_mendo:
- case aya_neo_air_pro:
- case aya_neo_flip:
- case aya_neo_geek:
- case aya_neo_kun:
- case oxp_mini_amd:
- case oxp_mini_amd_a07:
- ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
- if (ret)
- return ret;
- /* scale from range [0-100] */
- *val = (*val * 255) / 100;
- break;
- case aok_zoe_a1:
- case oxp_fly:
- case oxp_mini_amd_pro:
- default:
- ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
- if (ret)
- return ret;
- break;
- }
- return 0;
- case hwmon_pwm_enable:
- switch (board) {
- case orange_pi_neo:
- return read_from_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, 1, val);
- case aok_zoe_a1:
- case aya_neo_2:
- case aya_neo_air:
- case aya_neo_air_1s:
- case aya_neo_air_plus_mendo:
- case aya_neo_air_pro:
- case aya_neo_flip:
- case aya_neo_geek:
- case aya_neo_kun:
- case oxp_2:
- case oxp_fly:
- case oxp_mini_amd:
- case oxp_mini_amd_a07:
- case oxp_mini_amd_pro:
- case oxp_x1:
- return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val);
- default:
- break;
- }
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
- return -EOPNOTSUPP;
-}
-
-static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type,
- u32 attr, int channel, long val)
-{
- switch (type) {
- case hwmon_pwm:
- switch (attr) {
- case hwmon_pwm_enable:
- if (val == 1)
- return oxp_pwm_enable();
- else if (val == 0)
- return oxp_pwm_disable();
- return -EINVAL;
- case hwmon_pwm_input:
- if (val < 0 || val > 255)
- return -EINVAL;
- switch (board) {
- case orange_pi_neo:
- /* scale to range [1-244] */
- val = ((val - 1) * 243 / 254) + 1;
- return write_to_ec(ORANGEPI_SENSOR_PWM_REG, val);
- case oxp_2:
- case oxp_x1:
- /* scale to range [0-184] */
- val = (val * 184) / 255;
- return write_to_ec(OXP_SENSOR_PWM_REG, val);
- case aya_neo_2:
- case aya_neo_air:
- case aya_neo_air_1s:
- case aya_neo_air_plus_mendo:
- case aya_neo_air_pro:
- case aya_neo_flip:
- case aya_neo_geek:
- case aya_neo_kun:
- case oxp_mini_amd:
- case oxp_mini_amd_a07:
- /* scale to range [0-100] */
- val = (val * 100) / 255;
- return write_to_ec(OXP_SENSOR_PWM_REG, val);
- case aok_zoe_a1:
- case oxp_fly:
- case oxp_mini_amd_pro:
- return write_to_ec(OXP_SENSOR_PWM_REG, val);
- default:
- break;
- }
- break;
- default:
- break;
- }
- break;
- default:
- break;
- }
- return -EOPNOTSUPP;
-}
-
-/* Known sensors in the OXP EC controllers */
-static const struct hwmon_channel_info * const oxp_platform_sensors[] = {
- HWMON_CHANNEL_INFO(fan,
- HWMON_F_INPUT),
- HWMON_CHANNEL_INFO(pwm,
- HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
- NULL,
-};
-
-static struct attribute *oxp_ec_attrs[] = {
- &dev_attr_tt_toggle.attr,
- NULL
-};
-
-static struct attribute_group oxp_ec_attribute_group = {
- .is_visible = tt_toggle_is_visible,
- .attrs = oxp_ec_attrs,
-};
-
-static const struct attribute_group *oxp_ec_groups[] = {
- &oxp_ec_attribute_group,
- NULL
-};
-
-static const struct hwmon_ops oxp_ec_hwmon_ops = {
- .is_visible = oxp_ec_hwmon_is_visible,
- .read = oxp_platform_read,
- .write = oxp_platform_write,
-};
-
-static const struct hwmon_chip_info oxp_ec_chip_info = {
- .ops = &oxp_ec_hwmon_ops,
- .info = oxp_platform_sensors,
-};
-
-/* Initialization logic */
-static int oxp_platform_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device *hwdev;
-
- hwdev = devm_hwmon_device_register_with_info(dev, "oxpec", NULL,
- &oxp_ec_chip_info, NULL);
-
- return PTR_ERR_OR_ZERO(hwdev);
-}
-
-static struct platform_driver oxp_platform_driver = {
- .driver = {
- .name = "oxp-platform",
- .dev_groups = oxp_ec_groups,
- },
- .probe = oxp_platform_probe,
-};
-
-static struct platform_device *oxp_platform_device;
-
-static int __init oxp_platform_init(void)
-{
- const struct dmi_system_id *dmi_entry;
-
- dmi_entry = dmi_first_match(dmi_table);
- if (!dmi_entry)
- return -ENODEV;
-
- board = (enum oxp_board)(unsigned long)dmi_entry->driver_data;
-
- /*
- * Have to check for AMD processor here because DMI strings are the same
- * between Intel and AMD boards on older OneXPlayer devices, the only way
- * to tell them apart is the CPU. Old Intel boards have an unsupported EC.
- */
- if (board == oxp_mini_amd && boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
- return -ENODEV;
-
- oxp_platform_device =
- platform_create_bundle(&oxp_platform_driver,
- oxp_platform_probe, NULL, 0, NULL, 0);
-
- return PTR_ERR_OR_ZERO(oxp_platform_device);
-}
-
-static void __exit oxp_platform_exit(void)
-{
- platform_device_unregister(oxp_platform_device);
- platform_driver_unregister(&oxp_platform_driver);
-}
-
-MODULE_DEVICE_TABLE(dmi, dmi_table);
-
-module_init(oxp_platform_init);
-module_exit(oxp_platform_exit);
-
-MODULE_AUTHOR("JoaquĂ­n Ignacio AramendĂ­a <samsagax@gmail.com>");
-MODULE_DESCRIPTION("Platform driver that handles EC sensors of OneXPlayer devices");
-MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/peci/dimmtemp.c b/drivers/hwmon/peci/dimmtemp.c
index d6762259dd69..fbe82d9852e0 100644
--- a/drivers/hwmon/peci/dimmtemp.c
+++ b/drivers/hwmon/peci/dimmtemp.c
@@ -127,8 +127,6 @@ static int update_thresholds(struct peci_dimmtemp *priv, int dimm_no)
return 0;
ret = priv->gen_info->read_thresholds(priv, dimm_order, chan_rank, &data);
- if (ret == -ENODATA) /* Use default or previous value */
- return 0;
if (ret)
return ret;
@@ -509,11 +507,11 @@ read_thresholds_icx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u
ret = peci_ep_pci_local_read(priv->peci_dev, 0, 13, 0, 2, 0xd4, &reg_val);
if (ret || !(reg_val & BIT(31)))
- return -ENODATA; /* Use default or previous value */
+ return -ENODATA;
ret = peci_ep_pci_local_read(priv->peci_dev, 0, 13, 0, 2, 0xd0, &reg_val);
if (ret)
- return -ENODATA; /* Use default or previous value */
+ return -ENODATA;
/*
* Device 26, Offset 224e0: IMC 0 channel 0 -> rank 0
@@ -546,11 +544,11 @@ read_thresholds_spr(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u
ret = peci_ep_pci_local_read(priv->peci_dev, 0, 30, 0, 2, 0xd4, &reg_val);
if (ret || !(reg_val & BIT(31)))
- return -ENODATA; /* Use default or previous value */
+ return -ENODATA;
ret = peci_ep_pci_local_read(priv->peci_dev, 0, 30, 0, 2, 0xd0, &reg_val);
if (ret)
- return -ENODATA; /* Use default or previous value */
+ return -ENODATA;
/*
* Device 26, Offset 219a8: IMC 0 channel 0 -> rank 0
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index 419469f40ba0..441f984a859d 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -133,6 +133,15 @@ config SENSORS_DPS920AB
This driver can also be built as a module. If so, the module will
be called dps920ab.
+config SENSORS_INA233
+ tristate "Texas Instruments INA233 and compatibles"
+ help
+ If you say yes here you get hardware monitoring support for Texas
+ Instruments INA233.
+
+ This driver can also be built as a module. If so, the module will
+ be called ina233.
+
config SENSORS_INSPUR_IPSPS
tristate "INSPUR Power System Power Supply"
help
@@ -209,6 +218,24 @@ config SENSORS_LM25066_REGULATOR
If you say yes here you get regulator support for National
Semiconductor LM25066, LM5064, and LM5066.
+config SENSORS_LT3074
+ tristate "Analog Devices LT3074"
+ help
+ If you say yes here you get hardware monitoring support for Analog
+ Devices LT3074.
+
+ This driver can also be built as a module. If so, the module will
+ be called lt3074.
+
+config SENSORS_LT3074_REGULATOR
+ tristate "Regulator support for LT3074"
+ depends on SENSORS_LT3074 && REGULATOR
+ help
+ If you say yes here you get regulator support for Analog Devices
+ LT3074. The LT3074 is a low voltage, ultralow noise, high PSRR,
+ dropout linear regulator. The device supplies up to 3A with a
+ typical dropout voltage of 45mV.
+
config SENSORS_LT7182S
tristate "Analog Devices LT7182S"
help
@@ -233,9 +260,9 @@ config SENSORS_LTC2978_REGULATOR
depends on SENSORS_LTC2978 && REGULATOR
help
If you say yes here you get regulator support for Linear Technology
- LTC3880, LTC3883, LTC3884, LTC3886, LTC3887, LTC3889, LTC7841,
- LTC7880, LTM4644, LTM4675, LTM4676, LTM4677, LTM4678, LTM4680,
- LTM4686, and LTM4700.
+ LT7170, LT7171, LTC3880, LTC3883, LTC3884, LTC3886, LTC3887, LTC3889,
+ LTC7841, LTC7880, LTM4644, LTM4673, LTM4675, LTM4676, LTM4677,
+ LTM4678, LTM4680, LTM4686, and LTM4700.
config SENSORS_LTC3815
tristate "Linear Technologies LTC3815"
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile
index c7eb7739b7f8..29cd8a3317d2 100644
--- a/drivers/hwmon/pmbus/Makefile
+++ b/drivers/hwmon/pmbus/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_SENSORS_DELTA_AHE50DC_FAN) += delta-ahe50dc-fan.o
obj-$(CONFIG_SENSORS_FSP_3Y) += fsp-3y.o
obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o
obj-$(CONFIG_SENSORS_DPS920AB) += dps920ab.o
+obj-$(CONFIG_SENSORS_INA233) += ina233.o
obj-$(CONFIG_SENSORS_INSPUR_IPSPS) += inspur-ipsps.o
obj-$(CONFIG_SENSORS_IR35221) += ir35221.o
obj-$(CONFIG_SENSORS_IR36021) += ir36021.o
@@ -22,6 +23,7 @@ obj-$(CONFIG_SENSORS_IR38064) += ir38064.o
obj-$(CONFIG_SENSORS_IRPS5401) += irps5401.o
obj-$(CONFIG_SENSORS_ISL68137) += isl68137.o
obj-$(CONFIG_SENSORS_LM25066) += lm25066.o
+obj-$(CONFIG_SENSORS_LT3074) += lt3074.o
obj-$(CONFIG_SENSORS_LT7182S) += lt7182s.o
obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o
diff --git a/drivers/hwmon/pmbus/ina233.c b/drivers/hwmon/pmbus/ina233.c
new file mode 100644
index 000000000000..dde1e1678394
--- /dev/null
+++ b/drivers/hwmon/pmbus/ina233.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for ina233
+ *
+ * Copyright (c) 2025 Leo Yang
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "pmbus.h"
+
+#define MFR_READ_VSHUNT 0xd1
+#define MFR_CALIBRATION 0xd4
+
+#define INA233_MAX_CURRENT_DEFAULT 32768000 /* uA */
+#define INA233_RSHUNT_DEFAULT 2000 /* uOhm */
+
+#define MAX_M_VAL 32767
+
+static void calculate_coef(int *m, int *R, u32 current_lsb, int power_coef)
+{
+ u64 scaled_m;
+ int scale_factor = 0;
+ int scale_coef = 1;
+
+ /*
+ * 1000000 from Current_LSB A->uA .
+ * scale_coef is for scaling up to minimize rounding errors,
+ * If there is no decimal information, no need to scale.
+ */
+ if (1000000 % current_lsb) {
+ /* Scaling to keep integer precision */
+ scale_factor = -3;
+ scale_coef = 1000;
+ }
+
+ /*
+ * Unit Conversion (Current_LSB A->uA) and use scaling(scale_factor)
+ * to keep integer precision.
+ * Formulae referenced from spec.
+ */
+ scaled_m = div64_u64(1000000 * scale_coef, (u64)current_lsb * power_coef);
+
+ /* Maximize while keeping it bounded.*/
+ while (scaled_m > MAX_M_VAL) {
+ scaled_m = div_u64(scaled_m, 10);
+ scale_factor++;
+ }
+ /* Scale up only if fractional part exists. */
+ while (scaled_m * 10 < MAX_M_VAL && scale_coef != 1) {
+ scaled_m *= 10;
+ scale_factor--;
+ }
+
+ *m = scaled_m;
+ *R = scale_factor;
+}
+
+static int ina233_read_word_data(struct i2c_client *client, int page,
+ int phase, int reg)
+{
+ int ret;
+
+ switch (reg) {
+ case PMBUS_VIRT_READ_VMON:
+ ret = pmbus_read_word_data(client, 0, 0xff, MFR_READ_VSHUNT);
+
+ /* Adjust returned value to match VIN coefficients */
+ /* VIN: 1.25 mV VSHUNT: 2.5 uV LSB */
+ ret = DIV_ROUND_CLOSEST(ret * 25, 12500);
+ break;
+ default:
+ ret = -ENODATA;
+ break;
+ }
+ return ret;
+}
+
+static int ina233_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ int ret, m, R;
+ u32 rshunt;
+ u32 max_current;
+ u32 current_lsb;
+ u16 calibration;
+ struct pmbus_driver_info *info;
+
+ info = devm_kzalloc(dev, sizeof(struct pmbus_driver_info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->pages = 1;
+ info->format[PSC_VOLTAGE_IN] = direct;
+ info->format[PSC_VOLTAGE_OUT] = direct;
+ info->format[PSC_CURRENT_OUT] = direct;
+ info->format[PSC_POWER] = direct;
+ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_INPUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
+ | PMBUS_HAVE_POUT
+ | PMBUS_HAVE_VMON | PMBUS_HAVE_STATUS_VMON;
+ info->m[PSC_VOLTAGE_IN] = 8;
+ info->R[PSC_VOLTAGE_IN] = 2;
+ info->m[PSC_VOLTAGE_OUT] = 8;
+ info->R[PSC_VOLTAGE_OUT] = 2;
+ info->read_word_data = ina233_read_word_data;
+
+ /* If INA233 skips current/power, shunt-resistor and current-lsb aren't needed. */
+ /* read rshunt value (uOhm) */
+ ret = device_property_read_u32(dev, "shunt-resistor", &rshunt);
+ if (ret) {
+ if (ret != -EINVAL)
+ return dev_err_probe(dev, ret, "Shunt resistor property read fail.\n");
+ rshunt = INA233_RSHUNT_DEFAULT;
+ }
+ if (!rshunt)
+ return dev_err_probe(dev, -EINVAL,
+ "Shunt resistor cannot be zero.\n");
+
+ /* read Maximum expected current value (uA) */
+ ret = device_property_read_u32(dev, "ti,maximum-expected-current-microamp", &max_current);
+ if (ret) {
+ if (ret != -EINVAL)
+ return dev_err_probe(dev, ret,
+ "Maximum expected current property read fail.\n");
+ max_current = INA233_MAX_CURRENT_DEFAULT;
+ }
+ if (max_current < 32768)
+ return dev_err_probe(dev, -EINVAL,
+ "Maximum expected current cannot less then 32768.\n");
+
+ /* Calculate Current_LSB according to the spec formula */
+ current_lsb = max_current / 32768;
+
+ /* calculate current coefficient */
+ calculate_coef(&m, &R, current_lsb, 1);
+ info->m[PSC_CURRENT_OUT] = m;
+ info->R[PSC_CURRENT_OUT] = R;
+
+ /* calculate power coefficient */
+ calculate_coef(&m, &R, current_lsb, 25);
+ info->m[PSC_POWER] = m;
+ info->R[PSC_POWER] = R;
+
+ /* write MFR_CALIBRATION register, Apply formula from spec with unit scaling. */
+ calibration = div64_u64(5120000000ULL, (u64)rshunt * current_lsb);
+ if (calibration > 0x7FFF)
+ return dev_err_probe(dev, -EINVAL,
+ "Product of Current_LSB %u and shunt resistor %u too small, MFR_CALIBRATION reg exceeds 0x7FFF.\n",
+ current_lsb, rshunt);
+ ret = i2c_smbus_write_word_data(client, MFR_CALIBRATION, calibration);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Unable to write calibration.\n");
+
+ dev_dbg(dev, "power monitor %s (Rshunt = %u uOhm, Current_LSB = %u uA/bit)\n",
+ client->name, rshunt, current_lsb);
+
+ return pmbus_do_probe(client, info);
+}
+
+static const struct i2c_device_id ina233_id[] = {
+ {"ina233", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ina233_id);
+
+static const struct of_device_id __maybe_unused ina233_of_match[] = {
+ { .compatible = "ti,ina233" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ina233_of_match);
+
+static struct i2c_driver ina233_driver = {
+ .driver = {
+ .name = "ina233",
+ .of_match_table = of_match_ptr(ina233_of_match),
+ },
+ .probe = ina233_probe,
+ .id_table = ina233_id,
+};
+
+module_i2c_driver(ina233_driver);
+
+MODULE_AUTHOR("Leo Yang <leo.yang.sy0@gmail.com>");
+MODULE_DESCRIPTION("PMBus driver for INA233 and compatible chips");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c
index 40b0dda32ea6..dd7275a67a0a 100644
--- a/drivers/hwmon/pmbus/lm25066.c
+++ b/drivers/hwmon/pmbus/lm25066.c
@@ -437,7 +437,7 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg,
#if IS_ENABLED(CONFIG_SENSORS_LM25066_REGULATOR)
static const struct regulator_desc lm25066_reg_desc[] = {
- PMBUS_REGULATOR_ONE("vout"),
+ PMBUS_REGULATOR_ONE_NODE("vout"),
};
#endif
diff --git a/drivers/hwmon/pmbus/lt3074.c b/drivers/hwmon/pmbus/lt3074.c
new file mode 100644
index 000000000000..3704dbe7b54a
--- /dev/null
+++ b/drivers/hwmon/pmbus/lt3074.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Hardware monitoring driver for Analog Devices LT3074
+ *
+ * Copyright (C) 2025 Analog Devices, Inc.
+ */
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+
+#include "pmbus.h"
+
+#define LT3074_MFR_READ_VBIAS 0xc6
+#define LT3074_MFR_BIAS_OV_WARN_LIMIT 0xc7
+#define LT3074_MFR_BIAS_UV_WARN_LIMIT 0xc8
+#define LT3074_MFR_SPECIAL_ID 0xe7
+
+#define LT3074_SPECIAL_ID_VALUE 0x1c1d
+
+static const struct regulator_desc __maybe_unused lt3074_reg_desc[] = {
+ PMBUS_REGULATOR_ONE("regulator"),
+};
+
+static int lt3074_read_word_data(struct i2c_client *client, int page,
+ int phase, int reg)
+{
+ switch (reg) {
+ case PMBUS_VIRT_READ_VMON:
+ return pmbus_read_word_data(client, page, phase,
+ LT3074_MFR_READ_VBIAS);
+ case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
+ return pmbus_read_word_data(client, page, phase,
+ LT3074_MFR_BIAS_UV_WARN_LIMIT);
+ case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
+ return pmbus_read_word_data(client, page, phase,
+ LT3074_MFR_BIAS_OV_WARN_LIMIT);
+ default:
+ return -ENODATA;
+ }
+}
+
+static int lt3074_write_word_data(struct i2c_client *client, int page,
+ int reg, u16 word)
+{
+ switch (reg) {
+ case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
+ return pmbus_write_word_data(client, 0,
+ LT3074_MFR_BIAS_UV_WARN_LIMIT,
+ word);
+ case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
+ return pmbus_write_word_data(client, 0,
+ LT3074_MFR_BIAS_OV_WARN_LIMIT,
+ word);
+ default:
+ return -ENODATA;
+ }
+}
+
+static struct pmbus_driver_info lt3074_info = {
+ .pages = 1,
+ .format[PSC_VOLTAGE_IN] = linear,
+ .format[PSC_VOLTAGE_OUT] = linear,
+ .format[PSC_CURRENT_OUT] = linear,
+ .format[PSC_TEMPERATURE] = linear,
+ .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
+ PMBUS_HAVE_TEMP | PMBUS_HAVE_VMON |
+ PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |
+ PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
+ .read_word_data = lt3074_read_word_data,
+ .write_word_data = lt3074_write_word_data,
+#if IS_ENABLED(CONFIG_SENSORS_LT3074_REGULATOR)
+ .num_regulators = 1,
+ .reg_desc = lt3074_reg_desc,
+#endif
+};
+
+static int lt3074_probe(struct i2c_client *client)
+{
+ int ret;
+ struct device *dev = &client->dev;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_WORD_DATA))
+ return -ENODEV;
+
+ ret = i2c_smbus_read_word_data(client, LT3074_MFR_SPECIAL_ID);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to read ID\n");
+
+ if (ret != LT3074_SPECIAL_ID_VALUE)
+ return dev_err_probe(dev, -ENODEV, "ID mismatch\n");
+
+ return pmbus_do_probe(client, &lt3074_info);
+}
+
+static const struct i2c_device_id lt3074_id[] = {
+ { "lt3074", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, lt3074_id);
+
+static const struct of_device_id __maybe_unused lt3074_of_match[] = {
+ { .compatible = "adi,lt3074" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, lt3074_of_match);
+
+static struct i2c_driver lt3074_driver = {
+ .driver = {
+ .name = "lt3074",
+ .of_match_table = of_match_ptr(lt3074_of_match),
+ },
+ .probe = lt3074_probe,
+ .id_table = lt3074_id,
+};
+module_i2c_driver(lt3074_driver);
+
+MODULE_AUTHOR("Cedric Encarnacion <cedricjustine.encarnacion@analog.com>");
+MODULE_DESCRIPTION("PMBus driver for Analog Devices LT3074");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("PMBUS");
diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c
index 4c306943383a..8f5be520a15d 100644
--- a/drivers/hwmon/pmbus/ltc2978.c
+++ b/drivers/hwmon/pmbus/ltc2978.c
@@ -23,11 +23,11 @@ enum chips {
/* Managers */
ltc2972, ltc2974, ltc2975, ltc2977, ltc2978, ltc2979, ltc2980,
/* Controllers */
- ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7132,
- ltc7841, ltc7880,
+ lt7170, lt7171, ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887,
+ ltc3889, ltc7132, ltc7841, ltc7880,
/* Modules */
- ltm2987, ltm4664, ltm4675, ltm4676, ltm4677, ltm4678, ltm4680, ltm4686,
- ltm4700,
+ ltm2987, ltm4664, ltm4673, ltm4675, ltm4676, ltm4677, ltm4678, ltm4680,
+ ltm4686, ltm4700,
};
/* Common for all chips */
@@ -62,6 +62,7 @@ enum chips {
#define LTC2978_ID_MASK 0xfff0
+#define LT7170_ID 0x1C10
#define LTC2972_ID 0x0310
#define LTC2974_ID 0x0210
#define LTC2975_ID 0x0220
@@ -86,6 +87,8 @@ enum chips {
#define LTM2987_ID_A 0x8010 /* A/B for two die IDs */
#define LTM2987_ID_B 0x8020
#define LTM4664_ID 0x4120
+#define LTM4673_ID_REV1 0x0230
+#define LTM4673_ID 0x4480
#define LTM4675_ID 0x47a0
#define LTM4676_ID_REV1 0x4400
#define LTM4676_ID_REV2 0x4480
@@ -535,6 +538,8 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page,
}
static const struct i2c_device_id ltc2978_id[] = {
+ {"lt7170", lt7170},
+ {"lt7171", lt7171},
{"ltc2972", ltc2972},
{"ltc2974", ltc2974},
{"ltc2975", ltc2975},
@@ -554,6 +559,7 @@ static const struct i2c_device_id ltc2978_id[] = {
{"ltc7880", ltc7880},
{"ltm2987", ltm2987},
{"ltm4664", ltm4664},
+ {"ltm4673", ltm4673},
{"ltm4675", ltm4675},
{"ltm4676", ltm4676},
{"ltm4677", ltm4677},
@@ -612,7 +618,7 @@ static int ltc2978_get_id(struct i2c_client *client)
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
if (ret < 0)
return ret;
- if (ret < 3 || strncmp(buf, "LTC", 3))
+ if (ret < 3 || (strncmp(buf, "LTC", 3) && strncmp(buf, "ADI", 3)))
return -ENODEV;
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
@@ -627,6 +633,25 @@ static int ltc2978_get_id(struct i2c_client *client)
chip_id &= LTC2978_ID_MASK;
+ if (chip_id == LT7170_ID) {
+ u8 buf[I2C_SMBUS_BLOCK_MAX];
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(client, PMBUS_IC_DEVICE_ID,
+ sizeof(buf), buf);
+ if (ret < 0)
+ return ret;
+
+ if (!strncmp(buf + 1, "LT7170", 6) ||
+ !strncmp(buf + 1, "LT7170-1", 8))
+ return lt7170;
+ if (!strncmp(buf + 1, "LT7171", 6) ||
+ !strncmp(buf + 1, "LT7171-1", 8))
+ return lt7171;
+
+ return -ENODEV;
+ }
+
if (chip_id == LTC2972_ID)
return ltc2972;
else if (chip_id == LTC2974_ID)
@@ -665,6 +690,8 @@ static int ltc2978_get_id(struct i2c_client *client)
return ltm2987;
else if (chip_id == LTM4664_ID)
return ltm4664;
+ else if (chip_id == LTM4673_ID || chip_id == LTM4673_ID_REV1)
+ return ltm4673;
else if (chip_id == LTM4675_ID)
return ltm4675;
else if (chip_id == LTM4676_ID_REV1 || chip_id == LTM4676_ID_REV2 ||
@@ -736,6 +763,20 @@ static int ltc2978_probe(struct i2c_client *client)
data->temp2_max = 0x7c00;
switch (data->id) {
+ case lt7170:
+ case lt7171:
+ data->features |= FEAT_CLEAR_PEAKS | FEAT_NEEDS_POLLING;
+ info->read_word_data = ltc3883_read_word_data;
+ info->pages = LTC3883_NUM_PAGES;
+ info->format[PSC_VOLTAGE_IN] = ieee754;
+ info->format[PSC_VOLTAGE_OUT] = ieee754;
+ info->format[PSC_CURRENT_OUT] = ieee754;
+ info->format[PSC_TEMPERATURE] = ieee754;
+ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
+ | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
+ | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+ break;
case ltc2972:
info->read_word_data = ltc2975_read_word_data;
info->pages = LTC2972_NUM_PAGES;
@@ -869,6 +910,21 @@ static int ltc2978_probe(struct i2c_client *client)
| PMBUS_HAVE_IOUT
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
break;
+ case ltm4673:
+ data->features |= FEAT_NEEDS_POLLING;
+ info->read_word_data = ltc2975_read_word_data;
+ info->pages = LTC2974_NUM_PAGES;
+ info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
+ | PMBUS_HAVE_TEMP2;
+ for (i = 0; i < info->pages; i++) {
+ info->func[i] |= PMBUS_HAVE_IIN
+ | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+ | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
+ | PMBUS_HAVE_PIN
+ | PMBUS_HAVE_POUT
+ | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
+ }
+ break;
default:
return -ENODEV;
}
@@ -907,6 +963,8 @@ static int ltc2978_probe(struct i2c_client *client)
#ifdef CONFIG_OF
static const struct of_device_id ltc2978_of_match[] = {
+ { .compatible = "lltc,lt7170" },
+ { .compatible = "lltc,lt7171" },
{ .compatible = "lltc,ltc2972" },
{ .compatible = "lltc,ltc2974" },
{ .compatible = "lltc,ltc2975" },
@@ -926,6 +984,7 @@ static const struct of_device_id ltc2978_of_match[] = {
{ .compatible = "lltc,ltc7880" },
{ .compatible = "lltc,ltm2987" },
{ .compatible = "lltc,ltm4664" },
+ { .compatible = "lltc,ltm4673" },
{ .compatible = "lltc,ltm4675" },
{ .compatible = "lltc,ltm4676" },
{ .compatible = "lltc,ltm4677" },
diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c
index c9dda33831ff..56834d26f8ef 100644
--- a/drivers/hwmon/pmbus/max34440.c
+++ b/drivers/hwmon/pmbus/max34440.c
@@ -12,9 +12,26 @@
#include <linux/init.h>
#include <linux/err.h>
#include <linux/i2c.h>
+#include <linux/delay.h>
#include "pmbus.h"
-enum chips { max34440, max34441, max34446, max34451, max34460, max34461 };
+enum chips {
+ adpm12160,
+ max34440,
+ max34441,
+ max34446,
+ max34451,
+ max34460,
+ max34461,
+};
+
+/*
+ * Firmware is sometimes not ready if we try and read the
+ * data from the page immediately after setting. Maxim
+ * recommends 50us delay due to the chip failing to clock
+ * stretch long enough here.
+ */
+#define MAX34440_PAGE_CHANGE_DELAY 50
#define MAX34440_MFR_VOUT_PEAK 0xd4
#define MAX34440_MFR_IOUT_PEAK 0xd5
@@ -34,16 +51,21 @@ enum chips { max34440, max34441, max34446, max34451, max34460, max34461 };
/*
* The whole max344* family have IOUT_OC_WARN_LIMIT and IOUT_OC_FAULT_LIMIT
* swapped from the standard pmbus spec addresses.
+ * For max34451, version MAX34451ETNA6+ and later has this issue fixed.
*/
#define MAX34440_IOUT_OC_WARN_LIMIT 0x46
#define MAX34440_IOUT_OC_FAULT_LIMIT 0x4A
+#define MAX34451ETNA6_MFR_REV 0x0012
+
#define MAX34451_MFR_CHANNEL_CONFIG 0xe4
#define MAX34451_MFR_CHANNEL_CONFIG_SEL_MASK 0x3f
struct max34440_data {
int id;
struct pmbus_driver_info info;
+ u8 iout_oc_warn_limit;
+ u8 iout_oc_fault_limit;
};
#define to_max34440_data(x) container_of(x, struct max34440_data, info)
@@ -60,11 +82,11 @@ static int max34440_read_word_data(struct i2c_client *client, int page,
switch (reg) {
case PMBUS_IOUT_OC_FAULT_LIMIT:
ret = pmbus_read_word_data(client, page, phase,
- MAX34440_IOUT_OC_FAULT_LIMIT);
+ data->iout_oc_fault_limit);
break;
case PMBUS_IOUT_OC_WARN_LIMIT:
ret = pmbus_read_word_data(client, page, phase,
- MAX34440_IOUT_OC_WARN_LIMIT);
+ data->iout_oc_warn_limit);
break;
case PMBUS_VIRT_READ_VOUT_MIN:
ret = pmbus_read_word_data(client, page, phase,
@@ -75,7 +97,8 @@ static int max34440_read_word_data(struct i2c_client *client, int page,
MAX34440_MFR_VOUT_PEAK);
break;
case PMBUS_VIRT_READ_IOUT_AVG:
- if (data->id != max34446 && data->id != max34451)
+ if (data->id != max34446 && data->id != max34451 &&
+ data->id != adpm12160)
return -ENXIO;
ret = pmbus_read_word_data(client, page, phase,
MAX34446_MFR_IOUT_AVG);
@@ -133,11 +156,11 @@ static int max34440_write_word_data(struct i2c_client *client, int page,
switch (reg) {
case PMBUS_IOUT_OC_FAULT_LIMIT:
- ret = pmbus_write_word_data(client, page, MAX34440_IOUT_OC_FAULT_LIMIT,
+ ret = pmbus_write_word_data(client, page, data->iout_oc_fault_limit,
word);
break;
case PMBUS_IOUT_OC_WARN_LIMIT:
- ret = pmbus_write_word_data(client, page, MAX34440_IOUT_OC_WARN_LIMIT,
+ ret = pmbus_write_word_data(client, page, data->iout_oc_warn_limit,
word);
break;
case PMBUS_VIRT_RESET_POUT_HISTORY:
@@ -159,7 +182,8 @@ static int max34440_write_word_data(struct i2c_client *client, int page,
case PMBUS_VIRT_RESET_IOUT_HISTORY:
ret = pmbus_write_word_data(client, page,
MAX34440_MFR_IOUT_PEAK, 0);
- if (!ret && (data->id == max34446 || data->id == max34451))
+ if (!ret && (data->id == max34446 || data->id == max34451 ||
+ data->id == adpm12160))
ret = pmbus_write_word_data(client, page,
MAX34446_MFR_IOUT_AVG, 0);
@@ -235,9 +259,29 @@ static int max34451_set_supported_funcs(struct i2c_client *client,
*/
int page, rv;
+ bool max34451_na6 = false;
+
+ rv = i2c_smbus_read_word_data(client, PMBUS_MFR_REVISION);
+ if (rv < 0)
+ return rv;
+
+ if (rv >= MAX34451ETNA6_MFR_REV) {
+ max34451_na6 = true;
+ data->info.format[PSC_VOLTAGE_IN] = direct;
+ data->info.format[PSC_CURRENT_IN] = direct;
+ data->info.m[PSC_VOLTAGE_IN] = 1;
+ data->info.b[PSC_VOLTAGE_IN] = 0;
+ data->info.R[PSC_VOLTAGE_IN] = 3;
+ data->info.m[PSC_CURRENT_IN] = 1;
+ data->info.b[PSC_CURRENT_IN] = 0;
+ data->info.R[PSC_CURRENT_IN] = 2;
+ data->iout_oc_fault_limit = PMBUS_IOUT_OC_FAULT_LIMIT;
+ data->iout_oc_warn_limit = PMBUS_IOUT_OC_WARN_LIMIT;
+ }
for (page = 0; page < 16; page++) {
rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
+ fsleep(MAX34440_PAGE_CHANGE_DELAY);
if (rv < 0)
return rv;
@@ -251,16 +295,30 @@ static int max34451_set_supported_funcs(struct i2c_client *client,
case 0x20:
data->info.func[page] = PMBUS_HAVE_VOUT |
PMBUS_HAVE_STATUS_VOUT;
+
+ if (max34451_na6)
+ data->info.func[page] |= PMBUS_HAVE_VIN |
+ PMBUS_HAVE_STATUS_INPUT;
break;
case 0x21:
data->info.func[page] = PMBUS_HAVE_VOUT;
+
+ if (max34451_na6)
+ data->info.func[page] |= PMBUS_HAVE_VIN;
break;
case 0x22:
data->info.func[page] = PMBUS_HAVE_IOUT |
PMBUS_HAVE_STATUS_IOUT;
+
+ if (max34451_na6)
+ data->info.func[page] |= PMBUS_HAVE_IIN |
+ PMBUS_HAVE_STATUS_INPUT;
break;
case 0x23:
data->info.func[page] = PMBUS_HAVE_IOUT;
+
+ if (max34451_na6)
+ data->info.func[page] |= PMBUS_HAVE_IIN;
break;
default:
break;
@@ -271,6 +329,41 @@ static int max34451_set_supported_funcs(struct i2c_client *client,
}
static struct pmbus_driver_info max34440_info[] = {
+ [adpm12160] = {
+ .pages = 19,
+ .format[PSC_VOLTAGE_IN] = direct,
+ .format[PSC_VOLTAGE_OUT] = direct,
+ .format[PSC_CURRENT_IN] = direct,
+ .format[PSC_CURRENT_OUT] = direct,
+ .format[PSC_TEMPERATURE] = direct,
+ .m[PSC_VOLTAGE_IN] = 1,
+ .b[PSC_VOLTAGE_IN] = 0,
+ .R[PSC_VOLTAGE_IN] = 0,
+ .m[PSC_VOLTAGE_OUT] = 1,
+ .b[PSC_VOLTAGE_OUT] = 0,
+ .R[PSC_VOLTAGE_OUT] = 0,
+ .m[PSC_CURRENT_IN] = 1,
+ .b[PSC_CURRENT_IN] = 0,
+ .R[PSC_CURRENT_IN] = 2,
+ .m[PSC_CURRENT_OUT] = 1,
+ .b[PSC_CURRENT_OUT] = 0,
+ .R[PSC_CURRENT_OUT] = 2,
+ .m[PSC_TEMPERATURE] = 1,
+ .b[PSC_TEMPERATURE] = 0,
+ .R[PSC_TEMPERATURE] = 2,
+ /* absent func below [18] are not for monitoring */
+ .func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
+ .func[4] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[5] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[6] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[7] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[8] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+ .func[9] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT,
+ .func[10] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT,
+ .func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
+ .read_word_data = max34440_read_word_data,
+ .write_word_data = max34440_write_word_data,
+ },
[max34440] = {
.pages = 14,
.format[PSC_VOLTAGE_IN] = direct,
@@ -312,6 +405,7 @@ static struct pmbus_driver_info max34440_info[] = {
.read_byte_data = max34440_read_byte_data,
.read_word_data = max34440_read_word_data,
.write_word_data = max34440_write_word_data,
+ .page_change_delay = MAX34440_PAGE_CHANGE_DELAY,
},
[max34441] = {
.pages = 12,
@@ -355,6 +449,7 @@ static struct pmbus_driver_info max34440_info[] = {
.read_byte_data = max34440_read_byte_data,
.read_word_data = max34440_read_word_data,
.write_word_data = max34440_write_word_data,
+ .page_change_delay = MAX34440_PAGE_CHANGE_DELAY,
},
[max34446] = {
.pages = 7,
@@ -392,6 +487,7 @@ static struct pmbus_driver_info max34440_info[] = {
.read_byte_data = max34440_read_byte_data,
.read_word_data = max34440_read_word_data,
.write_word_data = max34440_write_word_data,
+ .page_change_delay = MAX34440_PAGE_CHANGE_DELAY,
},
[max34451] = {
.pages = 21,
@@ -415,6 +511,7 @@ static struct pmbus_driver_info max34440_info[] = {
.func[20] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.read_word_data = max34440_read_word_data,
.write_word_data = max34440_write_word_data,
+ .page_change_delay = MAX34440_PAGE_CHANGE_DELAY,
},
[max34460] = {
.pages = 18,
@@ -445,6 +542,7 @@ static struct pmbus_driver_info max34440_info[] = {
.func[17] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.read_word_data = max34440_read_word_data,
.write_word_data = max34440_write_word_data,
+ .page_change_delay = MAX34440_PAGE_CHANGE_DELAY,
},
[max34461] = {
.pages = 23,
@@ -480,6 +578,7 @@ static struct pmbus_driver_info max34440_info[] = {
.func[21] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.read_word_data = max34440_read_word_data,
.write_word_data = max34440_write_word_data,
+ .page_change_delay = MAX34440_PAGE_CHANGE_DELAY,
},
};
@@ -494,17 +593,23 @@ static int max34440_probe(struct i2c_client *client)
return -ENOMEM;
data->id = i2c_match_id(max34440_id, client)->driver_data;
data->info = max34440_info[data->id];
+ data->iout_oc_fault_limit = MAX34440_IOUT_OC_FAULT_LIMIT;
+ data->iout_oc_warn_limit = MAX34440_IOUT_OC_WARN_LIMIT;
if (data->id == max34451) {
rv = max34451_set_supported_funcs(client, data);
if (rv)
return rv;
+ } else if (data->id == adpm12160) {
+ data->iout_oc_fault_limit = PMBUS_IOUT_OC_FAULT_LIMIT;
+ data->iout_oc_warn_limit = PMBUS_IOUT_OC_WARN_LIMIT;
}
return pmbus_do_probe(client, &data->info);
}
static const struct i2c_device_id max34440_id[] = {
+ {"adpm12160", adpm12160},
{"max34440", max34440},
{"max34441", max34441},
{"max34446", max34446},
diff --git a/drivers/hwmon/pmbus/mpq7932.c b/drivers/hwmon/pmbus/mpq7932.c
index c1e2d0cb2fd0..8f10e37a7a76 100644
--- a/drivers/hwmon/pmbus/mpq7932.c
+++ b/drivers/hwmon/pmbus/mpq7932.c
@@ -51,8 +51,8 @@ static const struct regulator_desc mpq7932_regulators_desc[] = {
};
static const struct regulator_desc mpq7932_regulators_desc_one[] = {
- PMBUS_REGULATOR_STEP_ONE("buck", MPQ7932_N_VOLTAGES,
- MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN),
+ PMBUS_REGULATOR_STEP_ONE_NODE("buck", MPQ7932_N_VOLTAGES,
+ MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN),
};
#endif
diff --git a/drivers/hwmon/pmbus/mpq8785.c b/drivers/hwmon/pmbus/mpq8785.c
index 331c274ca892..1f56aaf4dde8 100644
--- a/drivers/hwmon/pmbus/mpq8785.c
+++ b/drivers/hwmon/pmbus/mpq8785.c
@@ -4,10 +4,23 @@
*/
#include <linux/i2c.h>
+#include <linux/bitops.h>
#include <linux/module.h>
+#include <linux/property.h>
#include <linux/of_device.h>
#include "pmbus.h"
+#define MPM82504_READ_TEMPERATURE_1_SIGN_POS 9
+
+enum chips { mpm3695, mpm3695_25, mpm82504, mpq8785 };
+
+static u16 voltage_scale_loop_max_val[] = {
+ [mpm3695] = GENMASK(9, 0),
+ [mpm3695_25] = GENMASK(11, 0),
+ [mpm82504] = GENMASK(9, 0),
+ [mpq8785] = GENMASK(10, 0),
+};
+
static int mpq8785_identify(struct i2c_client *client,
struct pmbus_driver_info *info)
{
@@ -34,6 +47,20 @@ static int mpq8785_identify(struct i2c_client *client,
return 0;
};
+static int mpm82504_read_word_data(struct i2c_client *client, int page,
+ int phase, int reg)
+{
+ int ret;
+
+ ret = pmbus_read_word_data(client, page, phase, reg);
+
+ if (ret < 0 || reg != PMBUS_READ_TEMPERATURE_1)
+ return ret;
+
+ /* Fix PMBUS_READ_TEMPERATURE_1 signedness */
+ return sign_extend32(ret, MPM82504_READ_TEMPERATURE_1_SIGN_POS) & 0xffff;
+}
+
static struct pmbus_driver_info mpq8785_info = {
.pages = 1,
.format[PSC_VOLTAGE_IN] = direct,
@@ -53,26 +80,74 @@ static struct pmbus_driver_info mpq8785_info = {
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
- .identify = mpq8785_identify,
-};
-
-static int mpq8785_probe(struct i2c_client *client)
-{
- return pmbus_do_probe(client, &mpq8785_info);
};
static const struct i2c_device_id mpq8785_id[] = {
- { "mpq8785" },
+ { "mpm3695", mpm3695 },
+ { "mpm3695-25", mpm3695_25 },
+ { "mpm82504", mpm82504 },
+ { "mpq8785", mpq8785 },
{ },
};
MODULE_DEVICE_TABLE(i2c, mpq8785_id);
static const struct of_device_id __maybe_unused mpq8785_of_match[] = {
- { .compatible = "mps,mpq8785" },
+ { .compatible = "mps,mpm3695", .data = (void *)mpm3695 },
+ { .compatible = "mps,mpm3695-25", .data = (void *)mpm3695_25 },
+ { .compatible = "mps,mpm82504", .data = (void *)mpm82504 },
+ { .compatible = "mps,mpq8785", .data = (void *)mpq8785 },
{}
};
MODULE_DEVICE_TABLE(of, mpq8785_of_match);
+static int mpq8785_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct pmbus_driver_info *info;
+ enum chips chip_id;
+ u32 voltage_scale;
+ int ret;
+
+ info = devm_kmemdup(dev, &mpq8785_info, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ if (dev->of_node)
+ chip_id = (kernel_ulong_t)of_device_get_match_data(dev);
+ else
+ chip_id = (kernel_ulong_t)i2c_get_match_data(client);
+
+ switch (chip_id) {
+ case mpm3695:
+ case mpm3695_25:
+ case mpm82504:
+ info->format[PSC_VOLTAGE_OUT] = direct;
+ info->m[PSC_VOLTAGE_OUT] = 8;
+ info->b[PSC_VOLTAGE_OUT] = 0;
+ info->R[PSC_VOLTAGE_OUT] = 2;
+ info->read_word_data = mpm82504_read_word_data;
+ break;
+ case mpq8785:
+ info->identify = mpq8785_identify;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ if (!device_property_read_u32(dev, "mps,vout-fb-divider-ratio-permille",
+ &voltage_scale)) {
+ if (voltage_scale > voltage_scale_loop_max_val[chip_id])
+ return -EINVAL;
+
+ ret = i2c_smbus_write_word_data(client, PMBUS_VOUT_SCALE_LOOP,
+ voltage_scale);
+ if (ret)
+ return ret;
+ }
+
+ return pmbus_do_probe(client, info);
+};
+
static struct i2c_driver mpq8785_driver = {
.driver = {
.name = "mpq8785",
diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c
index 77cf268e7d2d..920cd5408141 100644
--- a/drivers/hwmon/pmbus/pmbus.c
+++ b/drivers/hwmon/pmbus/pmbus.c
@@ -103,6 +103,8 @@ static int pmbus_identify(struct i2c_client *client,
if (pmbus_check_byte_register(client, 0, PMBUS_PAGE)) {
int page;
+ info->pages = PMBUS_PAGES;
+
for (page = 1; page < PMBUS_PAGES; page++) {
if (pmbus_set_page(client, page, 0xff) < 0)
break;
diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
index ddb19c9726d6..d2e9bfb5320f 100644
--- a/drivers/hwmon/pmbus/pmbus.h
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -482,6 +482,7 @@ struct pmbus_driver_info {
*/
int access_delay; /* in microseconds */
int write_delay; /* in microseconds */
+ int page_change_delay; /* in microseconds */
};
/* Regulator ops */
@@ -508,11 +509,11 @@ int pmbus_regulator_init_cb(struct regulator_dev *rdev,
#define PMBUS_REGULATOR(_name, _id) PMBUS_REGULATOR_STEP(_name, _id, 0, 0, 0)
-#define PMBUS_REGULATOR_STEP_ONE(_name, _voltages, _step, _min_uV) \
+#define __PMBUS_REGULATOR_STEP_ONE(_name, _node, _voltages, _step, _min_uV) \
{ \
.name = (_name), \
.of_match = of_match_ptr(_name), \
- .regulators_node = of_match_ptr("regulators"), \
+ .regulators_node = of_match_ptr(_node), \
.ops = &pmbus_regulator_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -522,7 +523,19 @@ int pmbus_regulator_init_cb(struct regulator_dev *rdev,
.init_cb = pmbus_regulator_init_cb, \
}
-#define PMBUS_REGULATOR_ONE(_name) PMBUS_REGULATOR_STEP_ONE(_name, 0, 0, 0)
+/*
+ * _NODE macros are defined for historic reasons and MUST NOT be used in new
+ * drivers.
+ */
+#define PMBUS_REGULATOR_STEP_ONE_NODE(_name, _voltages, _step, _min_uV) \
+ __PMBUS_REGULATOR_STEP_ONE(_name, "regulators", _voltages, _step, _min_uV)
+
+#define PMBUS_REGULATOR_ONE_NODE(_name) PMBUS_REGULATOR_STEP_ONE_NODE(_name, 0, 0, 0)
+
+#define PMBUS_REGULATOR_STEP_ONE(_name, _voltages, _step, _min_uV) \
+ __PMBUS_REGULATOR_STEP_ONE(_name, NULL, _voltages, _step, _min_uV)
+
+#define PMBUS_REGULATOR_ONE(_name) PMBUS_REGULATOR_STEP_ONE(_name, 0, 0, 0)
/* Function declarations */
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 787683e83db6..be6d05def115 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -8,6 +8,7 @@
#include <linux/debugfs.h>
#include <linux/delay.h>
+#include <linux/dcache.h>
#include <linux/kernel.h>
#include <linux/math64.h>
#include <linux/module.h>
@@ -31,6 +32,13 @@
#define PMBUS_ATTR_ALLOC_SIZE 32
#define PMBUS_NAME_SIZE 24
+/*
+ * The type of operation used for picking the delay between
+ * successive pmbus operations.
+ */
+#define PMBUS_OP_WRITE BIT(0)
+#define PMBUS_OP_PAGE_CHANGE BIT(1)
+
static int wp = -1;
module_param(wp, int, 0444);
@@ -44,8 +52,7 @@ struct pmbus_sensor {
enum pmbus_sensor_classes class; /* sensor class */
bool update; /* runtime sensor update needed */
bool convert; /* Whether or not to apply linear/vid/direct */
- int data; /* Sensor data.
- Negative if there was a read error */
+ int data; /* Sensor data; negative if there was a read error */
};
#define to_pmbus_sensor(_attr) \
container_of(_attr, struct pmbus_sensor, attribute)
@@ -100,7 +107,6 @@ struct pmbus_data {
int num_attributes;
struct attribute_group group;
const struct attribute_group **groups;
- struct dentry *debugfs; /* debugfs device directory */
struct pmbus_sensor *sensors;
@@ -114,8 +120,8 @@ struct pmbus_data {
int vout_low[PMBUS_PAGES]; /* voltage low margin */
int vout_high[PMBUS_PAGES]; /* voltage high margin */
- ktime_t write_time; /* Last SMBUS write timestamp */
- ktime_t access_time; /* Last SMBUS access timestamp */
+
+ ktime_t next_access_backoff; /* Wait until at least this time */
};
struct pmbus_debugfs_entry {
@@ -170,33 +176,26 @@ EXPORT_SYMBOL_NS_GPL(pmbus_set_update, "PMBUS");
static void pmbus_wait(struct i2c_client *client)
{
struct pmbus_data *data = i2c_get_clientdata(client);
- const struct pmbus_driver_info *info = data->info;
- s64 delta;
-
- if (info->access_delay) {
- delta = ktime_us_delta(ktime_get(), data->access_time);
+ s64 delay = ktime_us_delta(data->next_access_backoff, ktime_get());
- if (delta < info->access_delay)
- fsleep(info->access_delay - delta);
- } else if (info->write_delay) {
- delta = ktime_us_delta(ktime_get(), data->write_time);
-
- if (delta < info->write_delay)
- fsleep(info->write_delay - delta);
- }
+ if (delay > 0)
+ fsleep(delay);
}
-/* Sets the last accessed timestamp for pmbus_wait */
-static void pmbus_update_ts(struct i2c_client *client, bool write_op)
+/* Sets the last operation timestamp for pmbus_wait */
+static void pmbus_update_ts(struct i2c_client *client, int op)
{
struct pmbus_data *data = i2c_get_clientdata(client);
const struct pmbus_driver_info *info = data->info;
+ int delay = info->access_delay;
- if (info->access_delay) {
- data->access_time = ktime_get();
- } else if (info->write_delay && write_op) {
- data->write_time = ktime_get();
- }
+ if (op & PMBUS_OP_WRITE)
+ delay = max(delay, info->write_delay);
+ if (op & PMBUS_OP_PAGE_CHANGE)
+ delay = max(delay, info->page_change_delay);
+
+ if (delay > 0)
+ data->next_access_backoff = ktime_add_us(ktime_get(), delay);
}
int pmbus_set_page(struct i2c_client *client, int page, int phase)
@@ -211,13 +210,13 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase)
data->info->pages > 1 && page != data->currpage) {
pmbus_wait(client);
rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
- pmbus_update_ts(client, true);
+ pmbus_update_ts(client, PMBUS_OP_WRITE | PMBUS_OP_PAGE_CHANGE);
if (rv < 0)
return rv;
pmbus_wait(client);
rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
- pmbus_update_ts(client, false);
+ pmbus_update_ts(client, 0);
if (rv < 0)
return rv;
@@ -231,7 +230,7 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase)
pmbus_wait(client);
rv = i2c_smbus_write_byte_data(client, PMBUS_PHASE,
phase);
- pmbus_update_ts(client, true);
+ pmbus_update_ts(client, PMBUS_OP_WRITE);
if (rv)
return rv;
}
@@ -251,7 +250,7 @@ int pmbus_write_byte(struct i2c_client *client, int page, u8 value)
pmbus_wait(client);
rv = i2c_smbus_write_byte(client, value);
- pmbus_update_ts(client, true);
+ pmbus_update_ts(client, PMBUS_OP_WRITE);
return rv;
}
@@ -286,13 +285,12 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg,
pmbus_wait(client);
rv = i2c_smbus_write_word_data(client, reg, word);
- pmbus_update_ts(client, true);
+ pmbus_update_ts(client, PMBUS_OP_WRITE);
return rv;
}
EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, "PMBUS");
-
static int pmbus_write_virt_reg(struct i2c_client *client, int page, int reg,
u16 word)
{
@@ -381,14 +379,14 @@ int pmbus_update_fan(struct i2c_client *client, int page, int id,
u8 to;
from = _pmbus_read_byte_data(client, page,
- pmbus_fan_config_registers[id]);
+ pmbus_fan_config_registers[id]);
if (from < 0)
return from;
to = (from & ~mask) | (config & mask);
if (to != from) {
rv = _pmbus_write_byte_data(client, page,
- pmbus_fan_config_registers[id], to);
+ pmbus_fan_config_registers[id], to);
if (rv < 0)
return rv;
}
@@ -408,7 +406,7 @@ int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg)
pmbus_wait(client);
rv = i2c_smbus_read_word_data(client, reg);
- pmbus_update_ts(client, false);
+ pmbus_update_ts(client, 0);
return rv;
}
@@ -471,7 +469,7 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg)
pmbus_wait(client);
rv = i2c_smbus_read_byte_data(client, reg);
- pmbus_update_ts(client, false);
+ pmbus_update_ts(client, 0);
return rv;
}
@@ -487,7 +485,7 @@ int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value)
pmbus_wait(client);
rv = i2c_smbus_write_byte_data(client, reg, value);
- pmbus_update_ts(client, true);
+ pmbus_update_ts(client, PMBUS_OP_WRITE);
return rv;
}
@@ -523,7 +521,7 @@ static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg,
pmbus_wait(client);
rv = i2c_smbus_read_block_data(client, reg, data_buf);
- pmbus_update_ts(client, false);
+ pmbus_update_ts(client, 0);
return rv;
}
@@ -563,7 +561,7 @@ static int pmbus_get_fan_rate(struct i2c_client *client, int page, int id,
}
config = _pmbus_read_byte_data(client, page,
- pmbus_fan_config_registers[id]);
+ pmbus_fan_config_registers[id]);
if (config < 0)
return config;
@@ -788,7 +786,7 @@ static s64 pmbus_reg2data_linear(struct pmbus_data *data,
if (sensor->class == PSC_VOLTAGE_OUT) { /* LINEAR16 */
exponent = data->exponent[sensor->page];
- mantissa = (u16) sensor->data;
+ mantissa = (u16)sensor->data;
} else { /* LINEAR11 */
exponent = ((s16)sensor->data) >> 11;
mantissa = ((s16)((sensor->data & 0x7ff) << 5)) >> 5;
@@ -1173,7 +1171,6 @@ static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b,
} else {
pmbus_clear_fault_page(client, page);
}
-
}
if (s1 && s2) {
s64 v1, v2;
@@ -1470,8 +1467,7 @@ static int pmbus_add_label(struct pmbus_data *data,
snprintf(label->name, sizeof(label->name), "%s%d_label", name, seq);
if (!index) {
if (phase == 0xff)
- strncpy(label->label, lstring,
- sizeof(label->label) - 1);
+ strscpy(label->label, lstring);
else
snprintf(label->label, sizeof(label->label), "%s.%d",
lstring, phase);
@@ -1500,8 +1496,7 @@ struct pmbus_limit_attr {
u16 reg; /* Limit register */
u16 sbit; /* Alarm attribute status bit */
bool update; /* True if register needs updates */
- bool low; /* True if low limit; for limits with compare
- functions only */
+ bool low; /* True if low limit; for limits with compare functions only */
const char *attr; /* Attribute name */
const char *alarm; /* Alarm attribute name */
};
@@ -2212,8 +2207,8 @@ static const u32 pmbus_fan_status_flags[] = {
/* Precondition: FAN_CONFIG_x_y and FAN_COMMAND_x must exist for the fan ID */
static int pmbus_add_fan_ctrl(struct i2c_client *client,
- struct pmbus_data *data, int index, int page, int id,
- u8 config)
+ struct pmbus_data *data, int index, int page,
+ int id, u8 config)
{
struct pmbus_sensor *sensor;
@@ -2225,7 +2220,7 @@ static int pmbus_add_fan_ctrl(struct i2c_client *client,
return -ENOMEM;
if (!((data->info->func[page] & PMBUS_HAVE_PWM12) ||
- (data->info->func[page] & PMBUS_HAVE_PWM34)))
+ (data->info->func[page] & PMBUS_HAVE_PWM34)))
return 0;
sensor = pmbus_add_sensor(data, "pwm", NULL, index, page,
@@ -2530,7 +2525,7 @@ static int pmbus_read_coefficients(struct i2c_client *client,
rv = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_WRITE, PMBUS_COEFFICIENTS,
I2C_SMBUS_BLOCK_PROC_CALL, &data);
- pmbus_update_ts(client, true);
+ pmbus_update_ts(client, PMBUS_OP_WRITE);
if (rv < 0)
return rv;
@@ -2734,7 +2729,7 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
if (!(data->flags & PMBUS_NO_CAPABILITY)) {
pmbus_wait(client);
ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
- pmbus_update_ts(client, false);
+ pmbus_update_ts(client, 0);
if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) {
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC))
@@ -2750,13 +2745,13 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
data->read_status = pmbus_read_status_word;
pmbus_wait(client);
ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD);
- pmbus_update_ts(client, false);
+ pmbus_update_ts(client, 0);
if (ret < 0 || ret == 0xffff) {
data->read_status = pmbus_read_status_byte;
pmbus_wait(client);
ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE);
- pmbus_update_ts(client, false);
+ pmbus_update_ts(client, 0);
if (ret < 0 || ret == 0xff) {
dev_err(dev, "PMBus status register not found\n");
@@ -2935,7 +2930,7 @@ static void pmbus_notify(struct pmbus_data *data, int page, int reg, int flags)
}
static int _pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags,
- unsigned int *event, bool notify)
+ unsigned int *event, bool notify)
{
int i, status;
const struct pmbus_status_category *cat;
@@ -2964,7 +2959,6 @@ static int _pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flag
if (notify && status)
pmbus_notify(data, page, cat->reg, status);
-
}
/*
@@ -3015,7 +3009,6 @@ static int _pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flag
*event |= REGULATOR_EVENT_OVER_TEMP_WARN;
}
-
return 0;
}
@@ -3228,7 +3221,7 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
}
static int pmbus_regulator_list_voltage(struct regulator_dev *rdev,
- unsigned int selector)
+ unsigned int selector)
{
struct device *dev = rdev_get_dev(rdev);
struct i2c_client *client = to_i2c_client(dev->parent);
@@ -3320,17 +3313,16 @@ static int pmbus_regulator_register(struct pmbus_data *data)
return 0;
}
-static int pmbus_regulator_notify(struct pmbus_data *data, int page, int event)
+static void pmbus_regulator_notify(struct pmbus_data *data, int page, int event)
{
- int j;
+ int j;
- for (j = 0; j < data->info->num_regulators; j++) {
- if (page == rdev_get_id(data->rdevs[j])) {
- regulator_notifier_call_chain(data->rdevs[j], event, NULL);
- break;
- }
+ for (j = 0; j < data->info->num_regulators; j++) {
+ if (page == rdev_get_id(data->rdevs[j])) {
+ regulator_notifier_call_chain(data->rdevs[j], event, NULL);
+ break;
}
- return 0;
+ }
}
#else
static int pmbus_regulator_register(struct pmbus_data *data)
@@ -3338,9 +3330,8 @@ static int pmbus_regulator_register(struct pmbus_data *data)
return 0;
}
-static int pmbus_regulator_notify(struct pmbus_data *data, int page, int event)
+static void pmbus_regulator_notify(struct pmbus_data *data, int page, int event)
{
- return 0;
}
#endif
@@ -3363,8 +3354,8 @@ static irqreturn_t pmbus_fault_handler(int irq, void *pdata)
{
struct pmbus_data *data = pdata;
struct i2c_client *client = to_i2c_client(data->dev);
-
int i, status, event;
+
mutex_lock(&data->update_lock);
for (i = 0; i < data->info->pages; i++) {
_pmbus_get_flags(data, i, &status, &event, true);
@@ -3428,7 +3419,6 @@ static int pmbus_irq_setup(struct i2c_client *client, struct pmbus_data *data)
static struct dentry *pmbus_debugfs_dir; /* pmbus debugfs directory */
-#if IS_ENABLED(CONFIG_DEBUG_FS)
static int pmbus_debugfs_get(void *data, u64 *val)
{
int rc;
@@ -3471,8 +3461,8 @@ static int pmbus_debugfs_get_status(void *data, u64 *val)
DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_status, pmbus_debugfs_get_status,
NULL, "0x%04llx\n");
-static ssize_t pmbus_debugfs_mfr_read(struct file *file, char __user *buf,
- size_t count, loff_t *ppos)
+static ssize_t pmbus_debugfs_block_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
{
int rc;
struct pmbus_debugfs_entry *entry = file->private_data;
@@ -3497,51 +3487,98 @@ static ssize_t pmbus_debugfs_mfr_read(struct file *file, char __user *buf,
return simple_read_from_buffer(buf, count, ppos, data, rc);
}
-static const struct file_operations pmbus_debugfs_ops_mfr = {
+static const struct file_operations pmbus_debugfs_block_ops = {
.llseek = noop_llseek,
- .read = pmbus_debugfs_mfr_read,
+ .read = pmbus_debugfs_block_read,
.write = NULL,
.open = simple_open,
};
-static void pmbus_remove_debugfs(void *data)
+static void pmbus_remove_symlink(void *symlink)
{
- struct dentry *entry = data;
-
- debugfs_remove_recursive(entry);
+ debugfs_remove(symlink);
}
-static int pmbus_init_debugfs(struct i2c_client *client,
- struct pmbus_data *data)
+struct pmbus_debugfs_data {
+ u8 reg;
+ u32 flag;
+ const char *name;
+};
+
+static const struct pmbus_debugfs_data pmbus_debugfs_block_data[] = {
+ { .reg = PMBUS_MFR_ID, .name = "mfr_id" },
+ { .reg = PMBUS_MFR_MODEL, .name = "mfr_model" },
+ { .reg = PMBUS_MFR_REVISION, .name = "mfr_revision" },
+ { .reg = PMBUS_MFR_LOCATION, .name = "mfr_location" },
+ { .reg = PMBUS_MFR_DATE, .name = "mfr_date" },
+ { .reg = PMBUS_MFR_SERIAL, .name = "mfr_serial" },
+};
+
+static const struct pmbus_debugfs_data pmbus_debugfs_status_data[] = {
+ { .reg = PMBUS_STATUS_VOUT, .flag = PMBUS_HAVE_STATUS_VOUT, .name = "status%d_vout" },
+ { .reg = PMBUS_STATUS_IOUT, .flag = PMBUS_HAVE_STATUS_IOUT, .name = "status%d_iout" },
+ { .reg = PMBUS_STATUS_INPUT, .flag = PMBUS_HAVE_STATUS_INPUT, .name = "status%d_input" },
+ { .reg = PMBUS_STATUS_TEMPERATURE, .flag = PMBUS_HAVE_STATUS_TEMP,
+ .name = "status%d_temp" },
+ { .reg = PMBUS_STATUS_FAN_12, .flag = PMBUS_HAVE_STATUS_FAN12, .name = "status%d_fan12" },
+ { .reg = PMBUS_STATUS_FAN_34, .flag = PMBUS_HAVE_STATUS_FAN34, .name = "status%d_fan34" },
+ { .reg = PMBUS_STATUS_CML, .name = "status%d_cml" },
+ { .reg = PMBUS_STATUS_OTHER, .name = "status%d_other" },
+ { .reg = PMBUS_STATUS_MFR_SPECIFIC, .name = "status%d_mfr" },
+};
+
+static void pmbus_init_debugfs(struct i2c_client *client,
+ struct pmbus_data *data)
{
- int i, idx = 0;
- char name[PMBUS_NAME_SIZE];
+ struct dentry *symlink_d, *debugfs = client->debugfs;
struct pmbus_debugfs_entry *entries;
+ const char *pathname, *symlink;
+ char name[PMBUS_NAME_SIZE];
+ int page, i, idx = 0;
- if (!pmbus_debugfs_dir)
- return -ENODEV;
+ /*
+ * client->debugfs may be NULL or an ERR_PTR(). dentry_path_raw()
+ * does not check if its parameters are valid, so validate
+ * client->debugfs before using it.
+ */
+ if (!pmbus_debugfs_dir || IS_ERR_OR_NULL(debugfs))
+ return;
/*
- * Create the debugfs directory for this device. Use the hwmon device
- * name to avoid conflicts (hwmon numbers are globally unique).
+ * Backwards compatibility: Create symlink from /pmbus/<hwmon_device>
+ * to i2c debugfs directory.
*/
- data->debugfs = debugfs_create_dir(dev_name(data->hwmon_dev),
- pmbus_debugfs_dir);
- if (IS_ERR_OR_NULL(data->debugfs)) {
- data->debugfs = NULL;
- return -ENODEV;
- }
+ pathname = dentry_path_raw(debugfs, name, sizeof(name));
+ if (IS_ERR(pathname))
+ return;
+
+ /*
+ * The path returned by dentry_path_raw() starts with '/'. Prepend it
+ * with ".." to get the symlink relative to the pmbus root directory.
+ */
+ symlink = kasprintf(GFP_KERNEL, "..%s", pathname);
+ if (!symlink)
+ return;
+
+ symlink_d = debugfs_create_symlink(dev_name(data->hwmon_dev),
+ pmbus_debugfs_dir, symlink);
+ kfree(symlink);
+
+ devm_add_action_or_reset(data->dev, pmbus_remove_symlink, symlink_d);
/*
* Allocate the max possible entries we need.
- * 7 entries device-specific
- * 10 entries page-specific
+ * device specific:
+ * ARRAY_SIZE(pmbus_debugfs_block_data) + 2
+ * page specific:
+ * ARRAY_SIZE(pmbus_debugfs_status_data) + 1
*/
entries = devm_kcalloc(data->dev,
- 7 + data->info->pages * 10, sizeof(*entries),
- GFP_KERNEL);
+ ARRAY_SIZE(pmbus_debugfs_block_data) + 2 +
+ data->info->pages * (ARRAY_SIZE(pmbus_debugfs_status_data) + 1),
+ sizeof(*entries), GFP_KERNEL);
if (!entries)
- return -ENOMEM;
+ return;
/*
* Add device-specific entries.
@@ -3551,184 +3588,67 @@ static int pmbus_init_debugfs(struct i2c_client *client,
* assume that values of the following registers are the same for all
* pages and report values only for page 0.
*/
- if (pmbus_check_byte_register(client, 0, PMBUS_REVISION)) {
+ if (!(data->flags & PMBUS_NO_CAPABILITY) &&
+ pmbus_check_byte_register(client, 0, PMBUS_CAPABILITY)) {
entries[idx].client = client;
entries[idx].page = 0;
- entries[idx].reg = PMBUS_REVISION;
- debugfs_create_file("revision", 0444, data->debugfs,
+ entries[idx].reg = PMBUS_CAPABILITY;
+ debugfs_create_file("capability", 0444, debugfs,
&entries[idx++],
&pmbus_debugfs_ops);
}
-
- if (pmbus_check_block_register(client, 0, PMBUS_MFR_ID)) {
- entries[idx].client = client;
- entries[idx].page = 0;
- entries[idx].reg = PMBUS_MFR_ID;
- debugfs_create_file("mfr_id", 0444, data->debugfs,
- &entries[idx++],
- &pmbus_debugfs_ops_mfr);
- }
-
- if (pmbus_check_block_register(client, 0, PMBUS_MFR_MODEL)) {
- entries[idx].client = client;
- entries[idx].page = 0;
- entries[idx].reg = PMBUS_MFR_MODEL;
- debugfs_create_file("mfr_model", 0444, data->debugfs,
- &entries[idx++],
- &pmbus_debugfs_ops_mfr);
- }
-
- if (pmbus_check_block_register(client, 0, PMBUS_MFR_REVISION)) {
- entries[idx].client = client;
- entries[idx].page = 0;
- entries[idx].reg = PMBUS_MFR_REVISION;
- debugfs_create_file("mfr_revision", 0444, data->debugfs,
- &entries[idx++],
- &pmbus_debugfs_ops_mfr);
- }
-
- if (pmbus_check_block_register(client, 0, PMBUS_MFR_LOCATION)) {
+ if (pmbus_check_byte_register(client, 0, PMBUS_REVISION)) {
entries[idx].client = client;
entries[idx].page = 0;
- entries[idx].reg = PMBUS_MFR_LOCATION;
- debugfs_create_file("mfr_location", 0444, data->debugfs,
+ entries[idx].reg = PMBUS_REVISION;
+ debugfs_create_file("pmbus_revision", 0444, debugfs,
&entries[idx++],
- &pmbus_debugfs_ops_mfr);
+ &pmbus_debugfs_ops);
}
- if (pmbus_check_block_register(client, 0, PMBUS_MFR_DATE)) {
- entries[idx].client = client;
- entries[idx].page = 0;
- entries[idx].reg = PMBUS_MFR_DATE;
- debugfs_create_file("mfr_date", 0444, data->debugfs,
- &entries[idx++],
- &pmbus_debugfs_ops_mfr);
- }
+ for (i = 0; i < ARRAY_SIZE(pmbus_debugfs_block_data); i++) {
+ const struct pmbus_debugfs_data *d = &pmbus_debugfs_block_data[i];
- if (pmbus_check_block_register(client, 0, PMBUS_MFR_SERIAL)) {
- entries[idx].client = client;
- entries[idx].page = 0;
- entries[idx].reg = PMBUS_MFR_SERIAL;
- debugfs_create_file("mfr_serial", 0444, data->debugfs,
- &entries[idx++],
- &pmbus_debugfs_ops_mfr);
+ if (pmbus_check_block_register(client, 0, d->reg)) {
+ entries[idx].client = client;
+ entries[idx].page = 0;
+ entries[idx].reg = d->reg;
+ debugfs_create_file(d->name, 0444, debugfs,
+ &entries[idx++],
+ &pmbus_debugfs_block_ops);
+ }
}
/* Add page specific entries */
- for (i = 0; i < data->info->pages; ++i) {
+ for (page = 0; page < data->info->pages; ++page) {
/* Check accessibility of status register if it's not page 0 */
- if (!i || pmbus_check_status_register(client, i)) {
+ if (!page || pmbus_check_status_register(client, page)) {
/* No need to set reg as we have special read op. */
entries[idx].client = client;
- entries[idx].page = i;
- scnprintf(name, PMBUS_NAME_SIZE, "status%d", i);
- debugfs_create_file(name, 0444, data->debugfs,
+ entries[idx].page = page;
+ scnprintf(name, PMBUS_NAME_SIZE, "status%d", page);
+ debugfs_create_file(name, 0444, debugfs,
&entries[idx++],
&pmbus_debugfs_ops_status);
}
- if (data->info->func[i] & PMBUS_HAVE_STATUS_VOUT) {
- entries[idx].client = client;
- entries[idx].page = i;
- entries[idx].reg = PMBUS_STATUS_VOUT;
- scnprintf(name, PMBUS_NAME_SIZE, "status%d_vout", i);
- debugfs_create_file(name, 0444, data->debugfs,
- &entries[idx++],
- &pmbus_debugfs_ops);
- }
-
- if (data->info->func[i] & PMBUS_HAVE_STATUS_IOUT) {
- entries[idx].client = client;
- entries[idx].page = i;
- entries[idx].reg = PMBUS_STATUS_IOUT;
- scnprintf(name, PMBUS_NAME_SIZE, "status%d_iout", i);
- debugfs_create_file(name, 0444, data->debugfs,
- &entries[idx++],
- &pmbus_debugfs_ops);
- }
-
- if (data->info->func[i] & PMBUS_HAVE_STATUS_INPUT) {
- entries[idx].client = client;
- entries[idx].page = i;
- entries[idx].reg = PMBUS_STATUS_INPUT;
- scnprintf(name, PMBUS_NAME_SIZE, "status%d_input", i);
- debugfs_create_file(name, 0444, data->debugfs,
- &entries[idx++],
- &pmbus_debugfs_ops);
- }
-
- if (data->info->func[i] & PMBUS_HAVE_STATUS_TEMP) {
- entries[idx].client = client;
- entries[idx].page = i;
- entries[idx].reg = PMBUS_STATUS_TEMPERATURE;
- scnprintf(name, PMBUS_NAME_SIZE, "status%d_temp", i);
- debugfs_create_file(name, 0444, data->debugfs,
- &entries[idx++],
- &pmbus_debugfs_ops);
- }
-
- if (pmbus_check_byte_register(client, i, PMBUS_STATUS_CML)) {
- entries[idx].client = client;
- entries[idx].page = i;
- entries[idx].reg = PMBUS_STATUS_CML;
- scnprintf(name, PMBUS_NAME_SIZE, "status%d_cml", i);
- debugfs_create_file(name, 0444, data->debugfs,
- &entries[idx++],
- &pmbus_debugfs_ops);
- }
-
- if (pmbus_check_byte_register(client, i, PMBUS_STATUS_OTHER)) {
- entries[idx].client = client;
- entries[idx].page = i;
- entries[idx].reg = PMBUS_STATUS_OTHER;
- scnprintf(name, PMBUS_NAME_SIZE, "status%d_other", i);
- debugfs_create_file(name, 0444, data->debugfs,
- &entries[idx++],
- &pmbus_debugfs_ops);
- }
-
- if (pmbus_check_byte_register(client, i,
- PMBUS_STATUS_MFR_SPECIFIC)) {
- entries[idx].client = client;
- entries[idx].page = i;
- entries[idx].reg = PMBUS_STATUS_MFR_SPECIFIC;
- scnprintf(name, PMBUS_NAME_SIZE, "status%d_mfr", i);
- debugfs_create_file(name, 0444, data->debugfs,
- &entries[idx++],
- &pmbus_debugfs_ops);
- }
-
- if (data->info->func[i] & PMBUS_HAVE_STATUS_FAN12) {
- entries[idx].client = client;
- entries[idx].page = i;
- entries[idx].reg = PMBUS_STATUS_FAN_12;
- scnprintf(name, PMBUS_NAME_SIZE, "status%d_fan12", i);
- debugfs_create_file(name, 0444, data->debugfs,
- &entries[idx++],
- &pmbus_debugfs_ops);
- }
-
- if (data->info->func[i] & PMBUS_HAVE_STATUS_FAN34) {
- entries[idx].client = client;
- entries[idx].page = i;
- entries[idx].reg = PMBUS_STATUS_FAN_34;
- scnprintf(name, PMBUS_NAME_SIZE, "status%d_fan34", i);
- debugfs_create_file(name, 0444, data->debugfs,
- &entries[idx++],
- &pmbus_debugfs_ops);
+ for (i = 0; i < ARRAY_SIZE(pmbus_debugfs_status_data); i++) {
+ const struct pmbus_debugfs_data *d =
+ &pmbus_debugfs_status_data[i];
+
+ if ((data->info->func[page] & d->flag) ||
+ (!d->flag && pmbus_check_byte_register(client, page, d->reg))) {
+ entries[idx].client = client;
+ entries[idx].page = page;
+ entries[idx].reg = d->reg;
+ scnprintf(name, PMBUS_NAME_SIZE, d->name, page);
+ debugfs_create_file(name, 0444, debugfs,
+ &entries[idx++],
+ &pmbus_debugfs_ops);
+ }
}
}
-
- return devm_add_action_or_reset(data->dev,
- pmbus_remove_debugfs, data->debugfs);
}
-#else
-static int pmbus_init_debugfs(struct i2c_client *client,
- struct pmbus_data *data)
-{
- return 0;
-}
-#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */
int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info)
{
@@ -3800,8 +3720,8 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info)
data->groups[0] = &data->group;
memcpy(data->groups + 1, info->groups, sizeof(void *) * groups_num);
- data->hwmon_dev = devm_hwmon_device_register_with_groups(dev,
- name, data, data->groups);
+ data->hwmon_dev = devm_hwmon_device_register_with_groups(dev, name,
+ data, data->groups);
if (IS_ERR(data->hwmon_dev)) {
dev_err(dev, "Failed to register hwmon device\n");
return PTR_ERR(data->hwmon_dev);
@@ -3815,9 +3735,7 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info)
if (ret)
return ret;
- ret = pmbus_init_debugfs(client, data);
- if (ret)
- dev_warn(dev, "Failed to register debugfs\n");
+ pmbus_init_debugfs(client, data);
return 0;
}
@@ -3825,9 +3743,15 @@ EXPORT_SYMBOL_NS_GPL(pmbus_do_probe, "PMBUS");
struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client)
{
- struct pmbus_data *data = i2c_get_clientdata(client);
-
- return data->debugfs;
+ /*
+ * client->debugfs may be an ERR_PTR(). Returning that to
+ * the calling code would potentially require additional
+ * complexity in the calling code and otherwise add no
+ * value. Return NULL in that case.
+ */
+ if (IS_ERR_OR_NULL(client->debugfs))
+ return NULL;
+ return client->debugfs;
}
EXPORT_SYMBOL_NS_GPL(pmbus_get_debugfs_dir, "PMBUS");
diff --git a/drivers/hwmon/pmbus/tda38640.c b/drivers/hwmon/pmbus/tda38640.c
index 07fe58c24485..d902d39f49f4 100644
--- a/drivers/hwmon/pmbus/tda38640.c
+++ b/drivers/hwmon/pmbus/tda38640.c
@@ -15,7 +15,7 @@
#include "pmbus.h"
static const struct regulator_desc __maybe_unused tda38640_reg_desc[] = {
- PMBUS_REGULATOR_ONE("vout"),
+ PMBUS_REGULATOR_ONE_NODE("vout"),
};
struct tda38640_data {
diff --git a/drivers/hwmon/pmbus/tps25990.c b/drivers/hwmon/pmbus/tps25990.c
index 0d2655e69549..c13edd7e1abf 100644
--- a/drivers/hwmon/pmbus/tps25990.c
+++ b/drivers/hwmon/pmbus/tps25990.c
@@ -333,7 +333,7 @@ static int tps25990_write_byte_data(struct i2c_client *client,
#if IS_ENABLED(CONFIG_SENSORS_TPS25990_REGULATOR)
static const struct regulator_desc tps25990_reg_desc[] = {
- PMBUS_REGULATOR_ONE("vout"),
+ PMBUS_REGULATOR_ONE_NODE("vout"),
};
#endif
diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index 9b0eadc81a2e..2bc8cccb01fd 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -212,8 +212,8 @@ static int ucd9000_gpio_get(struct gpio_chip *gc, unsigned int offset)
return !!(ret & UCD9000_GPIO_CONFIG_STATUS);
}
-static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
- int value)
+static int ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
+ int value)
{
struct i2c_client *client = gpiochip_get_data(gc);
int ret;
@@ -222,17 +222,17 @@ static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
if (ret < 0) {
dev_dbg(&client->dev, "failed to read GPIO %d config: %d\n",
offset, ret);
- return;
+ return ret;
}
if (value) {
if (ret & UCD9000_GPIO_CONFIG_STATUS)
- return;
+ return 0;
ret |= UCD9000_GPIO_CONFIG_STATUS;
} else {
if (!(ret & UCD9000_GPIO_CONFIG_STATUS))
- return;
+ return 0;
ret &= ~UCD9000_GPIO_CONFIG_STATUS;
}
@@ -244,7 +244,7 @@ static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
if (ret < 0) {
dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
offset, ret);
- return;
+ return ret;
}
ret &= ~UCD9000_GPIO_CONFIG_ENABLE;
@@ -253,6 +253,8 @@ static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
if (ret < 0)
dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
offset, ret);
+
+ return ret;
}
static int ucd9000_gpio_get_direction(struct gpio_chip *gc,
@@ -362,7 +364,7 @@ static void ucd9000_probe_gpio(struct i2c_client *client,
data->gpio.direction_input = ucd9000_gpio_direction_input;
data->gpio.direction_output = ucd9000_gpio_direction_output;
data->gpio.get = ucd9000_gpio_get;
- data->gpio.set = ucd9000_gpio_set;
+ data->gpio.set_rv = ucd9000_gpio_set;
data->gpio.can_sleep = true;
data->gpio.base = -1;
data->gpio.parent = &client->dev;
diff --git a/drivers/hwmon/pt5161l.c b/drivers/hwmon/pt5161l.c
index a9f0b23f9e76..20e3cfa625f1 100644
--- a/drivers/hwmon/pt5161l.c
+++ b/drivers/hwmon/pt5161l.c
@@ -63,7 +63,6 @@ struct pt5161l_fw_ver {
/* Each client has this additional data */
struct pt5161l_data {
struct i2c_client *client;
- struct dentry *debugfs;
struct pt5161l_fw_ver fw_ver;
struct mutex lock; /* for atomic I2C transactions */
bool init_done;
@@ -72,8 +71,6 @@ struct pt5161l_data {
bool mm_wide_reg_access; /* MM assisted wide register access */
};
-static struct dentry *pt5161l_debugfs_dir;
-
/*
* Write multiple data bytes to Aries over I2C
*/
@@ -568,21 +565,16 @@ static const struct file_operations pt5161l_debugfs_ops_hb_sts = {
.open = simple_open,
};
-static int pt5161l_init_debugfs(struct pt5161l_data *data)
+static void pt5161l_init_debugfs(struct i2c_client *client, struct pt5161l_data *data)
{
- data->debugfs = debugfs_create_dir(dev_name(&data->client->dev),
- pt5161l_debugfs_dir);
-
- debugfs_create_file("fw_ver", 0444, data->debugfs, data,
+ debugfs_create_file("fw_ver", 0444, client->debugfs, data,
&pt5161l_debugfs_ops_fw_ver);
- debugfs_create_file("fw_load_status", 0444, data->debugfs, data,
+ debugfs_create_file("fw_load_status", 0444, client->debugfs, data,
&pt5161l_debugfs_ops_fw_load_sts);
- debugfs_create_file("heartbeat_status", 0444, data->debugfs, data,
+ debugfs_create_file("heartbeat_status", 0444, client->debugfs, data,
&pt5161l_debugfs_ops_hb_sts);
-
- return 0;
}
static int pt5161l_probe(struct i2c_client *client)
@@ -604,17 +596,12 @@ static int pt5161l_probe(struct i2c_client *client)
data,
&pt5161l_chip_info,
NULL);
+ if (IS_ERR(hwmon_dev))
+ return PTR_ERR(hwmon_dev);
- pt5161l_init_debugfs(data);
-
- return PTR_ERR_OR_ZERO(hwmon_dev);
-}
-
-static void pt5161l_remove(struct i2c_client *client)
-{
- struct pt5161l_data *data = i2c_get_clientdata(client);
+ pt5161l_init_debugfs(client, data);
- debugfs_remove_recursive(data->debugfs);
+ return 0;
}
static const struct of_device_id __maybe_unused pt5161l_of_match[] = {
@@ -643,24 +630,9 @@ static struct i2c_driver pt5161l_driver = {
.acpi_match_table = ACPI_PTR(pt5161l_acpi_match),
},
.probe = pt5161l_probe,
- .remove = pt5161l_remove,
.id_table = pt5161l_id,
};
-
-static int __init pt5161l_init(void)
-{
- pt5161l_debugfs_dir = debugfs_create_dir("pt5161l", NULL);
- return i2c_add_driver(&pt5161l_driver);
-}
-
-static void __exit pt5161l_exit(void)
-{
- i2c_del_driver(&pt5161l_driver);
- debugfs_remove_recursive(pt5161l_debugfs_dir);
-}
-
-module_init(pt5161l_init);
-module_exit(pt5161l_exit);
+module_i2c_driver(pt5161l_driver);
MODULE_AUTHOR("Cosmo Chou <cosmo.chou@quantatw.com>");
MODULE_DESCRIPTION("Hwmon driver for Astera Labs Aries PCIe retimer");
diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c
index 579d31bb9ac7..d0fe53451bdf 100644
--- a/drivers/hwmon/pwm-fan.c
+++ b/drivers/hwmon/pwm-fan.c
@@ -78,7 +78,7 @@ static irqreturn_t pulse_handler(int irq, void *dev_id)
static void sample_timer(struct timer_list *t)
{
- struct pwm_fan_ctx *ctx = from_timer(ctx, t, rpm_timer);
+ struct pwm_fan_ctx *ctx = timer_container_of(ctx, t, rpm_timer);
unsigned int delta = ktime_ms_delta(ktime_get(), ctx->sample_start);
int i;
@@ -483,7 +483,7 @@ static void pwm_fan_cleanup(void *__ctx)
{
struct pwm_fan_ctx *ctx = __ctx;
- del_timer_sync(&ctx->rpm_timer);
+ timer_delete_sync(&ctx->rpm_timer);
/* Switch off everything */
ctx->enable_mode = pwm_disable_reg_disable;
pwm_fan_power_off(ctx, true);
@@ -620,8 +620,8 @@ static int pwm_fan_probe(struct platform_device *pdev)
if (tach->irq == -EPROBE_DEFER)
return tach->irq;
if (tach->irq > 0) {
- ret = devm_request_irq(dev, tach->irq, pulse_handler, 0,
- pdev->name, tach);
+ ret = devm_request_irq(dev, tach->irq, pulse_handler,
+ IRQF_NO_THREAD, pdev->name, tach);
if (ret) {
dev_err(dev,
"Failed to request interrupt: %d\n",
diff --git a/drivers/hwmon/qnap-mcu-hwmon.c b/drivers/hwmon/qnap-mcu-hwmon.c
index 29057514739c..e86e64c4d391 100644
--- a/drivers/hwmon/qnap-mcu-hwmon.c
+++ b/drivers/hwmon/qnap-mcu-hwmon.c
@@ -6,7 +6,6 @@
* Copyright (C) 2024 Heiko Stuebner <heiko@sntech.de>
*/
-#include <linux/fwnode.h>
#include <linux/hwmon.h>
#include <linux/mfd/qnap-mcu.h>
#include <linux/module.h>
diff --git a/drivers/hwmon/sbrmi.c b/drivers/hwmon/sbrmi.c
deleted file mode 100644
index d48d8e5460ff..000000000000
--- a/drivers/hwmon/sbrmi.c
+++ /dev/null
@@ -1,357 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * sbrmi.c - hwmon driver for a SB-RMI mailbox
- * compliant AMD SoC device.
- *
- * Copyright (C) 2020-2021 Advanced Micro Devices, Inc.
- */
-
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/hwmon.h>
-#include <linux/i2c.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/of.h>
-
-/* Do not allow setting negative power limit */
-#define SBRMI_PWR_MIN 0
-/* Mask for Status Register bit[1] */
-#define SW_ALERT_MASK 0x2
-
-/* Software Interrupt for triggering */
-#define START_CMD 0x80
-#define TRIGGER_MAILBOX 0x01
-
-/*
- * SB-RMI supports soft mailbox service request to MP1 (power management
- * firmware) through SBRMI inbound/outbound message registers.
- * SB-RMI message IDs
- */
-enum sbrmi_msg_id {
- SBRMI_READ_PKG_PWR_CONSUMPTION = 0x1,
- SBRMI_WRITE_PKG_PWR_LIMIT,
- SBRMI_READ_PKG_PWR_LIMIT,
- SBRMI_READ_PKG_MAX_PWR_LIMIT,
-};
-
-/* SB-RMI registers */
-enum sbrmi_reg {
- SBRMI_CTRL = 0x01,
- SBRMI_STATUS,
- SBRMI_OUTBNDMSG0 = 0x30,
- SBRMI_OUTBNDMSG1,
- SBRMI_OUTBNDMSG2,
- SBRMI_OUTBNDMSG3,
- SBRMI_OUTBNDMSG4,
- SBRMI_OUTBNDMSG5,
- SBRMI_OUTBNDMSG6,
- SBRMI_OUTBNDMSG7,
- SBRMI_INBNDMSG0,
- SBRMI_INBNDMSG1,
- SBRMI_INBNDMSG2,
- SBRMI_INBNDMSG3,
- SBRMI_INBNDMSG4,
- SBRMI_INBNDMSG5,
- SBRMI_INBNDMSG6,
- SBRMI_INBNDMSG7,
- SBRMI_SW_INTERRUPT,
-};
-
-/* Each client has this additional data */
-struct sbrmi_data {
- struct i2c_client *client;
- struct mutex lock;
- u32 pwr_limit_max;
-};
-
-struct sbrmi_mailbox_msg {
- u8 cmd;
- bool read;
- u32 data_in;
- u32 data_out;
-};
-
-static int sbrmi_enable_alert(struct i2c_client *client)
-{
- int ctrl;
-
- /*
- * Enable the SB-RMI Software alert status
- * by writing 0 to bit 4 of Control register(0x1)
- */
- ctrl = i2c_smbus_read_byte_data(client, SBRMI_CTRL);
- if (ctrl < 0)
- return ctrl;
-
- if (ctrl & 0x10) {
- ctrl &= ~0x10;
- return i2c_smbus_write_byte_data(client,
- SBRMI_CTRL, ctrl);
- }
-
- return 0;
-}
-
-static int rmi_mailbox_xfer(struct sbrmi_data *data,
- struct sbrmi_mailbox_msg *msg)
-{
- int i, ret, retry = 10;
- int sw_status;
- u8 byte;
-
- mutex_lock(&data->lock);
-
- /* Indicate firmware a command is to be serviced */
- ret = i2c_smbus_write_byte_data(data->client,
- SBRMI_INBNDMSG7, START_CMD);
- if (ret < 0)
- goto exit_unlock;
-
- /* Write the command to SBRMI::InBndMsg_inst0 */
- ret = i2c_smbus_write_byte_data(data->client,
- SBRMI_INBNDMSG0, msg->cmd);
- if (ret < 0)
- goto exit_unlock;
-
- /*
- * For both read and write the initiator (BMC) writes
- * Command Data In[31:0] to SBRMI::InBndMsg_inst[4:1]
- * SBRMI_x3C(MSB):SBRMI_x39(LSB)
- */
- for (i = 0; i < 4; i++) {
- byte = (msg->data_in >> i * 8) & 0xff;
- ret = i2c_smbus_write_byte_data(data->client,
- SBRMI_INBNDMSG1 + i, byte);
- if (ret < 0)
- goto exit_unlock;
- }
-
- /*
- * Write 0x01 to SBRMI::SoftwareInterrupt to notify firmware to
- * perform the requested read or write command
- */
- ret = i2c_smbus_write_byte_data(data->client,
- SBRMI_SW_INTERRUPT, TRIGGER_MAILBOX);
- if (ret < 0)
- goto exit_unlock;
-
- /*
- * Firmware will write SBRMI::Status[SwAlertSts]=1 to generate
- * an ALERT (if enabled) to initiator (BMC) to indicate completion
- * of the requested command
- */
- do {
- sw_status = i2c_smbus_read_byte_data(data->client,
- SBRMI_STATUS);
- if (sw_status < 0) {
- ret = sw_status;
- goto exit_unlock;
- }
- if (sw_status & SW_ALERT_MASK)
- break;
- usleep_range(50, 100);
- } while (retry--);
-
- if (retry < 0) {
- dev_err(&data->client->dev,
- "Firmware fail to indicate command completion\n");
- ret = -EIO;
- goto exit_unlock;
- }
-
- /*
- * For a read operation, the initiator (BMC) reads the firmware
- * response Command Data Out[31:0] from SBRMI::OutBndMsg_inst[4:1]
- * {SBRMI_x34(MSB):SBRMI_x31(LSB)}.
- */
- if (msg->read) {
- for (i = 0; i < 4; i++) {
- ret = i2c_smbus_read_byte_data(data->client,
- SBRMI_OUTBNDMSG1 + i);
- if (ret < 0)
- goto exit_unlock;
- msg->data_out |= ret << i * 8;
- }
- }
-
- /*
- * BMC must write 1'b1 to SBRMI::Status[SwAlertSts] to clear the
- * ALERT to initiator
- */
- ret = i2c_smbus_write_byte_data(data->client, SBRMI_STATUS,
- sw_status | SW_ALERT_MASK);
-
-exit_unlock:
- mutex_unlock(&data->lock);
- return ret;
-}
-
-static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type,
- u32 attr, int channel, long *val)
-{
- struct sbrmi_data *data = dev_get_drvdata(dev);
- struct sbrmi_mailbox_msg msg = { 0 };
- int ret;
-
- if (type != hwmon_power)
- return -EINVAL;
-
- msg.read = true;
- switch (attr) {
- case hwmon_power_input:
- msg.cmd = SBRMI_READ_PKG_PWR_CONSUMPTION;
- ret = rmi_mailbox_xfer(data, &msg);
- break;
- case hwmon_power_cap:
- msg.cmd = SBRMI_READ_PKG_PWR_LIMIT;
- ret = rmi_mailbox_xfer(data, &msg);
- break;
- case hwmon_power_cap_max:
- msg.data_out = data->pwr_limit_max;
- ret = 0;
- break;
- default:
- return -EINVAL;
- }
- if (ret < 0)
- return ret;
- /* hwmon power attributes are in microWatt */
- *val = (long)msg.data_out * 1000;
- return ret;
-}
-
-static int sbrmi_write(struct device *dev, enum hwmon_sensor_types type,
- u32 attr, int channel, long val)
-{
- struct sbrmi_data *data = dev_get_drvdata(dev);
- struct sbrmi_mailbox_msg msg = { 0 };
-
- if (type != hwmon_power && attr != hwmon_power_cap)
- return -EINVAL;
- /*
- * hwmon power attributes are in microWatt
- * mailbox read/write is in mWatt
- */
- val /= 1000;
-
- val = clamp_val(val, SBRMI_PWR_MIN, data->pwr_limit_max);
-
- msg.cmd = SBRMI_WRITE_PKG_PWR_LIMIT;
- msg.data_in = val;
- msg.read = false;
-
- return rmi_mailbox_xfer(data, &msg);
-}
-
-static umode_t sbrmi_is_visible(const void *data,
- enum hwmon_sensor_types type,
- u32 attr, int channel)
-{
- switch (type) {
- case hwmon_power:
- switch (attr) {
- case hwmon_power_input:
- case hwmon_power_cap_max:
- return 0444;
- case hwmon_power_cap:
- return 0644;
- }
- break;
- default:
- break;
- }
- return 0;
-}
-
-static const struct hwmon_channel_info * const sbrmi_info[] = {
- HWMON_CHANNEL_INFO(power,
- HWMON_P_INPUT | HWMON_P_CAP | HWMON_P_CAP_MAX),
- NULL
-};
-
-static const struct hwmon_ops sbrmi_hwmon_ops = {
- .is_visible = sbrmi_is_visible,
- .read = sbrmi_read,
- .write = sbrmi_write,
-};
-
-static const struct hwmon_chip_info sbrmi_chip_info = {
- .ops = &sbrmi_hwmon_ops,
- .info = sbrmi_info,
-};
-
-static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data)
-{
- struct sbrmi_mailbox_msg msg = { 0 };
- int ret;
-
- msg.cmd = SBRMI_READ_PKG_MAX_PWR_LIMIT;
- msg.read = true;
- ret = rmi_mailbox_xfer(data, &msg);
- if (ret < 0)
- return ret;
- data->pwr_limit_max = msg.data_out;
-
- return ret;
-}
-
-static int sbrmi_probe(struct i2c_client *client)
-{
- struct device *dev = &client->dev;
- struct device *hwmon_dev;
- struct sbrmi_data *data;
- int ret;
-
- data = devm_kzalloc(dev, sizeof(struct sbrmi_data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- data->client = client;
- mutex_init(&data->lock);
-
- /* Enable alert for SB-RMI sequence */
- ret = sbrmi_enable_alert(client);
- if (ret < 0)
- return ret;
-
- /* Cache maximum power limit */
- ret = sbrmi_get_max_pwr_limit(data);
- if (ret < 0)
- return ret;
-
- hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
- &sbrmi_chip_info, NULL);
-
- return PTR_ERR_OR_ZERO(hwmon_dev);
-}
-
-static const struct i2c_device_id sbrmi_id[] = {
- {"sbrmi"},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, sbrmi_id);
-
-static const struct of_device_id __maybe_unused sbrmi_of_match[] = {
- {
- .compatible = "amd,sbrmi",
- },
- { },
-};
-MODULE_DEVICE_TABLE(of, sbrmi_of_match);
-
-static struct i2c_driver sbrmi_driver = {
- .driver = {
- .name = "sbrmi",
- .of_match_table = of_match_ptr(sbrmi_of_match),
- },
- .probe = sbrmi_probe,
- .id_table = sbrmi_id,
-};
-
-module_i2c_driver(sbrmi_driver);
-
-MODULE_AUTHOR("Akshay Gupta <akshay.gupta@amd.com>");
-MODULE_DESCRIPTION("Hwmon driver for AMD SB-RMI emulated sensor");
-MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/sg2042-mcu.c b/drivers/hwmon/sg2042-mcu.c
index aa3fb773602c..105131c4acf7 100644
--- a/drivers/hwmon/sg2042-mcu.c
+++ b/drivers/hwmon/sg2042-mcu.c
@@ -50,12 +50,9 @@
struct sg2042_mcu_data {
struct i2c_client *client;
- struct dentry *debugfs;
struct mutex mutex;
};
-static struct dentry *sgmcu_debugfs;
-
static ssize_t reset_count_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -292,18 +289,15 @@ static const struct hwmon_chip_info sg2042_mcu_chip_info = {
.info = sg2042_mcu_info,
};
-static void sg2042_mcu_debugfs_init(struct sg2042_mcu_data *mcu,
- struct device *dev)
+static void sg2042_mcu_debugfs_init(struct sg2042_mcu_data *mcu)
{
- mcu->debugfs = debugfs_create_dir(dev_name(dev), sgmcu_debugfs);
-
- debugfs_create_file("firmware_version", 0444, mcu->debugfs,
+ debugfs_create_file("firmware_version", 0444, mcu->client->debugfs,
mcu, &firmware_version_fops);
- debugfs_create_file("pcb_version", 0444, mcu->debugfs, mcu,
+ debugfs_create_file("pcb_version", 0444, mcu->client->debugfs, mcu,
&pcb_version_fops);
- debugfs_create_file("mcu_type", 0444, mcu->debugfs, mcu,
+ debugfs_create_file("mcu_type", 0444, mcu->client->debugfs, mcu,
&mcu_type_fops);
- debugfs_create_file("board_type", 0444, mcu->debugfs, mcu,
+ debugfs_create_file("board_type", 0444, mcu->client->debugfs, mcu,
&board_type_fops);
}
@@ -333,18 +327,11 @@ static int sg2042_mcu_i2c_probe(struct i2c_client *client)
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
- sg2042_mcu_debugfs_init(mcu, dev);
+ sg2042_mcu_debugfs_init(mcu);
return 0;
}
-static void sg2042_mcu_i2c_remove(struct i2c_client *client)
-{
- struct sg2042_mcu_data *mcu = i2c_get_clientdata(client);
-
- debugfs_remove_recursive(mcu->debugfs);
-}
-
static const struct i2c_device_id sg2042_mcu_id[] = {
{ "sg2042-hwmon-mcu" },
{ }
@@ -364,24 +351,9 @@ static struct i2c_driver sg2042_mcu_driver = {
.dev_groups = sg2042_mcu_groups,
},
.probe = sg2042_mcu_i2c_probe,
- .remove = sg2042_mcu_i2c_remove,
.id_table = sg2042_mcu_id,
};
-
-static int __init sg2042_mcu_init(void)
-{
- sgmcu_debugfs = debugfs_create_dir("sg2042-mcu", NULL);
- return i2c_add_driver(&sg2042_mcu_driver);
-}
-
-static void __exit sg2042_mcu_exit(void)
-{
- debugfs_remove_recursive(sgmcu_debugfs);
- i2c_del_driver(&sg2042_mcu_driver);
-}
-
-module_init(sg2042_mcu_init);
-module_exit(sg2042_mcu_exit);
+module_i2c_driver(sg2042_mcu_driver);
MODULE_AUTHOR("Inochi Amaoto <inochiama@outlook.com>");
MODULE_DESCRIPTION("MCU I2C driver for SG2042 soc platform");
diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c
index 650b0bcc2359..557ad3e7752a 100644
--- a/drivers/hwmon/sht3x.c
+++ b/drivers/hwmon/sht3x.c
@@ -44,8 +44,6 @@ static const unsigned char sht3x_cmd_read_status_reg[] = { 0xf3, 0x2d };
static const unsigned char sht3x_cmd_clear_status_reg[] = { 0x30, 0x41 };
static const unsigned char sht3x_cmd_read_serial_number[] = { 0x37, 0x80 };
-static struct dentry *debugfs;
-
/* delays for single-shot mode i2c commands, both in us */
#define SHT3X_SINGLE_WAIT_TIME_HPM 15000
#define SHT3X_SINGLE_WAIT_TIME_MPM 6000
@@ -167,7 +165,6 @@ struct sht3x_data {
enum sht3x_chips chip_id;
struct mutex i2c_lock; /* lock for sending i2c commands */
struct mutex data_lock; /* lock for updating driver data */
- struct dentry *sensor_dir;
u8 mode;
const unsigned char *command;
@@ -837,23 +834,7 @@ static int sht3x_write(struct device *dev, enum hwmon_sensor_types type,
}
}
-static void sht3x_debugfs_init(struct sht3x_data *data)
-{
- char name[32];
-
- snprintf(name, sizeof(name), "i2c%u-%02x",
- data->client->adapter->nr, data->client->addr);
- data->sensor_dir = debugfs_create_dir(name, debugfs);
- debugfs_create_u32("serial_number", 0444,
- data->sensor_dir, &data->serial_number);
-}
-
-static void sht3x_debugfs_remove(void *sensor_dir)
-{
- debugfs_remove_recursive(sensor_dir);
-}
-
-static int sht3x_serial_number_read(struct sht3x_data *data)
+static void sht3x_serial_number_read(struct sht3x_data *data)
{
int ret;
char buffer[SHT3X_RESPONSE_LENGTH];
@@ -864,11 +845,12 @@ static int sht3x_serial_number_read(struct sht3x_data *data)
buffer,
SHT3X_RESPONSE_LENGTH, 0);
if (ret)
- return ret;
+ return;
data->serial_number = (buffer[0] << 24) | (buffer[1] << 16) |
(buffer[3] << 8) | buffer[4];
- return ret;
+
+ debugfs_create_u32("serial_number", 0444, client->debugfs, &data->serial_number);
}
static const struct hwmon_ops sht3x_ops = {
@@ -930,28 +912,14 @@ static int sht3x_probe(struct i2c_client *client)
if (ret)
return ret;
- ret = sht3x_serial_number_read(data);
- if (ret) {
- dev_dbg(dev, "unable to read serial number\n");
- } else {
- sht3x_debugfs_init(data);
- ret = devm_add_action_or_reset(dev,
- sht3x_debugfs_remove,
- data->sensor_dir);
- if (ret)
- return ret;
- }
-
- hwmon_dev = devm_hwmon_device_register_with_info(dev,
- client->name,
- data,
- &sht3x_chip_info,
- sht3x_groups);
-
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
+ &sht3x_chip_info, sht3x_groups);
if (IS_ERR(hwmon_dev))
- dev_dbg(dev, "unable to register hwmon device\n");
+ return PTR_ERR(hwmon_dev);
+
+ sht3x_serial_number_read(data);
- return PTR_ERR_OR_ZERO(hwmon_dev);
+ return 0;
}
/* device ID table */
@@ -968,20 +936,7 @@ static struct i2c_driver sht3x_i2c_driver = {
.probe = sht3x_probe,
.id_table = sht3x_ids,
};
-
-static int __init sht3x_init(void)
-{
- debugfs = debugfs_create_dir("sht3x", NULL);
- return i2c_add_driver(&sht3x_i2c_driver);
-}
-module_init(sht3x_init);
-
-static void __exit sht3x_cleanup(void)
-{
- debugfs_remove_recursive(debugfs);
- i2c_del_driver(&sht3x_i2c_driver);
-}
-module_exit(sht3x_cleanup);
+module_i2c_driver(sht3x_i2c_driver);
MODULE_AUTHOR("David Frey <david.frey@sensirion.com>");
MODULE_AUTHOR("Pascal Sachs <pascal.sachs@sensirion.com>");
diff --git a/drivers/hwmon/spd5118.c b/drivers/hwmon/spd5118.c
index 358152868d96..5da44571b6a0 100644
--- a/drivers/hwmon/spd5118.c
+++ b/drivers/hwmon/spd5118.c
@@ -66,6 +66,9 @@ static const unsigned short normal_i2c[] = {
#define SPD5118_EEPROM_BASE 0x80
#define SPD5118_EEPROM_SIZE (SPD5118_PAGE_SIZE * SPD5118_NUM_PAGES)
+#define PAGE_ADDR0(page) (((page) & BIT(0)) << 6)
+#define PAGE_ADDR1_4(page) (((page) & GENMASK(4, 1)) >> 1)
+
/* Temperature unit in millicelsius */
#define SPD5118_TEMP_UNIT (MILLIDEGREE_PER_DEGREE / 4)
/* Representable temperature range in millicelsius */
@@ -75,6 +78,7 @@ static const unsigned short normal_i2c[] = {
struct spd5118_data {
struct regmap *regmap;
struct mutex nvmem_lock;
+ bool is_16bit;
};
/* hwmon */
@@ -305,51 +309,6 @@ static bool spd5118_vendor_valid(u8 bank, u8 id)
return id && id != 0x7f;
}
-/* Return 0 if detection is successful, -ENODEV otherwise */
-static int spd5118_detect(struct i2c_client *client, struct i2c_board_info *info)
-{
- struct i2c_adapter *adapter = client->adapter;
- int regval;
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
- I2C_FUNC_SMBUS_WORD_DATA))
- return -ENODEV;
-
- regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
- if (regval != 0x5118)
- return -ENODEV;
-
- regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR);
- if (regval < 0 || !spd5118_vendor_valid(regval & 0xff, regval >> 8))
- return -ENODEV;
-
- regval = i2c_smbus_read_byte_data(client, SPD5118_REG_CAPABILITY);
- if (regval < 0)
- return -ENODEV;
- if (!(regval & SPD5118_CAP_TS_SUPPORT) || (regval & 0xfc))
- return -ENODEV;
-
- regval = i2c_smbus_read_byte_data(client, SPD5118_REG_TEMP_CLR);
- if (regval)
- return -ENODEV;
- regval = i2c_smbus_read_byte_data(client, SPD5118_REG_ERROR_CLR);
- if (regval)
- return -ENODEV;
-
- regval = i2c_smbus_read_byte_data(client, SPD5118_REG_REVISION);
- if (regval < 0 || (regval & 0xc1))
- return -ENODEV;
-
- regval = i2c_smbus_read_byte_data(client, SPD5118_REG_TEMP_CONFIG);
- if (regval < 0)
- return -ENODEV;
- if (regval & ~SPD5118_TS_DISABLE)
- return -ENODEV;
-
- strscpy(info->type, "spd5118", I2C_NAME_SIZE);
- return 0;
-}
-
static const struct hwmon_channel_info *spd5118_info[] = {
HWMON_CHANNEL_INFO(chip,
HWMON_C_REGISTER_TZ),
@@ -376,11 +335,12 @@ static const struct hwmon_chip_info spd5118_chip_info = {
/* nvmem */
-static ssize_t spd5118_nvmem_read_page(struct regmap *regmap, char *buf,
+static ssize_t spd5118_nvmem_read_page(struct spd5118_data *data, char *buf,
unsigned int offset, size_t count)
{
- int addr = (offset >> SPD5118_PAGE_SHIFT) * 0x100 + SPD5118_EEPROM_BASE;
- int err;
+ int page = offset >> SPD5118_PAGE_SHIFT;
+ struct regmap *regmap = data->regmap;
+ int err, addr;
offset &= SPD5118_PAGE_MASK;
@@ -388,6 +348,12 @@ static ssize_t spd5118_nvmem_read_page(struct regmap *regmap, char *buf,
if (offset + count > SPD5118_PAGE_SIZE)
count = SPD5118_PAGE_SIZE - offset;
+ if (data->is_16bit) {
+ addr = SPD5118_EEPROM_BASE | PAGE_ADDR0(page) |
+ (PAGE_ADDR1_4(page) << 8);
+ } else {
+ addr = page * 0x100 + SPD5118_EEPROM_BASE;
+ }
err = regmap_bulk_read(regmap, addr + offset, buf, count);
if (err)
return err;
@@ -410,7 +376,7 @@ static int spd5118_nvmem_read(void *priv, unsigned int off, void *val, size_t co
mutex_lock(&data->nvmem_lock);
while (count) {
- ret = spd5118_nvmem_read_page(data->regmap, buf, off, count);
+ ret = spd5118_nvmem_read_page(data, buf, off, count);
if (ret < 0) {
mutex_unlock(&data->nvmem_lock);
return ret;
@@ -483,7 +449,7 @@ static bool spd5118_volatile_reg(struct device *dev, unsigned int reg)
}
}
-static const struct regmap_range_cfg spd5118_regmap_range_cfg[] = {
+static const struct regmap_range_cfg spd5118_i2c_regmap_range_cfg[] = {
{
.selector_reg = SPD5118_REG_I2C_LEGACY_MODE,
.selector_mask = SPD5118_LEGACY_PAGE_MASK,
@@ -495,7 +461,7 @@ static const struct regmap_range_cfg spd5118_regmap_range_cfg[] = {
},
};
-static const struct regmap_config spd5118_regmap_config = {
+static const struct regmap_config spd5118_regmap8_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x7ff,
@@ -503,89 +469,76 @@ static const struct regmap_config spd5118_regmap_config = {
.volatile_reg = spd5118_volatile_reg,
.cache_type = REGCACHE_MAPLE,
- .ranges = spd5118_regmap_range_cfg,
- .num_ranges = ARRAY_SIZE(spd5118_regmap_range_cfg),
+ .ranges = spd5118_i2c_regmap_range_cfg,
+ .num_ranges = ARRAY_SIZE(spd5118_i2c_regmap_range_cfg),
};
-static int spd5118_init(struct i2c_client *client)
-{
- struct i2c_adapter *adapter = client->adapter;
- int err, regval, mode;
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
- I2C_FUNC_SMBUS_WORD_DATA))
- return -ENODEV;
+static const struct regmap_config spd5118_regmap16_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = 0x7ff,
+ .writeable_reg = spd5118_writeable_reg,
+ .volatile_reg = spd5118_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
- regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
- if (regval < 0 || (regval && regval != 0x5118))
- return -ENODEV;
+static int spd5118_suspend(struct device *dev)
+{
+ struct spd5118_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+ u32 regval;
+ int err;
/*
- * If the device type registers return 0, it is possible that the chip
- * has a non-zero page selected and takes the specification literally,
- * i.e. disables access to volatile registers besides the page register
- * if the page is not 0. Try to identify such chips.
+ * Make sure the configuration register in the regmap cache is current
+ * before bypassing it.
*/
- if (!regval) {
- /* Vendor ID registers must also be 0 */
- regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR);
- if (regval)
- return -ENODEV;
-
- /* The selected page in MR11 must not be 0 */
- mode = i2c_smbus_read_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE);
- if (mode < 0 || (mode & ~SPD5118_LEGACY_MODE_MASK) ||
- !(mode & SPD5118_LEGACY_PAGE_MASK))
- return -ENODEV;
+ err = regmap_read(regmap, SPD5118_REG_TEMP_CONFIG, &regval);
+ if (err < 0)
+ return err;
- err = i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE,
- mode & SPD5118_LEGACY_MODE_ADDR);
- if (err)
- return -ENODEV;
+ regcache_cache_bypass(regmap, true);
+ regmap_update_bits(regmap, SPD5118_REG_TEMP_CONFIG, SPD5118_TS_DISABLE,
+ SPD5118_TS_DISABLE);
+ regcache_cache_bypass(regmap, false);
- /*
- * If the device type registers are still bad after selecting
- * page 0, this is not a SPD5118 device. Restore original
- * legacy mode register value and abort.
- */
- regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
- if (regval != 0x5118) {
- i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE, mode);
- return -ENODEV;
- }
- }
+ regcache_cache_only(regmap, true);
+ regcache_mark_dirty(regmap);
- /* We are reasonably sure that this is really a SPD5118 hub controller */
return 0;
}
-static int spd5118_probe(struct i2c_client *client)
+static int spd5118_resume(struct device *dev)
{
- struct device *dev = &client->dev;
- unsigned int regval, revision, vendor, bank;
+ struct spd5118_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+
+ regcache_cache_only(regmap, false);
+ return regcache_sync(regmap);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(spd5118_pm_ops, spd5118_suspend, spd5118_resume);
+
+static int spd5118_common_probe(struct device *dev, struct regmap *regmap,
+ bool is_16bit)
+{
+ unsigned int capability, revision, vendor, bank;
struct spd5118_data *data;
struct device *hwmon_dev;
- struct regmap *regmap;
int err;
- err = spd5118_init(client);
- if (err)
- return err;
-
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
- regmap = devm_regmap_init_i2c(client, &spd5118_regmap_config);
- if (IS_ERR(regmap))
- return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n");
-
- err = regmap_read(regmap, SPD5118_REG_CAPABILITY, &regval);
+ err = regmap_read(regmap, SPD5118_REG_CAPABILITY, &capability);
if (err)
return err;
- if (!(regval & SPD5118_CAP_TS_SUPPORT))
+ if (!(capability & SPD5118_CAP_TS_SUPPORT))
return -ENODEV;
+ data->is_16bit = is_16bit;
+
err = regmap_read(regmap, SPD5118_REG_REVISION, &revision);
if (err)
return err;
@@ -627,48 +580,176 @@ static int spd5118_probe(struct i2c_client *client)
return 0;
}
-static int spd5118_suspend(struct device *dev)
+/* I2C */
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int spd5118_detect(struct i2c_client *client, struct i2c_board_info *info)
{
- struct spd5118_data *data = dev_get_drvdata(dev);
- struct regmap *regmap = data->regmap;
- u32 regval;
- int err;
+ struct i2c_adapter *adapter = client->adapter;
+ int regval;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
+ if (regval != 0x5118)
+ return -ENODEV;
+
+ regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR);
+ if (regval < 0 || !spd5118_vendor_valid(regval & 0xff, regval >> 8))
+ return -ENODEV;
+
+ regval = i2c_smbus_read_byte_data(client, SPD5118_REG_CAPABILITY);
+ if (regval < 0)
+ return -ENODEV;
+ if (!(regval & SPD5118_CAP_TS_SUPPORT) || (regval & 0xfc))
+ return -ENODEV;
+
+ regval = i2c_smbus_read_byte_data(client, SPD5118_REG_TEMP_CLR);
+ if (regval)
+ return -ENODEV;
+ regval = i2c_smbus_read_byte_data(client, SPD5118_REG_ERROR_CLR);
+ if (regval)
+ return -ENODEV;
+
+ regval = i2c_smbus_read_byte_data(client, SPD5118_REG_REVISION);
+ if (regval < 0 || (regval & 0xc1))
+ return -ENODEV;
+
+ regval = i2c_smbus_read_byte_data(client, SPD5118_REG_TEMP_CONFIG);
+ if (regval < 0)
+ return -ENODEV;
+ if (regval & ~SPD5118_TS_DISABLE)
+ return -ENODEV;
+
+ strscpy(info->type, "spd5118", I2C_NAME_SIZE);
+ return 0;
+}
+
+static int spd5118_i2c_init(struct i2c_client *client)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int err, regval, mode;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
+ if (regval < 0 || (regval && regval != 0x5118))
+ return -ENODEV;
/*
- * Make sure the configuration register in the regmap cache is current
- * before bypassing it.
+ * If the device type registers return 0, it is possible that the chip
+ * has a non-zero page selected and takes the specification literally,
+ * i.e. disables access to volatile registers besides the page register
+ * if the page is not 0. The Renesas/ITD SPD5118 Hub Controller is known
+ * to show this behavior. Try to identify such chips.
*/
- err = regmap_read(regmap, SPD5118_REG_TEMP_CONFIG, &regval);
- if (err < 0)
- return err;
+ if (!regval) {
+ /* Vendor ID registers must also be 0 */
+ regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR);
+ if (regval)
+ return -ENODEV;
- regcache_cache_bypass(regmap, true);
- regmap_update_bits(regmap, SPD5118_REG_TEMP_CONFIG, SPD5118_TS_DISABLE,
- SPD5118_TS_DISABLE);
- regcache_cache_bypass(regmap, false);
+ /* The selected page in MR11 must not be 0 */
+ mode = i2c_smbus_read_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE);
+ if (mode < 0 || (mode & ~SPD5118_LEGACY_MODE_MASK) ||
+ !(mode & SPD5118_LEGACY_PAGE_MASK))
+ return -ENODEV;
- regcache_cache_only(regmap, true);
- regcache_mark_dirty(regmap);
+ err = i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE,
+ mode & SPD5118_LEGACY_MODE_ADDR);
+ if (err)
+ return -ENODEV;
+ /*
+ * If the device type registers are still bad after selecting
+ * page 0, this is not a SPD5118 device. Restore original
+ * legacy mode register value and abort.
+ */
+ regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
+ if (regval != 0x5118) {
+ i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE, mode);
+ return -ENODEV;
+ }
+ }
+
+ /* We are reasonably sure that this is really a SPD5118 hub controller */
return 0;
}
-static int spd5118_resume(struct device *dev)
+/*
+ * 16-bit addressing note:
+ *
+ * If I2C_FUNC_I2C is not supported by an I2C adapter driver, regmap uses
+ * SMBus operations as alternative. To simulate a read operation with a 16-bit
+ * address, it writes the address using i2c_smbus_write_byte_data(), followed
+ * by one or more calls to i2c_smbus_read_byte() to read the data.
+ * Per spd5118 standard, a read operation after writing the address must start
+ * with <Sr> (Repeat Start). However, a SMBus read byte operation starts with
+ * <S> (Start). This resets the register address in the spd5118 chip. As result,
+ * i2c_smbus_read_byte() always returns data from register address 0x00.
+ *
+ * A working alternative to access chips with 16-bit register addresses in the
+ * absence of I2C_FUNC_I2C support is not known.
+ *
+ * For this reason, 16-bit addressing can only be supported with I2C if the
+ * adapter supports I2C_FUNC_I2C.
+ *
+ * For I2C, the addressing mode selected by the BIOS must not be changed.
+ * Experiments show that at least some PC BIOS versions will not change the
+ * addressing mode on a soft reboot and end up in setup, claiming that some
+ * configuration change happened. This will happen again after a power cycle,
+ * which does reset the addressing mode. To prevent this from happening,
+ * detect if 16-bit addressing is enabled and always use the currently
+ * configured addressing mode.
+ */
+
+static int spd5118_i2c_probe(struct i2c_client *client)
{
- struct spd5118_data *data = dev_get_drvdata(dev);
- struct regmap *regmap = data->regmap;
+ const struct regmap_config *config;
+ struct device *dev = &client->dev;
+ struct regmap *regmap;
+ int err, mode;
+ bool is_16bit;
- regcache_cache_only(regmap, false);
- return regcache_sync(regmap);
-}
+ err = spd5118_i2c_init(client);
+ if (err)
+ return err;
-static DEFINE_SIMPLE_DEV_PM_OPS(spd5118_pm_ops, spd5118_suspend, spd5118_resume);
+ mode = i2c_smbus_read_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE);
+ if (mode < 0)
+ return mode;
+
+ is_16bit = mode & SPD5118_LEGACY_MODE_ADDR;
+ if (is_16bit) {
+ /*
+ * See 16-bit addressing note above explaining why it is
+ * necessary to check for I2C_FUNC_I2C support here.
+ */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(dev, "Adapter does not support 16-bit register addresses\n");
+ return -ENODEV;
+ }
+ config = &spd5118_regmap16_config;
+ } else {
+ config = &spd5118_regmap8_config;
+ }
+
+ regmap = devm_regmap_init_i2c(client, config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n");
+
+ return spd5118_common_probe(dev, regmap, is_16bit);
+}
-static const struct i2c_device_id spd5118_id[] = {
+static const struct i2c_device_id spd5118_i2c_id[] = {
{ "spd5118" },
{ }
};
-MODULE_DEVICE_TABLE(i2c, spd5118_id);
+MODULE_DEVICE_TABLE(i2c, spd5118_i2c_id);
static const struct of_device_id spd5118_of_ids[] = {
{ .compatible = "jedec,spd5118", },
@@ -676,20 +757,20 @@ static const struct of_device_id spd5118_of_ids[] = {
};
MODULE_DEVICE_TABLE(of, spd5118_of_ids);
-static struct i2c_driver spd5118_driver = {
+static struct i2c_driver spd5118_i2c_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "spd5118",
.of_match_table = spd5118_of_ids,
.pm = pm_sleep_ptr(&spd5118_pm_ops),
},
- .probe = spd5118_probe,
- .id_table = spd5118_id,
+ .probe = spd5118_i2c_probe,
+ .id_table = spd5118_i2c_id,
.detect = IS_ENABLED(CONFIG_SENSORS_SPD5118_DETECT) ? spd5118_detect : NULL,
.address_list = IS_ENABLED(CONFIG_SENSORS_SPD5118_DETECT) ? normal_i2c : NULL,
};
-module_i2c_driver(spd5118_driver);
+module_i2c_driver(spd5118_i2c_driver);
MODULE_AUTHOR("René Rebe <rene@exactcode.de>");
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c
index 8af44a33055f..a02daa496c9c 100644
--- a/drivers/hwmon/tmp102.c
+++ b/drivers/hwmon/tmp102.c
@@ -16,6 +16,7 @@
#include <linux/device.h>
#include <linux/jiffies.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/of.h>
#define DRIVER_NAME "tmp102"
@@ -204,6 +205,10 @@ static int tmp102_probe(struct i2c_client *client)
return -ENODEV;
}
+ err = devm_regulator_get_enable_optional(dev, "vcc");
+ if (err < 0 && err != -ENODEV)
+ return dev_err_probe(dev, err, "Failed to enable regulator\n");
+
tmp102 = devm_kzalloc(dev, sizeof(*tmp102), GFP_KERNEL);
if (!tmp102)
return -ENOMEM;
diff --git a/drivers/hwmon/tps23861.c b/drivers/hwmon/tps23861.c
index 80fb03f30c30..4cb3960d5170 100644
--- a/drivers/hwmon/tps23861.c
+++ b/drivers/hwmon/tps23861.c
@@ -114,7 +114,6 @@ struct tps23861_data {
struct regmap *regmap;
u32 shunt_resistor;
struct i2c_client *client;
- struct dentry *debugfs_dir;
};
static const struct regmap_config tps23861_regmap_config = {
@@ -503,25 +502,6 @@ static int tps23861_port_status_show(struct seq_file *s, void *data)
DEFINE_SHOW_ATTRIBUTE(tps23861_port_status);
-static void tps23861_init_debugfs(struct tps23861_data *data,
- struct device *hwmon_dev)
-{
- const char *debugfs_name;
-
- debugfs_name = devm_kasprintf(&data->client->dev, GFP_KERNEL, "%s-%s",
- data->client->name, dev_name(hwmon_dev));
- if (!debugfs_name)
- return;
-
- data->debugfs_dir = debugfs_create_dir(debugfs_name, NULL);
-
- debugfs_create_file("port_status",
- 0400,
- data->debugfs_dir,
- data,
- &tps23861_port_status_fops);
-}
-
static int tps23861_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
@@ -562,18 +542,12 @@ static int tps23861_probe(struct i2c_client *client)
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
- tps23861_init_debugfs(data, hwmon_dev);
+ debugfs_create_file("port_status", 0400, client->debugfs, data,
+ &tps23861_port_status_fops);
return 0;
}
-static void tps23861_remove(struct i2c_client *client)
-{
- struct tps23861_data *data = i2c_get_clientdata(client);
-
- debugfs_remove_recursive(data->debugfs_dir);
-}
-
static const struct of_device_id __maybe_unused tps23861_of_match[] = {
{ .compatible = "ti,tps23861", },
{ },
@@ -582,7 +556,6 @@ MODULE_DEVICE_TABLE(of, tps23861_of_match);
static struct i2c_driver tps23861_driver = {
.probe = tps23861_probe,
- .remove = tps23861_remove,
.driver = {
.name = "tps23861",
.of_match_table = of_match_ptr(tps23861_of_match),
diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c
index 1e3bd129a922..11c5d80428cd 100644
--- a/drivers/hwmon/xgene-hwmon.c
+++ b/drivers/hwmon/xgene-hwmon.c
@@ -103,9 +103,7 @@ struct xgene_hwmon_dev {
struct device *hwmon_dev;
bool temp_critical_alarm;
- phys_addr_t comm_base_addr;
- void *pcc_comm_addr;
- u64 usecs_lat;
+ unsigned int usecs_lat;
};
/*
@@ -125,7 +123,8 @@ static u16 xgene_word_tst_and_clr(u16 *addr, u16 mask)
static int xgene_hwmon_pcc_rd(struct xgene_hwmon_dev *ctx, u32 *msg)
{
- struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
+ struct acpi_pcct_shared_memory __iomem *generic_comm_base =
+ ctx->pcc_chan->shmem;
u32 *ptr = (void *)(generic_comm_base + 1);
int rc, i;
u16 val;
@@ -523,7 +522,8 @@ static void xgene_hwmon_rx_cb(struct mbox_client *cl, void *msg)
static void xgene_hwmon_pcc_rx_cb(struct mbox_client *cl, void *msg)
{
struct xgene_hwmon_dev *ctx = to_xgene_hwmon_dev(cl);
- struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
+ struct acpi_pcct_shared_memory __iomem *generic_comm_base =
+ ctx->pcc_chan->shmem;
struct slimpro_resp_msg amsg;
/*
@@ -649,7 +649,6 @@ static int xgene_hwmon_probe(struct platform_device *pdev)
} else {
struct pcc_mbox_chan *pcc_chan;
const struct acpi_device_id *acpi_id;
- int version;
acpi_id = acpi_match_device(pdev->dev.driver->acpi_match_table,
&pdev->dev);
@@ -658,8 +657,6 @@ static int xgene_hwmon_probe(struct platform_device *pdev)
goto out_mbox_free;
}
- version = (int)acpi_id->driver_data;
-
if (device_property_read_u32(&pdev->dev, "pcc-channel",
&ctx->mbox_idx)) {
dev_err(&pdev->dev, "no pcc-channel property\n");
@@ -686,34 +683,6 @@ static int xgene_hwmon_probe(struct platform_device *pdev)
}
/*
- * This is the shared communication region
- * for the OS and Platform to communicate over.
- */
- ctx->comm_base_addr = pcc_chan->shmem_base_addr;
- if (ctx->comm_base_addr) {
- if (version == XGENE_HWMON_V2)
- ctx->pcc_comm_addr = (void __force *)devm_ioremap(&pdev->dev,
- ctx->comm_base_addr,
- pcc_chan->shmem_size);
- else
- ctx->pcc_comm_addr = devm_memremap(&pdev->dev,
- ctx->comm_base_addr,
- pcc_chan->shmem_size,
- MEMREMAP_WB);
- } else {
- dev_err(&pdev->dev, "Failed to get PCC comm region\n");
- rc = -ENODEV;
- goto out;
- }
-
- if (!ctx->pcc_comm_addr) {
- dev_err(&pdev->dev,
- "Failed to ioremap PCC comm region\n");
- rc = -ENOMEM;
- goto out;
- }
-
- /*
* pcc_chan->latency is just a Nominal value. In reality
* the remote processor could be much slower to reply.
* So add an arbitrary amount of wait on top of Nominal.