[RESEND] thermal: intel: int340x_thermal: New IOCTLs for thermal

Message ID 20230523221339.2377625-1-srinivas.pandruvada@linux.intel.com
State New
Headers
Series [RESEND] thermal: intel: int340x_thermal: New IOCTLs for thermal |

Commit Message

srinivas pandruvada May 23, 2023, 10:13 p.m. UTC
  Export Passive version 2 table similar to the way _TRT and _ART tables
via IOCTLs.

This removes need for binary utility to read ACPI Passive 2 table by
providing open source support. This table already has open source
implementation in the user space thermald, when the table is part of
data vault exported by the int3400 sysfs.

This table is supported in some older platforms before Ice Lake
generation.

Passive 2 tables contain multiple entries. Each entry has following
fields:

Source: Named Reference (String). This is the source device for
temperature.
Target: Named Reference (String). This is the target device to control.
Priority: Priority of this device compared to others.
SamplingPeriod: Time Period in 1/10 of seconds unit.
PassiveTemp: Passive Temperature in 1/10 of Kelvin.
SourceDomain: Domain for the source (00:Processor, others reserved).
ControlKnob: Type of control knob (00:Power Limit 1, others: reserved)
Limit: The target state to set on reaching passive temperature.
This can be a string "max", "min" or a power limit value.
LimitStepSize: Step size during activation.
UnLimitStepSize: Step size during deactivation.
Reserved1: Reserved

Four IOCTLs are added similar to IOCTLs for reading TRT:

ACPI_THERMAL_GET_PSVT_COUNT: Number of passive 2 entries.
ACPI_THERMAL_GET_PSVT_LEN: Total return data size (count x each
passive 2 entry size).
ACPI_THERMAL_GET_PSVT: Get the data as an array of objects with
passive 2 entries.

This change is based on original development done by:
Todd Brandt <todd.e.brandt@linux.intel.com>

Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
---
Resent with the correct series. Sorry about previous posts.

 .../intel/int340x_thermal/acpi_thermal_rel.c  | 218 ++++++++++++++++++
 .../intel/int340x_thermal/acpi_thermal_rel.h  |  57 +++++
 2 files changed, 275 insertions(+)
  

Comments

kernel test robot May 24, 2023, 5:42 a.m. UTC | #1
Hi Srinivas,

kernel test robot noticed the following build warnings:

[auto build test WARNING on rafael-pm/thermal]
[also build test WARNING on linus/master v6.4-rc3 next-20230523]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Srinivas-Pandruvada/thermal-intel-int340x_thermal-New-IOCTLs-for-thermal/20230524-062514
base:   https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git thermal
patch link:    https://lore.kernel.org/r/20230523221339.2377625-1-srinivas.pandruvada%40linux.intel.com
patch subject: [RESEND][PATCH] thermal: intel: int340x_thermal: New IOCTLs for thermal
config: x86_64-randconfig-x083
compiler: gcc-11 (Debian 11.3.0-12) 11.3.0
reproduce (this is a W=1 build):
        # https://github.com/intel-lab-lkp/linux/commit/d9cdbb3a7b52afac5dcbf0aaa2d1593964554c7c
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Srinivas-Pandruvada/thermal-intel-int340x_thermal-New-IOCTLs-for-thermal/20230524-062514
        git checkout d9cdbb3a7b52afac5dcbf0aaa2d1593964554c7c
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 O=build_dir ARCH=x86_64 olddefconfig
        make W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash drivers/thermal/intel/int340x_thermal/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202305241341.gbrnvwNY-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c:214:5: warning: no previous prototype for 'acpi_parse_psvt' [-Wmissing-prototypes]
     214 | int acpi_parse_psvt(acpi_handle handle, int *psvt_count, struct psvt **psvtp)
         |     ^~~~~~~~~~~~~~~


vim +/acpi_parse_psvt +214 drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c

   205	
   206	/*
   207	 * acpi_parse_psvt - Passive Table (PSVT) for passive cooling
   208	 *
   209	 * @handle: ACPI handle of the device which contains PSVT
   210	 * @psvt_count: the number of valid entries resulted from parsing PSVT
   211	 * @psvtp: pointer to array of psvt entries
   212	 *
   213	 */
 > 214	int acpi_parse_psvt(acpi_handle handle, int *psvt_count, struct psvt **psvtp)
   215	{
   216		struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
   217		int nr_bad_entries = 0, revision;
   218		union acpi_object *p;
   219		acpi_status status;
   220		int i, result = 0;
   221		struct psvt *psvts;
   222	
   223		if (!acpi_has_method(handle, "PSVT"))
   224			return -ENODEV;
   225	
   226		status = acpi_evaluate_object(handle, "PSVT", NULL, &buffer);
   227		if (ACPI_FAILURE(status))
   228			return -ENODEV;
   229	
   230		p = buffer.pointer;
   231		if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
   232			result = -EFAULT;
   233			goto end;
   234		}
   235	
   236		/* first package is the revision number */
   237		if (p->package.count > 0) {
   238			union acpi_object *prev = &(p->package.elements[0]);
   239	
   240			if (prev->type == ACPI_TYPE_INTEGER)
   241				revision = (int)prev->integer.value;
   242		} else {
   243			result = -EFAULT;
   244			goto end;
   245		}
   246	
   247		/* Support only version 2 */
   248		if (revision != 2) {
   249			result = -EFAULT;
   250			goto end;
   251		}
   252	
   253		*psvt_count = p->package.count - 1;
   254		if (!*psvt_count) {
   255			result = -EFAULT;
   256			goto end;
   257		}
   258	
   259		psvts = kcalloc(*psvt_count, sizeof(*psvts), GFP_KERNEL);
   260		if (!psvts) {
   261			result = -ENOMEM;
   262			goto end;
   263		}
   264	
   265		/* Start index is 1 because the first package is the revision number */
   266		for (i = 1; i < p->package.count; i++) {
   267			struct acpi_buffer psvt_int_format = { sizeof("RRNNNNNNNNNN"), "RRNNNNNNNNNN" };
   268			struct acpi_buffer psvt_str_format = { sizeof("RRNNNNNSNNNN"), "RRNNNNNSNNNN" };
   269			union acpi_object *package = &(p->package.elements[i]);
   270			struct psvt *psvt = &psvts[i - 1 - nr_bad_entries];
   271			struct acpi_buffer *psvt_format = &psvt_int_format;
   272			struct acpi_buffer element = { 0, NULL };
   273			union acpi_object *knob;
   274			struct acpi_device *res;
   275			struct psvt *psvt_ptr;
   276	
   277			element.length = ACPI_ALLOCATE_BUFFER;
   278			element.pointer = NULL;
   279	
   280			if (package->package.count >= ACPI_NR_PSVT_ELEMENTS) {
   281				knob = &(package->package.elements[ACPI_PSVT_CONTROL_KNOB]);
   282			} else {
   283				nr_bad_entries++;
   284				pr_info("PSVT package %d is invalid, ignored\n", i);
   285				continue;
   286			}
   287	
   288			if (knob->type == ACPI_TYPE_STRING) {
   289				psvt_format = &psvt_str_format;
   290				if (knob->string.length > ACPI_LIMIT_STR_MAX_LEN) {
   291					pr_info("PSVT package %d limit string len exceeds max\n", i);
   292					knob->string.length = ACPI_LIMIT_STR_MAX_LEN;
   293				}
   294			}
   295	
   296			status = acpi_extract_package(&(p->package.elements[i]), psvt_format, &element);
   297			if (ACPI_FAILURE(status)) {
   298				nr_bad_entries++;
   299				pr_info("PSVT package %d is invalid, ignored\n", i);
   300				continue;
   301			}
   302	
   303			psvt_ptr = (struct psvt *)element.pointer;
   304	
   305			memcpy(psvt, psvt_ptr, sizeof(*psvt_ptr));
   306	
   307			/* The limit element can be string or U64 */
   308			psvt->control_knob_type = (u64)knob->type;
   309	
   310			if (knob->type == ACPI_TYPE_STRING) {
   311				memset(&psvt->limit, 0, sizeof(u64));
   312				strncpy(psvt->limit.string, psvt_ptr->limit.str_ptr, knob->string.length);
   313			} else {
   314				psvt->limit.integer = psvt_ptr->limit.integer;
   315			}
   316	
   317			kfree(element.pointer);
   318	
   319			res = acpi_fetch_acpi_dev(psvt->source);
   320			if (!res) {
   321				nr_bad_entries++;
   322				pr_info("Failed to get source ACPI device\n");
   323				continue;
   324			}
   325	
   326			res = acpi_fetch_acpi_dev(psvt->target);
   327			if (!res) {
   328				nr_bad_entries++;
   329				pr_info("Failed to get target ACPI device\n");
   330				continue;
   331			}
   332		}
   333	
   334		/* don't count bad entries */
   335		*psvt_count -= nr_bad_entries;
   336	
   337		if (!*psvt_count) {
   338			result = -EFAULT;
   339			kfree(psvts);
   340			goto end;
   341		}
   342	
   343		*psvtp = psvts;
   344	
   345		return 0;
   346	
   347	end:
   348		kfree(buffer.pointer);
   349		return result;
   350	}
   351
  
kernel test robot May 24, 2023, 10:59 a.m. UTC | #2
Hi Srinivas,

kernel test robot noticed the following build warnings:

[auto build test WARNING on rafael-pm/thermal]
[also build test WARNING on linus/master v6.4-rc3 next-20230524]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Srinivas-Pandruvada/thermal-intel-int340x_thermal-New-IOCTLs-for-thermal/20230524-062514
base:   https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git thermal
patch link:    https://lore.kernel.org/r/20230523221339.2377625-1-srinivas.pandruvada%40linux.intel.com
patch subject: [RESEND][PATCH] thermal: intel: int340x_thermal: New IOCTLs for thermal
config: x86_64-randconfig-x075
compiler: clang version 14.0.6 (https://github.com/llvm/llvm-project f28c006a5895fc0e329fe15fead81e37457cb1d1)
reproduce (this is a W=1 build):
        mkdir -p ~/bin
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/d9cdbb3a7b52afac5dcbf0aaa2d1593964554c7c
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Srinivas-Pandruvada/thermal-intel-int340x_thermal-New-IOCTLs-for-thermal/20230524-062514
        git checkout d9cdbb3a7b52afac5dcbf0aaa2d1593964554c7c
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang ~/bin/make.cross W=1 O=build_dir ARCH=x86_64 olddefconfig
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang ~/bin/make.cross W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash drivers/thermal/intel/int340x_thermal/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202305241812.MJMEMRiJ-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c:214:5: warning: no previous prototype for function 'acpi_parse_psvt' [-Wmissing-prototypes]
   int acpi_parse_psvt(acpi_handle handle, int *psvt_count, struct psvt **psvtp)
       ^
   drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c:214:1: note: declare 'static' if the function is not intended to be used outside of this translation unit
   int acpi_parse_psvt(acpi_handle handle, int *psvt_count, struct psvt **psvtp)
   ^
   static 
>> drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c:240:7: warning: variable 'revision' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
                   if (prev->type == ACPI_TYPE_INTEGER)
                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c:248:6: note: uninitialized use occurs here
           if (revision != 2) {
               ^~~~~~~~
   drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c:240:3: note: remove the 'if' if its condition is always true
                   if (prev->type == ACPI_TYPE_INTEGER)
                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c:217:34: note: initialize the variable 'revision' to silence this warning
           int nr_bad_entries = 0, revision;
                                           ^
                                            = 0
   2 warnings generated.

Kconfig warnings: (for reference only)
   WARNING: unmet direct dependencies detected for CRYPTO_CRC32C_INTEL
   Depends on [n]: CRYPTO [=y] && !KMSAN [=y] && X86 [=y]
   Selected by [m]:
   - ISCSI_TARGET [=m] && TARGET_CORE [=y] && INET [=y] && X86 [=y]


vim +/acpi_parse_psvt +214 drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c

   205	
   206	/*
   207	 * acpi_parse_psvt - Passive Table (PSVT) for passive cooling
   208	 *
   209	 * @handle: ACPI handle of the device which contains PSVT
   210	 * @psvt_count: the number of valid entries resulted from parsing PSVT
   211	 * @psvtp: pointer to array of psvt entries
   212	 *
   213	 */
 > 214	int acpi_parse_psvt(acpi_handle handle, int *psvt_count, struct psvt **psvtp)
   215	{
   216		struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
   217		int nr_bad_entries = 0, revision;
   218		union acpi_object *p;
   219		acpi_status status;
   220		int i, result = 0;
   221		struct psvt *psvts;
   222	
   223		if (!acpi_has_method(handle, "PSVT"))
   224			return -ENODEV;
   225	
   226		status = acpi_evaluate_object(handle, "PSVT", NULL, &buffer);
   227		if (ACPI_FAILURE(status))
   228			return -ENODEV;
   229	
   230		p = buffer.pointer;
   231		if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
   232			result = -EFAULT;
   233			goto end;
   234		}
   235	
   236		/* first package is the revision number */
   237		if (p->package.count > 0) {
   238			union acpi_object *prev = &(p->package.elements[0]);
   239	
 > 240			if (prev->type == ACPI_TYPE_INTEGER)
   241				revision = (int)prev->integer.value;
   242		} else {
   243			result = -EFAULT;
   244			goto end;
   245		}
   246	
   247		/* Support only version 2 */
   248		if (revision != 2) {
   249			result = -EFAULT;
   250			goto end;
   251		}
   252	
   253		*psvt_count = p->package.count - 1;
   254		if (!*psvt_count) {
   255			result = -EFAULT;
   256			goto end;
   257		}
   258	
   259		psvts = kcalloc(*psvt_count, sizeof(*psvts), GFP_KERNEL);
   260		if (!psvts) {
   261			result = -ENOMEM;
   262			goto end;
   263		}
   264	
   265		/* Start index is 1 because the first package is the revision number */
   266		for (i = 1; i < p->package.count; i++) {
   267			struct acpi_buffer psvt_int_format = { sizeof("RRNNNNNNNNNN"), "RRNNNNNNNNNN" };
   268			struct acpi_buffer psvt_str_format = { sizeof("RRNNNNNSNNNN"), "RRNNNNNSNNNN" };
   269			union acpi_object *package = &(p->package.elements[i]);
   270			struct psvt *psvt = &psvts[i - 1 - nr_bad_entries];
   271			struct acpi_buffer *psvt_format = &psvt_int_format;
   272			struct acpi_buffer element = { 0, NULL };
   273			union acpi_object *knob;
   274			struct acpi_device *res;
   275			struct psvt *psvt_ptr;
   276	
   277			element.length = ACPI_ALLOCATE_BUFFER;
   278			element.pointer = NULL;
   279	
   280			if (package->package.count >= ACPI_NR_PSVT_ELEMENTS) {
   281				knob = &(package->package.elements[ACPI_PSVT_CONTROL_KNOB]);
   282			} else {
   283				nr_bad_entries++;
   284				pr_info("PSVT package %d is invalid, ignored\n", i);
   285				continue;
   286			}
   287	
   288			if (knob->type == ACPI_TYPE_STRING) {
   289				psvt_format = &psvt_str_format;
   290				if (knob->string.length > ACPI_LIMIT_STR_MAX_LEN) {
   291					pr_info("PSVT package %d limit string len exceeds max\n", i);
   292					knob->string.length = ACPI_LIMIT_STR_MAX_LEN;
   293				}
   294			}
   295	
   296			status = acpi_extract_package(&(p->package.elements[i]), psvt_format, &element);
   297			if (ACPI_FAILURE(status)) {
   298				nr_bad_entries++;
   299				pr_info("PSVT package %d is invalid, ignored\n", i);
   300				continue;
   301			}
   302	
   303			psvt_ptr = (struct psvt *)element.pointer;
   304	
   305			memcpy(psvt, psvt_ptr, sizeof(*psvt_ptr));
   306	
   307			/* The limit element can be string or U64 */
   308			psvt->control_knob_type = (u64)knob->type;
   309	
   310			if (knob->type == ACPI_TYPE_STRING) {
   311				memset(&psvt->limit, 0, sizeof(u64));
   312				strncpy(psvt->limit.string, psvt_ptr->limit.str_ptr, knob->string.length);
   313			} else {
   314				psvt->limit.integer = psvt_ptr->limit.integer;
   315			}
   316	
   317			kfree(element.pointer);
   318	
   319			res = acpi_fetch_acpi_dev(psvt->source);
   320			if (!res) {
   321				nr_bad_entries++;
   322				pr_info("Failed to get source ACPI device\n");
   323				continue;
   324			}
   325	
   326			res = acpi_fetch_acpi_dev(psvt->target);
   327			if (!res) {
   328				nr_bad_entries++;
   329				pr_info("Failed to get target ACPI device\n");
   330				continue;
   331			}
   332		}
   333	
   334		/* don't count bad entries */
   335		*psvt_count -= nr_bad_entries;
   336	
   337		if (!*psvt_count) {
   338			result = -EFAULT;
   339			kfree(psvts);
   340			goto end;
   341		}
   342	
   343		*psvtp = psvts;
   344	
   345		return 0;
   346	
   347	end:
   348		kfree(buffer.pointer);
   349		return result;
   350	}
   351
  

Patch

diff --git a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c
index 01b80331eab6..c6c4c6ef9e32 100644
--- a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c
+++ b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c
@@ -203,6 +203,151 @@  int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp,
 }
 EXPORT_SYMBOL(acpi_parse_art);
 
+/*
+ * acpi_parse_psvt - Passive Table (PSVT) for passive cooling
+ *
+ * @handle: ACPI handle of the device which contains PSVT
+ * @psvt_count: the number of valid entries resulted from parsing PSVT
+ * @psvtp: pointer to array of psvt entries
+ *
+ */
+int acpi_parse_psvt(acpi_handle handle, int *psvt_count, struct psvt **psvtp)
+{
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	int nr_bad_entries = 0, revision;
+	union acpi_object *p;
+	acpi_status status;
+	int i, result = 0;
+	struct psvt *psvts;
+
+	if (!acpi_has_method(handle, "PSVT"))
+		return -ENODEV;
+
+	status = acpi_evaluate_object(handle, "PSVT", NULL, &buffer);
+	if (ACPI_FAILURE(status))
+		return -ENODEV;
+
+	p = buffer.pointer;
+	if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
+		result = -EFAULT;
+		goto end;
+	}
+
+	/* first package is the revision number */
+	if (p->package.count > 0) {
+		union acpi_object *prev = &(p->package.elements[0]);
+
+		if (prev->type == ACPI_TYPE_INTEGER)
+			revision = (int)prev->integer.value;
+	} else {
+		result = -EFAULT;
+		goto end;
+	}
+
+	/* Support only version 2 */
+	if (revision != 2) {
+		result = -EFAULT;
+		goto end;
+	}
+
+	*psvt_count = p->package.count - 1;
+	if (!*psvt_count) {
+		result = -EFAULT;
+		goto end;
+	}
+
+	psvts = kcalloc(*psvt_count, sizeof(*psvts), GFP_KERNEL);
+	if (!psvts) {
+		result = -ENOMEM;
+		goto end;
+	}
+
+	/* Start index is 1 because the first package is the revision number */
+	for (i = 1; i < p->package.count; i++) {
+		struct acpi_buffer psvt_int_format = { sizeof("RRNNNNNNNNNN"), "RRNNNNNNNNNN" };
+		struct acpi_buffer psvt_str_format = { sizeof("RRNNNNNSNNNN"), "RRNNNNNSNNNN" };
+		union acpi_object *package = &(p->package.elements[i]);
+		struct psvt *psvt = &psvts[i - 1 - nr_bad_entries];
+		struct acpi_buffer *psvt_format = &psvt_int_format;
+		struct acpi_buffer element = { 0, NULL };
+		union acpi_object *knob;
+		struct acpi_device *res;
+		struct psvt *psvt_ptr;
+
+		element.length = ACPI_ALLOCATE_BUFFER;
+		element.pointer = NULL;
+
+		if (package->package.count >= ACPI_NR_PSVT_ELEMENTS) {
+			knob = &(package->package.elements[ACPI_PSVT_CONTROL_KNOB]);
+		} else {
+			nr_bad_entries++;
+			pr_info("PSVT package %d is invalid, ignored\n", i);
+			continue;
+		}
+
+		if (knob->type == ACPI_TYPE_STRING) {
+			psvt_format = &psvt_str_format;
+			if (knob->string.length > ACPI_LIMIT_STR_MAX_LEN) {
+				pr_info("PSVT package %d limit string len exceeds max\n", i);
+				knob->string.length = ACPI_LIMIT_STR_MAX_LEN;
+			}
+		}
+
+		status = acpi_extract_package(&(p->package.elements[i]), psvt_format, &element);
+		if (ACPI_FAILURE(status)) {
+			nr_bad_entries++;
+			pr_info("PSVT package %d is invalid, ignored\n", i);
+			continue;
+		}
+
+		psvt_ptr = (struct psvt *)element.pointer;
+
+		memcpy(psvt, psvt_ptr, sizeof(*psvt_ptr));
+
+		/* The limit element can be string or U64 */
+		psvt->control_knob_type = (u64)knob->type;
+
+		if (knob->type == ACPI_TYPE_STRING) {
+			memset(&psvt->limit, 0, sizeof(u64));
+			strncpy(psvt->limit.string, psvt_ptr->limit.str_ptr, knob->string.length);
+		} else {
+			psvt->limit.integer = psvt_ptr->limit.integer;
+		}
+
+		kfree(element.pointer);
+
+		res = acpi_fetch_acpi_dev(psvt->source);
+		if (!res) {
+			nr_bad_entries++;
+			pr_info("Failed to get source ACPI device\n");
+			continue;
+		}
+
+		res = acpi_fetch_acpi_dev(psvt->target);
+		if (!res) {
+			nr_bad_entries++;
+			pr_info("Failed to get target ACPI device\n");
+			continue;
+		}
+	}
+
+	/* don't count bad entries */
+	*psvt_count -= nr_bad_entries;
+
+	if (!*psvt_count) {
+		result = -EFAULT;
+		kfree(psvts);
+		goto end;
+	}
+
+	*psvtp = psvts;
+
+	return 0;
+
+end:
+	kfree(buffer.pointer);
+	return result;
+}
 
 /* get device name from acpi handle */
 static void get_single_name(acpi_handle handle, char *name)
@@ -289,6 +434,57 @@  static int fill_trt(char __user *ubuf)
 	return ret;
 }
 
+static int fill_psvt(char __user *ubuf)
+{
+	int i, ret, count, psvt_len;
+	union psvt_object *psvt_user;
+	struct psvt *psvts;
+
+	ret = acpi_parse_psvt(acpi_thermal_rel_handle, &count, &psvts);
+	if (ret)
+		return ret;
+
+	psvt_len = count * sizeof(*psvt_user);
+
+	psvt_user = kzalloc(psvt_len, GFP_KERNEL);
+	if (!psvt_user) {
+		ret = -ENOMEM;
+		goto free_psvt;
+	}
+
+	/* now fill in user psvt data */
+	for (i = 0; i < count; i++) {
+		/* userspace psvt needs device name instead of acpi reference */
+		get_single_name(psvts[i].source, psvt_user[i].source_device);
+		get_single_name(psvts[i].target, psvt_user[i].target_device);
+
+		psvt_user[i].priority = psvts[i].priority;
+		psvt_user[i].sample_period = psvts[i].sample_period;
+		psvt_user[i].passive_temp = psvts[i].passive_temp;
+		psvt_user[i].source_domain = psvts[i].source_domain;
+		psvt_user[i].control_knob = psvts[i].control_knob;
+		psvt_user[i].step_size = psvts[i].step_size;
+		psvt_user[i].limit_coeff = psvts[i].limit_coeff;
+		psvt_user[i].unlimit_coeff = psvts[i].unlimit_coeff;
+		psvt_user[i].control_knob_type = psvts[i].control_knob_type;
+		if (psvt_user[i].control_knob_type == ACPI_TYPE_STRING)
+			strncpy(psvt_user[i].limit.string, psvts[i].limit.string,
+				ACPI_LIMIT_STR_MAX_LEN);
+		else
+			psvt_user[i].limit.integer = psvts[i].limit.integer;
+
+	}
+
+	if (copy_to_user(ubuf, psvt_user, psvt_len))
+		ret = -EFAULT;
+
+	kfree(psvt_user);
+
+free_psvt:
+	kfree(psvts);
+	return ret;
+}
+
 static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,
 				   unsigned long __arg)
 {
@@ -298,6 +494,7 @@  static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,
 	char __user *arg = (void __user *)__arg;
 	struct trt *trts = NULL;
 	struct art *arts = NULL;
+	struct psvt *psvts;
 
 	switch (cmd) {
 	case ACPI_THERMAL_GET_TRT_COUNT:
@@ -336,6 +533,27 @@  static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,
 	case ACPI_THERMAL_GET_ART:
 		return fill_art(arg);
 
+	case ACPI_THERMAL_GET_PSVT_COUNT:
+		ret = acpi_parse_psvt(acpi_thermal_rel_handle, &count, &psvts);
+		if (!ret) {
+			kfree(psvts);
+			return put_user(count, (unsigned long __user *)__arg);
+		}
+		return ret;
+
+	case ACPI_THERMAL_GET_PSVT_LEN:
+		/* total length of the data retrieved (count * PSVT entry size) */
+		ret = acpi_parse_psvt(acpi_thermal_rel_handle, &count, &psvts);
+		length = count * sizeof(union psvt_object);
+		if (!ret) {
+			kfree(psvts);
+			return put_user(length, (unsigned long __user *)__arg);
+		}
+		return ret;
+
+	case ACPI_THERMAL_GET_PSVT:
+		return fill_psvt(arg);
+
 	default:
 		return -ENOTTY;
 	}
diff --git a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.h b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.h
index 78d942477035..ac376d8f9ee4 100644
--- a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.h
+++ b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.h
@@ -14,6 +14,16 @@ 
 #define ACPI_THERMAL_GET_TRT	_IOR(ACPI_THERMAL_MAGIC, 5, unsigned long)
 #define ACPI_THERMAL_GET_ART	_IOR(ACPI_THERMAL_MAGIC, 6, unsigned long)
 
+/*
+ * ACPI_THERMAL_GET_PSVT_COUNT = Number of PSVT entries
+ * ACPI_THERMAL_GET_PSVT_LEN = Total return data size (PSVT count x each
+ * PSVT entry size)
+ * ACPI_THERMAL_GET_PSVT = Get the data as an array of psvt_objects
+ */
+#define ACPI_THERMAL_GET_PSVT_LEN _IOR(ACPI_THERMAL_MAGIC, 7, unsigned long)
+#define ACPI_THERMAL_GET_PSVT_COUNT _IOR(ACPI_THERMAL_MAGIC, 8, unsigned long)
+#define ACPI_THERMAL_GET_PSVT	_IOR(ACPI_THERMAL_MAGIC, 9, unsigned long)
+
 struct art {
 	acpi_handle source;
 	acpi_handle target;
@@ -43,6 +53,32 @@  struct trt {
 	u64 reserved4;
 } __packed;
 
+#define ACPI_NR_PSVT_ELEMENTS	12
+#define ACPI_PSVT_CONTROL_KNOB	7
+#define ACPI_LIMIT_STR_MAX_LEN	8
+
+struct psvt {
+	acpi_handle source;
+	acpi_handle target;
+	u64 priority;
+	u64 sample_period;
+	u64 passive_temp;
+	u64 source_domain;
+	u64 control_knob;
+	union {
+		/* For limit_type = ACPI_TYPE_INTEGER */
+		u64 integer;
+		/* For limit_type = ACPI_TYPE_STRING */
+		char string[ACPI_LIMIT_STR_MAX_LEN];
+		char *str_ptr;
+	} limit;
+	u64 step_size;
+	u64 limit_coeff;
+	u64 unlimit_coeff;
+	/* Spec calls this field reserved, so we borrow it for type info */
+	u64 control_knob_type; /* ACPI_TYPE_STRING or ACPI_TYPE_INTEGER */
+} __packed;
+
 #define ACPI_NR_ART_ELEMENTS 13
 /* for usrspace */
 union art_object {
@@ -77,6 +113,27 @@  union trt_object {
 	u64 __data[8];
 };
 
+union psvt_object {
+	struct {
+		char source_device[8];
+		char target_device[8];
+		u64 priority;
+		u64 sample_period;
+		u64 passive_temp;
+		u64 source_domain;
+		u64 control_knob;
+		union {
+			u64 integer;
+			char string[ACPI_LIMIT_STR_MAX_LEN];
+		} limit;
+		u64 step_size;
+		u64 limit_coeff;
+		u64 unlimit_coeff;
+		u64 control_knob_type;
+	};
+	u64 __data[ACPI_NR_PSVT_ELEMENTS];
+};
+
 #ifdef __KERNEL__
 int acpi_thermal_rel_misc_device_add(acpi_handle handle);
 int acpi_thermal_rel_misc_device_remove(acpi_handle handle);