Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,4 @@
obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o
obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o
obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o
obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o

View file

@ -0,0 +1,400 @@
/* acpi_thermal_rel.c driver for exporting ACPI thermal relationship
*
* Copyright (c) 2014 Intel Corp
*
* 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.
*
*/
/*
* Two functionalities included:
* 1. Export _TRT, _ART, via misc device interface to the userspace.
* 2. Provide parsing result to kernel drivers
*
*/
#include <linux/init.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/acpi.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include "acpi_thermal_rel.h"
static acpi_handle acpi_thermal_rel_handle;
static DEFINE_SPINLOCK(acpi_thermal_rel_chrdev_lock);
static int acpi_thermal_rel_chrdev_count; /* #times opened */
static int acpi_thermal_rel_chrdev_exclu; /* already open exclusive? */
static int acpi_thermal_rel_open(struct inode *inode, struct file *file)
{
spin_lock(&acpi_thermal_rel_chrdev_lock);
if (acpi_thermal_rel_chrdev_exclu ||
(acpi_thermal_rel_chrdev_count && (file->f_flags & O_EXCL))) {
spin_unlock(&acpi_thermal_rel_chrdev_lock);
return -EBUSY;
}
if (file->f_flags & O_EXCL)
acpi_thermal_rel_chrdev_exclu = 1;
acpi_thermal_rel_chrdev_count++;
spin_unlock(&acpi_thermal_rel_chrdev_lock);
return nonseekable_open(inode, file);
}
static int acpi_thermal_rel_release(struct inode *inode, struct file *file)
{
spin_lock(&acpi_thermal_rel_chrdev_lock);
acpi_thermal_rel_chrdev_count--;
acpi_thermal_rel_chrdev_exclu = 0;
spin_unlock(&acpi_thermal_rel_chrdev_lock);
return 0;
}
/**
* acpi_parse_trt - Thermal Relationship Table _TRT for passive cooling
*
* @handle: ACPI handle of the device contains _TRT
* @art_count: the number of valid entries resulted from parsing _TRT
* @artp: pointer to pointer of array of art entries in parsing result
* @create_dev: whether to create platform devices for target and source
*
*/
int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp,
bool create_dev)
{
acpi_status status;
int result = 0;
int i;
int nr_bad_entries = 0;
struct trt *trts;
struct acpi_device *adev;
union acpi_object *p;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
struct acpi_buffer element = { 0, NULL };
struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" };
if (!acpi_has_method(handle, "_TRT"))
return 0;
status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer);
if (ACPI_FAILURE(status))
return -ENODEV;
p = buffer.pointer;
if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
pr_err("Invalid _TRT data\n");
result = -EFAULT;
goto end;
}
*trt_count = p->package.count;
trts = kzalloc(*trt_count * sizeof(struct trt), GFP_KERNEL);
if (!trts) {
result = -ENOMEM;
goto end;
}
for (i = 0; i < *trt_count; i++) {
struct trt *trt = &trts[i - nr_bad_entries];
element.length = sizeof(struct trt);
element.pointer = trt;
status = acpi_extract_package(&(p->package.elements[i]),
&trt_format, &element);
if (ACPI_FAILURE(status)) {
nr_bad_entries++;
pr_warn("_TRT package %d is invalid, ignored\n", i);
continue;
}
if (!create_dev)
continue;
result = acpi_bus_get_device(trt->source, &adev);
if (!result)
acpi_create_platform_device(adev);
else
pr_warn("Failed to get source ACPI device\n");
result = acpi_bus_get_device(trt->target, &adev);
if (!result)
acpi_create_platform_device(adev);
else
pr_warn("Failed to get target ACPI device\n");
}
*trtp = trts;
/* don't count bad entries */
*trt_count -= nr_bad_entries;
end:
kfree(buffer.pointer);
return result;
}
EXPORT_SYMBOL(acpi_parse_trt);
/**
* acpi_parse_art - Parse Active Relationship Table _ART
*
* @handle: ACPI handle of the device contains _ART
* @art_count: the number of valid entries resulted from parsing _ART
* @artp: pointer to pointer of array of art entries in parsing result
* @create_dev: whether to create platform devices for target and source
*
*/
int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp,
bool create_dev)
{
acpi_status status;
int result = 0;
int i;
int nr_bad_entries = 0;
struct art *arts;
struct acpi_device *adev;
union acpi_object *p;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
struct acpi_buffer element = { 0, NULL };
struct acpi_buffer art_format = {
sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" };
if (!acpi_has_method(handle, "_ART"))
return 0;
status = acpi_evaluate_object(handle, "_ART", NULL, &buffer);
if (ACPI_FAILURE(status))
return -ENODEV;
p = buffer.pointer;
if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
pr_err("Invalid _ART data\n");
result = -EFAULT;
goto end;
}
/* ignore p->package.elements[0], as this is _ART Revision field */
*art_count = p->package.count - 1;
arts = kzalloc(*art_count * sizeof(struct art), GFP_KERNEL);
if (!arts) {
result = -ENOMEM;
goto end;
}
for (i = 0; i < *art_count; i++) {
struct art *art = &arts[i - nr_bad_entries];
element.length = sizeof(struct art);
element.pointer = art;
status = acpi_extract_package(&(p->package.elements[i + 1]),
&art_format, &element);
if (ACPI_FAILURE(status)) {
pr_warn("_ART package %d is invalid, ignored", i);
nr_bad_entries++;
continue;
}
if (!create_dev)
continue;
if (art->source) {
result = acpi_bus_get_device(art->source, &adev);
if (!result)
acpi_create_platform_device(adev);
else
pr_warn("Failed to get source ACPI device\n");
}
if (art->target) {
result = acpi_bus_get_device(art->target, &adev);
if (!result)
acpi_create_platform_device(adev);
else
pr_warn("Failed to get source ACPI device\n");
}
}
*artp = arts;
/* don't count bad entries */
*art_count -= nr_bad_entries;
end:
kfree(buffer.pointer);
return result;
}
EXPORT_SYMBOL(acpi_parse_art);
/* get device name from acpi handle */
static void get_single_name(acpi_handle handle, char *name)
{
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER};
if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)))
pr_warn("Failed get name from handle\n");
else {
memcpy(name, buffer.pointer, ACPI_NAME_SIZE);
kfree(buffer.pointer);
}
}
static int fill_art(char __user *ubuf)
{
int i;
int ret;
int count;
int art_len;
struct art *arts = NULL;
union art_object *art_user;
ret = acpi_parse_art(acpi_thermal_rel_handle, &count, &arts, false);
if (ret)
goto free_art;
art_len = count * sizeof(union art_object);
art_user = kzalloc(art_len, GFP_KERNEL);
if (!art_user) {
ret = -ENOMEM;
goto free_art;
}
/* now fill in user art data */
for (i = 0; i < count; i++) {
/* userspace art needs device name instead of acpi reference */
get_single_name(arts[i].source, art_user[i].source_device);
get_single_name(arts[i].target, art_user[i].target_device);
/* copy the rest int data in addition to source and target */
memcpy(&art_user[i].weight, &arts[i].weight,
sizeof(u64) * (ACPI_NR_ART_ELEMENTS - 2));
}
if (copy_to_user(ubuf, art_user, art_len))
ret = -EFAULT;
kfree(art_user);
free_art:
kfree(arts);
return ret;
}
static int fill_trt(char __user *ubuf)
{
int i;
int ret;
int count;
int trt_len;
struct trt *trts = NULL;
union trt_object *trt_user;
ret = acpi_parse_trt(acpi_thermal_rel_handle, &count, &trts, false);
if (ret)
goto free_trt;
trt_len = count * sizeof(union trt_object);
trt_user = kzalloc(trt_len, GFP_KERNEL);
if (!trt_user) {
ret = -ENOMEM;
goto free_trt;
}
/* now fill in user trt data */
for (i = 0; i < count; i++) {
/* userspace trt needs device name instead of acpi reference */
get_single_name(trts[i].source, trt_user[i].source_device);
get_single_name(trts[i].target, trt_user[i].target_device);
trt_user[i].sample_period = trts[i].sample_period;
trt_user[i].influence = trts[i].influence;
}
if (copy_to_user(ubuf, trt_user, trt_len))
ret = -EFAULT;
kfree(trt_user);
free_trt:
kfree(trts);
return ret;
}
static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,
unsigned long __arg)
{
int ret = 0;
unsigned long length = 0;
unsigned long count = 0;
char __user *arg = (void __user *)__arg;
struct trt *trts;
struct art *arts;
switch (cmd) {
case ACPI_THERMAL_GET_TRT_COUNT:
ret = acpi_parse_trt(acpi_thermal_rel_handle, (int *)&count,
&trts, false);
kfree(trts);
if (!ret)
return put_user(count, (unsigned long __user *)__arg);
return ret;
case ACPI_THERMAL_GET_TRT_LEN:
ret = acpi_parse_trt(acpi_thermal_rel_handle, (int *)&count,
&trts, false);
kfree(trts);
length = count * sizeof(union trt_object);
if (!ret)
return put_user(length, (unsigned long __user *)__arg);
return ret;
case ACPI_THERMAL_GET_TRT:
return fill_trt(arg);
case ACPI_THERMAL_GET_ART_COUNT:
ret = acpi_parse_art(acpi_thermal_rel_handle, (int *)&count,
&arts, false);
kfree(arts);
if (!ret)
return put_user(count, (unsigned long __user *)__arg);
return ret;
case ACPI_THERMAL_GET_ART_LEN:
ret = acpi_parse_art(acpi_thermal_rel_handle, (int *)&count,
&arts, false);
kfree(arts);
length = count * sizeof(union art_object);
if (!ret)
return put_user(length, (unsigned long __user *)__arg);
return ret;
case ACPI_THERMAL_GET_ART:
return fill_art(arg);
default:
return -ENOTTY;
}
}
static const struct file_operations acpi_thermal_rel_fops = {
.owner = THIS_MODULE,
.open = acpi_thermal_rel_open,
.release = acpi_thermal_rel_release,
.unlocked_ioctl = acpi_thermal_rel_ioctl,
.llseek = no_llseek,
};
static struct miscdevice acpi_thermal_rel_misc_device = {
.minor = MISC_DYNAMIC_MINOR,
"acpi_thermal_rel",
&acpi_thermal_rel_fops
};
int acpi_thermal_rel_misc_device_add(acpi_handle handle)
{
acpi_thermal_rel_handle = handle;
return misc_register(&acpi_thermal_rel_misc_device);
}
EXPORT_SYMBOL(acpi_thermal_rel_misc_device_add);
int acpi_thermal_rel_misc_device_remove(acpi_handle handle)
{
misc_deregister(&acpi_thermal_rel_misc_device);
return 0;
}
EXPORT_SYMBOL(acpi_thermal_rel_misc_device_remove);
MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com");
MODULE_DESCRIPTION("Intel acpi thermal rel misc dev driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,84 @@
#ifndef __ACPI_ACPI_THERMAL_H
#define __ACPI_ACPI_THERMAL_H
#include <asm/ioctl.h>
#define ACPI_THERMAL_MAGIC 's'
#define ACPI_THERMAL_GET_TRT_LEN _IOR(ACPI_THERMAL_MAGIC, 1, unsigned long)
#define ACPI_THERMAL_GET_ART_LEN _IOR(ACPI_THERMAL_MAGIC, 2, unsigned long)
#define ACPI_THERMAL_GET_TRT_COUNT _IOR(ACPI_THERMAL_MAGIC, 3, unsigned long)
#define ACPI_THERMAL_GET_ART_COUNT _IOR(ACPI_THERMAL_MAGIC, 4, unsigned long)
#define ACPI_THERMAL_GET_TRT _IOR(ACPI_THERMAL_MAGIC, 5, unsigned long)
#define ACPI_THERMAL_GET_ART _IOR(ACPI_THERMAL_MAGIC, 6, unsigned long)
struct art {
acpi_handle source;
acpi_handle target;
u64 weight;
u64 ac0_max;
u64 ac1_max;
u64 ac2_max;
u64 ac3_max;
u64 ac4_max;
u64 ac5_max;
u64 ac6_max;
u64 ac7_max;
u64 ac8_max;
u64 ac9_max;
} __packed;
struct trt {
acpi_handle source;
acpi_handle target;
u64 influence;
u64 sample_period;
u64 reverved1;
u64 reverved2;
u64 reverved3;
u64 reverved4;
} __packed;
#define ACPI_NR_ART_ELEMENTS 13
/* for usrspace */
union art_object {
struct {
char source_device[8]; /* ACPI single name */
char target_device[8]; /* ACPI single name */
u64 weight;
u64 ac0_max_level;
u64 ac1_max_level;
u64 ac2_max_level;
u64 ac3_max_level;
u64 ac4_max_level;
u64 ac5_max_level;
u64 ac6_max_level;
u64 ac7_max_level;
u64 ac8_max_level;
u64 ac9_max_level;
};
u64 __data[ACPI_NR_ART_ELEMENTS];
};
union trt_object {
struct {
char source_device[8]; /* ACPI single name */
char target_device[8]; /* ACPI single name */
u64 influence;
u64 sample_period;
u64 reserved[4];
};
u64 __data[8];
};
#ifdef __KERNEL__
int acpi_thermal_rel_misc_device_add(acpi_handle handle);
int acpi_thermal_rel_misc_device_remove(acpi_handle handle);
int acpi_parse_art(acpi_handle handle, int *art_count, struct art **arts,
bool create_dev);
int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trts,
bool create_dev);
#endif
#endif /* __ACPI_ACPI_THERMAL_H */

View file

@ -0,0 +1,271 @@
/*
* INT3400 thermal driver
*
* Copyright (C) 2014, Intel Corporation
* Authors: Zhang Rui <rui.zhang@intel.com>
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/thermal.h>
#include "acpi_thermal_rel.h"
enum int3400_thermal_uuid {
INT3400_THERMAL_PASSIVE_1,
INT3400_THERMAL_PASSIVE_2,
INT3400_THERMAL_ACTIVE,
INT3400_THERMAL_CRITICAL,
INT3400_THERMAL_COOLING_MODE,
INT3400_THERMAL_MAXIMUM_UUID,
};
static u8 *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = {
"42A441D6-AE6A-462b-A84B-4A8CE79027D3",
"9E04115A-AE87-4D1C-9500-0F3E340BFE75",
"3A95C389-E4B8-4629-A526-C52C88626BAE",
"97C68AE7-15FA-499c-B8C9-5DA81D606E0A",
"16CAF1B7-DD38-40ed-B1C1-1B8A1913D531",
};
struct int3400_thermal_priv {
struct acpi_device *adev;
struct thermal_zone_device *thermal;
int mode;
int art_count;
struct art *arts;
int trt_count;
struct trt *trts;
u8 uuid_bitmap;
int rel_misc_dev_res;
};
static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv)
{
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *obja, *objb;
int i, j;
int result = 0;
acpi_status status;
status = acpi_evaluate_object(priv->adev->handle, "IDSP", NULL, &buf);
if (ACPI_FAILURE(status))
return -ENODEV;
obja = (union acpi_object *)buf.pointer;
if (obja->type != ACPI_TYPE_PACKAGE) {
result = -EINVAL;
goto end;
}
for (i = 0; i < obja->package.count; i++) {
objb = &obja->package.elements[i];
if (objb->type != ACPI_TYPE_BUFFER) {
result = -EINVAL;
goto end;
}
/* UUID must be 16 bytes */
if (objb->buffer.length != 16) {
result = -EINVAL;
goto end;
}
for (j = 0; j < INT3400_THERMAL_MAXIMUM_UUID; j++) {
u8 uuid[16];
acpi_str_to_uuid(int3400_thermal_uuids[j], uuid);
if (!strncmp(uuid, objb->buffer.pointer, 16)) {
priv->uuid_bitmap |= (1 << j);
break;
}
}
}
end:
kfree(buf.pointer);
return result;
}
static int int3400_thermal_run_osc(acpi_handle handle,
enum int3400_thermal_uuid uuid, bool enable)
{
u32 ret, buf[2];
acpi_status status;
int result = 0;
struct acpi_osc_context context = {
.uuid_str = int3400_thermal_uuids[uuid],
.rev = 1,
.cap.length = 8,
};
buf[OSC_QUERY_DWORD] = 0;
buf[OSC_SUPPORT_DWORD] = enable;
context.cap.pointer = buf;
status = acpi_run_osc(handle, &context);
if (ACPI_SUCCESS(status)) {
ret = *((u32 *)(context.ret.pointer + 4));
if (ret != enable)
result = -EPERM;
} else
result = -EPERM;
kfree(context.ret.pointer);
return result;
}
static int int3400_thermal_get_temp(struct thermal_zone_device *thermal,
unsigned long *temp)
{
*temp = 20 * 1000; /* faked temp sensor with 20C */
return 0;
}
static int int3400_thermal_get_mode(struct thermal_zone_device *thermal,
enum thermal_device_mode *mode)
{
struct int3400_thermal_priv *priv = thermal->devdata;
if (!priv)
return -EINVAL;
*mode = priv->mode;
return 0;
}
static int int3400_thermal_set_mode(struct thermal_zone_device *thermal,
enum thermal_device_mode mode)
{
struct int3400_thermal_priv *priv = thermal->devdata;
bool enable;
int result = 0;
if (!priv)
return -EINVAL;
if (mode == THERMAL_DEVICE_ENABLED)
enable = true;
else if (mode == THERMAL_DEVICE_DISABLED)
enable = false;
else
return -EINVAL;
if (enable != priv->mode) {
priv->mode = enable;
/* currently, only PASSIVE COOLING is supported */
result = int3400_thermal_run_osc(priv->adev->handle,
INT3400_THERMAL_PASSIVE_1, enable);
}
return result;
}
static struct thermal_zone_device_ops int3400_thermal_ops = {
.get_temp = int3400_thermal_get_temp,
};
static struct thermal_zone_params int3400_thermal_params = {
.governor_name = "user_space",
.no_hwmon = true,
};
static int int3400_thermal_probe(struct platform_device *pdev)
{
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
struct int3400_thermal_priv *priv;
int result;
if (!adev)
return -ENODEV;
priv = kzalloc(sizeof(struct int3400_thermal_priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->adev = adev;
result = int3400_thermal_get_uuids(priv);
if (result)
goto free_priv;
result = acpi_parse_art(priv->adev->handle, &priv->art_count,
&priv->arts, true);
if (result)
goto free_priv;
result = acpi_parse_trt(priv->adev->handle, &priv->trt_count,
&priv->trts, true);
if (result)
goto free_art;
platform_set_drvdata(pdev, priv);
if (priv->uuid_bitmap & 1 << INT3400_THERMAL_PASSIVE_1) {
int3400_thermal_ops.get_mode = int3400_thermal_get_mode;
int3400_thermal_ops.set_mode = int3400_thermal_set_mode;
}
priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0,
priv, &int3400_thermal_ops,
&int3400_thermal_params, 0, 0);
if (IS_ERR(priv->thermal)) {
result = PTR_ERR(priv->thermal);
goto free_trt;
}
priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add(
priv->adev->handle);
return 0;
free_trt:
kfree(priv->trts);
free_art:
kfree(priv->arts);
free_priv:
kfree(priv);
return result;
}
static int int3400_thermal_remove(struct platform_device *pdev)
{
struct int3400_thermal_priv *priv = platform_get_drvdata(pdev);
if (!priv->rel_misc_dev_res)
acpi_thermal_rel_misc_device_remove(priv->adev->handle);
thermal_zone_device_unregister(priv->thermal);
kfree(priv->trts);
kfree(priv->arts);
kfree(priv);
return 0;
}
static const struct acpi_device_id int3400_thermal_match[] = {
{"INT3400", 0},
{}
};
MODULE_DEVICE_TABLE(acpi, int3400_thermal_match);
static struct platform_driver int3400_thermal_driver = {
.probe = int3400_thermal_probe,
.remove = int3400_thermal_remove,
.driver = {
.name = "int3400 thermal",
.owner = THIS_MODULE,
.acpi_match_table = ACPI_PTR(int3400_thermal_match),
},
};
module_platform_driver(int3400_thermal_driver);
MODULE_DESCRIPTION("INT3400 Thermal driver");
MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,242 @@
/*
* INT3402 thermal driver for memory temperature reporting
*
* Copyright (C) 2014, Intel Corporation
* Authors: Aaron Lu <aaron.lu@intel.com>
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/thermal.h>
#define ACPI_ACTIVE_COOLING_MAX_NR 10
struct active_trip {
unsigned long temp;
int id;
bool valid;
};
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;
};
static int int3402_thermal_get_zone_temp(struct thermal_zone_device *zone,
unsigned long *temp)
{
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;
/* _TMP returns the temperature in tenths of degrees Kelvin */
*temp = DECI_KELVIN_TO_MILLICELSIUS(tmp);
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;
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;
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;
if (!acpi_has_method(adev->handle, "_TMP"))
return -ENODEV;
d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL);
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;
}
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);
return 0;
}
static int int3402_thermal_remove(struct platform_device *pdev)
{
struct thermal_zone_device *zone = platform_get_drvdata(pdev);
thermal_zone_device_unregister(zone);
return 0;
}
static const struct acpi_device_id int3402_thermal_match[] = {
{"INT3402", 0},
{}
};
MODULE_DEVICE_TABLE(acpi, int3402_thermal_match);
static struct platform_driver int3402_thermal_driver = {
.probe = int3402_thermal_probe,
.remove = int3402_thermal_remove,
.driver = {
.name = "int3402 thermal",
.owner = THIS_MODULE,
.acpi_match_table = int3402_thermal_match,
},
};
module_platform_driver(int3402_thermal_driver);
MODULE_DESCRIPTION("INT3402 Thermal driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,483 @@
/*
* ACPI INT3403 thermal driver
* Copyright (c) 2013, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/acpi.h>
#include <linux/thermal.h>
#include <linux/platform_device.h>
#define INT3403_TYPE_SENSOR 0x03
#define INT3403_TYPE_CHARGER 0x0B
#define INT3403_TYPE_BATTERY 0x0C
#define INT3403_PERF_CHANGED_EVENT 0x80
#define INT3403_THERMAL_EVENT 0x90
#define DECI_KELVIN_TO_MILLI_CELSIUS(t, off) (((t) - (off)) * 100)
#define KELVIN_OFFSET 2732
#define MILLI_CELSIUS_TO_DECI_KELVIN(t, off) (((t) / 100) + (off))
struct int3403_sensor {
struct thermal_zone_device *tzone;
unsigned long *thresholds;
unsigned long crit_temp;
int crit_trip_id;
unsigned long psv_temp;
int psv_trip_id;
};
struct int3403_performance_state {
u64 performance;
u64 power;
u64 latency;
u64 linear;
u64 control;
u64 raw_performace;
char *raw_unit;
int reserved;
};
struct int3403_cdev {
struct thermal_cooling_device *cdev;
unsigned long max_state;
};
struct int3403_priv {
struct platform_device *pdev;
struct acpi_device *adev;
unsigned long long type;
void *priv;
};
static int sys_get_curr_temp(struct thermal_zone_device *tzone,
unsigned long *temp)
{
struct int3403_priv *priv = tzone->devdata;
struct acpi_device *device = priv->adev;
unsigned long long tmp;
acpi_status status;
status = acpi_evaluate_integer(device->handle, "_TMP", NULL, &tmp);
if (ACPI_FAILURE(status))
return -EIO;
*temp = DECI_KELVIN_TO_MILLI_CELSIUS(tmp, KELVIN_OFFSET);
return 0;
}
static int sys_get_trip_hyst(struct thermal_zone_device *tzone,
int trip, unsigned long *temp)
{
struct int3403_priv *priv = tzone->devdata;
struct acpi_device *device = priv->adev;
unsigned long long hyst;
acpi_status status;
status = acpi_evaluate_integer(device->handle, "GTSH", NULL, &hyst);
if (ACPI_FAILURE(status))
return -EIO;
/*
* Thermal hysteresis represents a temperature difference.
* Kelvin and Celsius have same degree size. So the
* conversion here between tenths of degree Kelvin unit
* and Milli-Celsius unit is just to multiply 100.
*/
*temp = hyst * 100;
return 0;
}
static int sys_get_trip_temp(struct thermal_zone_device *tzone,
int trip, unsigned long *temp)
{
struct int3403_priv *priv = tzone->devdata;
struct int3403_sensor *obj = priv->priv;
if (priv->type != INT3403_TYPE_SENSOR || !obj)
return -EINVAL;
if (trip == obj->crit_trip_id)
*temp = obj->crit_temp;
else if (trip == obj->psv_trip_id)
*temp = obj->psv_temp;
else {
/*
* get_trip_temp is a mandatory callback but
* PATx method doesn't return any value, so return
* cached value, which was last set from user space
*/
*temp = obj->thresholds[trip];
}
return 0;
}
static int sys_get_trip_type(struct thermal_zone_device *thermal,
int trip, enum thermal_trip_type *type)
{
struct int3403_priv *priv = thermal->devdata;
struct int3403_sensor *obj = priv->priv;
/* Mandatory callback, may not mean much here */
if (trip == obj->crit_trip_id)
*type = THERMAL_TRIP_CRITICAL;
else
*type = THERMAL_TRIP_PASSIVE;
return 0;
}
int sys_set_trip_temp(struct thermal_zone_device *tzone, int trip,
unsigned long temp)
{
struct int3403_priv *priv = tzone->devdata;
struct acpi_device *device = priv->adev;
struct int3403_sensor *obj = priv->priv;
acpi_status status;
char name[10];
int ret = 0;
snprintf(name, sizeof(name), "PAT%d", trip);
if (acpi_has_method(device->handle, name)) {
status = acpi_execute_simple_method(device->handle, name,
MILLI_CELSIUS_TO_DECI_KELVIN(temp,
KELVIN_OFFSET));
if (ACPI_FAILURE(status))
ret = -EIO;
else
obj->thresholds[trip] = temp;
} else {
ret = -EIO;
dev_err(&device->dev, "sys_set_trip_temp: method not found\n");
}
return ret;
}
static struct thermal_zone_device_ops tzone_ops = {
.get_temp = sys_get_curr_temp,
.get_trip_temp = sys_get_trip_temp,
.get_trip_type = sys_get_trip_type,
.set_trip_temp = sys_set_trip_temp,
.get_trip_hyst = sys_get_trip_hyst,
};
static struct thermal_zone_params int3403_thermal_params = {
.governor_name = "user_space",
.no_hwmon = true,
};
static void int3403_notify(acpi_handle handle,
u32 event, void *data)
{
struct int3403_priv *priv = data;
struct int3403_sensor *obj;
if (!priv)
return;
obj = priv->priv;
if (priv->type != INT3403_TYPE_SENSOR || !obj)
return;
switch (event) {
case INT3403_PERF_CHANGED_EVENT:
break;
case INT3403_THERMAL_EVENT:
thermal_zone_device_update(obj->tzone);
break;
default:
dev_err(&priv->pdev->dev, "Unsupported event [0x%x]\n", event);
break;
}
}
static int sys_get_trip_crt(struct acpi_device *device, unsigned long *temp)
{
unsigned long long crt;
acpi_status status;
status = acpi_evaluate_integer(device->handle, "_CRT", NULL, &crt);
if (ACPI_FAILURE(status))
return -EIO;
*temp = DECI_KELVIN_TO_MILLI_CELSIUS(crt, KELVIN_OFFSET);
return 0;
}
static int sys_get_trip_psv(struct acpi_device *device, unsigned long *temp)
{
unsigned long long psv;
acpi_status status;
status = acpi_evaluate_integer(device->handle, "_PSV", NULL, &psv);
if (ACPI_FAILURE(status))
return -EIO;
*temp = DECI_KELVIN_TO_MILLI_CELSIUS(psv, KELVIN_OFFSET);
return 0;
}
static int int3403_sensor_add(struct int3403_priv *priv)
{
int result = 0;
acpi_status status;
struct int3403_sensor *obj;
unsigned long long trip_cnt;
int trip_mask = 0;
obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL);
if (!obj)
return -ENOMEM;
priv->priv = obj;
status = acpi_evaluate_integer(priv->adev->handle, "PATC", NULL,
&trip_cnt);
if (ACPI_FAILURE(status))
trip_cnt = 0;
if (trip_cnt) {
/* We have to cache, thresholds can't be readback */
obj->thresholds = devm_kzalloc(&priv->pdev->dev,
sizeof(*obj->thresholds) * trip_cnt,
GFP_KERNEL);
if (!obj->thresholds) {
result = -ENOMEM;
goto err_free_obj;
}
trip_mask = BIT(trip_cnt) - 1;
}
obj->psv_trip_id = -1;
if (!sys_get_trip_psv(priv->adev, &obj->psv_temp))
obj->psv_trip_id = trip_cnt++;
obj->crit_trip_id = -1;
if (!sys_get_trip_crt(priv->adev, &obj->crit_temp))
obj->crit_trip_id = trip_cnt++;
obj->tzone = thermal_zone_device_register(acpi_device_bid(priv->adev),
trip_cnt, trip_mask, priv, &tzone_ops,
&int3403_thermal_params, 0, 0);
if (IS_ERR(obj->tzone)) {
result = PTR_ERR(obj->tzone);
obj->tzone = NULL;
goto err_free_obj;
}
result = acpi_install_notify_handler(priv->adev->handle,
ACPI_DEVICE_NOTIFY, int3403_notify,
(void *)priv);
if (result)
goto err_free_obj;
return 0;
err_free_obj:
if (obj->tzone)
thermal_zone_device_unregister(obj->tzone);
return result;
}
static int int3403_sensor_remove(struct int3403_priv *priv)
{
struct int3403_sensor *obj = priv->priv;
thermal_zone_device_unregister(obj->tzone);
return 0;
}
/* INT3403 Cooling devices */
static int int3403_get_max_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct int3403_priv *priv = cdev->devdata;
struct int3403_cdev *obj = priv->priv;
*state = obj->max_state;
return 0;
}
static int int3403_get_cur_state(struct thermal_cooling_device *cdev,
unsigned long *state)
{
struct int3403_priv *priv = cdev->devdata;
unsigned long long level;
acpi_status status;
status = acpi_evaluate_integer(priv->adev->handle, "PPPC", NULL, &level);
if (ACPI_SUCCESS(status)) {
*state = level;
return 0;
} else
return -EINVAL;
}
static int
int3403_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
{
struct int3403_priv *priv = cdev->devdata;
acpi_status status;
status = acpi_execute_simple_method(priv->adev->handle, "SPPC", state);
if (ACPI_SUCCESS(status))
return 0;
else
return -EINVAL;
}
static const struct thermal_cooling_device_ops int3403_cooling_ops = {
.get_max_state = int3403_get_max_state,
.get_cur_state = int3403_get_cur_state,
.set_cur_state = int3403_set_cur_state,
};
static int int3403_cdev_add(struct int3403_priv *priv)
{
int result = 0;
acpi_status status;
struct int3403_cdev *obj;
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *p;
obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL);
if (!obj)
return -ENOMEM;
status = acpi_evaluate_object(priv->adev->handle, "PPSS", NULL, &buf);
if (ACPI_FAILURE(status))
return -ENODEV;
p = buf.pointer;
if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
printk(KERN_WARNING "Invalid PPSS data\n");
return -EFAULT;
}
obj->max_state = p->package.count - 1;
obj->cdev =
thermal_cooling_device_register(acpi_device_bid(priv->adev),
priv, &int3403_cooling_ops);
if (IS_ERR(obj->cdev))
result = PTR_ERR(obj->cdev);
priv->priv = obj;
/* TODO: add ACPI notification support */
return result;
}
static int int3403_cdev_remove(struct int3403_priv *priv)
{
struct int3403_cdev *obj = priv->priv;
thermal_cooling_device_unregister(obj->cdev);
return 0;
}
static int int3403_add(struct platform_device *pdev)
{
struct int3403_priv *priv;
int result = 0;
acpi_status status;
priv = devm_kzalloc(&pdev->dev, sizeof(struct int3403_priv),
GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->pdev = pdev;
priv->adev = ACPI_COMPANION(&(pdev->dev));
if (!priv->adev) {
result = -EINVAL;
goto err;
}
status = acpi_evaluate_integer(priv->adev->handle, "PTYP",
NULL, &priv->type);
if (ACPI_FAILURE(status)) {
result = -EINVAL;
goto err;
}
platform_set_drvdata(pdev, priv);
switch (priv->type) {
case INT3403_TYPE_SENSOR:
result = int3403_sensor_add(priv);
break;
case INT3403_TYPE_CHARGER:
case INT3403_TYPE_BATTERY:
result = int3403_cdev_add(priv);
break;
default:
result = -EINVAL;
}
if (result)
goto err;
return result;
err:
return result;
}
static int int3403_remove(struct platform_device *pdev)
{
struct int3403_priv *priv = platform_get_drvdata(pdev);
switch (priv->type) {
case INT3403_TYPE_SENSOR:
int3403_sensor_remove(priv);
break;
case INT3403_TYPE_CHARGER:
case INT3403_TYPE_BATTERY:
int3403_cdev_remove(priv);
break;
default:
break;
}
return 0;
}
static const struct acpi_device_id int3403_device_ids[] = {
{"INT3403", 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, int3403_device_ids);
static struct platform_driver int3403_driver = {
.probe = int3403_add,
.remove = int3403_remove,
.driver = {
.name = "int3403 thermal",
.owner = THIS_MODULE,
.acpi_match_table = int3403_device_ids,
},
};
module_platform_driver(int3403_driver);
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("ACPI INT3403 thermal driver");