diff --git a/Documentation/ABI/testing/sysfs-class-thermal b/Documentation/ABI/testing/sysfs-class-thermal index 2c52bb1f864ce..8eee37982b2a8 100644 --- a/Documentation/ABI/testing/sysfs-class-thermal +++ b/Documentation/ABI/testing/sysfs-class-thermal @@ -203,7 +203,7 @@ Description: - for generic ACPI: should be "Fan", "Processor" or "LCD" - for memory controller device on intel_menlow platform: - should be "Memory controller". + should be "Memory controller". RO, Required diff --git a/Documentation/driver-api/thermal/index.rst b/Documentation/driver-api/thermal/index.rst index 4cb0b9b6bfb8b..030306ffa408b 100644 --- a/Documentation/driver-api/thermal/index.rst +++ b/Documentation/driver-api/thermal/index.rst @@ -17,3 +17,4 @@ Thermal intel_powerclamp nouveau_thermal x86_pkg_temperature_thermal + intel_dptf diff --git a/Documentation/driver-api/thermal/intel_dptf.rst b/Documentation/driver-api/thermal/intel_dptf.rst new file mode 100644 index 0000000000000..96668dca753a8 --- /dev/null +++ b/Documentation/driver-api/thermal/intel_dptf.rst @@ -0,0 +1,272 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=============================================================== +Intel(R) Dynamic Platform and Thermal Framework Sysfs Interface +=============================================================== + +:Copyright: |copy| 2022 Intel Corporation + +:Author: Srinivas Pandruvada + +Introduction +------------ + +Intel(R) Dynamic Platform and Thermal Framework (DPTF) is a platform +level hardware/software solution for power and thermal management. + +As a container for multiple power/thermal technologies, DPTF provides +a coordinated approach for different policies to effect the hardware +state of a system. + +Since it is a platform level framework, this has several components. +Some parts of the technology is implemented in the firmware and uses +ACPI and PCI devices to expose various features for monitoring and +control. Linux has a set of kernel drivers exposing hardware interface +to user space. This allows user space thermal solutions like +"Linux Thermal Daemon" to read platform specific thermal and power +tables to deliver adequate performance while keeping the system under +thermal limits. + +DPTF ACPI Drivers interface +---------------------------- + +:file:`/sys/bus/platform/devices//uuids`, where +=INT3400|INTC1040|INTC1041|INTC10A0 + +``available_uuids`` (RO) + A set of UUIDs strings presenting available policies + which should be notified to the firmware when the + user space can support those policies. + + UUID strings: + + "42A441D6-AE6A-462b-A84B-4A8CE79027D3" : Passive 1 + + "3A95C389-E4B8-4629-A526-C52C88626BAE" : Active + + "97C68AE7-15FA-499c-B8C9-5DA81D606E0A" : Critical + + "63BE270F-1C11-48FD-A6F7-3AF253FF3E2D" : Adaptive performance + + "5349962F-71E6-431D-9AE8-0A635B710AEE" : Emergency call + + "9E04115A-AE87-4D1C-9500-0F3E340BFE75" : Passive 2 + + "F5A35014-C209-46A4-993A-EB56DE7530A1" : Power Boss + + "6ED722A7-9240-48A5-B479-31EEF723D7CF" : Virtual Sensor + + "16CAF1B7-DD38-40ED-B1C1-1B8A1913D531" : Cooling mode + + "BE84BABF-C4D4-403D-B495-3128FD44dAC1" : HDC + +``current_uuid`` (RW) + User space can write strings from available UUIDs, one at a + time. + +:file:`/sys/bus/platform/devices//`, where +=INT3400|INTC1040|INTC1041|INTC10A0 + +``imok`` (WO) + User space daemon write 1 to respond to firmware event + for sending keep alive notification. User space receives + THERMAL_EVENT_KEEP_ALIVE kobject uevent notification when + firmware calls for user space to respond with imok ACPI + method. + +``odvp*`` (RO) + Firmware thermal status variable values. Thermal tables + calls for different processing based on these variable + values. + +``data_vault`` (RO) + Binary thermal table. Refer to + https:/github.com/intel/thermal_daemon for decoding + thermal table. + + +ACPI Thermal Relationship table interface +------------------------------------------ + +:file:`/dev/acpi_thermal_rel` + + This device provides IOCTL interface to read standard ACPI + thermal relationship tables via ACPI methods _TRT and _ART. + These IOCTLs are defined in + drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.h + + IOCTLs: + + ACPI_THERMAL_GET_TRT_LEN: Get length of TRT table + + ACPI_THERMAL_GET_ART_LEN: Get length of ART table + + ACPI_THERMAL_GET_TRT_COUNT: Number of records in TRT table + + ACPI_THERMAL_GET_ART_COUNT: Number of records in ART table + + ACPI_THERMAL_GET_TRT: Read binary TRT table, length to read is + provided via argument to ioctl(). + + ACPI_THERMAL_GET_ART: Read binary ART table, length to read is + provided via argument to ioctl(). + +DPTF ACPI Sensor drivers +------------------------- + +DPTF Sensor drivers are presented as standard thermal sysfs thermal_zone. + + +DPTF ACPI Cooling drivers +-------------------------- + +DPTF cooling drivers are presented as standard thermal sysfs cooling_device. + + +DPTF Processor thermal PCI Driver interface +-------------------------------------------- + +:file:`/sys/bus/pci/devices/0000\:00\:04.0/power_limits/` + +Refer to Documentation/power/powercap/powercap.rst for powercap +ABI. + +``power_limit_0_max_uw`` (RO) + Maximum powercap sysfs constraint_0_power_limit_uw for Intel RAPL + +``power_limit_0_step_uw`` (RO) + Power limit increment/decrements for Intel RAPL constraint 0 power limit + +``power_limit_0_min_uw`` (RO) + Minimum powercap sysfs constraint_0_power_limit_uw for Intel RAPL + +``power_limit_0_tmin_us`` (RO) + Minimum powercap sysfs constraint_0_time_window_us for Intel RAPL + +``power_limit_0_tmax_us`` (RO) + Maximum powercap sysfs constraint_0_time_window_us for Intel RAPL + +``power_limit_1_max_uw`` (RO) + Maximum powercap sysfs constraint_1_power_limit_uw for Intel RAPL + +``power_limit_1_step_uw`` (RO) + Power limit increment/decrements for Intel RAPL constraint 1 power limit + +``power_limit_1_min_uw`` (RO) + Minimum powercap sysfs constraint_1_power_limit_uw for Intel RAPL + +``power_limit_1_tmin_us`` (RO) + Minimum powercap sysfs constraint_1_time_window_us for Intel RAPL + +``power_limit_1_tmax_us`` (RO) + Maximum powercap sysfs constraint_1_time_window_us for Intel RAPL + +:file:`/sys/bus/pci/devices/0000\:00\:04.0/` + +``tcc_offset_degree_celsius`` (RW) + TCC offset from the critical temperature where hardware will throttle + CPU. + +:file:`/sys/bus/pci/devices/0000\:00\:04.0/workload_request` + +``workload_available_types`` (RO) + Available workload types. User space can specify one of the workload type + it is currently executing via workload_type. For example: idle, bursty, + sustained etc. + +``workload_type`` (RW) + User space can specify any one of the available workload type using + this interface. + +DPTF Processor thermal RFIM interface +-------------------------------------------- + +RFIM interface allows adjustment of FIVR (Fully Integrated Voltage Regulator) +and DDR (Double Data Rate)frequencies to avoid RF interference with WiFi and 5G. + +Switching voltage regulators (VR) generate radiated EMI or RFI at the +fundamental frequency and its harmonics. Some harmonics may interfere +with very sensitive wireless receivers such as Wi-Fi and cellular that +are integrated into host systems like notebook PCs. One of mitigation +methods is requesting SOC integrated VR (IVR) switching frequency to a +small % and shift away the switching noise harmonic interference from +radio channels. OEM or ODMs can use the driver to control SOC IVR +operation within the range where it does not impact IVR performance. + +DRAM devices of DDR IO interface and their power plane can generate EMI +at the data rates. Similar to IVR control mechanism, Intel offers a +mechanism by which DDR data rates can be changed if several conditions +are met: there is strong RFI interference because of DDR; CPU power +management has no other restriction in changing DDR data rates; +PC ODMs enable this feature (real time DDR RFI Mitigation referred to as +DDR-RFIM) for Wi-Fi from BIOS. + + +FIVR attributes + +:file:`/sys/bus/pci/devices/0000\:00\:04.0/fivr/` + +``vco_ref_code_lo`` (RW) + The VCO reference code is an 11-bit field and controls the FIVR + switching frequency. This is the 3-bit LSB field. + +``vco_ref_code_hi`` (RW) + The VCO reference code is an 11-bit field and controls the FIVR + switching frequency. This is the 8-bit MSB field. + +``spread_spectrum_pct`` (RW) + Set the FIVR spread spectrum clocking percentage + +``spread_spectrum_clk_enable`` (RW) + Enable/disable of the FIVR spread spectrum clocking feature + +``rfi_vco_ref_code`` (RW) + This field is a read only status register which reflects the + current FIVR switching frequency + +``fivr_fffc_rev`` (RW) + This field indicated the revision of the FIVR HW. + + +DVFS attributes + +:file:`/sys/bus/pci/devices/0000\:00\:04.0/dvfs/` + +``rfi_restriction_run_busy`` (RW) + Request the restriction of specific DDR data rate and set this + value 1. Self reset to 0 after operation. + +``rfi_restriction_err_code`` (RW) + 0 :Request is accepted, 1:Feature disabled, + 2: the request restricts more points than it is allowed + +``rfi_restriction_data_rate_Delta`` (RW) + Restricted DDR data rate for RFI protection: Lower Limit + +``rfi_restriction_data_rate_Base`` (RW) + Restricted DDR data rate for RFI protection: Upper Limit + +``ddr_data_rate_point_0`` (RO) + DDR data rate selection 1st point + +``ddr_data_rate_point_1`` (RO) + DDR data rate selection 2nd point + +``ddr_data_rate_point_2`` (RO) + DDR data rate selection 3rd point + +``ddr_data_rate_point_3`` (RO) + DDR data rate selection 4th point + +``rfi_disable (RW)`` + Disable DDR rate change feature + +DPTF Power supply and Battery Interface +---------------------------------------- + +Refer to Documentation/ABI/testing/sysfs-platform-dptf + +DPTF Fan Control +---------------------------------------- + +Refer to Documentation/admin-guide/acpi/fan_performance_states.rst diff --git a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c index e90690a234c40..01b80331eab66 100644 --- a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c +++ b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c @@ -72,7 +72,6 @@ int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp, int i; int nr_bad_entries = 0; struct trt *trts; - struct acpi_device *adev; union acpi_object *p; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer element = { 0, NULL }; @@ -112,12 +111,10 @@ int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp, if (!create_dev) continue; - result = acpi_bus_get_device(trt->source, &adev); - if (result) + if (!acpi_fetch_acpi_dev(trt->source)) pr_warn("Failed to get source ACPI device\n"); - result = acpi_bus_get_device(trt->target, &adev); - if (result) + if (!acpi_fetch_acpi_dev(trt->target)) pr_warn("Failed to get target ACPI device\n"); } @@ -149,7 +146,6 @@ int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp, int i; int nr_bad_entries = 0; struct art *arts; - struct acpi_device *adev; union acpi_object *p; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer element = { 0, NULL }; @@ -191,16 +187,11 @@ int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp, if (!create_dev) continue; - if (art->source) { - result = acpi_bus_get_device(art->source, &adev); - if (result) - pr_warn("Failed to get source ACPI device\n"); - } - if (art->target) { - result = acpi_bus_get_device(art->target, &adev); - if (result) - pr_warn("Failed to get target ACPI device\n"); - } + if (!acpi_fetch_acpi_dev(art->source)) + pr_warn("Failed to get source ACPI device\n"); + + if (!acpi_fetch_acpi_dev(art->target)) + pr_warn("Failed to get target ACPI device\n"); } *artp = arts; diff --git a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c index 4f478812cb514..4954800b98502 100644 --- a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c @@ -17,8 +17,8 @@ #define INT3400_KEEP_ALIVE 0xA0 enum int3400_thermal_uuid { + INT3400_THERMAL_ACTIVE = 0, INT3400_THERMAL_PASSIVE_1, - INT3400_THERMAL_ACTIVE, INT3400_THERMAL_CRITICAL, INT3400_THERMAL_ADAPTIVE_PERFORMANCE, INT3400_THERMAL_EMERGENCY_CALL_MODE, @@ -31,8 +31,8 @@ enum int3400_thermal_uuid { }; static char *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = { - "42A441D6-AE6A-462b-A84B-4A8CE79027D3", "3A95C389-E4B8-4629-A526-C52C88626BAE", + "42A441D6-AE6A-462b-A84B-4A8CE79027D3", "97C68AE7-15FA-499c-B8C9-5DA81D606E0A", "63BE270F-1C11-48FD-A6F7-3AF253FF3E2D", "5349962F-71E6-431D-9AE8-0A635B710AEE", @@ -53,12 +53,13 @@ struct int3400_thermal_priv { struct art *arts; int trt_count; struct trt *trts; - u8 uuid_bitmap; + u32 uuid_bitmap; int rel_misc_dev_res; int current_uuid_index; char *data_vault; int odvp_count; int *odvp; + u32 os_uuid_mask; struct odvp_attr *odvp_attrs; }; @@ -142,12 +143,55 @@ static ssize_t current_uuid_show(struct device *dev, struct device_attribute *devattr, char *buf) { struct int3400_thermal_priv *priv = dev_get_drvdata(dev); + int i, length = 0; - if (priv->current_uuid_index == -1) - return sprintf(buf, "INVALID\n"); + if (priv->current_uuid_index > 0) + return sprintf(buf, "%s\n", + int3400_thermal_uuids[priv->current_uuid_index]); - return sprintf(buf, "%s\n", - int3400_thermal_uuids[priv->current_uuid_index]); + for (i = 0; i <= INT3400_THERMAL_CRITICAL; i++) { + if (priv->os_uuid_mask & BIT(i)) + length += scnprintf(&buf[length], + PAGE_SIZE - length, + "%s\n", + int3400_thermal_uuids[i]); + } + + if (length) + return length; + + return sprintf(buf, "INVALID\n"); +} + +static int int3400_thermal_run_osc(acpi_handle handle, char *uuid_str, int *enable) +{ + u32 ret, buf[2]; + acpi_status status; + int result = 0; + struct acpi_osc_context context = { + .uuid_str = NULL, + .rev = 1, + .cap.length = 8, + }; + + context.uuid_str = uuid_str; + + buf[OSC_QUERY_DWORD] = 0; + buf[OSC_SUPPORT_DWORD] = *enable; + + context.cap.pointer = buf; + + status = acpi_run_osc(handle, &context); + if (ACPI_SUCCESS(status)) { + ret = *((u32 *)(context.ret.pointer + 4)); + if (ret != *enable) + result = -EPERM; + } else + result = -EPERM; + + kfree(context.ret.pointer); + + return result; } static ssize_t current_uuid_store(struct device *dev, @@ -164,16 +208,47 @@ static ssize_t current_uuid_store(struct device *dev, * If we have a list of supported UUIDs, make sure * this one is supported. */ - if (priv->uuid_bitmap && - !(priv->uuid_bitmap & (1 << i))) + if (priv->uuid_bitmap & BIT(i)) { + priv->current_uuid_index = i; + return count; + } + + /* + * There is support of only 3 policies via the new + * _OSC to inform OS capability: + * INT3400_THERMAL_ACTIVE + * INT3400_THERMAL_PASSIVE_1 + * INT3400_THERMAL_CRITICAL + */ + + if (i > INT3400_THERMAL_CRITICAL) return -EINVAL; - priv->current_uuid_index = i; - return count; + priv->os_uuid_mask |= BIT(i); + + break; } } - return -EINVAL; + if (priv->os_uuid_mask) { + int cap, ret; + + /* + * Capability bits: + * Bit 0: set to 1 to indicate DPTF is active + * Bi1 1: set to 1 to active cooling is supported by user space daemon + * Bit 2: set to 1 to passive cooling is supported by user space daemon + * Bit 3: set to 1 to critical trip is handled by user space daemon + */ + cap = ((priv->os_uuid_mask << 1) | 0x01); + ret = int3400_thermal_run_osc(priv->adev->handle, + "b23ba85d-c8b7-3542-88de-8de2ffcfd698", + &cap); + if (ret) + return ret; + } + + return count; } static DEVICE_ATTR_RW(current_uuid); @@ -236,41 +311,6 @@ static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv) return result; } -static int int3400_thermal_run_osc(acpi_handle handle, - enum int3400_thermal_uuid uuid, bool enable) -{ - u32 ret, buf[2]; - acpi_status status; - int result = 0; - struct acpi_osc_context context = { - .uuid_str = NULL, - .rev = 1, - .cap.length = 8, - }; - - if (uuid < 0 || uuid >= INT3400_THERMAL_MAXIMUM_UUID) - return -EINVAL; - - context.uuid_str = int3400_thermal_uuids[uuid]; - - buf[OSC_QUERY_DWORD] = 0; - buf[OSC_SUPPORT_DWORD] = enable; - - context.cap.pointer = buf; - - status = acpi_run_osc(handle, &context); - if (ACPI_SUCCESS(status)) { - ret = *((u32 *)(context.ret.pointer + 4)); - if (ret != enable) - result = -EPERM; - } else - result = -EPERM; - - kfree(context.ret.pointer); - - return result; -} - static ssize_t odvp_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -426,10 +466,18 @@ static int int3400_thermal_change_mode(struct thermal_zone_device *thermal, if (!priv) return -EINVAL; - if (mode != thermal->mode) + if (mode != thermal->mode) { + int enabled; + + if (priv->current_uuid_index < 0 || + priv->current_uuid_index >= INT3400_THERMAL_MAXIMUM_UUID) + return -EINVAL; + + enabled = (mode == THERMAL_DEVICE_ENABLED); result = int3400_thermal_run_osc(priv->adev->handle, - priv->current_uuid_index, - mode == THERMAL_DEVICE_ENABLED); + int3400_thermal_uuids[priv->current_uuid_index], + &enabled); + } evaluate_odvp(priv); @@ -468,6 +516,11 @@ static void int3400_setup_gddv(struct int3400_thermal_priv *priv) priv->data_vault = kmemdup(obj->package.elements[0].buffer.pointer, obj->package.elements[0].buffer.length, GFP_KERNEL); + if (!priv->data_vault) { + kfree(buffer.pointer); + return; + } + bin_attr_data_vault.private = priv->data_vault; bin_attr_data_vault.size = obj->package.elements[0].buffer.length; kfree(buffer.pointer); diff --git a/drivers/thermal/intel/intel_powerclamp.c b/drivers/thermal/intel/intel_powerclamp.c index 14256421d98c2..c841ab37e7c6d 100644 --- a/drivers/thermal/intel/intel_powerclamp.c +++ b/drivers/thermal/intel/intel_powerclamp.c @@ -556,12 +556,9 @@ static void end_power_clamp(void) * stop faster. */ clamping = false; - if (bitmap_weight(cpu_clamping_mask, num_possible_cpus())) { - for_each_set_bit(i, cpu_clamping_mask, num_possible_cpus()) { - pr_debug("clamping worker for cpu %d alive, destroy\n", - i); - stop_power_clamp_worker(i); - } + for_each_set_bit(i, cpu_clamping_mask, num_possible_cpus()) { + pr_debug("clamping worker for cpu %d alive, destroy\n", i); + stop_power_clamp_worker(i); } }