thermal/int340x_thermal: Export OEM vendor variables
authorMatthew Garrett <mjg59@google.com>
Tue, 14 Apr 2020 02:09:52 +0000 (19:09 -0700)
committerDaniel Lezcano <daniel.lezcano@linaro.org>
Fri, 29 May 2020 18:26:51 +0000 (20:26 +0200)
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.

Conflicts:
drivers/thermal/intel/int340x_thermal/int3400_thermal.c

Signed-off-by: Matthew Garrett <mjg59@google.com>
Tested-by: Pandruvada, Srinivas <srinivas.pandruvada@linux.intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Link: https://lore.kernel.org/r/20200414020953.255364-2-matthewgarrett@google.com
drivers/thermal/intel/int340x_thermal/int3400_thermal.c

index 0ad0469e91bae1d22da003c1f3a89dd25716a690..87d9b075363cf7e05c2df5850e03b9d495b52f3d 100644 (file)
@@ -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;
 }
 
@@ -338,6 +460,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);
@@ -358,6 +481,8 @@ static int int3400_thermal_probe(struct platform_device *pdev)
 
        int3400_setup_gddv(priv);
 
+       evaluate_odvp(priv);
+
        priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0,
                                                priv, &int3400_thermal_ops,
                                                &int3400_thermal_params, 0, 0);
@@ -389,8 +514,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:
@@ -413,6 +541,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);