Message ID | 20200414020953.255364-2-matthewgarrett@google.com (mailing list archive) |
---|---|
State | Not Applicable, archived |
Delegated to: | Zhang Rui |
Headers | show |
Series | [V2,1/3] thermal/int340x_thermal: Export GDDV | expand |
On Mon, 2020-04-13 at 19:09 -0700, Matthew Garrett wrote: > From: Matthew Garrett <mjg59@google.com> > > The platform vendor may expose an array of OEM-specific values to be > used > in determining DPTF policy. These are obtained via the ODVP method, > and > then simply exposed in sysfs. In addition, they are updated when a > notification is received or when the DPTF policy is changed by > userland. > > Signed-off-by: Matthew Garrett <mjg59@google.com> Tested-by: Pandruvada, Srinivas <srinivas.pandruvada@linux.intel.com> > --- > .../intel/int340x_thermal/int3400_thermal.c | 132 > +++++++++++++++++- > 1 file changed, 131 insertions(+), 1 deletion(-) > > diff --git a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c > b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c > index 00a7732724cd0..3919098a73b47 100644 > --- a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c > +++ b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c > @@ -13,6 +13,7 @@ > #include "acpi_thermal_rel.h" > > #define INT3400_THERMAL_TABLE_CHANGED 0x83 > +#define INT3400_ODVP_CHANGED 0x88 > > enum int3400_thermal_uuid { > INT3400_THERMAL_PASSIVE_1, > @@ -41,8 +42,11 @@ static char > *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = { > "BE84BABF-C4D4-403D-B495-3128FD44dAC1", > }; > > +struct odvp_attr; > + > struct int3400_thermal_priv { > struct acpi_device *adev; > + struct platform_device *pdev; > struct thermal_zone_device *thermal; > int mode; > int art_count; > @@ -53,6 +57,17 @@ struct int3400_thermal_priv { > int rel_misc_dev_res; > int current_uuid_index; > char *data_vault; > + int odvp_count; > + int *odvp; > + struct odvp_attr *odvp_attrs; > +}; > + > +static int evaluate_odvp(struct int3400_thermal_priv *priv); > + > +struct odvp_attr { > + int odvp; > + struct int3400_thermal_priv *priv; > + struct kobj_attribute attr; > }; > > static ssize_t data_vault_read(struct file *file, struct kobject > *kobj, > @@ -210,9 +225,110 @@ static int int3400_thermal_run_osc(acpi_handle > handle, > result = -EPERM; > > kfree(context.ret.pointer); > + > return result; > } > > +static ssize_t odvp_show(struct kobject *kobj, struct kobj_attribute > *attr, > + char *buf) > +{ > + struct odvp_attr *odvp_attr; > + > + odvp_attr = container_of(attr, struct odvp_attr, attr); > + > + return sprintf(buf, "%d\n", odvp_attr->priv->odvp[odvp_attr- > >odvp]); > +} > + > +static void cleanup_odvp(struct int3400_thermal_priv *priv) > +{ > + int i; > + > + if (priv->odvp_attrs) { > + for (i = 0; i < priv->odvp_count; i++) { > + sysfs_remove_file(&priv->pdev->dev.kobj, > + &priv- > >odvp_attrs[i].attr.attr); > + kfree(priv->odvp_attrs[i].attr.attr.name); > + } > + kfree(priv->odvp_attrs); > + } > + kfree(priv->odvp); > + priv->odvp_count = 0; > +} > + > +static int evaluate_odvp(struct int3400_thermal_priv *priv) > +{ > + struct acpi_buffer odvp = { ACPI_ALLOCATE_BUFFER, NULL }; > + union acpi_object *obj = NULL; > + acpi_status status; > + int i, ret; > + > + status = acpi_evaluate_object(priv->adev->handle, "ODVP", NULL, > &odvp); > + if (ACPI_FAILURE(status)) { > + ret = -EINVAL; > + goto out_err; > + } > + > + obj = odvp.pointer; > + if (obj->type != ACPI_TYPE_PACKAGE) { > + ret = -EINVAL; > + goto out_err; > + } > + > + if (priv->odvp == NULL) { > + priv->odvp_count = obj->package.count; > + priv->odvp = kmalloc_array(priv->odvp_count, > sizeof(int), > + GFP_KERNEL); > + if (!priv->odvp) { > + ret = -ENOMEM; > + goto out_err; > + } > + } > + > + if (priv->odvp_attrs == NULL) { > + priv->odvp_attrs = kcalloc(priv->odvp_count, > + sizeof(struct odvp_attr), > + GFP_KERNEL); > + if (!priv->odvp_attrs) { > + ret = -ENOMEM; > + goto out_err; > + } > + for (i = 0; i < priv->odvp_count; i++) { > + struct odvp_attr *odvp = &priv->odvp_attrs[i]; > + > + sysfs_attr_init(&odvp->attr.attr); > + odvp->priv = priv; > + odvp->odvp = i; > + odvp->attr.attr.name = kasprintf(GFP_KERNEL, > + "odvp%d", i); > + > + if (!odvp->attr.attr.name) { > + ret = -ENOMEM; > + goto out_err; > + } > + odvp->attr.attr.mode = 0444; > + odvp->attr.show = odvp_show; > + odvp->attr.store = NULL; > + ret = sysfs_create_file(&priv->pdev->dev.kobj, > + &odvp->attr.attr); > + if (ret) > + goto out_err; > + } > + } > + > + for (i = 0; i < obj->package.count; i++) { > + if (obj->package.elements[i].type == ACPI_TYPE_INTEGER) > + priv->odvp[i] = obj- > >package.elements[i].integer.value; > + } > + > + kfree(obj); > + return 0; > + > +out_err: > + cleanup_odvp(priv); > + kfree(obj); > + return ret; > +} > + > static void int3400_notify(acpi_handle handle, > u32 event, > void *data) > @@ -236,6 +352,9 @@ static void int3400_notify(acpi_handle handle, > kobject_uevent_env(&priv->thermal->device.kobj, > KOBJ_CHANGE, > thermal_prop); > break; > + case INT3400_ODVP_CHANGED: > + evaluate_odvp(priv); > + break; > default: > /* Ignore unknown notification codes sent to INT3400 > device */ > break; > @@ -285,6 +404,9 @@ static int int3400_thermal_set_mode(struct > thermal_zone_device *thermal, > priv- > >current_uuid_index, > enable); > } > + > + evaluate_odvp(priv); > + > return result; > } > > @@ -336,6 +458,7 @@ static int int3400_thermal_probe(struct > platform_device *pdev) > if (!priv) > return -ENOMEM; > > + priv->pdev = pdev; > priv->adev = adev; > > result = int3400_thermal_get_uuids(priv); > @@ -356,6 +479,8 @@ static int int3400_thermal_probe(struct > platform_device *pdev) > > int3400_setup_gddv(priv); > > + evaluate_odvp(priv); > + > int3400_thermal_ops.get_mode = int3400_thermal_get_mode; > int3400_thermal_ops.set_mode = int3400_thermal_set_mode; > > @@ -390,8 +515,11 @@ static int int3400_thermal_probe(struct > platform_device *pdev) > return 0; > > free_sysfs: > - if (priv->data_vault) > + cleanup_odvp(priv); > + if (priv->data_vault) { > sysfs_remove_group(&pdev->dev.kobj, > &data_attribute_group); > + kfree(priv->data_vault); > + } > free_uuid: > sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group); > free_rel_misc: > @@ -414,6 +542,8 @@ static int int3400_thermal_remove(struct > platform_device *pdev) > priv->adev->handle, ACPI_DEVICE_NOTIFY, > int3400_notify); > > + cleanup_odvp(priv); > + > if (!priv->rel_misc_dev_res) > acpi_thermal_rel_misc_device_remove(priv->adev- > >handle); >
diff --git a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c index 00a7732724cd0..3919098a73b47 100644 --- a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c @@ -13,6 +13,7 @@ #include "acpi_thermal_rel.h" #define INT3400_THERMAL_TABLE_CHANGED 0x83 +#define INT3400_ODVP_CHANGED 0x88 enum int3400_thermal_uuid { INT3400_THERMAL_PASSIVE_1, @@ -41,8 +42,11 @@ static char *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = { "BE84BABF-C4D4-403D-B495-3128FD44dAC1", }; +struct odvp_attr; + struct int3400_thermal_priv { struct acpi_device *adev; + struct platform_device *pdev; struct thermal_zone_device *thermal; int mode; int art_count; @@ -53,6 +57,17 @@ struct int3400_thermal_priv { int rel_misc_dev_res; int current_uuid_index; char *data_vault; + int odvp_count; + int *odvp; + struct odvp_attr *odvp_attrs; +}; + +static int evaluate_odvp(struct int3400_thermal_priv *priv); + +struct odvp_attr { + int odvp; + struct int3400_thermal_priv *priv; + struct kobj_attribute attr; }; static ssize_t data_vault_read(struct file *file, struct kobject *kobj, @@ -210,9 +225,110 @@ static int int3400_thermal_run_osc(acpi_handle handle, result = -EPERM; kfree(context.ret.pointer); + return result; } +static ssize_t odvp_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + struct odvp_attr *odvp_attr; + + odvp_attr = container_of(attr, struct odvp_attr, attr); + + return sprintf(buf, "%d\n", odvp_attr->priv->odvp[odvp_attr->odvp]); +} + +static void cleanup_odvp(struct int3400_thermal_priv *priv) +{ + int i; + + if (priv->odvp_attrs) { + for (i = 0; i < priv->odvp_count; i++) { + sysfs_remove_file(&priv->pdev->dev.kobj, + &priv->odvp_attrs[i].attr.attr); + kfree(priv->odvp_attrs[i].attr.attr.name); + } + kfree(priv->odvp_attrs); + } + kfree(priv->odvp); + priv->odvp_count = 0; +} + +static int evaluate_odvp(struct int3400_thermal_priv *priv) +{ + struct acpi_buffer odvp = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj = NULL; + acpi_status status; + int i, ret; + + status = acpi_evaluate_object(priv->adev->handle, "ODVP", NULL, &odvp); + if (ACPI_FAILURE(status)) { + ret = -EINVAL; + goto out_err; + } + + obj = odvp.pointer; + if (obj->type != ACPI_TYPE_PACKAGE) { + ret = -EINVAL; + goto out_err; + } + + if (priv->odvp == NULL) { + priv->odvp_count = obj->package.count; + priv->odvp = kmalloc_array(priv->odvp_count, sizeof(int), + GFP_KERNEL); + if (!priv->odvp) { + ret = -ENOMEM; + goto out_err; + } + } + + if (priv->odvp_attrs == NULL) { + priv->odvp_attrs = kcalloc(priv->odvp_count, + sizeof(struct odvp_attr), + GFP_KERNEL); + if (!priv->odvp_attrs) { + ret = -ENOMEM; + goto out_err; + } + for (i = 0; i < priv->odvp_count; i++) { + struct odvp_attr *odvp = &priv->odvp_attrs[i]; + + sysfs_attr_init(&odvp->attr.attr); + odvp->priv = priv; + odvp->odvp = i; + odvp->attr.attr.name = kasprintf(GFP_KERNEL, + "odvp%d", i); + + if (!odvp->attr.attr.name) { + ret = -ENOMEM; + goto out_err; + } + odvp->attr.attr.mode = 0444; + odvp->attr.show = odvp_show; + odvp->attr.store = NULL; + ret = sysfs_create_file(&priv->pdev->dev.kobj, + &odvp->attr.attr); + if (ret) + goto out_err; + } + } + + for (i = 0; i < obj->package.count; i++) { + if (obj->package.elements[i].type == ACPI_TYPE_INTEGER) + priv->odvp[i] = obj->package.elements[i].integer.value; + } + + kfree(obj); + return 0; + +out_err: + cleanup_odvp(priv); + kfree(obj); + return ret; +} + static void int3400_notify(acpi_handle handle, u32 event, void *data) @@ -236,6 +352,9 @@ static void int3400_notify(acpi_handle handle, kobject_uevent_env(&priv->thermal->device.kobj, KOBJ_CHANGE, thermal_prop); break; + case INT3400_ODVP_CHANGED: + evaluate_odvp(priv); + break; default: /* Ignore unknown notification codes sent to INT3400 device */ break; @@ -285,6 +404,9 @@ static int int3400_thermal_set_mode(struct thermal_zone_device *thermal, priv->current_uuid_index, enable); } + + evaluate_odvp(priv); + return result; } @@ -336,6 +458,7 @@ static int int3400_thermal_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + priv->pdev = pdev; priv->adev = adev; result = int3400_thermal_get_uuids(priv); @@ -356,6 +479,8 @@ static int int3400_thermal_probe(struct platform_device *pdev) int3400_setup_gddv(priv); + evaluate_odvp(priv); + int3400_thermal_ops.get_mode = int3400_thermal_get_mode; int3400_thermal_ops.set_mode = int3400_thermal_set_mode; @@ -390,8 +515,11 @@ static int int3400_thermal_probe(struct platform_device *pdev) return 0; free_sysfs: - if (priv->data_vault) + cleanup_odvp(priv); + if (priv->data_vault) { sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group); + kfree(priv->data_vault); + } free_uuid: sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group); free_rel_misc: @@ -414,6 +542,8 @@ static int int3400_thermal_remove(struct platform_device *pdev) priv->adev->handle, ACPI_DEVICE_NOTIFY, int3400_notify); + cleanup_odvp(priv); + if (!priv->rel_misc_dev_res) acpi_thermal_rel_misc_device_remove(priv->adev->handle);