diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-02-22 22:31:09 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-02-22 22:31:09 +0300 |
commit | 241ed6ab74f94eb3d64ee6b950cd8091b1213225 (patch) | |
tree | ddbdc90494c64b4390ac3c62f8fbe102ee1b19a9 /drivers/hwmon | |
parent | 6c71297eaf713ece684a367ce9aff06069d715b9 (diff) | |
parent | 5720a18baa4686d56d0a235e6ecbcc55f8d716d7 (diff) | |
download | linux-241ed6ab74f94eb3d64ee6b950cd8091b1213225.tar.xz |
Merge tag 'hwmon-for-v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck:
"New drivers:
- Infineon TDA38640 Voltage Regulator
- NXP MC34VR500 PMIC
- GXP fan controller
- MPQ7932 Power Management IC
New chip or board support added to existing drivers:
- it87: IT87952E; also other cleanup/improvements
- intel-m10-bmc-hwmon: N6000
- pmbus/max16601: MAX16600
- aquacomputer_d5next: Aquacomputer Aquastream Ultimate, Aquacomputer
Poweradjust 3, Aquacomputer Aquaero
- nct6775: Support for B650/B660/X670 ASUS boards
- oxp-sensors: AYANEO AIR and AIR Pro
Other notable changes:
- Various kernel documentation fixes
- Various devicetree bindings fixes
- Explicitly deprecated [devm_]hwmon_device_register_with_groups
- ftsteutates: Support for fanX_fault and other cleanup
- ltc2945: Support for setting shunt resistor and other cleanup/fixes
- coretemp: Avoid RDMSR interrupts to isolated CPUs, and simplify
platform device handling
... and various other minor cleanups and fixes"
* tag 'hwmon-for-v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (66 commits)
hwmon: Deprecate [devm_]hwmon_device_register_with_groups
hwmon: (mlxreg-fan) Return zero speed for broken fan
hwmon: (gxp-fan-ctrl) use devm_platform_get_and_ioremap_resource()
hwmon: (aquacomputer_d5next) Add support for Aquacomputer Aquastream Ultimate
hwmon: (aquacomputer_d5next) Add support for Aquacomputer Poweradjust 3
hwmon: (iio_hwmon) use dev_err_probe
hwmon: intel-m10-bmc-hwmon: Add N6000 sensors
Docs/hwmon/index: Add missing SPDX License Identifier
hwmon: (it87) Updated documentation for recent updates to it87
hwmon: (it87) Add new chipset IT87952E
hwmon: (it87) Allow multiple chip IDs for force_id
hwmon: (it87) Add chip_id in some info message
hwmon: (it87) List full chip model name
hwmon: (it87) Disable configuration exit for certain chips
hwmon: (it87) Allow disabling exiting of configuration mode
Documentation: hwmon: correct spelling
hwmon: (pmbus/max16601) Add support for MAX16600
hwmon: (ltc2945) Allow setting shunt resistor
hwmon: (ltc2945) Handle error case in ltc2945_value_store
hwmon: (ltc2945) Add devicetree match table
...
Diffstat (limited to 'drivers/hwmon')
31 files changed, 2152 insertions, 643 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 497cbfb460e5..0c4cdb21ae13 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -714,6 +714,15 @@ config SENSORS_GPIO_FAN This driver can also be built as a module. If so, the module will be called gpio-fan. +config SENSORS_GXP_FAN_CTRL + tristate "HPE GXP fan controller" + depends on ARCH_HPE_GXP || COMPILE_TEST + help + If you say yes here you get support for GXP fan control functionality. + + The GXP controls fan function via the CPLD through the use of PWM + registers. This driver reports status and pwm setting of the fans. + config SENSORS_HIH6130 tristate "Honeywell Humidicon HIH-6130 humidity/temperature sensor" depends on I2C @@ -1166,6 +1175,13 @@ config SENSORS_MAX31790 This driver can also be built as a module. If so, the module will be called max31790. +config SENSORS_MC34VR500 + tristate "NXP MC34VR500 hardware monitoring driver" + depends on I2C + help + If you say yes here you get support for the temperature and input + voltage sensors of the NXP MC34VR500. + config SENSORS_MCP3021 tristate "Microchip MCP3021 and compatibles" depends on I2C @@ -1516,7 +1532,7 @@ config SENSORS_NCT6775_CORE config SENSORS_NCT6775 tristate "Platform driver for Nuvoton NCT6775F and compatibles" depends on !PPC - depends on ACPI_WMI || ACPI_WMI=n + depends on ACPI || ACPI=n select HWMON_VID select SENSORS_NCT6775_CORE help diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 4f71d9807dae..88712b5031c8 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -83,6 +83,7 @@ obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o obj-$(CONFIG_SENSORS_GSC) += gsc-hwmon.o 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_ULTRA45) += ultra45_env.o obj-$(CONFIG_SENSORS_I5500) += i5500_temp.o @@ -149,6 +150,7 @@ obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_MAX6697) += max6697.o obj-$(CONFIG_SENSORS_MAX31790) += max31790.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o +obj-$(CONFIG_SENSORS_MC34VR500) += mc34vr500.o obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o obj-$(CONFIG_SENSORS_TC654) += tc654.o obj-$(CONFIG_SENSORS_TPS23861) += tps23861.o diff --git a/drivers/hwmon/aht10.c b/drivers/hwmon/aht10.c index d76f3441ecf1..9babd69d54a3 100644 --- a/drivers/hwmon/aht10.c +++ b/drivers/hwmon/aht10.c @@ -79,7 +79,6 @@ struct aht10_data { /** * aht10_init() - Initialize an AHT10 chip - * @client: the i2c client associated with the AHT10 * @data: the data associated with this AHT10 chip * Return: 0 if succesfull, 1 if not */ @@ -124,7 +123,7 @@ static int aht10_polltime_expired(struct aht10_data *data) /** * aht10_read_values() - read and parse the raw data from the AHT10 - * @aht10_data: the struct aht10_data to use for the lock + * @data: the struct aht10_data to use for the lock * Return: 0 if succesfull, 1 if not */ static int aht10_read_values(struct aht10_data *data) diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c index 9cc10080160b..12682a610ce7 100644 --- a/drivers/hwmon/aquacomputer_d5next.c +++ b/drivers/hwmon/aquacomputer_d5next.c @@ -1,10 +1,11 @@ // SPDX-License-Identifier: GPL-2.0+ /* * hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360, Octo, - * Quadro, High Flow Next) + * Quadro, High Flow Next, Aquaero, Aquastream Ultimate) * * Aquacomputer devices send HID reports (with ID 0x01) every second to report - * sensor values. + * sensor values, except for devices that communicate through the + * legacy way (currently, Poweradjust 3). * * Copyright 2021 Aleksa Savic <savicaleksa83@gmail.com> * Copyright 2022 Jack Doan <me@jackdoan.com> @@ -21,14 +22,20 @@ #include <asm/unaligned.h> #define USB_VENDOR_ID_AQUACOMPUTER 0x0c70 +#define USB_PRODUCT_ID_AQUAERO 0xf001 #define USB_PRODUCT_ID_FARBWERK 0xf00a #define USB_PRODUCT_ID_QUADRO 0xf00d #define USB_PRODUCT_ID_D5NEXT 0xf00e #define USB_PRODUCT_ID_FARBWERK360 0xf010 #define USB_PRODUCT_ID_OCTO 0xf011 #define USB_PRODUCT_ID_HIGHFLOWNEXT 0xf012 +#define USB_PRODUCT_ID_AQUASTREAMULT 0xf00b +#define USB_PRODUCT_ID_POWERADJUST3 0xf0bd -enum kinds { d5next, farbwerk, farbwerk360, octo, quadro, highflownext }; +enum kinds { + d5next, farbwerk, farbwerk360, octo, quadro, + highflownext, aquaero, poweradjust3, aquastreamult +}; static const char *const aqc_device_names[] = { [d5next] = "d5next", @@ -36,16 +43,17 @@ static const char *const aqc_device_names[] = { [farbwerk360] = "farbwerk360", [octo] = "octo", [quadro] = "quadro", - [highflownext] = "highflownext" + [highflownext] = "highflownext", + [aquaero] = "aquaero", + [aquastreamult] = "aquastreamultimate", + [poweradjust3] = "poweradjust3" }; #define DRIVER_NAME "aquacomputer_d5next" #define STATUS_REPORT_ID 0x01 #define STATUS_UPDATE_INTERVAL (2 * HZ) /* In seconds */ -#define SERIAL_FIRST_PART 3 -#define SERIAL_SECOND_PART 5 -#define FIRMWARE_VERSION 13 +#define SERIAL_PART_OFFSET 2 #define CTRL_REPORT_ID 0x03 @@ -59,8 +67,14 @@ static u8 secondary_ctrl_report[] = { 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x34, 0xC6 }; -/* Sensor sizes and offsets for all Aquacomputer devices */ -#define AQC_TEMP_SENSOR_SIZE 0x02 +/* Report IDs for legacy devices */ +#define POWERADJUST3_STATUS_REPORT_ID 0x03 + +/* Info, sensor sizes and offsets for most Aquacomputer devices */ +#define AQC_SERIAL_START 0x3 +#define AQC_FIRMWARE_VERSION 0xD + +#define AQC_SENSOR_SIZE 0x02 #define AQC_TEMP_SENSOR_DISCONNECTED 0x7FFF #define AQC_FAN_PERCENT_OFFSET 0x00 #define AQC_FAN_VOLTAGE_OFFSET 0x02 @@ -68,6 +82,26 @@ static u8 secondary_ctrl_report[] = { #define AQC_FAN_POWER_OFFSET 0x06 #define AQC_FAN_SPEED_OFFSET 0x08 +/* Specs of the Aquaero fan controllers */ +#define AQUAERO_SERIAL_START 0x07 +#define AQUAERO_FIRMWARE_VERSION 0x0B +#define AQUAERO_NUM_FANS 4 +#define AQUAERO_NUM_SENSORS 8 +#define AQUAERO_NUM_VIRTUAL_SENSORS 8 +#define AQUAERO_NUM_CALC_VIRTUAL_SENSORS 4 +#define AQUAERO_NUM_FLOW_SENSORS 2 + +/* Sensor report offsets for Aquaero fan controllers */ +#define AQUAERO_SENSOR_START 0x65 +#define AQUAERO_VIRTUAL_SENSOR_START 0x85 +#define AQUAERO_CALC_VIRTUAL_SENSOR_START 0x95 +#define AQUAERO_FLOW_SENSORS_START 0xF9 +#define AQUAERO_FAN_VOLTAGE_OFFSET 0x04 +#define AQUAERO_FAN_CURRENT_OFFSET 0x06 +#define AQUAERO_FAN_POWER_OFFSET 0x08 +#define AQUAERO_FAN_SPEED_OFFSET 0x00 +static u16 aquaero_sensor_fan_offsets[] = { 0x167, 0x173, 0x17f, 0x18B }; + /* Specs of the D5 Next pump */ #define D5NEXT_NUM_FANS 2 #define D5NEXT_NUM_SENSORS 1 @@ -82,12 +116,32 @@ static u8 secondary_ctrl_report[] = { #define D5NEXT_5V_VOLTAGE 0x39 #define D5NEXT_12V_VOLTAGE 0x37 #define D5NEXT_VIRTUAL_SENSORS_START 0x3f -static u8 d5next_sensor_fan_offsets[] = { D5NEXT_PUMP_OFFSET, D5NEXT_FAN_OFFSET }; +static u16 d5next_sensor_fan_offsets[] = { D5NEXT_PUMP_OFFSET, D5NEXT_FAN_OFFSET }; /* Control report offsets for the D5 Next pump */ #define D5NEXT_TEMP_CTRL_OFFSET 0x2D /* Temperature sensor offsets location */ static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 }; /* Pump and fan speed (from 0-100%) */ +/* Specs of the Aquastream Ultimate pump */ +/* Pump does not follow the standard structure, so only consider the fan */ +#define AQUASTREAMULT_NUM_FANS 1 +#define AQUASTREAMULT_NUM_SENSORS 2 + +/* Sensor report offsets for the Aquastream Ultimate pump */ +#define AQUASTREAMULT_SENSOR_START 0x2D +#define AQUASTREAMULT_PUMP_OFFSET 0x51 +#define AQUASTREAMULT_PUMP_VOLTAGE 0x3D +#define AQUASTREAMULT_PUMP_CURRENT 0x53 +#define AQUASTREAMULT_PUMP_POWER 0x55 +#define AQUASTREAMULT_FAN_OFFSET 0x41 +#define AQUASTREAMULT_PRESSURE_OFFSET 0x57 +#define AQUASTREAMULT_FLOW_SENSOR_OFFSET 0x37 +#define AQUASTREAMULT_FAN_VOLTAGE_OFFSET 0x02 +#define AQUASTREAMULT_FAN_CURRENT_OFFSET 0x00 +#define AQUASTREAMULT_FAN_POWER_OFFSET 0x04 +#define AQUASTREAMULT_FAN_SPEED_OFFSET 0x06 +static u16 aquastreamult_sensor_fan_offsets[] = { AQUASTREAMULT_FAN_OFFSET }; + /* Spec and sensor report offset for the Farbwerk RGB controller */ #define FARBWERK_NUM_SENSORS 4 #define FARBWERK_SENSOR_START 0x2f @@ -114,7 +168,7 @@ static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 }; /* Pump and fan speed (fr #define OCTO_POWER_CYCLES 0x18 #define OCTO_SENSOR_START 0x3D #define OCTO_VIRTUAL_SENSORS_START 0x45 -static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 }; +static u16 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 }; /* Control report offsets for the Octo */ #define OCTO_TEMP_CTRL_OFFSET 0xA @@ -125,6 +179,7 @@ static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0 #define QUADRO_NUM_FANS 4 #define QUADRO_NUM_SENSORS 4 #define QUADRO_NUM_VIRTUAL_SENSORS 16 +#define QUADRO_NUM_FLOW_SENSORS 1 #define QUADRO_CTRL_REPORT_SIZE 0x3c1 /* Sensor report offsets for the Quadro */ @@ -132,7 +187,7 @@ static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0 #define QUADRO_SENSOR_START 0x34 #define QUADRO_VIRTUAL_SENSORS_START 0x3c #define QUADRO_FLOW_SENSOR_OFFSET 0x6e -static u8 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 }; +static u16 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 }; /* Control report offsets for the Quadro */ #define QUADRO_TEMP_CTRL_OFFSET 0xA @@ -141,6 +196,7 @@ static u16 quadro_ctrl_fan_offsets[] = { 0x37, 0x8c, 0xe1, 0x136 }; /* Fan speed /* Specs of High Flow Next flow sensor */ #define HIGHFLOWNEXT_NUM_SENSORS 2 +#define HIGHFLOWNEXT_NUM_FLOW_SENSORS 1 /* Sensor report offsets for the High Flow Next */ #define HIGHFLOWNEXT_SENSOR_START 85 @@ -151,6 +207,13 @@ static u16 quadro_ctrl_fan_offsets[] = { 0x37, 0x8c, 0xe1, 0x136 }; /* Fan speed #define HIGHFLOWNEXT_5V_VOLTAGE 97 #define HIGHFLOWNEXT_5V_VOLTAGE_USB 99 +/* Specs of the Poweradjust 3 */ +#define POWERADJUST3_NUM_SENSORS 1 +#define POWERADJUST3_SENSOR_REPORT_SIZE 0x32 + +/* Sensor report offsets for the Poweradjust 3 */ +#define POWERADJUST3_SENSOR_START 0x03 + /* Labels for D5 Next */ static const char *const label_d5next_temp[] = { "Coolant temp" @@ -178,12 +241,16 @@ static const char *const label_d5next_current[] = { "Fan current" }; -/* Labels for Farbwerk, Farbwerk 360 and Octo and Quadro temperature sensors */ +/* Labels for Aquaero, Farbwerk, Farbwerk 360 and Octo and Quadro temperature sensors */ static const char *const label_temp_sensors[] = { "Sensor 1", "Sensor 2", "Sensor 3", - "Sensor 4" + "Sensor 4", + "Sensor 5", + "Sensor 6", + "Sensor 7", + "Sensor 8" }; static const char *const label_virtual_temp_sensors[] = { @@ -205,6 +272,13 @@ static const char *const label_virtual_temp_sensors[] = { "Virtual sensor 16", }; +static const char *const label_aquaero_calc_temp_sensors[] = { + "Calc. virtual sensor 1", + "Calc. virtual sensor 2", + "Calc. virtual sensor 3", + "Calc. virtual sensor 4" +}; + /* Labels for Octo and Quadro (except speed) */ static const char *const label_fan_speed[] = { "Fan 1 speed", @@ -259,6 +333,16 @@ static const char *const label_quadro_speeds[] = { "Flow speed [dL/h]" }; +/* Labels for Aquaero fan speeds */ +static const char *const label_aquaero_speeds[] = { + "Fan 1 speed", + "Fan 2 speed", + "Fan 3 speed", + "Fan 4 speed", + "Flow sensor 1 [dL/h]", + "Flow sensor 2 [dL/h]" +}; + /* Labels for High Flow Next */ static const char *const label_highflownext_temp_sensors[] = { "Coolant temp", @@ -280,6 +364,70 @@ static const char *const label_highflownext_voltage[] = { "+5V USB voltage" }; +/* Labels for Aquastream Ultimate */ +static const char *const label_aquastreamult_temp[] = { + "Coolant temp", + "External temp" +}; + +static const char *const label_aquastreamult_speeds[] = { + "Fan speed", + "Pump speed", + "Pressure [mbar]", + "Flow speed [dL/h]" +}; + +static const char *const label_aquastreamult_power[] = { + "Fan power", + "Pump power" +}; + +static const char *const label_aquastreamult_voltages[] = { + "Fan voltage", + "Pump voltage" +}; + +static const char *const label_aquastreamult_current[] = { + "Fan current", + "Pump current" +}; + +/* Labels for Poweradjust 3 */ +static const char *const label_poweradjust3_temp_sensors[] = { + "External sensor" +}; + +struct aqc_fan_structure_offsets { + u8 voltage; + u8 curr; + u8 power; + u8 speed; +}; + +/* Fan structure offsets for Aquaero */ +static struct aqc_fan_structure_offsets aqc_aquaero_fan_structure = { + .voltage = AQUAERO_FAN_VOLTAGE_OFFSET, + .curr = AQUAERO_FAN_CURRENT_OFFSET, + .power = AQUAERO_FAN_POWER_OFFSET, + .speed = AQUAERO_FAN_SPEED_OFFSET +}; + +/* Fan structure offsets for Aquastream Ultimate */ +static struct aqc_fan_structure_offsets aqc_aquastreamult_fan_structure = { + .voltage = AQUASTREAMULT_FAN_VOLTAGE_OFFSET, + .curr = AQUASTREAMULT_FAN_CURRENT_OFFSET, + .power = AQUASTREAMULT_FAN_POWER_OFFSET, + .speed = AQUASTREAMULT_FAN_SPEED_OFFSET +}; + +/* Fan structure offsets for all devices except those above */ +static struct aqc_fan_structure_offsets aqc_general_fan_structure = { + .voltage = AQC_FAN_VOLTAGE_OFFSET, + .curr = AQC_FAN_CURRENT_OFFSET, + .power = AQC_FAN_POWER_OFFSET, + .speed = AQC_FAN_SPEED_OFFSET +}; + struct aqc_data { struct hid_device *hdev; struct device *hwmon_dev; @@ -288,6 +436,8 @@ struct aqc_data { enum kinds kind; const char *name; + int status_report_id; /* Used for legacy devices, report is stored in buffer */ + int buffer_size; u8 *buffer; int checksum_start; @@ -295,26 +445,32 @@ struct aqc_data { int checksum_offset; int num_fans; - u8 *fan_sensor_offsets; + u16 *fan_sensor_offsets; u16 *fan_ctrl_offsets; int num_temp_sensors; int temp_sensor_start_offset; int num_virtual_temp_sensors; int virtual_temp_sensor_start_offset; + int num_calc_virt_temp_sensors; + int calc_virt_temp_sensor_start_offset; u16 temp_ctrl_offset; u16 power_cycle_count_offset; - u8 flow_sensor_offset; + int num_flow_sensors; + u8 flow_sensors_start_offset; u8 flow_pulses_ctrl_offset; + struct aqc_fan_structure_offsets *fan_structure; /* General info, same across all devices */ + u8 serial_number_start_offset; u32 serial_number[2]; + u8 firmware_version_offset; u16 firmware_version; /* How many times the device was powered on, if available */ u32 power_cycles; /* Sensor values */ - s32 temp_input[20]; /* Max 4 physical and 16 virtual */ + s32 temp_input[20]; /* Max 4 physical and 16 virtual or 8 physical and 12 virtual */ u16 speed_input[8]; u32 power_input[8]; u16 voltage_input[8]; @@ -323,6 +479,7 @@ struct aqc_data { /* Label values */ const char *const *temp_label; const char *const *virtual_temp_label; + const char *const *calc_virt_temp_label; /* For Aquaero */ const char *const *speed_label; const char *const *power_label; const char *const *voltage_label; @@ -443,7 +600,9 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 } } - if (channel < priv->num_temp_sensors + priv->num_virtual_temp_sensors) + if (channel < + priv->num_temp_sensors + priv->num_virtual_temp_sensors + + priv->num_calc_virt_temp_sensors) switch (attr) { case hwmon_temp_label: case hwmon_temp_input: @@ -467,6 +626,14 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 case hwmon_fan_input: case hwmon_fan_label: switch (priv->kind) { + case aquastreamult: + /* + * Special case to support pump RPM, fan RPM, + * pressure and flow sensor + */ + if (channel < 4) + return 0444; + break; case highflownext: /* Special case to support flow sensor, water quality * and conductivity @@ -474,9 +641,10 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 if (channel < 3) return 0444; break; + case aquaero: case quadro: - /* Special case to support flow sensor */ - if (channel < priv->num_fans + 1) + /* Special case to support flow sensors */ + if (channel < priv->num_fans + priv->num_flow_sensors) return 0444; break; default: @@ -496,6 +664,11 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 break; case hwmon_power: switch (priv->kind) { + case aquastreamult: + /* Special case to support pump and fan power */ + if (channel < 2) + return 0444; + break; case highflownext: /* Special case to support one power sensor */ if (channel == 0) @@ -508,8 +681,17 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 } break; case hwmon_curr: - if (channel < priv->num_fans) - return 0444; + switch (priv->kind) { + case aquastreamult: + /* Special case to support pump and fan current */ + if (channel < 2) + return 0444; + break; + default: + if (channel < priv->num_fans) + return 0444; + break; + } break; case hwmon_in: switch (priv->kind) { @@ -518,6 +700,7 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 if (channel < priv->num_fans + 2) return 0444; break; + case aquastreamult: case highflownext: /* Special case to support two voltage sensors */ if (channel < 2) @@ -536,14 +719,49 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 return 0; } +/* Read device sensors by manually requesting the sensor report (legacy way) */ +static int aqc_legacy_read(struct aqc_data *priv) +{ + int ret, i, sensor_value; + + mutex_lock(&priv->mutex); + + memset(priv->buffer, 0x00, priv->buffer_size); + ret = hid_hw_raw_request(priv->hdev, priv->status_report_id, priv->buffer, + priv->buffer_size, HID_FEATURE_REPORT, HID_REQ_GET_REPORT); + if (ret < 0) + goto unlock_and_return; + + /* Temperature sensor readings */ + for (i = 0; i < priv->num_temp_sensors; i++) { + sensor_value = get_unaligned_le16(priv->buffer + priv->temp_sensor_start_offset + + i * AQC_SENSOR_SIZE); + priv->temp_input[i] = sensor_value * 10; + } + + priv->updated = jiffies; + +unlock_and_return: + mutex_unlock(&priv->mutex); + return ret; +} + static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { int ret; struct aqc_data *priv = dev_get_drvdata(dev); - if (time_after(jiffies, priv->updated + STATUS_UPDATE_INTERVAL)) - return -ENODATA; + if (time_after(jiffies, priv->updated + STATUS_UPDATE_INTERVAL)) { + if (priv->status_report_id != 0) { + /* Legacy devices require manual reads */ + ret = aqc_legacy_read(priv); + if (ret < 0) + return -ENODATA; + } else { + return -ENODATA; + } + } switch (type) { case hwmon_temp: @@ -557,7 +775,7 @@ static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, case hwmon_temp_offset: ret = aqc_get_ctrl_val(priv, priv->temp_ctrl_offset + - channel * AQC_TEMP_SENSOR_SIZE, val); + channel * AQC_SENSOR_SIZE, val); if (ret < 0) return ret; @@ -611,12 +829,20 @@ static int aqc_read_string(struct device *dev, enum hwmon_sensor_types type, u32 { struct aqc_data *priv = dev_get_drvdata(dev); + /* Number of sensors that are not calculated */ + int num_non_calc_sensors = priv->num_temp_sensors + priv->num_virtual_temp_sensors; + switch (type) { case hwmon_temp: - if (channel < priv->num_temp_sensors) + if (channel < priv->num_temp_sensors) { *str = priv->temp_label[channel]; - else - *str = priv->virtual_temp_label[channel - priv->num_temp_sensors]; + } else { + if (priv->kind == aquaero && channel >= num_non_calc_sensors) + *str = + priv->calc_virt_temp_label[channel - num_non_calc_sensors]; + else + *str = priv->virtual_temp_label[channel - priv->num_temp_sensors]; + } break; case hwmon_fan: *str = priv->speed_label[channel]; @@ -651,7 +877,7 @@ static int aqc_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, val = clamp_val(val, -15000, 15000) / 10; ret = aqc_set_ctrl_val(priv, priv->temp_ctrl_offset + - channel * AQC_TEMP_SENSOR_SIZE, val); + channel * AQC_SENSOR_SIZE, val); if (ret < 0) return ret; break; @@ -789,15 +1015,16 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 priv = hid_get_drvdata(hdev); /* Info provided with every report */ - priv->serial_number[0] = get_unaligned_be16(data + SERIAL_FIRST_PART); - priv->serial_number[1] = get_unaligned_be16(data + SERIAL_SECOND_PART); - priv->firmware_version = get_unaligned_be16(data + FIRMWARE_VERSION); + priv->serial_number[0] = get_unaligned_be16(data + priv->serial_number_start_offset); + priv->serial_number[1] = get_unaligned_be16(data + priv->serial_number_start_offset + + SERIAL_PART_OFFSET); + priv->firmware_version = get_unaligned_be16(data + priv->firmware_version_offset); /* Physical temperature sensor readings */ for (i = 0; i < priv->num_temp_sensors; i++) { sensor_value = get_unaligned_be16(data + priv->temp_sensor_start_offset + - i * AQC_TEMP_SENSOR_SIZE); + i * AQC_SENSOR_SIZE); if (sensor_value == AQC_TEMP_SENSOR_DISCONNECTED) priv->temp_input[i] = -ENODATA; else @@ -808,7 +1035,7 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 for (j = 0; j < priv->num_virtual_temp_sensors; j++) { sensor_value = get_unaligned_be16(data + priv->virtual_temp_sensor_start_offset + - j * AQC_TEMP_SENSOR_SIZE); + j * AQC_SENSOR_SIZE); if (sensor_value == AQC_TEMP_SENSOR_DISCONNECTED) priv->temp_input[i] = -ENODATA; else @@ -819,15 +1046,24 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 /* Fan speed and related readings */ for (i = 0; i < priv->num_fans; i++) { priv->speed_input[i] = - get_unaligned_be16(data + priv->fan_sensor_offsets[i] + AQC_FAN_SPEED_OFFSET); + get_unaligned_be16(data + priv->fan_sensor_offsets[i] + + priv->fan_structure->speed); priv->power_input[i] = get_unaligned_be16(data + priv->fan_sensor_offsets[i] + - AQC_FAN_POWER_OFFSET) * 10000; + priv->fan_structure->power) * 10000; priv->voltage_input[i] = get_unaligned_be16(data + priv->fan_sensor_offsets[i] + - AQC_FAN_VOLTAGE_OFFSET) * 10; + priv->fan_structure->voltage) * 10; priv->current_input[i] = - get_unaligned_be16(data + priv->fan_sensor_offsets[i] + AQC_FAN_CURRENT_OFFSET); + get_unaligned_be16(data + priv->fan_sensor_offsets[i] + + priv->fan_structure->curr); + } + + /* Flow sensor readings */ + for (j = 0; j < priv->num_flow_sensors; j++) { + priv->speed_input[i] = get_unaligned_be16(data + priv->flow_sensors_start_offset + + j * AQC_SENSOR_SIZE); + i++; } if (priv->power_cycle_count_offset != 0) @@ -835,13 +1071,35 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 /* Special-case sensor readings */ switch (priv->kind) { + case aquaero: + /* Read calculated virtual temp sensors */ + i = priv->num_temp_sensors + priv->num_virtual_temp_sensors; + for (j = 0; j < priv->num_calc_virt_temp_sensors; j++) { + sensor_value = get_unaligned_be16(data + + priv->calc_virt_temp_sensor_start_offset + + j * AQC_SENSOR_SIZE); + if (sensor_value == AQC_TEMP_SENSOR_DISCONNECTED) + priv->temp_input[i] = -ENODATA; + else + priv->temp_input[i] = sensor_value * 10; + i++; + } + break; + case aquastreamult: + priv->speed_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_OFFSET); + priv->speed_input[2] = get_unaligned_be16(data + AQUASTREAMULT_PRESSURE_OFFSET); + priv->speed_input[3] = get_unaligned_be16(data + AQUASTREAMULT_FLOW_SENSOR_OFFSET); + + priv->power_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_POWER) * 10000; + + priv->voltage_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_VOLTAGE) * 10; + + priv->current_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_CURRENT); + break; case d5next: priv->voltage_input[2] = get_unaligned_be16(data + D5NEXT_5V_VOLTAGE) * 10; priv->voltage_input[3] = get_unaligned_be16(data + D5NEXT_12V_VOLTAGE) * 10; break; - case quadro: - priv->speed_input[4] = get_unaligned_be16(data + priv->flow_sensor_offset); - break; case highflownext: /* If external temp sensor is not connected, its power reading is also N/A */ if (priv->temp_input[1] == -ENODATA) @@ -854,7 +1112,6 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 priv->voltage_input[1] = get_unaligned_be16(data + HIGHFLOWNEXT_5V_VOLTAGE_USB) * 10; - priv->speed_input[0] = get_unaligned_be16(data + HIGHFLOWNEXT_FLOW); priv->speed_input[1] = get_unaligned_be16(data + HIGHFLOWNEXT_WATER_QUALITY); priv->speed_input[2] = get_unaligned_be16(data + HIGHFLOWNEXT_CONDUCTIVITY); break; @@ -907,9 +1164,13 @@ static void aqc_debugfs_init(struct aqc_data *priv) dev_name(&priv->hdev->dev)); priv->debugfs = debugfs_create_dir(name, NULL); - debugfs_create_file("serial_number", 0444, priv->debugfs, priv, &serial_number_fops); - debugfs_create_file("firmware_version", 0444, priv->debugfs, priv, &firmware_version_fops); + if (priv->serial_number_start_offset != 0) + debugfs_create_file("serial_number", 0444, priv->debugfs, priv, + &serial_number_fops); + if (priv->firmware_version_offset != 0) + debugfs_create_file("firmware_version", 0444, priv->debugfs, priv, + &firmware_version_fops); if (priv->power_cycle_count_offset != 0) debugfs_create_file("power_cycles", 0444, priv->debugfs, priv, &power_cycles_fops); } @@ -949,6 +1210,45 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) goto fail_and_stop; switch (hdev->product) { + case USB_PRODUCT_ID_AQUAERO: + /* + * Aquaero presents itself as three HID devices under the same product ID: + * "aquaero keyboard/mouse", "aquaero System Control" and "aquaero Device", + * which is the one we want to communicate with. Unlike most other Aquacomputer + * devices, Aquaero does not return meaningful data when explicitly requested + * using GET_FEATURE_REPORT. + * + * The difference between "aquaero Device" and the other two is in the collections + * they present. The two other devices have the type of the second element in + * their respective collections set to 1, while the real device has it set to 0. + */ + if (hdev->collection[1].type != 0) { + ret = -ENODEV; + goto fail_and_close; + } + + priv->kind = aquaero; + + priv->num_fans = AQUAERO_NUM_FANS; + priv->fan_sensor_offsets = aquaero_sensor_fan_offsets; + + priv->num_temp_sensors = AQUAERO_NUM_SENSORS; + priv->temp_sensor_start_offset = AQUAERO_SENSOR_START; + priv->num_virtual_temp_sensors = AQUAERO_NUM_VIRTUAL_SENSORS; + priv->virtual_temp_sensor_start_offset = AQUAERO_VIRTUAL_SENSOR_START; + priv->num_calc_virt_temp_sensors = AQUAERO_NUM_CALC_VIRTUAL_SENSORS; + priv->calc_virt_temp_sensor_start_offset = AQUAERO_CALC_VIRTUAL_SENSOR_START; + priv->num_flow_sensors = AQUAERO_NUM_FLOW_SENSORS; + priv->flow_sensors_start_offset = AQUAERO_FLOW_SENSORS_START; + + priv->temp_label = label_temp_sensors; + priv->virtual_temp_label = label_virtual_temp_sensors; + priv->calc_virt_temp_label = label_aquaero_calc_temp_sensors; + priv->speed_label = label_aquaero_speeds; + priv->power_label = label_fan_power; + priv->voltage_label = label_fan_voltage; + priv->current_label = label_fan_current; + break; case USB_PRODUCT_ID_D5NEXT: priv->kind = d5next; @@ -1034,11 +1334,13 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) priv->temp_sensor_start_offset = QUADRO_SENSOR_START; priv->num_virtual_temp_sensors = QUADRO_NUM_VIRTUAL_SENSORS; priv->virtual_temp_sensor_start_offset = QUADRO_VIRTUAL_SENSORS_START; + priv->num_flow_sensors = QUADRO_NUM_FLOW_SENSORS; + priv->flow_sensors_start_offset = QUADRO_FLOW_SENSOR_OFFSET; + priv->temp_ctrl_offset = QUADRO_TEMP_CTRL_OFFSET; priv->buffer_size = QUADRO_CTRL_REPORT_SIZE; - priv->flow_sensor_offset = QUADRO_FLOW_SENSOR_OFFSET; priv->flow_pulses_ctrl_offset = QUADRO_FLOW_PULSES_CTRL_OFFSET; priv->power_cycle_count_offset = QUADRO_POWER_CYCLES; @@ -1056,6 +1358,8 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) priv->num_temp_sensors = HIGHFLOWNEXT_NUM_SENSORS; priv->temp_sensor_start_offset = HIGHFLOWNEXT_SENSOR_START; + priv->num_flow_sensors = HIGHFLOWNEXT_NUM_FLOW_SENSORS; + priv->flow_sensors_start_offset = HIGHFLOWNEXT_FLOW; priv->power_cycle_count_offset = QUADRO_POWER_CYCLES; @@ -1064,10 +1368,57 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) priv->power_label = label_highflownext_power; priv->voltage_label = label_highflownext_voltage; break; + case USB_PRODUCT_ID_AQUASTREAMULT: + priv->kind = aquastreamult; + + priv->num_fans = AQUASTREAMULT_NUM_FANS; + priv->fan_sensor_offsets = aquastreamult_sensor_fan_offsets; + + priv->num_temp_sensors = AQUASTREAMULT_NUM_SENSORS; + priv->temp_sensor_start_offset = AQUASTREAMULT_SENSOR_START; + + priv->temp_label = label_aquastreamult_temp; + priv->speed_label = label_aquastreamult_speeds; + priv->power_label = label_aquastreamult_power; + priv->voltage_label = label_aquastreamult_voltages; + priv->current_label = label_aquastreamult_current; + break; + case USB_PRODUCT_ID_POWERADJUST3: + priv->kind = poweradjust3; + + priv->num_fans = 0; + + priv->num_temp_sensors = POWERADJUST3_NUM_SENSORS; + priv->temp_sensor_start_offset = POWERADJUST3_SENSOR_START; + priv->buffer_size = POWERADJUST3_SENSOR_REPORT_SIZE; + + priv->temp_label = label_poweradjust3_temp_sensors; + break; default: break; } + switch (priv->kind) { + case aquaero: + priv->serial_number_start_offset = AQUAERO_SERIAL_START; + priv->firmware_version_offset = AQUAERO_FIRMWARE_VERSION; + + priv->fan_structure = &aqc_aquaero_fan_structure; + break; + case poweradjust3: + priv->status_report_id = POWERADJUST3_STATUS_REPORT_ID; + break; + default: + priv->serial_number_start_offset = AQC_SERIAL_START; + priv->firmware_version_offset = AQC_FIRMWARE_VERSION; + + if (priv->kind == aquastreamult) + priv->fan_structure = &aqc_aquastreamult_fan_structure; + else + priv->fan_structure = &aqc_general_fan_structure; + break; + } + if (priv->buffer_size != 0) { priv->checksum_start = 0x01; priv->checksum_length = priv->buffer_size - 3; @@ -1115,12 +1466,15 @@ static void aqc_remove(struct hid_device *hdev) } static const struct hid_device_id aqc_table[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_AQUAERO) }, { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_D5NEXT) }, { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK) }, { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK360) }, { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_OCTO) }, { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_QUADRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_HIGHFLOWNEXT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_AQUASTREAMULT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_POWERADJUST3) }, { } }; diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index a901e4e33d81..2768b7511684 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -299,6 +299,7 @@ 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 | SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE, + .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX, .family = family_amd_500_series, }; @@ -466,6 +467,8 @@ static const struct dmi_system_id dmi_table[] = { &board_info_strix_z690_a_gaming_wifi_d4), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG ZENITH II EXTREME", &board_info_zenith_ii_extreme), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG ZENITH II EXTREME ALPHA", + &board_info_zenith_ii_extreme), {}, }; diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index ca7a9b373bbd..30d77f451937 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -27,6 +27,7 @@ #include <asm/msr.h> #include <asm/processor.h> #include <asm/cpu_device_id.h> +#include <linux/sched/isolation.h> #define DRVNAME "coretemp" @@ -502,6 +503,9 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu, u32 eax, edx; int err, index, attr_no; + if (!housekeeping_cpu(cpu, HK_TYPE_MISC)) + return 0; + /* * Find attr number for sysfs: * We map the attr number to core id of the CPU @@ -588,66 +592,49 @@ static void coretemp_remove_core(struct platform_data *pdata, int indx) ida_free(&pdata->ida, indx - BASE_SYSFS_ATTR_NO); } -static int coretemp_probe(struct platform_device *pdev) +static int coretemp_device_add(int zoneid) { - struct device *dev = &pdev->dev; + struct platform_device *pdev; struct platform_data *pdata; + int err; /* Initialize the per-zone data structures */ - pdata = devm_kzalloc(dev, sizeof(struct platform_data), GFP_KERNEL); + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; - pdata->pkg_id = pdev->id; + pdata->pkg_id = zoneid; ida_init(&pdata->ida); - platform_set_drvdata(pdev, pdata); - - pdata->hwmon_dev = devm_hwmon_device_register_with_groups(dev, DRVNAME, - pdata, NULL); - return PTR_ERR_OR_ZERO(pdata->hwmon_dev); -} -static int coretemp_remove(struct platform_device *pdev) -{ - struct platform_data *pdata = platform_get_drvdata(pdev); - int i; + pdev = platform_device_alloc(DRVNAME, zoneid); + if (!pdev) { + err = -ENOMEM; + goto err_free_pdata; + } - for (i = MAX_CORE_DATA - 1; i >= 0; --i) - if (pdata->core_data[i]) - coretemp_remove_core(pdata, i); + err = platform_device_add(pdev); + if (err) + goto err_put_dev; - ida_destroy(&pdata->ida); + platform_set_drvdata(pdev, pdata); + zone_devices[zoneid] = pdev; return 0; -} -static struct platform_driver coretemp_driver = { - .driver = { - .name = DRVNAME, - }, - .probe = coretemp_probe, - .remove = coretemp_remove, -}; +err_put_dev: + platform_device_put(pdev); +err_free_pdata: + kfree(pdata); + return err; +} -static struct platform_device *coretemp_device_add(unsigned int cpu) +static void coretemp_device_remove(int zoneid) { - int err, zoneid = topology_logical_die_id(cpu); - struct platform_device *pdev; - - if (zoneid < 0) - return ERR_PTR(-ENOMEM); - - pdev = platform_device_alloc(DRVNAME, zoneid); - if (!pdev) - return ERR_PTR(-ENOMEM); - - err = platform_device_add(pdev); - if (err) { - platform_device_put(pdev); - return ERR_PTR(err); - } + struct platform_device *pdev = zone_devices[zoneid]; + struct platform_data *pdata = platform_get_drvdata(pdev); - zone_devices[zoneid] = pdev; - return pdev; + ida_destroy(&pdata->ida); + kfree(pdata); + platform_device_unregister(pdev); } static int coretemp_cpu_online(unsigned int cpu) @@ -671,7 +658,10 @@ static int coretemp_cpu_online(unsigned int cpu) if (!cpu_has(c, X86_FEATURE_DTHERM)) return -ENODEV; - if (!pdev) { + pdata = platform_get_drvdata(pdev); + if (!pdata->hwmon_dev) { + struct device *hwmon; + /* Check the microcode version of the CPU */ if (chk_ucode_version(cpu)) return -EINVAL; @@ -682,9 +672,11 @@ static int coretemp_cpu_online(unsigned int cpu) * online. So, initialize per-pkg data structures and * then bring this core online. */ - pdev = coretemp_device_add(cpu); - if (IS_ERR(pdev)) - return PTR_ERR(pdev); + hwmon = hwmon_device_register_with_groups(&pdev->dev, DRVNAME, + pdata, NULL); + if (IS_ERR(hwmon)) + return PTR_ERR(hwmon); + pdata->hwmon_dev = hwmon; /* * Check whether pkgtemp support is available. @@ -694,7 +686,6 @@ static int coretemp_cpu_online(unsigned int cpu) coretemp_add_core(pdev, cpu, 1); } - pdata = platform_get_drvdata(pdev); /* * Check whether a thread sibling is already online. If not add the * interface for this CPU core. @@ -713,18 +704,14 @@ static int coretemp_cpu_offline(unsigned int cpu) struct temp_data *tdata; int i, indx = -1, target; - /* - * Don't execute this on suspend as the device remove locks - * up the machine. - */ + /* No need to tear down any interfaces for suspend */ if (cpuhp_tasks_frozen) return 0; /* If the physical CPU device does not exist, just return */ - if (!pdev) - return 0; - pd = platform_get_drvdata(pdev); + if (!pd->hwmon_dev) + return 0; for (i = 0; i < NUM_REAL_CORES; i++) { if (pd->cpu_map[i] == topology_core_id(cpu)) { @@ -756,13 +743,14 @@ static int coretemp_cpu_offline(unsigned int cpu) } /* - * If all cores in this pkg are offline, remove the device. This - * will invoke the platform driver remove function, which cleans up - * the rest. + * If all cores in this pkg are offline, remove the interface. */ + tdata = pd->core_data[PKG_SYSFS_ATTR_NO]; if (cpumask_empty(&pd->cpumask)) { - zone_devices[topology_logical_die_id(cpu)] = NULL; - platform_device_unregister(pdev); + if (tdata) + coretemp_remove_core(pd, PKG_SYSFS_ATTR_NO); + hwmon_device_unregister(pd->hwmon_dev); + pd->hwmon_dev = NULL; return 0; } @@ -770,7 +758,6 @@ static int coretemp_cpu_offline(unsigned int cpu) * Check whether this core is the target for the package * interface. We need to assign it to some other cpu. */ - tdata = pd->core_data[PKG_SYSFS_ATTR_NO]; if (tdata && tdata->cpu == cpu) { target = cpumask_first(&pd->cpumask); mutex_lock(&tdata->update_lock); @@ -789,7 +776,7 @@ static enum cpuhp_state coretemp_hp_online; static int __init coretemp_init(void) { - int err; + int i, err; /* * CPUID.06H.EAX[0] indicates whether the CPU has thermal @@ -805,20 +792,22 @@ static int __init coretemp_init(void) if (!zone_devices) return -ENOMEM; - err = platform_driver_register(&coretemp_driver); - if (err) - goto outzone; + for (i = 0; i < max_zones; i++) { + err = coretemp_device_add(i); + if (err) + goto outzone; + } err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/coretemp:online", coretemp_cpu_online, coretemp_cpu_offline); if (err < 0) - goto outdrv; + goto outzone; coretemp_hp_online = err; return 0; -outdrv: - platform_driver_unregister(&coretemp_driver); outzone: + while (i--) + coretemp_device_remove(i); kfree(zone_devices); return err; } @@ -826,8 +815,11 @@ module_init(coretemp_init) static void __exit coretemp_exit(void) { + int i; + cpuhp_remove_state(coretemp_hp_online); - platform_driver_unregister(&coretemp_driver); + for (i = 0; i < max_zones; i++) + coretemp_device_remove(i); kfree(zone_devices); } module_exit(coretemp_exit) diff --git a/drivers/hwmon/emc2305.c b/drivers/hwmon/emc2305.c index 6ad055e5868e..f65467fbd86c 100644 --- a/drivers/hwmon/emc2305.c +++ b/drivers/hwmon/emc2305.c @@ -59,10 +59,11 @@ static const struct i2c_device_id emc2305_ids[] = { MODULE_DEVICE_TABLE(i2c, emc2305_ids); /** - * @cdev: cooling device; - * @curr_state: cooling current state; - * @last_hwmon_state: last cooling state updated by hwmon subsystem; - * @last_thermal_state: last cooling state updated by thermal subsystem; + * struct emc2305_cdev_data - device-specific cooling device state + * @cdev: cooling device + * @cur_state: cooling current state + * @last_hwmon_state: last cooling state updated by hwmon subsystem + * @last_thermal_state: last cooling state updated by thermal subsystem * * The 'last_hwmon_state' and 'last_thermal_state' fields are provided to support fan low limit * speed feature. The purpose of this feature is to provides ability to limit fan speed @@ -86,13 +87,14 @@ struct emc2305_cdev_data { }; /** - * @client: i2c client; - * @hwmon_dev: hwmon device; - * @max_state: maximum cooling state of the cooling device; - * @pwm_num: number of PWM channels; - * @pwm_separate: separate PWM settings for every channel; - * @pwm_min: array of minimum PWM per channel; - * @cdev_data: array of cooling devices data; + * struct emc2305_data - device-specific data + * @client: i2c client + * @hwmon_dev: hwmon device + * @max_state: maximum cooling state of the cooling device + * @pwm_num: number of PWM channels + * @pwm_separate: separate PWM settings for every channel + * @pwm_min: array of minimum PWM per channel + * @cdev_data: array of cooling devices data */ struct emc2305_data { struct i2c_client *client; diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c index f5b8e724a8ca..25afd9167a34 100644 --- a/drivers/hwmon/ftsteutates.c +++ b/drivers/hwmon/ftsteutates.c @@ -6,17 +6,14 @@ * Thilo Cestonaro <thilo.cestonaro@ts.fujitsu.com> */ #include <linux/err.h> -#include <linux/fs.h> #include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/jiffies.h> +#include <linux/math.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/slab.h> -#include <linux/sysfs.h> -#include <linux/uaccess.h> #include <linux/watchdog.h> #define FTS_DEVICE_ID_REG 0x0000 @@ -48,6 +45,8 @@ #define FTS_NO_TEMP_SENSORS 0x10 #define FTS_NO_VOLT_SENSORS 0x04 +#define FTS_FAN_SOURCE_INVALID 0xff + static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; static const struct i2c_device_id fts_id[] = { @@ -187,7 +186,7 @@ static int fts_update_device(struct fts_data *data) data->fan_source[i] = err; } else { data->fan_input[i] = 0; - data->fan_source[i] = 0; + data->fan_source[i] = FTS_FAN_SOURCE_INVALID; } } @@ -336,373 +335,243 @@ static int fts_watchdog_init(struct fts_data *data) /* max timeout 255 minutes. */ data->wdd.max_hw_heartbeat_ms = 0xFF * 60 * MSEC_PER_SEC; - return watchdog_register_device(&data->wdd); -} - -/*****************************************************************************/ -/* SysFS handler functions */ -/*****************************************************************************/ -static ssize_t in_value_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct fts_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(devattr)->index; - int err; - - err = fts_update_device(data); - if (err < 0) - return err; - - return sprintf(buf, "%u\n", data->volt[index]); + return devm_watchdog_register_device(&data->client->dev, &data->wdd); } -static ssize_t temp_value_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static umode_t fts_is_visible(const void *devdata, enum hwmon_sensor_types type, u32 attr, + int channel) { - struct fts_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(devattr)->index; - int err; - - err = fts_update_device(data); - if (err < 0) - return err; - - return sprintf(buf, "%u\n", data->temp_input[index]); -} - -static ssize_t temp_fault_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct fts_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(devattr)->index; - int err; - - err = fts_update_device(data); - if (err < 0) - return err; - - /* 00h Temperature = Sensor Error */ - return sprintf(buf, "%d\n", data->temp_input[index] == 0); -} - -static ssize_t temp_alarm_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct fts_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(devattr)->index; - int err; - - err = fts_update_device(data); - if (err < 0) - return err; + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_fault: + return 0444; + case hwmon_temp_alarm: + return 0644; + default: + break; + } + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_fault: + return 0444; + case hwmon_fan_alarm: + return 0644; + default: + break; + } + break; + case hwmon_pwm: + case hwmon_in: + return 0444; + default: + break; + } - return sprintf(buf, "%u\n", !!(data->temp_alarm & BIT(index))); + return 0; } -static ssize_t -temp_alarm_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) +static int fts_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, + long *val) { struct fts_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(devattr)->index; - long ret; + int ret = fts_update_device(data); - ret = fts_update_device(data); if (ret < 0) return ret; - if (kstrtoul(buf, 10, &ret) || ret != 0) - return -EINVAL; - - mutex_lock(&data->update_lock); - ret = fts_read_byte(data->client, FTS_REG_TEMP_CONTROL(index)); - if (ret < 0) - goto error; - - ret = fts_write_byte(data->client, FTS_REG_TEMP_CONTROL(index), - ret | 0x1); - if (ret < 0) - goto error; + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + *val = (data->temp_input[channel] - 64) * 1000; - data->valid = false; - ret = count; -error: - mutex_unlock(&data->update_lock); - return ret; -} - -static ssize_t fan_value_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct fts_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(devattr)->index; - int err; - - err = fts_update_device(data); - if (err < 0) - return err; + return 0; + case hwmon_temp_alarm: + *val = !!(data->temp_alarm & BIT(channel)); - return sprintf(buf, "%u\n", data->fan_input[index]); -} + return 0; + case hwmon_temp_fault: + /* 00h Temperature = Sensor Error */; + *val = (data->temp_input[channel] == 0); -static ssize_t fan_source_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct fts_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(devattr)->index; - int err; + return 0; + default: + break; + } + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + *val = data->fan_input[channel] * 60; - err = fts_update_device(data); - if (err < 0) - return err; + return 0; + case hwmon_fan_alarm: + *val = !!(data->fan_alarm & BIT(channel)); - return sprintf(buf, "%u\n", data->fan_source[index]); -} + return 0; + case hwmon_fan_fault: + *val = !(data->fan_present & BIT(channel)); -static ssize_t fan_alarm_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct fts_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(devattr)->index; - int err; + return 0; + default: + break; + } + break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_auto_channels_temp: + if (data->fan_source[channel] == FTS_FAN_SOURCE_INVALID) + *val = 0; + else + *val = BIT(data->fan_source[channel]); + + return 0; + default: + break; + } + break; + case hwmon_in: + switch (attr) { + case hwmon_in_input: + *val = DIV_ROUND_CLOSEST(data->volt[channel] * 3300, 255); - err = fts_update_device(data); - if (err < 0) - return err; + return 0; + default: + break; + } + break; + default: + break; + } - return sprintf(buf, "%d\n", !!(data->fan_alarm & BIT(index))); + return -EOPNOTSUPP; } -static ssize_t -fan_alarm_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) +static int fts_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, + long val) { struct fts_data *data = dev_get_drvdata(dev); - int index = to_sensor_dev_attr(devattr)->index; - long ret; + int ret = fts_update_device(data); - ret = fts_update_device(data); if (ret < 0) return ret; - if (kstrtoul(buf, 10, &ret) || ret != 0) - return -EINVAL; - - mutex_lock(&data->update_lock); - ret = fts_read_byte(data->client, FTS_REG_FAN_CONTROL(index)); - if (ret < 0) - goto error; - - ret = fts_write_byte(data->client, FTS_REG_FAN_CONTROL(index), - ret | 0x1); - if (ret < 0) - goto error; + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_alarm: + if (val) + return -EINVAL; + + mutex_lock(&data->update_lock); + ret = fts_read_byte(data->client, FTS_REG_TEMP_CONTROL(channel)); + if (ret >= 0) + ret = fts_write_byte(data->client, FTS_REG_TEMP_CONTROL(channel), + ret | 0x1); + if (ret >= 0) + data->valid = false; + + mutex_unlock(&data->update_lock); + if (ret < 0) + return ret; + + return 0; + default: + break; + } + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_alarm: + if (val) + return -EINVAL; + + mutex_lock(&data->update_lock); + ret = fts_read_byte(data->client, FTS_REG_FAN_CONTROL(channel)); + if (ret >= 0) + ret = fts_write_byte(data->client, FTS_REG_FAN_CONTROL(channel), + ret | 0x1); + if (ret >= 0) + data->valid = false; + + mutex_unlock(&data->update_lock); + if (ret < 0) + return ret; + + return 0; + default: + break; + } + break; + default: + break; + } - data->valid = false; - ret = count; -error: - mutex_unlock(&data->update_lock); - return ret; + return -EOPNOTSUPP; } -/*****************************************************************************/ -/* SysFS structs */ -/*****************************************************************************/ - -/* Temperature sensors */ -static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_value, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_input, temp_value, 1); -static SENSOR_DEVICE_ATTR_RO(temp3_input, temp_value, 2); -static SENSOR_DEVICE_ATTR_RO(temp4_input, temp_value, 3); -static SENSOR_DEVICE_ATTR_RO(temp5_input, temp_value, 4); -static SENSOR_DEVICE_ATTR_RO(temp6_input, temp_value, 5); -static SENSOR_DEVICE_ATTR_RO(temp7_input, temp_value, 6); -static SENSOR_DEVICE_ATTR_RO(temp8_input, temp_value, 7); -static SENSOR_DEVICE_ATTR_RO(temp9_input, temp_value, 8); -static SENSOR_DEVICE_ATTR_RO(temp10_input, temp_value, 9); -static SENSOR_DEVICE_ATTR_RO(temp11_input, temp_value, 10); -static SENSOR_DEVICE_ATTR_RO(temp12_input, temp_value, 11); -static SENSOR_DEVICE_ATTR_RO(temp13_input, temp_value, 12); -static SENSOR_DEVICE_ATTR_RO(temp14_input, temp_value, 13); -static SENSOR_DEVICE_ATTR_RO(temp15_input, temp_value, 14); -static SENSOR_DEVICE_ATTR_RO(temp16_input, temp_value, 15); - -static SENSOR_DEVICE_ATTR_RO(temp1_fault, temp_fault, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_fault, temp_fault, 1); -static SENSOR_DEVICE_ATTR_RO(temp3_fault, temp_fault, 2); -static SENSOR_DEVICE_ATTR_RO(temp4_fault, temp_fault, 3); -static SENSOR_DEVICE_ATTR_RO(temp5_fault, temp_fault, 4); -static SENSOR_DEVICE_ATTR_RO(temp6_fault, temp_fault, 5); -static SENSOR_DEVICE_ATTR_RO(temp7_fault, temp_fault, 6); -static SENSOR_DEVICE_ATTR_RO(temp8_fault, temp_fault, 7); -static SENSOR_DEVICE_ATTR_RO(temp9_fault, temp_fault, 8); -static SENSOR_DEVICE_ATTR_RO(temp10_fault, temp_fault, 9); -static SENSOR_DEVICE_ATTR_RO(temp11_fault, temp_fault, 10); -static SENSOR_DEVICE_ATTR_RO(temp12_fault, temp_fault, 11); -static SENSOR_DEVICE_ATTR_RO(temp13_fault, temp_fault, 12); -static SENSOR_DEVICE_ATTR_RO(temp14_fault, temp_fault, 13); -static SENSOR_DEVICE_ATTR_RO(temp15_fault, temp_fault, 14); -static SENSOR_DEVICE_ATTR_RO(temp16_fault, temp_fault, 15); - -static SENSOR_DEVICE_ATTR_RW(temp1_alarm, temp_alarm, 0); -static SENSOR_DEVICE_ATTR_RW(temp2_alarm, temp_alarm, 1); -static SENSOR_DEVICE_ATTR_RW(temp3_alarm, temp_alarm, 2); -static SENSOR_DEVICE_ATTR_RW(temp4_alarm, temp_alarm, 3); -static SENSOR_DEVICE_ATTR_RW(temp5_alarm, temp_alarm, 4); -static SENSOR_DEVICE_ATTR_RW(temp6_alarm, temp_alarm, 5); -static SENSOR_DEVICE_ATTR_RW(temp7_alarm, temp_alarm, 6); -static SENSOR_DEVICE_ATTR_RW(temp8_alarm, temp_alarm, 7); -static SENSOR_DEVICE_ATTR_RW(temp9_alarm, temp_alarm, 8); -static SENSOR_DEVICE_ATTR_RW(temp10_alarm, temp_alarm, 9); -static SENSOR_DEVICE_ATTR_RW(temp11_alarm, temp_alarm, 10); -static SENSOR_DEVICE_ATTR_RW(temp12_alarm, temp_alarm, 11); -static SENSOR_DEVICE_ATTR_RW(temp13_alarm, temp_alarm, 12); -static SENSOR_DEVICE_ATTR_RW(temp14_alarm, temp_alarm, 13); -static SENSOR_DEVICE_ATTR_RW(temp15_alarm, temp_alarm, 14); -static SENSOR_DEVICE_ATTR_RW(temp16_alarm, temp_alarm, 15); - -static struct attribute *fts_temp_attrs[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp3_input.dev_attr.attr, - &sensor_dev_attr_temp4_input.dev_attr.attr, - &sensor_dev_attr_temp5_input.dev_attr.attr, - &sensor_dev_attr_temp6_input.dev_attr.attr, - &sensor_dev_attr_temp7_input.dev_attr.attr, - &sensor_dev_attr_temp8_input.dev_attr.attr, - &sensor_dev_attr_temp9_input.dev_attr.attr, - &sensor_dev_attr_temp10_input.dev_attr.attr, - &sensor_dev_attr_temp11_input.dev_attr.attr, - &sensor_dev_attr_temp12_input.dev_attr.attr, - &sensor_dev_attr_temp13_input.dev_attr.attr, - &sensor_dev_attr_temp14_input.dev_attr.attr, - &sensor_dev_attr_temp15_input.dev_attr.attr, - &sensor_dev_attr_temp16_input.dev_attr.attr, - - &sensor_dev_attr_temp1_fault.dev_attr.attr, - &sensor_dev_attr_temp2_fault.dev_attr.attr, - &sensor_dev_attr_temp3_fault.dev_attr.attr, - &sensor_dev_attr_temp4_fault.dev_attr.attr, - &sensor_dev_attr_temp5_fault.dev_attr.attr, - &sensor_dev_attr_temp6_fault.dev_attr.attr, - &sensor_dev_attr_temp7_fault.dev_attr.attr, - &sensor_dev_attr_temp8_fault.dev_attr.attr, - &sensor_dev_attr_temp9_fault.dev_attr.attr, - &sensor_dev_attr_temp10_fault.dev_attr.attr, - &sensor_dev_attr_temp11_fault.dev_attr.attr, - &sensor_dev_attr_temp12_fault.dev_attr.attr, - &sensor_dev_attr_temp13_fault.dev_attr.attr, - &sensor_dev_attr_temp14_fault.dev_attr.attr, - &sensor_dev_attr_temp15_fault.dev_attr.attr, - &sensor_dev_attr_temp16_fault.dev_attr.attr, - - &sensor_dev_attr_temp1_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_alarm.dev_attr.attr, - &sensor_dev_attr_temp4_alarm.dev_attr.attr, - &sensor_dev_attr_temp5_alarm.dev_attr.attr, - &sensor_dev_attr_temp6_alarm.dev_attr.attr, - &sensor_dev_attr_temp7_alarm.dev_attr.attr, - &sensor_dev_attr_temp8_alarm.dev_attr.attr, - &sensor_dev_attr_temp9_alarm.dev_attr.attr, - &sensor_dev_attr_temp10_alarm.dev_attr.attr, - &sensor_dev_attr_temp11_alarm.dev_attr.attr, - &sensor_dev_attr_temp12_alarm.dev_attr.attr, - &sensor_dev_attr_temp13_alarm.dev_attr.attr, - &sensor_dev_attr_temp14_alarm.dev_attr.attr, - &sensor_dev_attr_temp15_alarm.dev_attr.attr, - &sensor_dev_attr_temp16_alarm.dev_attr.attr, - NULL -}; - -/* Fans */ -static SENSOR_DEVICE_ATTR_RO(fan1_input, fan_value, 0); -static SENSOR_DEVICE_ATTR_RO(fan2_input, fan_value, 1); -static SENSOR_DEVICE_ATTR_RO(fan3_input, fan_value, 2); -static SENSOR_DEVICE_ATTR_RO(fan4_input, fan_value, 3); -static SENSOR_DEVICE_ATTR_RO(fan5_input, fan_value, 4); -static SENSOR_DEVICE_ATTR_RO(fan6_input, fan_value, 5); -static SENSOR_DEVICE_ATTR_RO(fan7_input, fan_value, 6); -static SENSOR_DEVICE_ATTR_RO(fan8_input, fan_value, 7); - -static SENSOR_DEVICE_ATTR_RO(fan1_source, fan_source, 0); -static SENSOR_DEVICE_ATTR_RO(fan2_source, fan_source, 1); -static SENSOR_DEVICE_ATTR_RO(fan3_source, fan_source, 2); -static SENSOR_DEVICE_ATTR_RO(fan4_source, fan_source, 3); -static SENSOR_DEVICE_ATTR_RO(fan5_source, fan_source, 4); -static SENSOR_DEVICE_ATTR_RO(fan6_source, fan_source, 5); -static SENSOR_DEVICE_ATTR_RO(fan7_source, fan_source, 6); -static SENSOR_DEVICE_ATTR_RO(fan8_source, fan_source, 7); - -static SENSOR_DEVICE_ATTR_RW(fan1_alarm, fan_alarm, 0); -static SENSOR_DEVICE_ATTR_RW(fan2_alarm, fan_alarm, 1); -static SENSOR_DEVICE_ATTR_RW(fan3_alarm, fan_alarm, 2); -static SENSOR_DEVICE_ATTR_RW(fan4_alarm, fan_alarm, 3); -static SENSOR_DEVICE_ATTR_RW(fan5_alarm, fan_alarm, 4); -static SENSOR_DEVICE_ATTR_RW(fan6_alarm, fan_alarm, 5); -static SENSOR_DEVICE_ATTR_RW(fan7_alarm, fan_alarm, 6); -static SENSOR_DEVICE_ATTR_RW(fan8_alarm, fan_alarm, 7); - -static struct attribute *fts_fan_attrs[] = { - &sensor_dev_attr_fan1_input.dev_attr.attr, - &sensor_dev_attr_fan2_input.dev_attr.attr, - &sensor_dev_attr_fan3_input.dev_attr.attr, - &sensor_dev_attr_fan4_input.dev_attr.attr, - &sensor_dev_attr_fan5_input.dev_attr.attr, - &sensor_dev_attr_fan6_input.dev_attr.attr, - &sensor_dev_attr_fan7_input.dev_attr.attr, - &sensor_dev_attr_fan8_input.dev_attr.attr, - - &sensor_dev_attr_fan1_source.dev_attr.attr, - &sensor_dev_attr_fan2_source.dev_attr.attr, - &sensor_dev_attr_fan3_source.dev_attr.attr, - &sensor_dev_attr_fan4_source.dev_attr.attr, - &sensor_dev_attr_fan5_source.dev_attr.attr, - &sensor_dev_attr_fan6_source.dev_attr.attr, - &sensor_dev_attr_fan7_source.dev_attr.attr, - &sensor_dev_attr_fan8_source.dev_attr.attr, - - &sensor_dev_attr_fan1_alarm.dev_attr.attr, - &sensor_dev_attr_fan2_alarm.dev_attr.attr, - &sensor_dev_attr_fan3_alarm.dev_attr.attr, - &sensor_dev_attr_fan4_alarm.dev_attr.attr, - &sensor_dev_attr_fan5_alarm.dev_attr.attr, - &sensor_dev_attr_fan6_alarm.dev_attr.attr, - &sensor_dev_attr_fan7_alarm.dev_attr.attr, - &sensor_dev_attr_fan8_alarm.dev_attr.attr, - NULL +static const struct hwmon_ops fts_ops = { + .is_visible = fts_is_visible, + .read = fts_read, + .write = fts_write, }; -/* Voltages */ -static SENSOR_DEVICE_ATTR_RO(in1_input, in_value, 0); -static SENSOR_DEVICE_ATTR_RO(in2_input, in_value, 1); -static SENSOR_DEVICE_ATTR_RO(in3_input, in_value, 2); -static SENSOR_DEVICE_ATTR_RO(in4_input, in_value, 3); -static struct attribute *fts_voltage_attrs[] = { - &sensor_dev_attr_in1_input.dev_attr.attr, - &sensor_dev_attr_in2_input.dev_attr.attr, - &sensor_dev_attr_in3_input.dev_attr.attr, - &sensor_dev_attr_in4_input.dev_attr.attr, +static const struct hwmon_channel_info *fts_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT + ), + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT + ), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_AUTO_CHANNELS_TEMP, + HWMON_PWM_AUTO_CHANNELS_TEMP, + HWMON_PWM_AUTO_CHANNELS_TEMP, + HWMON_PWM_AUTO_CHANNELS_TEMP, + HWMON_PWM_AUTO_CHANNELS_TEMP, + HWMON_PWM_AUTO_CHANNELS_TEMP, + HWMON_PWM_AUTO_CHANNELS_TEMP, + HWMON_PWM_AUTO_CHANNELS_TEMP + ), + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT + ), NULL }; -static const struct attribute_group fts_voltage_attr_group = { - .attrs = fts_voltage_attrs -}; - -static const struct attribute_group fts_temp_attr_group = { - .attrs = fts_temp_attrs -}; - -static const struct attribute_group fts_fan_attr_group = { - .attrs = fts_fan_attrs -}; - -static const struct attribute_group *fts_attr_groups[] = { - &fts_voltage_attr_group, - &fts_temp_attr_group, - &fts_fan_attr_group, - NULL +static const struct hwmon_chip_info fts_chip_info = { + .ops = &fts_ops, + .info = fts_info, }; /*****************************************************************************/ @@ -744,13 +613,6 @@ static int fts_detect(struct i2c_client *client, return 0; } -static void fts_remove(struct i2c_client *client) -{ - struct fts_data *data = dev_get_drvdata(&client->dev); - - watchdog_unregister_device(&data->wdd); -} - static int fts_probe(struct i2c_client *client) { u8 revision; @@ -793,10 +655,8 @@ static int fts_probe(struct i2c_client *client) return err; revision = err; - hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, - "ftsteutates", - data, - fts_attr_groups); + hwmon_dev = devm_hwmon_device_register_with_info(&client->dev, "ftsteutates", data, + &fts_chip_info, NULL); if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); @@ -819,7 +679,6 @@ static struct i2c_driver fts_driver = { }, .id_table = fts_id, .probe_new = fts_probe, - .remove = fts_remove, .detect = fts_detect, .address_list = normal_i2c, }; diff --git a/drivers/hwmon/gxp-fan-ctrl.c b/drivers/hwmon/gxp-fan-ctrl.c new file mode 100644 index 000000000000..0014b8b0fd41 --- /dev/null +++ b/drivers/hwmon/gxp-fan-ctrl.c @@ -0,0 +1,253 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (C) 2022 Hewlett-Packard Enterprise Development Company, L.P. */ + +#include <linux/bits.h> +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +#define OFS_FAN_INST 0 /* Is 0 because plreg base will be set at INST */ +#define OFS_FAN_FAIL 2 /* Is 2 bytes after base */ +#define OFS_SEVSTAT 0 /* Is 0 because fn2 base will be set at SEVSTAT */ +#define POWER_BIT 24 + +struct gxp_fan_ctrl_drvdata { + void __iomem *base; + void __iomem *plreg; + void __iomem *fn2; +}; + +static bool fan_installed(struct device *dev, int fan) +{ + struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev); + u8 val; + + val = readb(drvdata->plreg + OFS_FAN_INST); + + return !!(val & BIT(fan)); +} + +static long fan_failed(struct device *dev, int fan) +{ + struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev); + u8 val; + + val = readb(drvdata->plreg + OFS_FAN_FAIL); + + return !!(val & BIT(fan)); +} + +static long fan_enabled(struct device *dev, int fan) +{ + struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev); + u32 val; + + /* + * Check the power status as if the platform is off the value + * reported for the PWM will be incorrect. Report fan as + * disabled. + */ + val = readl(drvdata->fn2 + OFS_SEVSTAT); + + return !!((val & BIT(POWER_BIT)) && fan_installed(dev, fan)); +} + +static int gxp_pwm_write(struct device *dev, u32 attr, int channel, long val) +{ + struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_pwm_input: + if (val > 255 || val < 0) + return -EINVAL; + writeb(val, drvdata->base + channel); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int gxp_fan_ctrl_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_pwm: + return gxp_pwm_write(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static int gxp_fan_read(struct device *dev, u32 attr, int channel, long *val) +{ + switch (attr) { + case hwmon_fan_enable: + *val = fan_enabled(dev, channel); + return 0; + case hwmon_fan_fault: + *val = fan_failed(dev, channel); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int gxp_pwm_read(struct device *dev, u32 attr, int channel, long *val) +{ + struct gxp_fan_ctrl_drvdata *drvdata = dev_get_drvdata(dev); + u32 reg; + + /* + * Check the power status of the platform. If the platform is off + * the value reported for the PWM will be incorrect. In this case + * report a PWM of zero. + */ + + reg = readl(drvdata->fn2 + OFS_SEVSTAT); + + if (reg & BIT(POWER_BIT)) + *val = fan_installed(dev, channel) ? readb(drvdata->base + channel) : 0; + else + *val = 0; + + return 0; +} + +static int gxp_fan_ctrl_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_fan: + return gxp_fan_read(dev, attr, channel, val); + case hwmon_pwm: + return gxp_pwm_read(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static umode_t gxp_fan_ctrl_is_visible(const void *_data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + umode_t mode = 0; + + switch (type) { + case hwmon_fan: + switch (attr) { + case hwmon_fan_enable: + case hwmon_fan_fault: + mode = 0444; + break; + default: + break; + } + break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + mode = 0644; + break; + default: + break; + } + break; + default: + break; + } + + return mode; +} + +static const struct hwmon_ops gxp_fan_ctrl_ops = { + .is_visible = gxp_fan_ctrl_is_visible, + .read = gxp_fan_ctrl_read, + .write = gxp_fan_ctrl_write, +}; + +static const struct hwmon_channel_info *gxp_fan_ctrl_info[] = { + HWMON_CHANNEL_INFO(fan, + HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_FAULT | HWMON_F_ENABLE), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT), + NULL +}; + +static const struct hwmon_chip_info gxp_fan_ctrl_chip_info = { + .ops = &gxp_fan_ctrl_ops, + .info = gxp_fan_ctrl_info, + +}; + +static int gxp_fan_ctrl_probe(struct platform_device *pdev) +{ + struct gxp_fan_ctrl_drvdata *drvdata; + struct device *dev = &pdev->dev; + struct device *hwmon_dev; + + drvdata = devm_kzalloc(dev, sizeof(struct gxp_fan_ctrl_drvdata), + GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(drvdata->base)) + return dev_err_probe(dev, PTR_ERR(drvdata->base), + "failed to map base\n"); + + drvdata->plreg = devm_platform_ioremap_resource_byname(pdev, + "pl"); + if (IS_ERR(drvdata->plreg)) + return dev_err_probe(dev, PTR_ERR(drvdata->plreg), + "failed to map plreg\n"); + + drvdata->fn2 = devm_platform_ioremap_resource_byname(pdev, + "fn2"); + if (IS_ERR(drvdata->fn2)) + return dev_err_probe(dev, PTR_ERR(drvdata->fn2), + "failed to map fn2\n"); + + hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, + "hpe_gxp_fan_ctrl", + drvdata, + &gxp_fan_ctrl_chip_info, + NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct of_device_id gxp_fan_ctrl_of_match[] = { + { .compatible = "hpe,gxp-fan-ctrl", }, + {}, +}; +MODULE_DEVICE_TABLE(of, gxp_fan_ctrl_of_match); + +static struct platform_driver gxp_fan_ctrl_driver = { + .probe = gxp_fan_ctrl_probe, + .driver = { + .name = "gxp-fan-ctrl", + .of_match_table = gxp_fan_ctrl_of_match, + }, +}; +module_platform_driver(gxp_fan_ctrl_driver); + +MODULE_AUTHOR("Nick Hawkins <nick.hawkins@hpe.com>"); +MODULE_DESCRIPTION("HPE GXP fan controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/hih6130.c b/drivers/hwmon/hih6130.c index d9394e19fea8..3a7582824f94 100644 --- a/drivers/hwmon/hih6130.c +++ b/drivers/hwmon/hih6130.c @@ -150,7 +150,7 @@ out: } /** - * hih6130_show_temperature() - show temperature measurement value in sysfs + * hih6130_temperature_show() - show temperature measurement value in sysfs * @dev: device * @attr: device attribute * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to @@ -172,7 +172,7 @@ static ssize_t hih6130_temperature_show(struct device *dev, } /** - * hih6130_show_humidity() - show humidity measurement value in sysfs + * hih6130_humidity_show() - show humidity measurement value in sysfs * @dev: device * @attr: device attribute * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c index 1837cccd993c..db066b368918 100644 --- a/drivers/hwmon/ibmpex.c +++ b/drivers/hwmon/ibmpex.c @@ -546,7 +546,7 @@ static void ibmpex_bmc_gone(int iface) static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) { - struct ibmpex_bmc_data *data = (struct ibmpex_bmc_data *)user_msg_data; + struct ibmpex_bmc_data *data = user_msg_data; if (msg->msgid != data->tx_msgid) { dev_err(data->bmc_device, diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index 3aa40893fc09..4c8a80847891 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -77,9 +77,11 @@ static int iio_hwmon_probe(struct platform_device *pdev) channels = devm_iio_channel_get_all(dev); if (IS_ERR(channels)) { - if (PTR_ERR(channels) == -ENODEV) - return -EPROBE_DEFER; - return PTR_ERR(channels); + ret = PTR_ERR(channels); + if (ret == -ENODEV) + ret = -EPROBE_DEFER; + return dev_err_probe(dev, ret, + "Failed to get channels\n"); } st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); diff --git a/drivers/hwmon/intel-m10-bmc-hwmon.c b/drivers/hwmon/intel-m10-bmc-hwmon.c index 6e82f7200d1c..2f0323c14bab 100644 --- a/drivers/hwmon/intel-m10-bmc-hwmon.c +++ b/drivers/hwmon/intel-m10-bmc-hwmon.c @@ -340,6 +340,231 @@ static const struct m10bmc_hwmon_board_data n5010bmc_hwmon_bdata = { .hinfo = n5010bmc_hinfo, }; +static const struct m10bmc_sdata n6000bmc_temp_tbl[] = { + { 0x444, 0x448, 0x44c, 0x0, 0x0, 500, "FPGA E-TILE Temperature #1" }, + { 0x450, 0x454, 0x458, 0x0, 0x0, 500, "FPGA E-TILE Temperature #2" }, + { 0x45c, 0x460, 0x464, 0x0, 0x0, 500, "FPGA E-TILE Temperature #3" }, + { 0x468, 0x46c, 0x470, 0x0, 0x0, 500, "FPGA E-TILE Temperature #4" }, + { 0x474, 0x478, 0x47c, 0x0, 0x0, 500, "FPGA P-TILE Temperature" }, + { 0x484, 0x488, 0x48c, 0x0, 0x0, 500, "FPGA FABRIC Digital Temperature #1" }, + { 0x490, 0x494, 0x498, 0x0, 0x0, 500, "FPGA FABRIC Digital Temperature #2" }, + { 0x49c, 0x4a0, 0x4a4, 0x0, 0x0, 500, "FPGA FABRIC Digital Temperature #3" }, + { 0x4a8, 0x4ac, 0x4b0, 0x0, 0x0, 500, "FPGA FABRIC Digital Temperature #4" }, + { 0x4b4, 0x4b8, 0x4bc, 0x0, 0x0, 500, "FPGA FABRIC Digital Temperature #5" }, + { 0x4c0, 0x4c4, 0x4c8, 0x0, 0x0, 500, "FPGA FABRIC Remote Digital Temperature #1" }, + { 0x4cc, 0x4d0, 0x4d4, 0x0, 0x0, 500, "FPGA FABRIC Remote Digital Temperature #2" }, + { 0x4d8, 0x4dc, 0x4e0, 0x0, 0x0, 500, "FPGA FABRIC Remote Digital Temperature #3" }, + { 0x4e4, 0x4e8, 0x4ec, 0x0, 0x0, 500, "FPGA FABRIC Remote Digital Temperature #4" }, + { 0x4f0, 0x4f4, 0x4f8, 0x52c, 0x0, 500, "Board Top Near FPGA Temperature" }, + { 0x4fc, 0x500, 0x504, 0x52c, 0x0, 500, "Board Bottom Near CVL Temperature" }, + { 0x508, 0x50c, 0x510, 0x52c, 0x0, 500, "Board Top East Near VRs Temperature" }, + { 0x514, 0x518, 0x51c, 0x52c, 0x0, 500, "Columbiaville Die Temperature" }, + { 0x520, 0x524, 0x528, 0x52c, 0x0, 500, "Board Rear Side Temperature" }, + { 0x530, 0x534, 0x538, 0x52c, 0x0, 500, "Board Front Side Temperature" }, + { 0x53c, 0x540, 0x544, 0x0, 0x0, 500, "QSFP1 Case Temperature" }, + { 0x548, 0x54c, 0x550, 0x0, 0x0, 500, "QSFP2 Case Temperature" }, + { 0x554, 0x0, 0x0, 0x0, 0x0, 500, "FPGA Core Voltage Phase 0 VR Temperature" }, + { 0x560, 0x0, 0x0, 0x0, 0x0, 500, "FPGA Core Voltage Phase 1 VR Temperature" }, + { 0x56c, 0x0, 0x0, 0x0, 0x0, 500, "FPGA Core Voltage Phase 2 VR Temperature" }, + { 0x578, 0x0, 0x0, 0x0, 0x0, 500, "FPGA Core Voltage VR Controller Temperature" }, + { 0x584, 0x0, 0x0, 0x0, 0x0, 500, "FPGA VCCH VR Temperature" }, + { 0x590, 0x0, 0x0, 0x0, 0x0, 500, "FPGA VCC_1V2 VR Temperature" }, + { 0x59c, 0x0, 0x0, 0x0, 0x0, 500, "FPGA VCCH, VCC_1V2 VR Controller Temperature" }, + { 0x5a8, 0x0, 0x0, 0x0, 0x0, 500, "3V3 VR Temperature" }, + { 0x5b4, 0x0, 0x0, 0x0, 0x0, 500, "CVL Core Voltage VR Temperature" }, + { 0x5c4, 0x5c8, 0x5cc, 0x5c0, 0x0, 500, "FPGA P-Tile Temperature [Remote]" }, + { 0x5d0, 0x5d4, 0x5d8, 0x5c0, 0x0, 500, "FPGA E-Tile Temperature [Remote]" }, + { 0x5dc, 0x5e0, 0x5e4, 0x5c0, 0x0, 500, "FPGA SDM Temperature [Remote]" }, + { 0x5e8, 0x5ec, 0x5f0, 0x5c0, 0x0, 500, "FPGA Corner Temperature [Remote]" }, +}; + +static const struct m10bmc_sdata n6000bmc_in_tbl[] = { + { 0x5f4, 0x0, 0x0, 0x0, 0x0, 1, "Inlet 12V PCIe Rail Voltage" }, + { 0x60c, 0x0, 0x0, 0x0, 0x0, 1, "Inlet 12V Aux Rail Voltage" }, + { 0x624, 0x0, 0x0, 0x0, 0x0, 1, "Inlet 3V3 PCIe Rail Voltage" }, + { 0x63c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA Core Voltage Rail Voltage" }, + { 0x644, 0x0, 0x0, 0x0, 0x0, 1, "FPGA VCCH Rail Voltage" }, + { 0x64c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA VCC_1V2 Rail Voltage" }, + { 0x654, 0x0, 0x0, 0x0, 0x0, 1, "FPGA VCCH_GXER_1V1, VCCA_1V8 Voltage" }, + { 0x664, 0x0, 0x0, 0x0, 0x0, 1, "FPGA VCCIO_1V2 Voltage" }, + { 0x674, 0x0, 0x0, 0x0, 0x0, 1, "CVL Non Core Rails Inlet Voltage" }, + { 0x684, 0x0, 0x0, 0x0, 0x0, 1, "MAX10 & Board CLK PWR 3V3 Inlet Voltage" }, + { 0x694, 0x0, 0x0, 0x0, 0x0, 1, "CVL Core Voltage Rail Voltage" }, + { 0x6ac, 0x0, 0x0, 0x0, 0x0, 1, "Board 3V3 VR Voltage" }, + { 0x6b4, 0x0, 0x0, 0x0, 0x0, 1, "QSFP 3V3 Rail Voltage" }, + { 0x6c4, 0x0, 0x0, 0x0, 0x0, 1, "QSFP (Primary) Supply Rail Voltage" }, + { 0x6c8, 0x0, 0x0, 0x0, 0x0, 1, "QSFP (Secondary) Supply Rail Voltage" }, + { 0x6cc, 0x0, 0x0, 0x0, 0x0, 1, "VCCCLK_GXER_2V5 Voltage" }, + { 0x6d0, 0x0, 0x0, 0x0, 0x0, 1, "AVDDH_1V1_CVL Voltage" }, + { 0x6d4, 0x0, 0x0, 0x0, 0x0, 1, "VDDH_1V8_CVL Voltage" }, + { 0x6d8, 0x0, 0x0, 0x0, 0x0, 1, "VCCA_PLL Voltage" }, + { 0x6e0, 0x0, 0x0, 0x0, 0x0, 1, "VCCRT_GXER_0V9 Voltage" }, + { 0x6e8, 0x0, 0x0, 0x0, 0x0, 1, "VCCRT_GXPL_0V9 Voltage" }, + { 0x6f0, 0x0, 0x0, 0x0, 0x0, 1, "VCCH_GXPL_1V8 Voltage" }, + { 0x6f4, 0x0, 0x0, 0x0, 0x0, 1, "VCCPT_1V8 Voltage" }, + { 0x6fc, 0x0, 0x0, 0x0, 0x0, 1, "VCC_3V3_M10 Voltage" }, + { 0x700, 0x0, 0x0, 0x0, 0x0, 1, "VCC_1V8_M10 Voltage" }, + { 0x704, 0x0, 0x0, 0x0, 0x0, 1, "VCC_1V2_EMIF1_2_3 Voltage" }, + { 0x70c, 0x0, 0x0, 0x0, 0x0, 1, "VCC_1V2_EMIF4_5 Voltage" }, + { 0x714, 0x0, 0x0, 0x0, 0x0, 1, "VCCA_1V8 Voltage" }, + { 0x718, 0x0, 0x0, 0x0, 0x0, 1, "VCCH_GXER_1V1 Voltage" }, + { 0x71c, 0x0, 0x0, 0x0, 0x0, 1, "AVDD_ETH_0V9_CVL Voltage" }, + { 0x720, 0x0, 0x0, 0x0, 0x0, 1, "AVDD_PCIE_0V9_CVL Voltage" }, +}; + +static const struct m10bmc_sdata n6000bmc_curr_tbl[] = { + { 0x600, 0x604, 0x608, 0x0, 0x0, 1, "Inlet 12V PCIe Rail Current" }, + { 0x618, 0x61c, 0x620, 0x0, 0x0, 1, "Inlet 12V Aux Rail Current" }, + { 0x630, 0x634, 0x638, 0x0, 0x0, 1, "Inlet 3V3 PCIe Rail Current" }, + { 0x640, 0x0, 0x0, 0x0, 0x0, 1, "FPGA Core Voltage Rail Current" }, + { 0x648, 0x0, 0x0, 0x0, 0x0, 1, "FPGA VCCH Rail Current" }, + { 0x650, 0x0, 0x0, 0x0, 0x0, 1, "FPGA VCC_1V2 Rail Current" }, + { 0x658, 0x65c, 0x660, 0x0, 0x0, 1, "FPGA VCCH_GXER_1V1, VCCA_1V8 Current" }, + { 0x668, 0x66c, 0x670, 0x0, 0x0, 1, "FPGA VCCIO_1V2 Current" }, + { 0x678, 0x67c, 0x680, 0x0, 0x0, 1, "CVL Non Core Rails Inlet Current" }, + { 0x688, 0x68c, 0x690, 0x0, 0x0, 1, "MAX10 & Board CLK PWR 3V3 Inlet Current" }, + { 0x698, 0x0, 0x0, 0x0, 0x0, 1, "CVL Core Voltage Rail Current" }, + { 0x6b0, 0x0, 0x0, 0x0, 0x0, 1, "Board 3V3 VR Current" }, + { 0x6b8, 0x6bc, 0x6c0, 0x0, 0x0, 1, "QSFP 3V3 Rail Current" }, +}; + +static const struct m10bmc_sdata n6000bmc_power_tbl[] = { + { 0x724, 0x0, 0x0, 0x0, 0x0, 1, "Board Power" }, +}; + +static const struct hwmon_channel_info *n6000bmc_hinfo[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | 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_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_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_MAX | HWMON_C_CRIT | + HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_CRIT | + HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_CRIT | + HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_CRIT | + HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_CRIT | + HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_CRIT | + HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_CRIT | + HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_CRIT | + HWMON_C_LABEL), + HWMON_CHANNEL_INFO(power, + HWMON_P_INPUT | HWMON_P_LABEL), + NULL +}; + +static const struct m10bmc_hwmon_board_data n6000bmc_hwmon_bdata = { + .tables = { + [hwmon_temp] = n6000bmc_temp_tbl, + [hwmon_in] = n6000bmc_in_tbl, + [hwmon_curr] = n6000bmc_curr_tbl, + [hwmon_power] = n6000bmc_power_tbl, + }, + + .hinfo = n6000bmc_hinfo, +}; + static umode_t m10bmc_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel) @@ -549,6 +774,10 @@ static const struct platform_device_id intel_m10bmc_hwmon_ids[] = { .name = "n5010bmc-hwmon", .driver_data = (unsigned long)&n5010bmc_hwmon_bdata, }, + { + .name = "n6000bmc-hwmon", + .driver_data = (unsigned long)&n6000bmc_hwmon_bdata, + }, { } }; diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 9997f76b1f4a..66f7ceaa7c3f 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -34,6 +34,7 @@ * IT8786E Super I/O chip w/LPC interface * IT8790E Super I/O chip w/LPC interface * IT8792E Super I/O chip w/LPC interface + * IT87952E Super I/O chip w/LPC interface * Sis950 A clone of the IT8705F * * Copyright (C) 2001 Chris Gauthron @@ -63,15 +64,7 @@ enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8732, it8771, it8772, it8781, it8782, it8783, it8786, it8790, - it8792, it8603, it8620, it8622, it8628 }; - -static unsigned short force_id; -module_param(force_id, ushort, 0); -MODULE_PARM_DESC(force_id, "Override the detected device ID"); - -static bool ignore_resource_conflict; -module_param(ignore_resource_conflict, bool, 0); -MODULE_PARM_DESC(ignore_resource_conflict, "Ignore ACPI resource conflict"); + it8792, it8603, it8620, it8622, it8628, it87952 }; static struct platform_device *it87_pdev[2]; @@ -87,6 +80,14 @@ static struct platform_device *it87_pdev[2]; #define DEVID 0x20 /* Register: Device ID */ #define DEVREV 0x22 /* Register: Device Revision */ +static inline void __superio_enter(int ioreg) +{ + outb(0x87, ioreg); + outb(0x01, ioreg); + outb(0x55, ioreg); + outb(ioreg == REG_4E ? 0xaa : 0x55, ioreg); +} + static inline int superio_inb(int ioreg, int reg) { outb(reg, ioreg); @@ -124,17 +125,16 @@ static inline int superio_enter(int ioreg) if (!request_muxed_region(ioreg, 2, DRVNAME)) return -EBUSY; - outb(0x87, ioreg); - outb(0x01, ioreg); - outb(0x55, ioreg); - outb(ioreg == REG_4E ? 0xaa : 0x55, ioreg); + __superio_enter(ioreg); return 0; } -static inline void superio_exit(int ioreg) +static inline void superio_exit(int ioreg, bool noexit) { - outb(0x02, ioreg); - outb(0x02, ioreg + 1); + if (!noexit) { + outb(0x02, ioreg); + outb(0x02, ioreg + 1); + } release_region(ioreg, 2); } @@ -161,6 +161,7 @@ static inline void superio_exit(int ioreg) #define IT8622E_DEVID 0x8622 #define IT8623E_DEVID 0x8623 #define IT8628E_DEVID 0x8628 +#define IT87952E_DEVID 0x8695 #define IT87_ACT_REG 0x30 #define IT87_BASE_REG 0x60 @@ -176,6 +177,13 @@ static inline void superio_exit(int ioreg) #define IT87_SIO_VID_REG 0xfc /* VID value */ #define IT87_SIO_BEEP_PIN_REG 0xf6 /* Beep pin mapping */ +/* Force chip IDs to specified values. Should only be used for testing */ +static unsigned short force_id[2]; +static unsigned int force_id_cnt; + +/* ACPI resource conflicts are ignored if this parameter is set to 1 */ +static bool ignore_resource_conflict; + /* Update battery voltage after every reading if true */ static bool update_vbat; @@ -272,7 +280,7 @@ static const u8 IT87_REG_AUTO_BASE[] = { 0x60, 0x68, 0x70, 0x78, 0xa0, 0xa8 }; struct it87_devices { const char *name; - const char * const suffix; + const char * const model; u32 features; u8 peci_mask; u8 old_peci_mask; @@ -297,28 +305,35 @@ struct it87_devices { #define FEAT_PWM_FREQ2 BIT(16) /* Separate pwm freq 2 */ #define FEAT_SIX_TEMP BIT(17) /* Up to 6 temp sensors */ #define FEAT_VIN3_5V BIT(18) /* VIN3 connected to +5V */ +/* + * Disabling configuration mode on some chips can result in system + * hang-ups and access failures to the Super-IO chip at the + * second SIO address. Never exit configuration mode on these + * chips to avoid the problem. + */ +#define FEAT_CONF_NOEXIT BIT(19) /* Chip should not exit conf mode */ static const struct it87_devices it87_devices[] = { [it87] = { .name = "it87", - .suffix = "F", + .model = "IT87F", .features = FEAT_OLD_AUTOPWM, /* may need to overwrite */ }, [it8712] = { .name = "it8712", - .suffix = "F", + .model = "IT8712F", .features = FEAT_OLD_AUTOPWM | FEAT_VID, /* may need to overwrite */ }, [it8716] = { .name = "it8716", - .suffix = "F", + .model = "IT8716F", .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS | FEAT_PWM_FREQ2, }, [it8718] = { .name = "it8718", - .suffix = "F", + .model = "IT8718F", .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS | FEAT_PWM_FREQ2, @@ -326,7 +341,7 @@ static const struct it87_devices it87_devices[] = { }, [it8720] = { .name = "it8720", - .suffix = "F", + .model = "IT8720F", .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS | FEAT_PWM_FREQ2, @@ -334,7 +349,7 @@ static const struct it87_devices it87_devices[] = { }, [it8721] = { .name = "it8721", - .suffix = "F", + .model = "IT8721F", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS | FEAT_IN7_INTERNAL @@ -344,7 +359,7 @@ static const struct it87_devices it87_devices[] = { }, [it8728] = { .name = "it8728", - .suffix = "F", + .model = "IT8728F", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_FIVE_FANS | FEAT_IN7_INTERNAL | FEAT_PWM_FREQ2, @@ -352,7 +367,7 @@ static const struct it87_devices it87_devices[] = { }, [it8732] = { .name = "it8732", - .suffix = "F", + .model = "IT8732F", .features = FEAT_NEWER_AUTOPWM | FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI | FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL, @@ -361,7 +376,7 @@ static const struct it87_devices it87_devices[] = { }, [it8771] = { .name = "it8771", - .suffix = "E", + .model = "IT8771E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL | FEAT_PWM_FREQ2, @@ -373,7 +388,7 @@ static const struct it87_devices it87_devices[] = { }, [it8772] = { .name = "it8772", - .suffix = "E", + .model = "IT8772E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL | FEAT_PWM_FREQ2, @@ -385,28 +400,28 @@ static const struct it87_devices it87_devices[] = { }, [it8781] = { .name = "it8781", - .suffix = "F", + .model = "IT8781F", .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_PWM_FREQ2, .old_peci_mask = 0x4, }, [it8782] = { .name = "it8782", - .suffix = "F", + .model = "IT8782F", .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_PWM_FREQ2, .old_peci_mask = 0x4, }, [it8783] = { .name = "it8783", - .suffix = "E/F", + .model = "IT8783E/F", .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_PWM_FREQ2, .old_peci_mask = 0x4, }, [it8786] = { .name = "it8786", - .suffix = "E", + .model = "IT8786E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL | FEAT_PWM_FREQ2, @@ -414,24 +429,24 @@ static const struct it87_devices it87_devices[] = { }, [it8790] = { .name = "it8790", - .suffix = "E", + .model = "IT8790E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL - | FEAT_PWM_FREQ2, + | FEAT_PWM_FREQ2 | FEAT_CONF_NOEXIT, .peci_mask = 0x07, }, [it8792] = { .name = "it8792", - .suffix = "E", + .model = "IT8792E/IT8795E", .features = FEAT_NEWER_AUTOPWM | FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI - | FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL, + | FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL | FEAT_CONF_NOEXIT, .peci_mask = 0x07, .old_peci_mask = 0x02, /* Actually reports PCH */ }, [it8603] = { .name = "it8603", - .suffix = "E", + .model = "IT8603E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL | FEAT_AVCC3 | FEAT_PWM_FREQ2, @@ -439,7 +454,7 @@ static const struct it87_devices it87_devices[] = { }, [it8620] = { .name = "it8620", - .suffix = "E", + .model = "IT8620E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_SIX_FANS | FEAT_IN7_INTERNAL | FEAT_SIX_PWM | FEAT_PWM_FREQ2 @@ -448,7 +463,7 @@ static const struct it87_devices it87_devices[] = { }, [it8622] = { .name = "it8622", - .suffix = "E", + .model = "IT8622E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_FIVE_FANS | FEAT_FIVE_PWM | FEAT_IN7_INTERNAL | FEAT_PWM_FREQ2 @@ -457,13 +472,22 @@ static const struct it87_devices it87_devices[] = { }, [it8628] = { .name = "it8628", - .suffix = "E", + .model = "IT8628E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_SIX_FANS | FEAT_IN7_INTERNAL | FEAT_SIX_PWM | FEAT_PWM_FREQ2 | FEAT_SIX_TEMP | FEAT_VIN3_5V, .peci_mask = 0x07, }, + [it87952] = { + .name = "it87952", + .model = "IT87952E", + .features = FEAT_NEWER_AUTOPWM | FEAT_16BIT_FANS + | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI + | FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL | FEAT_CONF_NOEXIT, + .peci_mask = 0x07, + .old_peci_mask = 0x02, /* Actually reports PCH */ + }, }; #define has_16bit_fans(data) ((data)->features & FEAT_16BIT_FANS) @@ -490,6 +514,7 @@ static const struct it87_devices it87_devices[] = { #define has_pwm_freq2(data) ((data)->features & FEAT_PWM_FREQ2) #define has_six_temp(data) ((data)->features & FEAT_SIX_TEMP) #define has_vin3_5v(data) ((data)->features & FEAT_VIN3_5V) +#define has_conf_noexit(data) ((data)->features & FEAT_CONF_NOEXIT) struct it87_sio_data { int sioaddr; @@ -2397,11 +2422,11 @@ static const struct attribute_group it87_group_auto_pwm = { /* SuperIO detection - will change isa_address if a chip is found */ static int __init it87_find(int sioaddr, unsigned short *address, - struct it87_sio_data *sio_data) + struct it87_sio_data *sio_data, int chip_cnt) { int err; u16 chip_type; - const struct it87_devices *config; + const struct it87_devices *config = NULL; err = superio_enter(sioaddr); if (err) @@ -2413,8 +2438,12 @@ static int __init it87_find(int sioaddr, unsigned short *address, if (chip_type == 0xffff) goto exit; - if (force_id) - chip_type = force_id; + if (force_id_cnt == 1) { + /* If only one value given use for all chips */ + if (force_id[0]) + chip_type = force_id[0]; + } else if (force_id[chip_cnt]) + chip_type = force_id[chip_cnt]; switch (chip_type) { case IT8705F_DEVID: @@ -2479,6 +2508,9 @@ static int __init it87_find(int sioaddr, unsigned short *address, case IT8628E_DEVID: sio_data->type = it8628; break; + case IT87952E_DEVID: + sio_data->type = it87952; + break; case 0xffff: /* No device at all */ goto exit; default: @@ -2486,27 +2518,29 @@ static int __init it87_find(int sioaddr, unsigned short *address, goto exit; } + config = &it87_devices[sio_data->type]; + superio_select(sioaddr, PME); if (!(superio_inb(sioaddr, IT87_ACT_REG) & 0x01)) { - pr_info("Device not activated, skipping\n"); + pr_info("Device (chip %s ioreg 0x%x) not activated, skipping\n", + config->model, sioaddr); goto exit; } *address = superio_inw(sioaddr, IT87_BASE_REG) & ~(IT87_EXTENT - 1); if (*address == 0) { - pr_info("Base address not set, skipping\n"); + pr_info("Base address not set (chip %s ioreg 0x%x), skipping\n", + config->model, sioaddr); goto exit; } err = 0; sio_data->sioaddr = sioaddr; sio_data->revision = superio_inb(sioaddr, DEVREV) & 0x0f; - pr_info("Found IT%04x%s chip at 0x%x, revision %d\n", chip_type, - it87_devices[sio_data->type].suffix, + pr_info("Found %s chip at 0x%x, revision %d\n", + it87_devices[sio_data->type].model, *address, sio_data->revision); - config = &it87_devices[sio_data->type]; - /* in7 (VSB or VCCH5V) is always internal on some chips */ if (has_in7_internal(config)) sio_data->internal |= BIT(1); @@ -2824,7 +2858,7 @@ static int __init it87_find(int sioaddr, unsigned short *address, sio_data->skip_pwm |= dmi_data->skip_pwm; exit: - superio_exit(sioaddr); + superio_exit(sioaddr, config ? has_conf_noexit(config) : false); return err; } @@ -3210,7 +3244,7 @@ static void it87_resume_sio(struct platform_device *pdev) reg2c); } - superio_exit(data->sioaddr); + superio_exit(data->sioaddr, has_conf_noexit(data)); } static int it87_resume(struct device *dev) @@ -3311,6 +3345,27 @@ static int it87_dmi_cb(const struct dmi_system_id *dmi_entry) } /* + * On various Gigabyte AM4 boards (AB350, AX370), the second Super-IO chip + * (IT8792E) needs to be in configuration mode before accessing the first + * due to a bug in IT8792E which otherwise results in LPC bus access errors. + * This needs to be done before accessing the first Super-IO chip since + * the second chip may have been accessed prior to loading this driver. + * + * The problem is also reported to affect IT8795E, which is used on X299 boards + * and has the same chip ID as IT8792E (0x8733). It also appears to affect + * systems with IT8790E, which is used on some Z97X-Gaming boards as well as + * Z87X-OC. + * DMI entries for those systems will be added as they become available and + * as the problem is confirmed to affect those boards. + */ +static int it87_sio_force(const struct dmi_system_id *dmi_entry) +{ + __superio_enter(REG_4E); + + return it87_dmi_cb(dmi_entry); +}; + +/* * On the Shuttle SN68PT, FAN_CTL2 is apparently not * connected to a fan, but to something else. One user * has reported instant system power-off when changing @@ -3332,7 +3387,34 @@ static struct it87_dmi_data nvidia_fn68pt = { .driver_data = data, \ } +#define IT87_DMI_MATCH_GBT(name, cb, data) \ + IT87_DMI_MATCH_VND("Gigabyte Technology Co., Ltd.", name, cb, data) + static const struct dmi_system_id it87_dmi_table[] __initconst = { + IT87_DMI_MATCH_GBT("AB350", it87_sio_force, NULL), + /* ? + IT8792E/IT8795E */ + IT87_DMI_MATCH_GBT("AX370", it87_sio_force, NULL), + /* ? + IT8792E/IT8795E */ + IT87_DMI_MATCH_GBT("Z97X-Gaming G1", it87_sio_force, NULL), + /* ? + IT8790E */ + IT87_DMI_MATCH_GBT("TRX40 AORUS XTREME", it87_sio_force, NULL), + /* IT8688E + IT8792E/IT8795E */ + IT87_DMI_MATCH_GBT("Z390 AORUS ULTRA-CF", it87_sio_force, NULL), + /* IT8688E + IT8792E/IT8795E */ + IT87_DMI_MATCH_GBT("B550 AORUS PRO AC", it87_sio_force, NULL), + /* IT8688E + IT8792E/IT8795E */ + IT87_DMI_MATCH_GBT("X570 AORUS MASTER", it87_sio_force, NULL), + /* IT8688E + IT8792E/IT8795E */ + IT87_DMI_MATCH_GBT("X570 AORUS PRO", it87_sio_force, NULL), + /* IT8688E + IT8792E/IT8795E */ + IT87_DMI_MATCH_GBT("X570 AORUS PRO WIFI", it87_sio_force, NULL), + /* IT8688E + IT8792E/IT8795E */ + IT87_DMI_MATCH_GBT("X570S AERO G", it87_sio_force, NULL), + /* IT8689E + IT87952E */ + IT87_DMI_MATCH_GBT("Z690 AORUS PRO DDR4", it87_sio_force, NULL), + /* IT8689E + IT87952E */ + IT87_DMI_MATCH_GBT("Z690 AORUS PRO", it87_sio_force, NULL), + /* IT8689E + IT87952E */ IT87_DMI_MATCH_VND("nVIDIA", "FN68PT", it87_dmi_cb, &nvidia_fn68pt), { } @@ -3356,7 +3438,7 @@ static int __init sm_it87_init(void) for (i = 0; i < ARRAY_SIZE(sioaddr); i++) { memset(&sio_data, 0, sizeof(struct it87_sio_data)); isa_address[i] = 0; - err = it87_find(sioaddr[i], &isa_address[i], &sio_data); + err = it87_find(sioaddr[i], &isa_address[i], &sio_data, i); if (err || isa_address[i] == 0) continue; /* @@ -3404,11 +3486,20 @@ static void __exit sm_it87_exit(void) MODULE_AUTHOR("Chris Gauthron, Jean Delvare <jdelvare@suse.de>"); MODULE_DESCRIPTION("IT8705F/IT871xF/IT872xF hardware monitoring driver"); + +module_param_array(force_id, ushort, &force_id_cnt, 0); +MODULE_PARM_DESC(force_id, "Override one or more detected device ID(s)"); + +module_param(ignore_resource_conflict, bool, 0); +MODULE_PARM_DESC(ignore_resource_conflict, "Ignore ACPI resource conflict"); + module_param(update_vbat, bool, 0); MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value"); + module_param(fix_pwm_polarity, bool, 0); MODULE_PARM_DESC(fix_pwm_polarity, "Force PWM polarity to active high (DANGEROUS)"); + MODULE_LICENSE("GPL"); module_init(sm_it87_init); diff --git a/drivers/hwmon/ltc2945.c b/drivers/hwmon/ltc2945.c index 9adebb59f604..3494f7261ebf 100644 --- a/drivers/hwmon/ltc2945.c +++ b/drivers/hwmon/ltc2945.c @@ -58,6 +58,22 @@ #define CONTROL_MULT_SELECT (1 << 0) #define CONTROL_TEST_MODE (1 << 4) +static const struct of_device_id __maybe_unused ltc2945_of_match[] = { + { .compatible = "adi,ltc2945" }, + { } +}; +MODULE_DEVICE_TABLE(of, ltc2945_of_match); + +/** + * struct ltc2945_data - LTC2945 device data + * @regmap: regmap device + * @shunt_resistor: shunt resistor value in micro ohms (1000 by default) + */ +struct ltc2945_data { + struct regmap *regmap; + u32 shunt_resistor; +}; + static inline bool is_power_reg(u8 reg) { return reg < LTC2945_SENSE_H; @@ -66,7 +82,9 @@ static inline bool is_power_reg(u8 reg) /* Return the value from the given register in uW, mV, or mA */ static long long ltc2945_reg_to_val(struct device *dev, u8 reg) { - struct regmap *regmap = dev_get_drvdata(dev); + struct ltc2945_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + u32 shunt_resistor = data->shunt_resistor; unsigned int control; u8 buf[3]; long long val; @@ -78,10 +96,10 @@ static long long ltc2945_reg_to_val(struct device *dev, u8 reg) return ret; if (is_power_reg(reg)) { - /* power */ + /* 24-bit power */ val = (buf[0] << 16) + (buf[1] << 8) + buf[2]; } else { - /* current, voltage */ + /* 12-bit current, voltage */ val = (buf[0] << 4) + (buf[1] >> 4); } @@ -92,9 +110,7 @@ static long long ltc2945_reg_to_val(struct device *dev, u8 reg) case LTC2945_MAX_POWER_THRES_H: case LTC2945_MIN_POWER_THRES_H: /* - * Convert to uW by assuming current is measured with - * an 1mOhm sense resistor, similar to current - * measurements. + * Convert to uW * Control register bit 0 selects if voltage at SENSE+/VDD * or voltage at ADIN is used to measure power. */ @@ -108,6 +124,14 @@ static long long ltc2945_reg_to_val(struct device *dev, u8 reg) /* 0.5 mV * 25 uV = 0.0125 uV resolution. */ val = (val * 25LL) >> 1; } + val *= 1000; + /* Overflow check: Assuming max 24-bit power, val is at most 53 bits right now. */ + val = DIV_ROUND_CLOSEST_ULL(val, shunt_resistor); + /* + * Overflow check: After division, depending on shunt resistor, + * val can still be > 32 bits so returning long long makes sense + */ + break; case LTC2945_VIN_H: case LTC2945_MAX_VIN_H: @@ -130,14 +154,11 @@ static long long ltc2945_reg_to_val(struct device *dev, u8 reg) case LTC2945_MIN_SENSE_H: case LTC2945_MAX_SENSE_THRES_H: case LTC2945_MIN_SENSE_THRES_H: - /* - * 25 uV resolution. Convert to current as measured with - * an 1 mOhm sense resistor, in mA. If a different sense - * resistor is installed, calculate the actual current by - * dividing the reported current by the sense resistor value - * in mOhm. - */ - val *= 25; + /* 25 uV resolution. Convert to mA. */ + val *= 25 * 1000; + /* Overflow check: Assuming max 12-bit sense, val is at most 27 bits right now */ + val = DIV_ROUND_CLOSEST_ULL(val, shunt_resistor); + /* Overflow check: After division, <= 27 bits */ break; default: return -EINVAL; @@ -145,13 +166,18 @@ static long long ltc2945_reg_to_val(struct device *dev, u8 reg) return val; } -static int ltc2945_val_to_reg(struct device *dev, u8 reg, - unsigned long val) +static long long ltc2945_val_to_reg(struct device *dev, u8 reg, + unsigned long long val) { - struct regmap *regmap = dev_get_drvdata(dev); + struct ltc2945_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + u32 shunt_resistor = data->shunt_resistor; unsigned int control; int ret; + /* Ensure we don't overflow */ + val = clamp_val(val, 0, U32_MAX); + switch (reg) { case LTC2945_POWER_H: case LTC2945_MAX_POWER_H: @@ -159,9 +185,6 @@ static int ltc2945_val_to_reg(struct device *dev, u8 reg, case LTC2945_MAX_POWER_THRES_H: case LTC2945_MIN_POWER_THRES_H: /* - * Convert to register value by assuming current is measured - * with an 1mOhm sense resistor, similar to current - * measurements. * Control register bit 0 selects if voltage at SENSE+/VDD * or voltage at ADIN is used to measure power, which in turn * determines register calculations. @@ -171,14 +194,16 @@ static int ltc2945_val_to_reg(struct device *dev, u8 reg, return ret; if (control & CONTROL_MULT_SELECT) { /* 25 mV * 25 uV = 0.625 uV resolution. */ - val = DIV_ROUND_CLOSEST(val, 625); + val *= shunt_resistor; + /* Overflow check: Assuming 32-bit val and shunt resistor, val <= 64bits */ + val = DIV_ROUND_CLOSEST_ULL(val, 625 * 1000); + /* Overflow check: val is now <= 44 bits */ } else { - /* - * 0.5 mV * 25 uV = 0.0125 uV resolution. - * Divide first to avoid overflow; - * accept loss of accuracy. - */ - val = DIV_ROUND_CLOSEST(val, 25) * 2; + /* 0.5 mV * 25 uV = 0.0125 uV resolution. */ + val *= shunt_resistor; + /* Overflow check: Assuming 32-bit val and shunt resistor, val <= 64bits */ + val = DIV_ROUND_CLOSEST_ULL(val, 25 * 1000) * 2; + /* Overflow check: val is now <= 51 bits */ } break; case LTC2945_VIN_H: @@ -187,7 +212,7 @@ static int ltc2945_val_to_reg(struct device *dev, u8 reg, case LTC2945_MAX_VIN_THRES_H: case LTC2945_MIN_VIN_THRES_H: /* 25 mV resolution. */ - val /= 25; + val = DIV_ROUND_CLOSEST_ULL(val, 25); break; case LTC2945_ADIN_H: case LTC2945_MAX_ADIN_H: @@ -202,14 +227,11 @@ static int ltc2945_val_to_reg(struct device *dev, u8 reg, case LTC2945_MIN_SENSE_H: case LTC2945_MAX_SENSE_THRES_H: case LTC2945_MIN_SENSE_THRES_H: - /* - * 25 uV resolution. Convert to current as measured with - * an 1 mOhm sense resistor, in mA. If a different sense - * resistor is installed, calculate the actual current by - * dividing the reported current by the sense resistor value - * in mOhm. - */ - val = DIV_ROUND_CLOSEST(val, 25); + /* 25 uV resolution. Convert to mA. */ + val *= shunt_resistor; + /* Overflow check: Assuming 32-bit val and 32-bit shunt resistor, val is 64bits */ + val = DIV_ROUND_CLOSEST_ULL(val, 25 * 1000); + /* Overflow check: val is now <= 50 bits */ break; default: return -EINVAL; @@ -234,20 +256,23 @@ static ssize_t ltc2945_value_store(struct device *dev, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct regmap *regmap = dev_get_drvdata(dev); + struct ltc2945_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; u8 reg = attr->index; - unsigned long val; + unsigned int val; u8 regbuf[3]; int num_regs; - int regval; + long long regval; int ret; - ret = kstrtoul(buf, 10, &val); + ret = kstrtouint(buf, 10, &val); if (ret) return ret; /* convert to register value, then clamp and write result */ regval = ltc2945_val_to_reg(dev, reg, val); + if (regval < 0) + return regval; if (is_power_reg(reg)) { regval = clamp_val(regval, 0, 0xffffff); regbuf[0] = regval >> 16; @@ -269,7 +294,8 @@ static ssize_t ltc2945_history_store(struct device *dev, const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct regmap *regmap = dev_get_drvdata(dev); + struct ltc2945_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; u8 reg = attr->index; int num_regs = is_power_reg(reg) ? 3 : 2; u8 buf_min[3] = { 0xff, 0xff, 0xff }; @@ -321,7 +347,8 @@ static ssize_t ltc2945_bool_show(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct regmap *regmap = dev_get_drvdata(dev); + struct ltc2945_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; unsigned int fault; int ret; @@ -450,6 +477,12 @@ static int ltc2945_probe(struct i2c_client *client) struct device *dev = &client->dev; struct device *hwmon_dev; struct regmap *regmap; + struct ltc2945_data *data; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + dev_set_drvdata(dev, data); regmap = devm_regmap_init_i2c(client, <c2945_regmap_config); if (IS_ERR(regmap)) { @@ -457,11 +490,19 @@ static int ltc2945_probe(struct i2c_client *client) return PTR_ERR(regmap); } + data->regmap = regmap; + if (device_property_read_u32(dev, "shunt-resistor-micro-ohms", + &data->shunt_resistor)) + data->shunt_resistor = 1000; + + if (data->shunt_resistor == 0) + return -EINVAL; + /* Clear faults */ regmap_write(regmap, LTC2945_FAULT, 0x00); hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - regmap, + data, ltc2945_groups); return PTR_ERR_OR_ZERO(hwmon_dev); } @@ -475,8 +516,9 @@ MODULE_DEVICE_TABLE(i2c, ltc2945_id); static struct i2c_driver ltc2945_driver = { .driver = { - .name = "ltc2945", - }, + .name = "ltc2945", + .of_match_table = of_match_ptr(ltc2945_of_match), + }, .probe_new = ltc2945_probe, .id_table = ltc2945_id, }; diff --git a/drivers/hwmon/mc34vr500.c b/drivers/hwmon/mc34vr500.c new file mode 100644 index 000000000000..6268e973049c --- /dev/null +++ b/drivers/hwmon/mc34vr500.c @@ -0,0 +1,263 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * An hwmon driver for the NXP MC34VR500 PMIC + * + * Author: Mario Kicherer <dev@kicherer.org> + */ + +#include <linux/bits.h> +#include <linux/dev_printk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/hwmon.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/irqreturn.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> + +#define MC34VR500_I2C_ADDR 0x08 +#define MC34VR500_DEVICEID_VALUE 0x14 + +/* INTSENSE0 */ +#define ENS_BIT BIT(0) +#define LOWVINS_BIT BIT(1) +#define THERM110S_BIT BIT(2) +#define THERM120S_BIT BIT(3) +#define THERM125S_BIT BIT(4) +#define THERM130S_BIT BIT(5) + +#define MC34VR500_DEVICEID 0x00 + +#define MC34VR500_SILICONREVID 0x03 +#define MC34VR500_FABID 0x04 +#define MC34VR500_INTSTAT0 0x05 +#define MC34VR500_INTMASK0 0x06 +#define MC34VR500_INTSENSE0 0x07 + +struct mc34vr500_data { + struct device *hwmon_dev; + struct regmap *regmap; +}; + +static irqreturn_t mc34vr500_process_interrupt(int irq, void *userdata) +{ + struct mc34vr500_data *data = (struct mc34vr500_data *)userdata; + unsigned int reg; + int ret; + + ret = regmap_read(data->regmap, MC34VR500_INTSTAT0, ®); + if (ret < 0) + return IRQ_HANDLED; + + if (reg) { + if (reg & LOWVINS_BIT) + hwmon_notify_event(data->hwmon_dev, hwmon_in, + hwmon_in_min_alarm, 0); + + if (reg & THERM110S_BIT) + hwmon_notify_event(data->hwmon_dev, hwmon_temp, + hwmon_temp_max_alarm, 0); + + if (reg & THERM120S_BIT) + hwmon_notify_event(data->hwmon_dev, hwmon_temp, + hwmon_temp_crit_alarm, 0); + + if (reg & THERM130S_BIT) + hwmon_notify_event(data->hwmon_dev, hwmon_temp, + hwmon_temp_emergency_alarm, 0); + + /* write 1 to clear */ + regmap_write(data->regmap, MC34VR500_INTSTAT0, LOWVINS_BIT | + THERM110S_BIT | THERM120S_BIT | THERM130S_BIT); + } + + return IRQ_HANDLED; +} + +static umode_t mc34vr500_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (attr) { + case hwmon_in_min_alarm: + case hwmon_temp_max_alarm: + case hwmon_temp_crit_alarm: + case hwmon_temp_emergency_alarm: + return 0444; + default: + break; + } + + return 0; +} + +static int mc34vr500_alarm_read(struct mc34vr500_data *data, int index, + long *val) +{ + unsigned int reg; + int ret; + + ret = regmap_read(data->regmap, MC34VR500_INTSENSE0, ®); + if (ret < 0) + return ret; + + *val = !!(reg & index); + + return 0; +} + +static int mc34vr500_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct mc34vr500_data *data = dev_get_drvdata(dev); + + switch (type) { + case hwmon_in: + switch (attr) { + case hwmon_in_min_alarm: + return mc34vr500_alarm_read(data, LOWVINS_BIT, val); + default: + return -EOPNOTSUPP; + } + case hwmon_temp: + switch (attr) { + case hwmon_temp_max_alarm: + return mc34vr500_alarm_read(data, THERM110S_BIT, val); + case hwmon_temp_crit_alarm: + return mc34vr500_alarm_read(data, THERM120S_BIT, val); + case hwmon_temp_emergency_alarm: + return mc34vr500_alarm_read(data, THERM130S_BIT, val); + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_channel_info *mc34vr500_info[] = { + HWMON_CHANNEL_INFO(in, HWMON_I_MIN_ALARM), + HWMON_CHANNEL_INFO(temp, HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM + | HWMON_T_EMERGENCY_ALARM), + NULL, +}; + +static const struct hwmon_ops mc34vr500_hwmon_ops = { + .is_visible = mc34vr500_is_visible, + .read = mc34vr500_read, +}; + +static const struct hwmon_chip_info mc34vr500_chip_info = { + .ops = &mc34vr500_hwmon_ops, + .info = mc34vr500_info, +}; + +static const struct regmap_config mc34vr500_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MC34VR500_INTSENSE0, +}; + +static int mc34vr500_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct mc34vr500_data *data; + struct device *hwmon_dev; + int ret; + unsigned int reg, revid, fabid; + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, &mc34vr500_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + data = devm_kzalloc(dev, sizeof(struct mc34vr500_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->regmap = regmap; + + ret = regmap_read(regmap, MC34VR500_DEVICEID, ®); + if (ret < 0) + return ret; + + if (reg != MC34VR500_DEVICEID_VALUE) + return -ENODEV; + + ret = regmap_read(regmap, MC34VR500_SILICONREVID, &revid); + if (ret < 0) + return ret; + + ret = regmap_read(regmap, MC34VR500_FABID, &fabid); + if (ret < 0) + return ret; + + dev_dbg(dev, "mc34vr500: revid 0x%x fabid 0x%x\n", revid, fabid); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, + &mc34vr500_chip_info, + NULL); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + data->hwmon_dev = hwmon_dev; + + if (client->irq) { + ret = devm_request_threaded_irq(dev, client->irq, NULL, + mc34vr500_process_interrupt, + IRQF_TRIGGER_RISING | + IRQF_ONESHOT | + IRQF_SHARED, + dev_name(dev), data); + if (ret) + return ret; + + /* write 1 to clear interrupts */ + ret = regmap_write(regmap, MC34VR500_INTSTAT0, LOWVINS_BIT | + THERM110S_BIT | THERM120S_BIT | + THERM130S_BIT); + if (ret) + return ret; + + /* unmask interrupts */ + ret = regmap_write(regmap, MC34VR500_INTMASK0, + (unsigned int) ~(LOWVINS_BIT | THERM110S_BIT | + THERM120S_BIT | THERM130S_BIT)); + if (ret) + return ret; + } + + return 0; +} + +static const struct i2c_device_id mc34vr500_id[] = { + { "mc34vr500", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, mc34vr500_id); + +static const struct of_device_id __maybe_unused mc34vr500_of_match[] = { + { .compatible = "nxp,mc34vr500" }, + { }, +}; +MODULE_DEVICE_TABLE(of, mc34vr500_of_match); + +static struct i2c_driver mc34vr500_driver = { + .driver = { + .name = "mc34vr500", + .of_match_table = of_match_ptr(mc34vr500_of_match), + }, + .probe_new = mc34vr500_probe, + .id_table = mc34vr500_id, +}; + +module_i2c_driver(mc34vr500_driver); + +MODULE_AUTHOR("Mario Kicherer <dev@kicherer.org>"); + +MODULE_DESCRIPTION("MC34VR500 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/mlxreg-fan.c b/drivers/hwmon/mlxreg-fan.c index b48bd7c961d6..96017cc8da7e 100644 --- a/drivers/hwmon/mlxreg-fan.c +++ b/drivers/hwmon/mlxreg-fan.c @@ -155,6 +155,12 @@ mlxreg_fan_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, if (err) return err; + if (MLXREG_FAN_GET_FAULT(regval, tacho->mask)) { + /* FAN is broken - return zero for FAN speed. */ + *val = 0; + return 0; + } + *val = MLXREG_FAN_GET_RPM(regval, fan->divider, fan->samples); break; diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c index da9ec6983e13..c54233f0369b 100644 --- a/drivers/hwmon/nct6775-core.c +++ b/drivers/hwmon/nct6775-core.c @@ -1150,7 +1150,7 @@ static int nct6775_write_fan_div(struct nct6775_data *data, int nr) if (err) return err; reg &= 0x70 >> oddshift; - reg |= data->fan_div[nr] & (0x7 << oddshift); + reg |= (data->fan_div[nr] & 0x7) << oddshift; return nct6775_write_value(data, fandiv_reg, reg); } diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c index bf43f73dc835..76c6b564d7fc 100644 --- a/drivers/hwmon/nct6775-platform.c +++ b/drivers/hwmon/nct6775-platform.c @@ -17,7 +17,6 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> -#include <linux/wmi.h> #include "nct6775.h" @@ -107,40 +106,51 @@ struct nct6775_sio_data { void (*sio_exit)(struct nct6775_sio_data *sio_data); }; -#define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66" +#define ASUSWMI_METHOD "WMBD" #define ASUSWMI_METHODID_RSIO 0x5253494F #define ASUSWMI_METHODID_WSIO 0x5753494F #define ASUSWMI_METHODID_RHWM 0x5248574D #define ASUSWMI_METHODID_WHWM 0x5748574D #define ASUSWMI_UNSUPPORTED_METHOD 0xFFFFFFFE +#define ASUSWMI_DEVICE_HID "PNP0C14" +#define ASUSWMI_DEVICE_UID "ASUSWMI" +#define ASUSMSI_DEVICE_UID "AsusMbSwInterface" + +#if IS_ENABLED(CONFIG_ACPI) +/* + * ASUS boards have only one device with WMI "WMBD" method and have provided + * access to only one SuperIO chip at 0x0290. + */ +static struct acpi_device *asus_acpi_dev; +#endif static int nct6775_asuswmi_evaluate_method(u32 method_id, u8 bank, u8 reg, u8 val, u32 *retval) { -#if IS_ENABLED(CONFIG_ACPI_WMI) +#if IS_ENABLED(CONFIG_ACPI) + acpi_handle handle = acpi_device_handle(asus_acpi_dev); u32 args = bank | (reg << 8) | (val << 16); - struct acpi_buffer input = { (acpi_size) sizeof(args), &args }; - struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_object_list input; + union acpi_object params[3]; + unsigned long long result; acpi_status status; - union acpi_object *obj; - u32 tmp = ASUSWMI_UNSUPPORTED_METHOD; - - status = wmi_evaluate_method(ASUSWMI_MONITORING_GUID, 0, - method_id, &input, &output); + params[0].type = ACPI_TYPE_INTEGER; + params[0].integer.value = 0; + params[1].type = ACPI_TYPE_INTEGER; + params[1].integer.value = method_id; + params[2].type = ACPI_TYPE_BUFFER; + params[2].buffer.length = sizeof(args); + params[2].buffer.pointer = (void *)&args; + input.count = 3; + input.pointer = params; + + status = acpi_evaluate_integer(handle, ASUSWMI_METHOD, &input, &result); if (ACPI_FAILURE(status)) return -EIO; - obj = output.pointer; - if (obj && obj->type == ACPI_TYPE_INTEGER) - tmp = obj->integer.value; - if (retval) - *retval = tmp; - - kfree(obj); + *retval = (u32)result & 0xFFFFFFFF; - if (tmp == ASUSWMI_UNSUPPORTED_METHOD) - return -ENODEV; return 0; #else return -EOPNOTSUPP; @@ -1099,6 +1109,91 @@ static const char * const asus_wmi_boards[] = { "TUF GAMING Z490-PLUS (WI-FI)", }; +static const char * const asus_msi_boards[] = { + "EX-B660M-V5 PRO D4", + "PRIME B650-PLUS", + "PRIME B650M-A", + "PRIME B650M-A AX", + "PRIME B650M-A II", + "PRIME B650M-A WIFI", + "PRIME B650M-A WIFI II", + "PRIME B660M-A D4", + "PRIME B660M-A WIFI D4", + "PRIME X670-P", + "PRIME X670-P WIFI", + "PRIME X670E-PRO WIFI", + "Pro B660M-C-D4", + "ProArt B660-CREATOR D4", + "ProArt X670E-CREATOR WIFI", + "ROG CROSSHAIR X670E EXTREME", + "ROG CROSSHAIR X670E GENE", + "ROG CROSSHAIR X670E HERO", + "ROG MAXIMUS XIII EXTREME GLACIAL", + "ROG MAXIMUS Z690 EXTREME", + "ROG MAXIMUS Z690 EXTREME GLACIAL", + "ROG STRIX B650-A GAMING WIFI", + "ROG STRIX B650E-E GAMING WIFI", + "ROG STRIX B650E-F GAMING WIFI", + "ROG STRIX B650E-I GAMING WIFI", + "ROG STRIX B660-A GAMING WIFI D4", + "ROG STRIX B660-F GAMING WIFI", + "ROG STRIX B660-G GAMING WIFI", + "ROG STRIX B660-I GAMING WIFI", + "ROG STRIX X670E-A GAMING WIFI", + "ROG STRIX X670E-E GAMING WIFI", + "ROG STRIX X670E-F GAMING WIFI", + "ROG STRIX X670E-I GAMING WIFI", + "ROG STRIX Z590-A GAMING WIFI II", + "ROG STRIX Z690-A GAMING WIFI D4", + "TUF GAMING B650-PLUS", + "TUF GAMING B650-PLUS WIFI", + "TUF GAMING B650M-PLUS", + "TUF GAMING B650M-PLUS WIFI", + "TUF GAMING B660M-PLUS WIFI", + "TUF GAMING X670E-PLUS", + "TUF GAMING X670E-PLUS WIFI", + "TUF GAMING Z590-PLUS WIFI", +}; + +#if IS_ENABLED(CONFIG_ACPI) +/* + * Callback for acpi_bus_for_each_dev() to find the right device + * by _UID and _HID and return 1 to stop iteration. + */ +static int nct6775_asuswmi_device_match(struct device *dev, void *data) +{ + struct acpi_device *adev = to_acpi_device(dev); + const char *uid = acpi_device_uid(adev); + const char *hid = acpi_device_hid(adev); + + if (hid && !strcmp(hid, ASUSWMI_DEVICE_HID) && uid && !strcmp(uid, data)) { + asus_acpi_dev = adev; + return 1; + } + + return 0; +} +#endif + +static enum sensor_access nct6775_determine_access(const char *device_uid) +{ +#if IS_ENABLED(CONFIG_ACPI) + u8 tmp; + + acpi_bus_for_each_dev(nct6775_asuswmi_device_match, (void *)device_uid); + if (!asus_acpi_dev) + return access_direct; + + /* if reading chip id via ACPI succeeds, use WMI "WMBD" method for access */ + if (!nct6775_asuswmi_read(0, NCT6775_PORT_CHIPID, &tmp) && tmp) { + pr_debug("Using Asus WMBD method of %s to access %#x chip.\n", device_uid, tmp); + return access_asuswmi; + } +#endif + + return access_direct; +} + static int __init sensors_nct6775_platform_init(void) { int i, err; @@ -1109,7 +1204,6 @@ static int __init sensors_nct6775_platform_init(void) int sioaddr[2] = { 0x2e, 0x4e }; enum sensor_access access = access_direct; const char *board_vendor, *board_name; - u8 tmp; err = platform_driver_register(&nct6775_driver); if (err) @@ -1122,15 +1216,13 @@ static int __init sensors_nct6775_platform_init(void) !strcmp(board_vendor, "ASUSTeK COMPUTER INC.")) { err = match_string(asus_wmi_boards, ARRAY_SIZE(asus_wmi_boards), board_name); - if (err >= 0) { - /* if reading chip id via WMI succeeds, use WMI */ - if (!nct6775_asuswmi_read(0, NCT6775_PORT_CHIPID, &tmp) && tmp) { - pr_info("Using Asus WMI to access %#x chip.\n", tmp); - access = access_asuswmi; - } else { - pr_err("Can't read ChipID by Asus WMI.\n"); - } - } + if (err >= 0) + access = nct6775_determine_access(ASUSWMI_DEVICE_UID); + + err = match_string(asus_msi_boards, ARRAY_SIZE(asus_msi_boards), + board_name); + if (err >= 0) + access = nct6775_determine_access(ASUSMSI_DEVICE_UID); } /* diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c index 533f38b0b4e9..2b93ba89610a 100644 --- a/drivers/hwmon/nzxt-smart2.c +++ b/drivers/hwmon/nzxt-smart2.c @@ -791,6 +791,7 @@ static const struct hid_device_id nzxt_smart2_hid_id_table[] = { { HID_USB_DEVICE(0x1e71, 0x2009) }, /* NZXT RGB & Fan Controller */ { HID_USB_DEVICE(0x1e71, 0x200e) }, /* NZXT RGB & Fan Controller */ { HID_USB_DEVICE(0x1e71, 0x2010) }, /* NZXT RGB & Fan Controller */ + { HID_USB_DEVICE(0x1e71, 0x2019) }, /* NZXT RGB & Fan Controller */ {}, }; diff --git a/drivers/hwmon/oxp-sensors.c b/drivers/hwmon/oxp-sensors.c index f84ec8f8eda9..36872b57912a 100644 --- a/drivers/hwmon/oxp-sensors.c +++ b/drivers/hwmon/oxp-sensors.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Platform driver for OXP Handhelds that expose fan reading and control - * via hwmon sysfs. + * Platform driver for OneXPlayer, AOK ZOE, and Aya Neo Handhelds that expose + * fan reading and control via hwmon sysfs. * - * Old boards have the same DMI strings and they are told appart by the - * boot cpu vendor (Intel/AMD). Currently only AMD boards are supported - * but the code is made to be simple to add other handheld boards in the - * future. + * Old OXP boards have the same DMI strings and they are told apart by + * the boot cpu vendor (Intel/AMD). Currently only AMD boards are + * supported but the code is made to be simple to add other handheld + * boards in the future. * 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 @@ -42,6 +42,8 @@ static bool unlock_global_acpi_lock(void) enum oxp_board { aok_zoe_a1 = 1, + aya_neo_air, + aya_neo_air_pro, oxp_mini_amd, oxp_mini_amd_pro, }; @@ -62,6 +64,20 @@ static const struct dmi_system_id dmi_table[] = { }, { .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR"), + }, + .driver_data = (void *) &(enum oxp_board) {aya_neo_air}, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR Pro"), + }, + .driver_data = (void *) &(enum oxp_board) {aya_neo_air_pro}, + }, + { + .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONE XPLAYER"), }, @@ -161,8 +177,17 @@ static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type, ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); if (ret) return ret; - if (board == oxp_mini_amd) + switch (board) { + case aya_neo_air: + case aya_neo_air_pro: + case oxp_mini_amd: *val = (*val * 255) / 100; + break; + case oxp_mini_amd_pro: + case aok_zoe_a1: + default: + break; + } return 0; case hwmon_pwm_enable: return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val); @@ -191,8 +216,17 @@ static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type, case hwmon_pwm_input: if (val < 0 || val > 255) return -EINVAL; - if (board == oxp_mini_amd) + switch (board) { + case aya_neo_air: + case aya_neo_air_pro: + case oxp_mini_amd: val = (val * 100) / 255; + break; + case aok_zoe_a1: + case oxp_mini_amd_pro: + default: + break; + } return write_to_ec(dev, OXP_SENSOR_PWM_REG, val); default: break; @@ -233,7 +267,7 @@ static int oxp_platform_probe(struct platform_device *pdev) /* * Have to check for AMD processor here because DMI strings are the - * same between Intel and AMD boards, the only way to tell them appart + * same between Intel and AMD boards, the only way to tell them apart * is the CPU. * Intel boards seem to have different EC registers and values to * read/write. diff --git a/drivers/hwmon/peci/cputemp.c b/drivers/hwmon/peci/cputemp.c index 57470fda5f6c..30850a479f61 100644 --- a/drivers/hwmon/peci/cputemp.c +++ b/drivers/hwmon/peci/cputemp.c @@ -402,7 +402,7 @@ static int create_temp_label(struct peci_cputemp *priv) unsigned long core_max = find_last_bit(priv->core_mask, CORE_NUMS_MAX); int i; - priv->coretemp_label = devm_kzalloc(priv->dev, core_max * sizeof(char *), GFP_KERNEL); + priv->coretemp_label = devm_kzalloc(priv->dev, (core_max + 1) * sizeof(char *), GFP_KERNEL); if (!priv->coretemp_label) return -ENOMEM; diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 89668af67206..59d9a7430499 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -237,10 +237,10 @@ config SENSORS_MAX16064 be called max16064. config SENSORS_MAX16601 - tristate "Maxim MAX16508, MAX16601, MAX16602" + tristate "Maxim MAX16508, MAX16600, MAX16601, and MAX16602" help If you say yes here you get hardware monitoring support for Maxim - MAX16508, MAX16601 and MAX16602. + MAX16508, MAX16600, MAX16601, and MAX16602. This driver can also be built as a module. If so, the module will be called max16601. @@ -317,6 +317,22 @@ config SENSORS_MP5023 This driver can also be built as a module. If so, the module will be called mp5023. +config SENSORS_MPQ7932_REGULATOR + bool "Regulator support for MPQ7932" + depends on SENSORS_MPQ7932 && REGULATOR + help + If you say yes here you get six integrated buck converter regulator + support for power management IC MPS MPQ7932. + +config SENSORS_MPQ7932 + tristate "MPS MPQ7932" + help + If you say yes here you get hardware monitoring functionality support + for power management IC MPS MPQ7932. + + This driver can also be built as a module. If so, the module will + be called mpq7932. + config SENSORS_PIM4328 tristate "Flex PIM4328 and compatibles" help @@ -379,6 +395,22 @@ config SENSORS_STPDDC60 This driver can also be built as a module. If so, the module will be called stpddc60. +config SENSORS_TDA38640 + tristate "Infineon TDA38640" + help + If you say yes here you get hardware monitoring support for Infineon + TDA38640. + + This driver can also be built as a module. If so, the module will + be called tda38640. + +config SENSORS_TDA38640_REGULATOR + bool "Regulator support for TDA38640 and compatibles" + depends on SENSORS_TDA38640 && REGULATOR + help + If you say yes here you get regulator support for Infineon + TDA38640 as regulator. + config SENSORS_TPS40422 tristate "TI TPS40422" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 0002dbe22d52..3ae019916267 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -34,11 +34,13 @@ obj-$(CONFIG_SENSORS_MAX8688) += max8688.o obj-$(CONFIG_SENSORS_MP2888) += mp2888.o obj-$(CONFIG_SENSORS_MP2975) += mp2975.o obj-$(CONFIG_SENSORS_MP5023) += mp5023.o +obj-$(CONFIG_SENSORS_MPQ7932) += mpq7932.o obj-$(CONFIG_SENSORS_PLI1209BC) += pli1209bc.o obj-$(CONFIG_SENSORS_PM6764TR) += pm6764tr.o obj-$(CONFIG_SENSORS_PXE1610) += pxe1610.o obj-$(CONFIG_SENSORS_Q54SJ108A2) += q54sj108a2.o obj-$(CONFIG_SENSORS_STPDDC60) += stpddc60.o +obj-$(CONFIG_SENSORS_TDA38640) += tda38640.o obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o obj-$(CONFIG_SENSORS_TPS546D24) += tps546d24.o diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index 79f480b4425d..91df8e895147 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -570,14 +570,14 @@ MODULE_DEVICE_TABLE(i2c, ltc2978_id); #define LTC2978_N_VOLTAGES ((LTC2978_MAX_UV / LTC2978_UV_STEP) + 1) static const struct regulator_desc ltc2978_reg_desc[] = { - PMBUS_REGULATOR_STEP("vout", 0, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), - PMBUS_REGULATOR_STEP("vout", 1, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), - PMBUS_REGULATOR_STEP("vout", 2, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), - PMBUS_REGULATOR_STEP("vout", 3, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), - PMBUS_REGULATOR_STEP("vout", 4, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), - PMBUS_REGULATOR_STEP("vout", 5, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), - PMBUS_REGULATOR_STEP("vout", 6, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), - PMBUS_REGULATOR_STEP("vout", 7, LTC2978_N_VOLTAGES, LTC2978_UV_STEP), + PMBUS_REGULATOR_STEP("vout", 0, LTC2978_N_VOLTAGES, LTC2978_UV_STEP, 0), + PMBUS_REGULATOR_STEP("vout", 1, LTC2978_N_VOLTAGES, LTC2978_UV_STEP, 0), + PMBUS_REGULATOR_STEP("vout", 2, LTC2978_N_VOLTAGES, LTC2978_UV_STEP, 0), + PMBUS_REGULATOR_STEP("vout", 3, LTC2978_N_VOLTAGES, LTC2978_UV_STEP, 0), + PMBUS_REGULATOR_STEP("vout", 4, LTC2978_N_VOLTAGES, LTC2978_UV_STEP, 0), + PMBUS_REGULATOR_STEP("vout", 5, LTC2978_N_VOLTAGES, LTC2978_UV_STEP, 0), + PMBUS_REGULATOR_STEP("vout", 6, LTC2978_N_VOLTAGES, LTC2978_UV_STEP, 0), + PMBUS_REGULATOR_STEP("vout", 7, LTC2978_N_VOLTAGES, LTC2978_UV_STEP, 0), }; static const struct regulator_desc ltc2978_reg_desc_default[] = { diff --git a/drivers/hwmon/pmbus/max16601.c b/drivers/hwmon/pmbus/max16601.c index b628405e6586..6724f723f74c 100644 --- a/drivers/hwmon/pmbus/max16601.c +++ b/drivers/hwmon/pmbus/max16601.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Hardware monitoring driver for Maxim MAX16508, MAX16601 and MAX16602. + * Hardware monitoring driver for Maxim MAX16508, MAX16600, MAX16601, + * and MAX16602. * * Implementation notes: * @@ -31,7 +32,7 @@ #include "pmbus.h" -enum chips { max16508, max16601, max16602 }; +enum chips { max16508, max16600, max16601, max16602 }; #define REG_DEFAULT_NUM_POP 0xc4 #define REG_SETPT_DVID 0xd1 @@ -202,7 +203,7 @@ static int max16601_identify(struct i2c_client *client, else info->vrm_version[0] = vr12; - if (data->id != max16601 && data->id != max16602) + if (data->id != max16600 && data->id != max16601 && data->id != max16602) return 0; reg = i2c_smbus_read_byte_data(client, REG_DEFAULT_NUM_POP); @@ -263,6 +264,7 @@ static void max16601_remove(void *_data) static const struct i2c_device_id max16601_id[] = { {"max16508", max16508}, + {"max16600", max16600}, {"max16601", max16601}, {"max16602", max16602}, {} @@ -281,11 +283,13 @@ static int max16601_get_id(struct i2c_client *client) return -ENODEV; /* - * PMBUS_IC_DEVICE_ID is expected to return "MAX16601y.xx" or - * MAX16602y.xx or "MAX16500y.xx".cdxxcccccccccc + * PMBUS_IC_DEVICE_ID is expected to return MAX1660[012]y.xx" or + * "MAX16500y.xx".cdxxcccccccccc */ if (!strncmp(buf, "MAX16500", 8)) { id = max16508; + } else if (!strncmp(buf, "MAX16600", 8)) { + id = max16600; } else if (!strncmp(buf, "MAX16601", 8)) { id = max16601; } else if (!strncmp(buf, "MAX16602", 8)) { diff --git a/drivers/hwmon/pmbus/mpq7932.c b/drivers/hwmon/pmbus/mpq7932.c new file mode 100644 index 000000000000..ff939881dc3b --- /dev/null +++ b/drivers/hwmon/pmbus/mpq7932.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * mpq7932.c - hwmon with optional regulator driver for mps mpq7932 + * Copyright 2022 Monolithic Power Systems, Inc + * + * Author: Saravanan Sekar <saravanan@linumiz.com> + */ + +#include <linux/bits.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/pmbus.h> +#include "pmbus.h" + +#define MPQ7932_BUCK_UV_MIN 206250 +#define MPQ7932_UV_STEP 6250 +#define MPQ7932_N_VOLTAGES 256 +#define MPQ7932_VOUT_MAX 0xFF +#define MPQ7932_NUM_PAGES 6 + +#define MPQ7932_TON_DELAY 0x60 +#define MPQ7932_VOUT_STARTUP_SLEW 0xA3 +#define MPQ7932_VOUT_SHUTDOWN_SLEW 0xA5 +#define MPQ7932_VOUT_SLEW_MASK GENMASK(1, 0) +#define MPQ7932_TON_DELAY_MASK GENMASK(4, 0) + +struct mpq7932_data { + struct pmbus_driver_info info; + struct pmbus_platform_data pdata; +}; + +#if IS_ENABLED(CONFIG_SENSORS_MPQ7932_REGULATOR) +static struct regulator_desc mpq7932_regulators_desc[] = { + PMBUS_REGULATOR_STEP("buck", 0, MPQ7932_N_VOLTAGES, + MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN), + PMBUS_REGULATOR_STEP("buck", 1, MPQ7932_N_VOLTAGES, + MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN), + PMBUS_REGULATOR_STEP("buck", 2, MPQ7932_N_VOLTAGES, + MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN), + PMBUS_REGULATOR_STEP("buck", 3, MPQ7932_N_VOLTAGES, + MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN), + PMBUS_REGULATOR_STEP("buck", 4, MPQ7932_N_VOLTAGES, + MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN), + PMBUS_REGULATOR_STEP("buck", 5, MPQ7932_N_VOLTAGES, + MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN), +}; +#endif + +static int mpq7932_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + switch (reg) { + /* + * chip supports only byte access for VOUT_COMMAND otherwise + * access results -EREMOTEIO + */ + case PMBUS_VOUT_COMMAND: + return pmbus_write_byte_data(client, page, reg, word & 0xFF); + + default: + return -ENODATA; + } +} + +static int mpq7932_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + switch (reg) { + /* + * chip supports neither (PMBUS_VOUT_MARGIN_HIGH, PMBUS_VOUT_MARGIN_LOW) + * nor (PMBUS_MFR_VOUT_MIN, PMBUS_MFR_VOUT_MAX). As a result set voltage + * fails due to error in pmbus_regulator_get_low_margin, so faked. + */ + case PMBUS_MFR_VOUT_MIN: + return 0; + + case PMBUS_MFR_VOUT_MAX: + return MPQ7932_VOUT_MAX; + + /* + * chip supports only byte access for VOUT_COMMAND otherwise + * access results in -EREMOTEIO + */ + case PMBUS_READ_VOUT: + return pmbus_read_byte_data(client, page, PMBUS_VOUT_COMMAND); + + default: + return -ENODATA; + } +} + +static int mpq7932_probe(struct i2c_client *client) +{ + struct mpq7932_data *data; + struct pmbus_driver_info *info; + struct device *dev = &client->dev; + int i; + + data = devm_kzalloc(dev, sizeof(struct mpq7932_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + info = &data->info; + info->pages = MPQ7932_NUM_PAGES; + info->format[PSC_VOLTAGE_OUT] = direct; + info->m[PSC_VOLTAGE_OUT] = 160; + info->b[PSC_VOLTAGE_OUT] = -33; + for (i = 0; i < info->pages; i++) { + info->func[i] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_STATUS_TEMP; + } + +#if IS_ENABLED(CONFIG_SENSORS_MPQ7932_REGULATOR) + info->num_regulators = ARRAY_SIZE(mpq7932_regulators_desc); + info->reg_desc = mpq7932_regulators_desc; +#endif + + info->read_word_data = mpq7932_read_word_data; + info->write_word_data = mpq7932_write_word_data; + + data->pdata.flags = PMBUS_NO_CAPABILITY; + dev->platform_data = &data->pdata; + + return pmbus_do_probe(client, info); +} + +static const struct of_device_id mpq7932_of_match[] = { + { .compatible = "mps,mpq7932"}, + {}, +}; +MODULE_DEVICE_TABLE(of, mpq7932_of_match); + +static const struct i2c_device_id mpq7932_id[] = { + { "mpq7932", }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, mpq7932_id); + +static struct i2c_driver mpq7932_regulator_driver = { + .driver = { + .name = "mpq7932", + .of_match_table = mpq7932_of_match, + }, + .probe_new = mpq7932_probe, + .id_table = mpq7932_id, +}; +module_i2c_driver(mpq7932_regulator_driver); + +MODULE_AUTHOR("Saravanan Sekar <saravanan@linumiz.com>"); +MODULE_DESCRIPTION("MPQ7932 PMIC regulator driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index 10fb17879f8e..713ea7915425 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -464,7 +464,7 @@ struct pmbus_driver_info { extern const struct regulator_ops pmbus_regulator_ops; /* Macros for filling in array of struct regulator_desc */ -#define PMBUS_REGULATOR_STEP(_name, _id, _voltages, _step) \ +#define PMBUS_REGULATOR_STEP(_name, _id, _voltages, _step, _min_uV) \ [_id] = { \ .name = (_name # _id), \ .id = (_id), \ @@ -475,9 +475,10 @@ extern const struct regulator_ops pmbus_regulator_ops; .owner = THIS_MODULE, \ .n_voltages = _voltages, \ .uV_step = _step, \ + .min_uV = _min_uV, \ } -#define PMBUS_REGULATOR(_name, _id) PMBUS_REGULATOR_STEP(_name, _id, 0, 0) +#define PMBUS_REGULATOR(_name, _id) PMBUS_REGULATOR_STEP(_name, _id, 0, 0, 0) /* Function declarations */ diff --git a/drivers/hwmon/pmbus/tda38640.c b/drivers/hwmon/pmbus/tda38640.c new file mode 100644 index 000000000000..c3e781319cd1 --- /dev/null +++ b/drivers/hwmon/pmbus/tda38640.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Hardware monitoring driver for Infineon TDA38640 + * + * Copyright (c) 2023 9elements GmbH + * + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/regulator/driver.h> +#include "pmbus.h" + +static const struct regulator_desc __maybe_unused tda38640_reg_desc[] = { + PMBUS_REGULATOR("vout", 0), +}; + +static struct pmbus_driver_info tda38640_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_POWER] = linear, + .format[PSC_TEMPERATURE] = linear, + + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_IIN + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_POUT | PMBUS_HAVE_PIN, +#if IS_ENABLED(CONFIG_SENSORS_TDA38640_REGULATOR) + .num_regulators = 1, + .reg_desc = tda38640_reg_desc, +#endif +}; + +static int tda38640_probe(struct i2c_client *client) +{ + return pmbus_do_probe(client, &tda38640_info); +} + +static const struct i2c_device_id tda38640_id[] = { + {"tda38640", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, tda38640_id); + +static const struct of_device_id __maybe_unused tda38640_of_match[] = { + { .compatible = "infineon,tda38640"}, + { }, +}; +MODULE_DEVICE_TABLE(of, tda38640_of_match); + +/* This is the driver that will be inserted */ +static struct i2c_driver tda38640_driver = { + .driver = { + .name = "tda38640", + .of_match_table = of_match_ptr(tda38640_of_match), + }, + .probe_new = tda38640_probe, + .id_table = tda38640_id, +}; + +module_i2c_driver(tda38640_driver); + +MODULE_AUTHOR("Patrick Rudolph <patrick.rudolph@9elements.com>"); +MODULE_DESCRIPTION("PMBus driver for Infineon TDA38640"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index ae4d14257a11..32a41fc56fc9 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -668,7 +668,7 @@ static inline int sht15_calc_humid(struct sht15_data *data) } /** - * sht15_show_status() - show status information in sysfs + * sht15_status_show() - show status information in sysfs * @dev: device. * @attr: device attribute. * @buf: sysfs buffer where information is written to. @@ -690,7 +690,7 @@ static ssize_t sht15_status_show(struct device *dev, } /** - * sht15_store_heater() - change heater state via sysfs + * sht15_status_store() - change heater state via sysfs * @dev: device. * @attr: device attribute. * @buf: sysfs buffer to read the new heater state from. @@ -725,7 +725,7 @@ static ssize_t sht15_status_store(struct device *dev, } /** - * sht15_show_temp() - show temperature measurement value in sysfs + * sht15_temp_show() - show temperature measurement value in sysfs * @dev: device. * @attr: device attribute. * @buf: sysfs buffer where measurement values are written to. @@ -747,7 +747,7 @@ static ssize_t sht15_temp_show(struct device *dev, } /** - * sht15_show_humidity() - show humidity measurement value in sysfs + * sht15_humidity_show() - show humidity measurement value in sysfs * @dev: device. * @attr: device attribute. * @buf: sysfs buffer where measurement values are written to. diff --git a/drivers/hwmon/sht21.c b/drivers/hwmon/sht21.c index e23dbf287233..f50b90198f23 100644 --- a/drivers/hwmon/sht21.c +++ b/drivers/hwmon/sht21.c @@ -114,7 +114,7 @@ out: } /** - * sht21_show_temperature() - show temperature measurement value in sysfs + * sht21_temperature_show() - show temperature measurement value in sysfs * @dev: device * @attr: device attribute * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to @@ -136,7 +136,7 @@ static ssize_t sht21_temperature_show(struct device *dev, } /** - * sht21_show_humidity() - show humidity measurement value in sysfs + * sht21_humidity_show() - show humidity measurement value in sysfs * @dev: device * @attr: device attribute * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to |