Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+77 −31
Original line number Diff line number Diff line
@@ -198,49 +198,33 @@ static struct thermal_zone_device_ops proc_thermal_local_ops = {
	.get_temp       = proc_thermal_get_zone_temp,
};

static int proc_thermal_add(struct device *dev,
			    struct proc_thermal_device **priv)
static int proc_thermal_read_ppcc(struct proc_thermal_device *proc_priv)
{
	struct proc_thermal_device *proc_priv;
	struct acpi_device *adev;
	int i;
	acpi_status status;
	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object *elements, *ppcc;
	union acpi_object *p;
	unsigned long long tmp;
	struct thermal_zone_device_ops *ops = NULL;
	int i;
	int ret;
	int ret = 0;

	adev = ACPI_COMPANION(dev);
	if (!adev)
		return -ENODEV;

	status = acpi_evaluate_object(adev->handle, "PPCC", NULL, &buf);
	status = acpi_evaluate_object(proc_priv->adev->handle, "PPCC",
				      NULL, &buf);
	if (ACPI_FAILURE(status))
		return -ENODEV;

	p = buf.pointer;
	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;
		goto free_buffer;
	}

	if (!p->package.count) {
		dev_err(dev, "Invalid PPCC package size\n");
		dev_err(proc_priv->dev, "Invalid PPCC package size\n");
		ret = -EFAULT;
		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) {
		elements = &(p->package.elements[i+1]);
		if (elements->type != ACPI_TYPE_PACKAGE ||
@@ -257,12 +241,62 @@ static int proc_thermal_add(struct device *dev,
		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;

	ret = proc_thermal_read_ppcc(proc_priv);
	if (!ret) {
		ret = sysfs_create_group(&dev->kobj,
					 &power_limit_attribute_group);

	}
	if (ret)
		goto free_buffer;
		return ret;

	status = acpi_evaluate_integer(adev->handle, "_TMP", NULL, &tmp);
	if (ACPI_FAILURE(status)) {
@@ -274,20 +308,32 @@ static int proc_thermal_add(struct device *dev,

	proc_priv->int340x_zone = int340x_thermal_zone_add(adev, ops);
	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);
		goto remove_group;
	} else
		ret = 0;

free_buffer:
	kfree(buf.pointer);
	ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
					  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;
}

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);
	sysfs_remove_group(&proc_priv->dev->kobj,
			   &power_limit_attribute_group);