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);