Commit f1ba9eb8 authored by Srinivas Pandruvada's avatar Srinivas Pandruvada Committed by Zhang Rui

thermal: int340x: processor_thermal: support acpi notification

BIOS/EC can change PPCC element dynamically and inform OS about the change.
When this driver receives notification, it will read PPCC element again.
Signed-off-by: default avatarSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Signed-off-by: default avatarZhang Rui <rui.zhang@intel.com>
parent b721ca0d
...@@ -198,49 +198,33 @@ static struct thermal_zone_device_ops proc_thermal_local_ops = { ...@@ -198,49 +198,33 @@ static struct thermal_zone_device_ops proc_thermal_local_ops = {
.get_temp = proc_thermal_get_zone_temp, .get_temp = proc_thermal_get_zone_temp,
}; };
static int proc_thermal_add(struct device *dev, static int proc_thermal_read_ppcc(struct proc_thermal_device *proc_priv)
struct proc_thermal_device **priv)
{ {
struct proc_thermal_device *proc_priv; int i;
struct acpi_device *adev;
acpi_status status; acpi_status status;
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *elements, *ppcc; union acpi_object *elements, *ppcc;
union acpi_object *p; union acpi_object *p;
unsigned long long tmp; int ret = 0;
struct thermal_zone_device_ops *ops = NULL;
int i;
int ret;
adev = ACPI_COMPANION(dev); status = acpi_evaluate_object(proc_priv->adev->handle, "PPCC",
if (!adev) NULL, &buf);
return -ENODEV;
status = acpi_evaluate_object(adev->handle, "PPCC", NULL, &buf);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return -ENODEV; return -ENODEV;
p = buf.pointer; p = buf.pointer;
if (!p || (p->type != ACPI_TYPE_PACKAGE)) { if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
dev_err(dev, "Invalid PPCC data\n"); dev_err(proc_priv->dev, "Invalid PPCC data\n");
ret = -EFAULT; ret = -EFAULT;
goto free_buffer; goto free_buffer;
} }
if (!p->package.count) { if (!p->package.count) {
dev_err(dev, "Invalid PPCC package size\n"); dev_err(proc_priv->dev, "Invalid PPCC package size\n");
ret = -EFAULT; ret = -EFAULT;
goto free_buffer; goto free_buffer;
} }
proc_priv = devm_kzalloc(dev, sizeof(*proc_priv), GFP_KERNEL);
if (!proc_priv) {
ret = -ENOMEM;
goto free_buffer;
}
proc_priv->dev = dev;
proc_priv->adev = adev;
for (i = 0; i < min((int)p->package.count - 1, 2); ++i) { for (i = 0; i < min((int)p->package.count - 1, 2); ++i) {
elements = &(p->package.elements[i+1]); elements = &(p->package.elements[i+1]);
if (elements->type != ACPI_TYPE_PACKAGE || if (elements->type != ACPI_TYPE_PACKAGE ||
...@@ -257,12 +241,62 @@ static int proc_thermal_add(struct device *dev, ...@@ -257,12 +241,62 @@ static int proc_thermal_add(struct device *dev,
proc_priv->power_limits[i].step_uw = ppcc[5].integer.value; proc_priv->power_limits[i].step_uw = ppcc[5].integer.value;
} }
free_buffer:
kfree(buf.pointer);
return ret;
}
#define PROC_POWER_CAPABILITY_CHANGED 0x83
static void proc_thermal_notify(acpi_handle handle, u32 event, void *data)
{
struct proc_thermal_device *proc_priv = data;
if (!proc_priv)
return;
switch (event) {
case PROC_POWER_CAPABILITY_CHANGED:
proc_thermal_read_ppcc(proc_priv);
int340x_thermal_zone_device_update(proc_priv->int340x_zone);
break;
default:
dev_err(proc_priv->dev, "Unsupported event [0x%x]\n", event);
break;
}
}
static int proc_thermal_add(struct device *dev,
struct proc_thermal_device **priv)
{
struct proc_thermal_device *proc_priv;
struct acpi_device *adev;
acpi_status status;
unsigned long long tmp;
struct thermal_zone_device_ops *ops = NULL;
int ret;
adev = ACPI_COMPANION(dev);
if (!adev)
return -ENODEV;
proc_priv = devm_kzalloc(dev, sizeof(*proc_priv), GFP_KERNEL);
if (!proc_priv)
return -ENOMEM;
proc_priv->dev = dev;
proc_priv->adev = adev;
*priv = proc_priv; *priv = proc_priv;
ret = proc_thermal_read_ppcc(proc_priv);
if (!ret) {
ret = sysfs_create_group(&dev->kobj, ret = sysfs_create_group(&dev->kobj,
&power_limit_attribute_group); &power_limit_attribute_group);
}
if (ret) if (ret)
goto free_buffer; return ret;
status = acpi_evaluate_integer(adev->handle, "_TMP", NULL, &tmp); status = acpi_evaluate_integer(adev->handle, "_TMP", NULL, &tmp);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
...@@ -274,20 +308,32 @@ static int proc_thermal_add(struct device *dev, ...@@ -274,20 +308,32 @@ static int proc_thermal_add(struct device *dev,
proc_priv->int340x_zone = int340x_thermal_zone_add(adev, ops); proc_priv->int340x_zone = int340x_thermal_zone_add(adev, ops);
if (IS_ERR(proc_priv->int340x_zone)) { if (IS_ERR(proc_priv->int340x_zone)) {
sysfs_remove_group(&proc_priv->dev->kobj,
&power_limit_attribute_group);
ret = PTR_ERR(proc_priv->int340x_zone); ret = PTR_ERR(proc_priv->int340x_zone);
goto remove_group;
} else } else
ret = 0; ret = 0;
free_buffer: ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
kfree(buf.pointer); proc_thermal_notify,
(void *)proc_priv);
if (ret)
goto remove_zone;
return 0;
remove_zone:
int340x_thermal_zone_remove(proc_priv->int340x_zone);
remove_group:
sysfs_remove_group(&proc_priv->dev->kobj,
&power_limit_attribute_group);
return ret; return ret;
} }
static void proc_thermal_remove(struct proc_thermal_device *proc_priv) static void proc_thermal_remove(struct proc_thermal_device *proc_priv)
{ {
acpi_remove_notify_handler(proc_priv->adev->handle,
ACPI_DEVICE_NOTIFY, proc_thermal_notify);
int340x_thermal_zone_remove(proc_priv->int340x_zone); int340x_thermal_zone_remove(proc_priv->int340x_zone);
sysfs_remove_group(&proc_priv->dev->kobj, sysfs_remove_group(&proc_priv->dev->kobj,
&power_limit_attribute_group); &power_limit_attribute_group);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment