Re: [PATCH v2 1/2] ACPI PSVT table addition to acpi_thermal_rel
From: Pandruvada, Srinivas
Date: Tue May 23 2023 - 18:03:06 EST
Sent by mistake. Please ignore.
Thanks,
Srinivas
On Tue, 2023-05-23 at 15:01 -0700, Srinivas Pandruvada wrote:
> From: Todd Brandt <todd.e.brandt@xxxxxxxxxxxxxxx>
>
> Added PSVT table to acpi_thermal_rel driver. Its exported to
> userspace
> via an ioctl to the acpi_thermal_rel device. The PSVT table consists
> of 12 fields:
>
> source: B0D4
> target: SEN3
> priority: 2
> sample_period: 300
> target_domain: 3032
> passive_temp: 9
> source_domain: 65536
> control_knob: 12000
> limit: 500
> limit_step_size: 10
> unlimit_step_size: 20
> control_knob_type: 1=int, 2=string
>
> The control_knob_type field was added since the control knob value
> can be either a string or integer. The field is necessary for
> userspace to know how to read its value. The ACPI spec calls for
> a reserved field at the end, so I just borrowed the space for the
> new val.
>
> Signed-off-by: Todd Brandt <todd.e.brandt@xxxxxxxxxxxxxxx>
> ---
> drivers/thermal/int340x_thermal/acpi_thermal_rel.c | 188
> +++++++++++++++++++++
> drivers/thermal/int340x_thermal/acpi_thermal_rel.h | 55 ++++++
> 2 files changed, 243 insertions(+)
>
> diff --git a/drivers/thermal/int340x_thermal/acpi_thermal_rel.c
> b/drivers/thermal/int340x_thermal/acpi_thermal_rel.c
> index 2c2ec76..0e8849d 100644
> --- a/drivers/thermal/int340x_thermal/acpi_thermal_rel.c
> +++ b/drivers/thermal/int340x_thermal/acpi_thermal_rel.c
> @@ -221,6 +221,128 @@ end:
> }
> EXPORT_SYMBOL(acpi_parse_art);
>
> +/*
> + * acpi_parse_psvt - Passive Table PSVT for passive cooling
> + *
> + * @handle: ACPI handle of the device contains PSVT
> + * @psvt_revision: the revision number of the PSVT, used to
> interpret limit
> + * @psvt_count: the number of valid entries resulted from parsing
> PSVT
> + * @psvtp: pointer to array of psvt entries in parsing result
> + * @create_dev: whether to create platform devices for target and
> source
> + *
> + */
> +int acpi_parse_psvt(acpi_handle handle, int *psvt_revision, int
> *psvt_count,
> + struct psvt **psvtp, bool create_dev)
> +{
> + acpi_status status;
> + int result = 0;
> + int i;
> + int nr_bad_entries = 0;
> + struct psvt *psvts;
> + struct acpi_device *adev;
> + union acpi_object *p;
> + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
> + struct acpi_buffer element = { 0, NULL };
> + struct acpi_buffer psvt_int_format = {
> sizeof("RRNNNNNNNNNN"), "RRNNNNNNNNNN" };
> + struct acpi_buffer psvt_str_format = {
> sizeof("RRNNNNNSNNNN"), "RRNNNNNSNNNN" };
> +
> + 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)) {
> + pr_err("Invalid PSVT data\n");
> + 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)
> + *psvt_revision = (int)prev->integer.value;
> + } else {
> + pr_err("Invalid PSVT data\n");
> + result = -EFAULT;
> + goto end;
> + }
> +
> + *psvt_count = p->package.count - 1;
> + psvts = kzalloc(*psvt_count * sizeof(struct psvt),
> GFP_KERNEL);
> + if (!psvts) {
> + result = -ENOMEM;
> + goto end;
> + }
> +
> + for (i = 1; i < p->package.count; i++) {
> + struct psvt *psvt = &psvts[i - 1 - nr_bad_entries];
> + struct psvt *psvt_ptr;
> + struct acpi_buffer *psvt_format = &psvt_int_format;
> + union acpi_object *package = &(p-
> >package.elements[i]);
> + union acpi_object *knob;
> +
> + 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_warn("PSVT package %d is invalid,
> ignored\n", i);
> + continue;
> + }
> +
> + if(knob->type == ACPI_TYPE_STRING) {
> + psvt_format = &psvt_str_format;
> + if(knob->string.length > 8) {
> + pr_warn("PSVT package %d ctrlknob
> string len > 8\n", i);
> + knob->string.length = 8;
> + }
> + }
> + status = acpi_extract_package(&(p-
> >package.elements[i]),
> + psvt_format, &element);
> + if (ACPI_FAILURE(status)) {
> + nr_bad_entries++;
> + pr_warn("PSVT package %d is invalid,
> ignored\n", i);
> + continue;
> + }
> +
> + psvt_ptr = (struct psvt *)element.pointer;
> + memcpy(psvt, psvt_ptr, sizeof(struct psvt));
> + psvt->control_knob_type = (u64)knob->type;
> + if(knob->type == ACPI_TYPE_STRING) {
> + char *val = psvt_ptr-
> >control_knob.string_ptr;
> + memset(&psvt->control_knob, 0, sizeof(u64));
> + memcpy(&psvt->control_knob, val, knob-
> >string.length*sizeof(char));
> + }
> + kfree(element.pointer);
> +
> + if (!create_dev)
> + continue;
> +
> + result = acpi_bus_get_device(psvt->source, &adev);
> + if (result)
> + pr_warn("Failed to get source ACPI
> device\n");
> +
> + result = acpi_bus_get_device(psvt->target, &adev);
> + if (result)
> + pr_warn("Failed to get target ACPI
> device\n");
> + }
> +
> + result = 0;
> +
> + *psvtp = psvts;
> + /* don't count bad entries */
> + *psvt_count -= nr_bad_entries;
> +end:
> + kfree(buffer.pointer);
> + return result;
> +}
> +EXPORT_SYMBOL(acpi_parse_psvt);
>
> /* get device name from acpi handle */
> static void get_single_name(acpi_handle handle, char *name)
> @@ -306,15 +428,55 @@ free_trt:
> return ret;
> }
>
> +static int fill_psvt(char __user *ubuf)
> +{
> + int i;
> + int ret;
> + int count;
> + int revision = 0;
> + int psvt_len;
> + struct psvt *psvts = NULL;
> + union psvt_object *psvt_user;
> +
> + ret = acpi_parse_psvt(acpi_thermal_rel_handle, &revision,
> + &count, &psvts, false);
> + if (ret)
> + goto free_psvt;
> + psvt_len = count * sizeof(union psvt_object);
> + 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);
> + /* copy the rest int data in addition to source and
> target */
> + memcpy(&psvt_user[i].priority, &psvts[i].priority,
> + sizeof(u64) * (ACPI_NR_PSVT_ELEMENTS - 2));
> + }
> +
> + 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)
> {
> int ret = 0;
> unsigned long length = 0;
> + int revision = 0;
> int count = 0;
> char __user *arg = (void __user *)__arg;
> struct trt *trts = NULL;
> struct art *arts = NULL;
> + struct psvt *psvts = NULL;
>
> switch (cmd) {
> case ACPI_THERMAL_GET_TRT_COUNT:
> @@ -353,6 +515,32 @@ 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_REV:
> + ret = acpi_parse_psvt(acpi_thermal_rel_handle,
> &revision, &count,
> + &psvts, false);
> + kfree(psvts);
> + if (!ret)
> + return put_user(revision, (unsigned long
> __user *)__arg);
> + return ret;
> + case ACPI_THERMAL_GET_PSVT_COUNT:
> + ret = acpi_parse_psvt(acpi_thermal_rel_handle,
> &revision, &count,
> + &psvts, false);
> + kfree(psvts);
> + if (!ret)
> + 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,
> &revision, &count,
> + &psvts, false);
> + kfree(psvts);
> + length = count * sizeof(union psvt_object);
> + if (!ret)
> + 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/int340x_thermal/acpi_thermal_rel.h
> b/drivers/thermal/int340x_thermal/acpi_thermal_rel.h
> index f00700b..fd8f3db 100644
> --- a/drivers/thermal/int340x_thermal/acpi_thermal_rel.h
> +++ b/drivers/thermal/int340x_thermal/acpi_thermal_rel.h
> @@ -13,6 +13,18 @@
> #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_REV = revision number
> + * identifies limit type: 1=true porportional limit, 2=depth limit
> + * ACPI_THERMAL_GET_PSVT_COUNT = number of package entries
> + * ACPI_THERMAL_GET_PSVT_LEN = total return data size (pkg count x
> pkg size)
> + * ACPI_THERMAL_GET_PSVT = get the data as an array of psvt_objects
> + */
> +#define ACPI_THERMAL_GET_PSVT_REV _IOR(ACPI_THERMAL_MAGIC, 7,
> unsigned long)
> +#define ACPI_THERMAL_GET_PSVT_LEN _IOR(ACPI_THERMAL_MAGIC, 8,
> unsigned long)
> +#define ACPI_THERMAL_GET_PSVT_COUNT _IOR(ACPI_THERMAL_MAGIC, 9,
> unsigned long)
> +#define ACPI_THERMAL_GET_PSVT _IOR(ACPI_THERMAL_MAGIC, 10, unsigned
> long)
> +
> struct art {
> acpi_handle source;
> acpi_handle target;
> @@ -40,6 +52,28 @@ struct trt {
> u64 reverved4;
> } __packed;
>
> +#define ACPI_NR_PSVT_ELEMENTS 12
> +#define ACPI_PSVT_CONTROL_KNOB 7
> +struct psvt {
> + acpi_handle source;
> + acpi_handle target;
> + u64 priority;
> + u64 sample_period;
> + u64 target_domain;
> + u64 passive_temp;
> + u64 source_domain;
> + union {
> + u64 integer; /* control_knob_type =
> ACPI_TYPE_INTEGER */
> + char string[8]; /* control_knob_type =
> ACPI_TYPE_STRING */
> + char *string_ptr;
> + } control_knob;
> + u64 limit;
> + u64 limit_step_size;
> + u64 unlimit_step_size;
> + /* ACPI 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 {
> @@ -72,6 +106,27 @@ union trt_object {
> u64 __data[8];
> };
>
> +union psvt_object {
> + struct {
> + char source_device[8]; /* ACPI single name */
> + char target_device[8]; /* ACPI single name */
> + u64 priority;
> + u64 sample_period;
> + u64 target_domain;
> + u64 passive_temp;
> + u64 source_domain;
> + union {
> + u64 integer; /* control_knob_type =
> ACPI_TYPE_INTEGER */
> + char string[8]; /* control_knob_type =
> ACPI_TYPE_STRING */
> + } control_knob;
> + u64 limit;
> + u64 limit_step_size;
> + u64 unlimit_step_size;
> + u64 control_knob_type; /* ACPI_TYPE_STRING or
> ACPI_TYPE_INTEGER */
> + };
> + 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);