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

Commit 8cb68501 authored by Zhang Rui's avatar Zhang Rui
Browse files

Merge branches 'intel-dts-soc-thermal' and 'int340x-enhancement' of .git into next

parents 6c355faf 317d9dda
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o
ifdef CONFIG_ACPI_VIDEO
acpi-y				+= video_detect.o
endif
acpi-y				+= acpi_lpat.o

# These are (potentially) separate modules

+161 −0
Original line number Diff line number Diff line
/*
 * acpi_lpat.c - LPAT table processing functions
 *
 * Copyright (C) 2015 Intel Corporation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/module.h>
#include <linux/acpi.h>
#include <acpi/acpi_lpat.h>

/**
 * acpi_lpat_raw_to_temp(): Return temperature from raw value through
 * LPAT conversion table
 *
 * @lpat_table: the temperature_raw mapping table structure
 * @raw: the raw value, used as a key to get the temerature from the
 *       above mapping table
 *
 * A positive converted temperarure value will be returned on success,
 * a negative errno will be returned in error cases.
 */
int acpi_lpat_raw_to_temp(struct acpi_lpat_conversion_table *lpat_table,
			  int raw)
{
	int i, delta_temp, delta_raw, temp;
	struct acpi_lpat *lpat = lpat_table->lpat;

	for (i = 0; i < lpat_table->lpat_count - 1; i++) {
		if ((raw >= lpat[i].raw && raw <= lpat[i+1].raw) ||
		    (raw <= lpat[i].raw && raw >= lpat[i+1].raw))
			break;
	}

	if (i == lpat_table->lpat_count - 1)
		return -ENOENT;

	delta_temp = lpat[i+1].temp - lpat[i].temp;
	delta_raw = lpat[i+1].raw - lpat[i].raw;
	temp = lpat[i].temp + (raw - lpat[i].raw) * delta_temp / delta_raw;

	return temp;
}
EXPORT_SYMBOL_GPL(acpi_lpat_raw_to_temp);

/**
 * acpi_lpat_temp_to_raw(): Return raw value from temperature through
 * LPAT conversion table
 *
 * @lpat: the temperature_raw mapping table
 * @temp: the temperature, used as a key to get the raw value from the
 *        above mapping table
 *
 * A positive converted temperature value will be returned on success,
 * a negative errno will be returned in error cases.
 */
int acpi_lpat_temp_to_raw(struct acpi_lpat_conversion_table *lpat_table,
			  int temp)
{
	int i, delta_temp, delta_raw, raw;
	struct acpi_lpat *lpat = lpat_table->lpat;

	for (i = 0; i < lpat_table->lpat_count - 1; i++) {
		if (temp >= lpat[i].temp && temp <= lpat[i+1].temp)
			break;
	}

	if (i ==  lpat_table->lpat_count - 1)
		return -ENOENT;

	delta_temp = lpat[i+1].temp - lpat[i].temp;
	delta_raw = lpat[i+1].raw - lpat[i].raw;
	raw = lpat[i].raw + (temp - lpat[i].temp) * delta_raw / delta_temp;

	return raw;
}
EXPORT_SYMBOL_GPL(acpi_lpat_temp_to_raw);

/**
 * acpi_lpat_get_conversion_table(): Parse ACPI LPAT table if present.
 *
 * @handle: Handle to acpi device
 *
 * Parse LPAT table to a struct of type acpi_lpat_table. On success
 * it returns a pointer to newly allocated table. This table must
 * be freed by the caller when finished processing, using a call to
 * acpi_lpat_free_conversion_table.
 */
struct acpi_lpat_conversion_table *acpi_lpat_get_conversion_table(acpi_handle
								  handle)
{
	struct acpi_lpat_conversion_table *lpat_table = NULL;
	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object *obj_p, *obj_e;
	int *lpat, i;
	acpi_status status;

	status = acpi_evaluate_object(handle, "LPAT", NULL, &buffer);
	if (ACPI_FAILURE(status))
		return NULL;

	obj_p = (union acpi_object *)buffer.pointer;
	if (!obj_p || (obj_p->type != ACPI_TYPE_PACKAGE) ||
	    (obj_p->package.count % 2) || (obj_p->package.count < 4))
		goto out;

	lpat = kcalloc(obj_p->package.count, sizeof(int), GFP_KERNEL);
	if (!lpat)
		goto out;

	for (i = 0; i < obj_p->package.count; i++) {
		obj_e = &obj_p->package.elements[i];
		if (obj_e->type != ACPI_TYPE_INTEGER) {
			kfree(lpat);
			goto out;
		}
		lpat[i] = (s64)obj_e->integer.value;
	}

	lpat_table = kzalloc(sizeof(*lpat_table), GFP_KERNEL);
	if (!lpat_table) {
		kfree(lpat);
		goto out;
	}

	lpat_table->lpat = (struct acpi_lpat *)lpat;
	lpat_table->lpat_count = obj_p->package.count / 2;

out:
	kfree(buffer.pointer);
	return lpat_table;
}
EXPORT_SYMBOL_GPL(acpi_lpat_get_conversion_table);

/**
 * acpi_lpat_free_conversion_table(): Free LPAT table.
 *
 * @lpat_table: the temperature_raw mapping table structure
 *
 * Frees the LPAT table previously allocated by a call to
 * acpi_lpat_get_conversion_table.
 */
void acpi_lpat_free_conversion_table(struct acpi_lpat_conversion_table
				     *lpat_table)
{
	if (lpat_table) {
		kfree(lpat_table->lpat);
		kfree(lpat_table);
	}
}
EXPORT_SYMBOL_GPL(acpi_lpat_free_conversion_table);

MODULE_LICENSE("GPL");
+18 −115
Original line number Diff line number Diff line
@@ -16,20 +16,15 @@
#include <linux/module.h>
#include <linux/acpi.h>
#include <linux/regmap.h>
#include <acpi/acpi_lpat.h>
#include "intel_pmic.h"

#define PMIC_POWER_OPREGION_ID		0x8d
#define PMIC_THERMAL_OPREGION_ID	0x8c

struct acpi_lpat {
	int temp;
	int raw;
};

struct intel_pmic_opregion {
	struct mutex lock;
	struct acpi_lpat *lpat;
	int lpat_count;
	struct acpi_lpat_conversion_table *lpat_table;
	struct regmap *regmap;
	struct intel_pmic_opregion_data *data;
};
@@ -50,105 +45,6 @@ static int pmic_get_reg_bit(int address, struct pmic_table *table,
	return -ENOENT;
}

/**
 * raw_to_temp(): Return temperature from raw value through LPAT table
 *
 * @lpat: the temperature_raw mapping table
 * @count: the count of the above mapping table
 * @raw: the raw value, used as a key to get the temerature from the
 *       above mapping table
 *
 * A positive value will be returned on success, a negative errno will
 * be returned in error cases.
 */
static int raw_to_temp(struct acpi_lpat *lpat, int count, int raw)
{
	int i, delta_temp, delta_raw, temp;

	for (i = 0; i < count - 1; i++) {
		if ((raw >= lpat[i].raw && raw <= lpat[i+1].raw) ||
		    (raw <= lpat[i].raw && raw >= lpat[i+1].raw))
			break;
	}

	if (i == count - 1)
		return -ENOENT;

	delta_temp = lpat[i+1].temp - lpat[i].temp;
	delta_raw = lpat[i+1].raw - lpat[i].raw;
	temp = lpat[i].temp + (raw - lpat[i].raw) * delta_temp / delta_raw;

	return temp;
}

/**
 * temp_to_raw(): Return raw value from temperature through LPAT table
 *
 * @lpat: the temperature_raw mapping table
 * @count: the count of the above mapping table
 * @temp: the temperature, used as a key to get the raw value from the
 *        above mapping table
 *
 * A positive value will be returned on success, a negative errno will
 * be returned in error cases.
 */
static int temp_to_raw(struct acpi_lpat *lpat, int count, int temp)
{
	int i, delta_temp, delta_raw, raw;

	for (i = 0; i < count - 1; i++) {
		if (temp >= lpat[i].temp && temp <= lpat[i+1].temp)
			break;
	}

	if (i == count - 1)
		return -ENOENT;

	delta_temp = lpat[i+1].temp - lpat[i].temp;
	delta_raw = lpat[i+1].raw - lpat[i].raw;
	raw = lpat[i].raw + (temp - lpat[i].temp) * delta_raw / delta_temp;

	return raw;
}

static void pmic_thermal_lpat(struct intel_pmic_opregion *opregion,
			      acpi_handle handle, struct device *dev)
{
	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object *obj_p, *obj_e;
	int *lpat, i;
	acpi_status status;

	status = acpi_evaluate_object(handle, "LPAT", NULL, &buffer);
	if (ACPI_FAILURE(status))
		return;

	obj_p = (union acpi_object *)buffer.pointer;
	if (!obj_p || (obj_p->type != ACPI_TYPE_PACKAGE) ||
	    (obj_p->package.count % 2) || (obj_p->package.count < 4))
		goto out;

	lpat = devm_kmalloc(dev, sizeof(int) * obj_p->package.count,
			    GFP_KERNEL);
	if (!lpat)
		goto out;

	for (i = 0; i < obj_p->package.count; i++) {
		obj_e = &obj_p->package.elements[i];
		if (obj_e->type != ACPI_TYPE_INTEGER) {
			devm_kfree(dev, lpat);
			goto out;
		}
		lpat[i] = (s64)obj_e->integer.value;
	}

	opregion->lpat = (struct acpi_lpat *)lpat;
	opregion->lpat_count = obj_p->package.count / 2;

out:
	kfree(buffer.pointer);
}

static acpi_status intel_pmic_power_handler(u32 function,
		acpi_physical_address address, u32 bits, u64 *value64,
		void *handler_context, void *region_context)
@@ -192,12 +88,12 @@ static int pmic_read_temp(struct intel_pmic_opregion *opregion,
	if (raw_temp < 0)
		return raw_temp;

	if (!opregion->lpat) {
	if (!opregion->lpat_table) {
		*value = raw_temp;
		return 0;
	}

	temp = raw_to_temp(opregion->lpat, opregion->lpat_count, raw_temp);
	temp = acpi_lpat_raw_to_temp(opregion->lpat_table, raw_temp);
	if (temp < 0)
		return temp;

@@ -223,9 +119,8 @@ static int pmic_thermal_aux(struct intel_pmic_opregion *opregion, int reg,
	if (!opregion->data->update_aux)
		return -ENXIO;

	if (opregion->lpat) {
		raw_temp = temp_to_raw(opregion->lpat, opregion->lpat_count,
				       *value);
	if (opregion->lpat_table) {
		raw_temp = acpi_lpat_temp_to_raw(opregion->lpat_table, *value);
		if (raw_temp < 0)
			return raw_temp;
	} else {
@@ -314,6 +209,7 @@ int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle,
{
	acpi_status status;
	struct intel_pmic_opregion *opregion;
	int ret;

	if (!dev || !regmap || !d)
		return -EINVAL;
@@ -327,14 +223,16 @@ int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle,

	mutex_init(&opregion->lock);
	opregion->regmap = regmap;
	pmic_thermal_lpat(opregion, handle, dev);
	opregion->lpat_table = acpi_lpat_get_conversion_table(handle);

	status = acpi_install_address_space_handler(handle,
						    PMIC_POWER_OPREGION_ID,
						    intel_pmic_power_handler,
						    NULL, opregion);
	if (ACPI_FAILURE(status))
		return -ENODEV;
	if (ACPI_FAILURE(status)) {
		ret = -ENODEV;
		goto out_error;
	}

	status = acpi_install_address_space_handler(handle,
						    PMIC_THERMAL_OPREGION_ID,
@@ -343,11 +241,16 @@ int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle,
	if (ACPI_FAILURE(status)) {
		acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID,
						  intel_pmic_power_handler);
		return -ENODEV;
		ret = -ENODEV;
		goto out_error;
	}

	opregion->data = d;
	return 0;

out_error:
	acpi_lpat_free_conversion_table(opregion->lpat_table);
	return ret;
}
EXPORT_SYMBOL_GPL(intel_pmic_install_opregion_handler);

+1 −0
Original line number Diff line number Diff line
obj-$(CONFIG_INT340X_THERMAL)	+= int3400_thermal.o
obj-$(CONFIG_INT340X_THERMAL)	+= int340x_thermal_zone.o
obj-$(CONFIG_INT340X_THERMAL)	+= int3402_thermal.o
obj-$(CONFIG_INT340X_THERMAL)	+= int3403_thermal.o
obj-$(CONFIG_INT340X_THERMAL)	+= processor_thermal_device.o
+37 −171
Original line number Diff line number Diff line
@@ -14,152 +14,39 @@
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/thermal.h>
#include "int340x_thermal_zone.h"

#define ACPI_ACTIVE_COOLING_MAX_NR 10

struct active_trip {
	unsigned long temp;
	int id;
	bool valid;
};
#define INT3402_PERF_CHANGED_EVENT	0x80
#define INT3402_THERMAL_EVENT		0x90

struct int3402_thermal_data {
	unsigned long *aux_trips;
	int aux_trip_nr;
	unsigned long psv_temp;
	int psv_trip_id;
	unsigned long crt_temp;
	int crt_trip_id;
	unsigned long hot_temp;
	int hot_trip_id;
	struct active_trip act_trips[ACPI_ACTIVE_COOLING_MAX_NR];
	acpi_handle *handle;
	struct int34x_thermal_zone *int340x_zone;
};

static int int3402_thermal_get_zone_temp(struct thermal_zone_device *zone,
					 unsigned long *temp)
static void int3402_notify(acpi_handle handle, u32 event, void *data)
{
	struct int3402_thermal_data *d = zone->devdata;
	unsigned long long tmp;
	acpi_status status;

	status = acpi_evaluate_integer(d->handle, "_TMP", NULL, &tmp);
	if (ACPI_FAILURE(status))
		return -ENODEV;
	struct int3402_thermal_data *priv = data;

	/* _TMP returns the temperature in tenths of degrees Kelvin */
	*temp = DECI_KELVIN_TO_MILLICELSIUS(tmp);
	if (!priv)
		return;

	return 0;
}

static int int3402_thermal_get_trip_temp(struct thermal_zone_device *zone,
					 int trip, unsigned long *temp)
{
	struct int3402_thermal_data *d = zone->devdata;
	int i;

	if (trip < d->aux_trip_nr)
		*temp = d->aux_trips[trip];
	else if (trip == d->crt_trip_id)
		*temp = d->crt_temp;
	else if (trip == d->psv_trip_id)
		*temp = d->psv_temp;
	else if (trip == d->hot_trip_id)
		*temp = d->hot_temp;
	else {
		for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) {
			if (d->act_trips[i].valid &&
			    d->act_trips[i].id == trip) {
				*temp = d->act_trips[i].temp;
	switch (event) {
	case INT3402_PERF_CHANGED_EVENT:
		break;
			}
		}
		if (i == ACPI_ACTIVE_COOLING_MAX_NR)
			return -EINVAL;
	}
	return 0;
}

static int int3402_thermal_get_trip_type(struct thermal_zone_device *zone,
					 int trip, enum thermal_trip_type *type)
{
	struct int3402_thermal_data *d = zone->devdata;
	int i;

	if (trip < d->aux_trip_nr)
		*type = THERMAL_TRIP_PASSIVE;
	else if (trip == d->crt_trip_id)
		*type = THERMAL_TRIP_CRITICAL;
	else if (trip == d->hot_trip_id)
		*type = THERMAL_TRIP_HOT;
	else if (trip == d->psv_trip_id)
		*type = THERMAL_TRIP_PASSIVE;
	else {
		for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) {
			if (d->act_trips[i].valid &&
			    d->act_trips[i].id == trip) {
				*type = THERMAL_TRIP_ACTIVE;
	case INT3402_THERMAL_EVENT:
		int340x_thermal_zone_device_update(priv->int340x_zone);
		break;
	default:
		break;
	}
}
		if (i == ACPI_ACTIVE_COOLING_MAX_NR)
			return -EINVAL;
	}
	return 0;
}

static int int3402_thermal_set_trip_temp(struct thermal_zone_device *zone, int trip,
				  unsigned long temp)
{
	struct int3402_thermal_data *d = zone->devdata;
	acpi_status status;
	char name[10];

	snprintf(name, sizeof(name), "PAT%d", trip);
	status = acpi_execute_simple_method(d->handle, name,
			MILLICELSIUS_TO_DECI_KELVIN(temp));
	if (ACPI_FAILURE(status))
		return -EIO;

	d->aux_trips[trip] = temp;
	return 0;
}

static struct thermal_zone_device_ops int3402_thermal_zone_ops = {
	.get_temp       = int3402_thermal_get_zone_temp,
	.get_trip_temp	= int3402_thermal_get_trip_temp,
	.get_trip_type	= int3402_thermal_get_trip_type,
	.set_trip_temp	= int3402_thermal_set_trip_temp,
};

static struct thermal_zone_params int3402_thermal_params = {
	.governor_name = "user_space",
	.no_hwmon = true,
};

static int int3402_thermal_get_temp(acpi_handle handle, char *name,
				    unsigned long *temp)
{
	unsigned long long r;
	acpi_status status;

	status = acpi_evaluate_integer(handle, name, NULL, &r);
	if (ACPI_FAILURE(status))
		return -EIO;

	*temp = DECI_KELVIN_TO_MILLICELSIUS(r);
	return 0;
}

static int int3402_thermal_probe(struct platform_device *pdev)
{
	struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
	struct int3402_thermal_data *d;
	struct thermal_zone_device *zone;
	acpi_status status;
	unsigned long long trip_cnt;
	int trip_mask = 0, i;
	int ret;

	if (!acpi_has_method(adev->handle, "_TMP"))
		return -ENODEV;
@@ -168,54 +55,33 @@ static int int3402_thermal_probe(struct platform_device *pdev)
	if (!d)
		return -ENOMEM;

	status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt);
	if (ACPI_FAILURE(status))
		trip_cnt = 0;
	else {
		d->aux_trips = devm_kzalloc(&pdev->dev,
				sizeof(*d->aux_trips) * trip_cnt, GFP_KERNEL);
		if (!d->aux_trips)
			return -ENOMEM;
		trip_mask = trip_cnt - 1;
		d->handle = adev->handle;
		d->aux_trip_nr = trip_cnt;
	}

	d->crt_trip_id = -1;
	if (!int3402_thermal_get_temp(adev->handle, "_CRT", &d->crt_temp))
		d->crt_trip_id = trip_cnt++;
	d->hot_trip_id = -1;
	if (!int3402_thermal_get_temp(adev->handle, "_HOT", &d->hot_temp))
		d->hot_trip_id = trip_cnt++;
	d->psv_trip_id = -1;
	if (!int3402_thermal_get_temp(adev->handle, "_PSV", &d->psv_temp))
		d->psv_trip_id = trip_cnt++;
	for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) {
		char name[5] = { '_', 'A', 'C', '0' + i, '\0' };
		if (int3402_thermal_get_temp(adev->handle, name,
					     &d->act_trips[i].temp))
			break;
		d->act_trips[i].id = trip_cnt++;
		d->act_trips[i].valid = true;
	d->int340x_zone = int340x_thermal_zone_add(adev, NULL);
	if (IS_ERR(d->int340x_zone))
		return PTR_ERR(d->int340x_zone);

	ret = acpi_install_notify_handler(adev->handle,
					  ACPI_DEVICE_NOTIFY,
					  int3402_notify,
					  d);
	if (ret) {
		int340x_thermal_zone_remove(d->int340x_zone);
		return ret;
	}

	zone = thermal_zone_device_register(acpi_device_bid(adev), trip_cnt,
					    trip_mask, d,
					    &int3402_thermal_zone_ops,
					    &int3402_thermal_params,
					    0, 0);
	if (IS_ERR(zone))
		return PTR_ERR(zone);
	platform_set_drvdata(pdev, zone);
	d->handle = adev->handle;
	platform_set_drvdata(pdev, d);

	return 0;
}

static int int3402_thermal_remove(struct platform_device *pdev)
{
	struct thermal_zone_device *zone = platform_get_drvdata(pdev);
	struct int3402_thermal_data *d = platform_get_drvdata(pdev);

	acpi_remove_notify_handler(d->handle,
				   ACPI_DEVICE_NOTIFY, int3402_notify);
	int340x_thermal_zone_remove(d->int340x_zone);

	thermal_zone_device_unregister(zone);
	return 0;
}

Loading