drivers: thermal: Move various drivers for intel platforms into a subdir
This cleans up the directory a bit, now that we have several other platforms using platform-specific sub-directories. Compile-tested with ARCH=x86 defconfig and the drivers explicitly enabled with menuconfig. Signed-off-by: Amit Kucheria <amit.kucheria@linaro.org> Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org> Signed-off-by: Zhang Rui <rui.zhang@intel.com>
Tento commit je obsažen v:
42
drivers/thermal/intel/int340x_thermal/Kconfig
Normální soubor
42
drivers/thermal/intel/int340x_thermal/Kconfig
Normální soubor
@@ -0,0 +1,42 @@
|
||||
#
|
||||
# ACPI INT340x thermal drivers configuration
|
||||
#
|
||||
|
||||
config INT340X_THERMAL
|
||||
tristate "ACPI INT340X thermal drivers"
|
||||
depends on X86 && ACPI
|
||||
select THERMAL_GOV_USER_SPACE
|
||||
select ACPI_THERMAL_REL
|
||||
select ACPI_FAN
|
||||
select INTEL_SOC_DTS_IOSF_CORE
|
||||
help
|
||||
Newer laptops and tablets that use ACPI may have thermal sensors and
|
||||
other devices with thermal control capabilities outside the core
|
||||
CPU/SOC, for thermal safety reasons.
|
||||
They are exposed for the OS to use via the INT3400 ACPI device object
|
||||
as the master, and INT3401~INT340B ACPI device objects as the slaves.
|
||||
Enable this to expose the temperature information and cooling ability
|
||||
from these objects to userspace via the normal thermal framework.
|
||||
This means that a wide range of applications and GUI widgets can show
|
||||
the information to the user or use this information for making
|
||||
decisions. For example, the Intel Thermal Daemon can use this
|
||||
information to allow the user to select his laptop to run without
|
||||
turning on the fans.
|
||||
|
||||
config ACPI_THERMAL_REL
|
||||
tristate
|
||||
depends on ACPI
|
||||
|
||||
if INT340X_THERMAL
|
||||
|
||||
config INT3406_THERMAL
|
||||
tristate "ACPI INT3406 display thermal driver"
|
||||
depends on ACPI_VIDEO
|
||||
help
|
||||
The display thermal device represents the LED/LCD display panel
|
||||
that may or may not include touch support. The main function of
|
||||
the display thermal device is to allow control of the display
|
||||
brightness in order to address a thermal condition or to reduce
|
||||
power consumed by display device.
|
||||
|
||||
endif
|
8
drivers/thermal/intel/int340x_thermal/Makefile
Normální soubor
8
drivers/thermal/intel/int340x_thermal/Makefile
Normální soubor
@@ -0,0 +1,8 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
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
|
||||
obj-$(CONFIG_INT3406_THERMAL) += int3406_thermal.o
|
||||
obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o
|
394
drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c
Normální soubor
394
drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c
Normální soubor
@@ -0,0 +1,394 @@
|
||||
/* 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
|
||||
* @trt_count: the number of valid entries resulted from parsing _TRT
|
||||
* @trtp: pointer to pointer of array of _TRT 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 -ENODEV;
|
||||
|
||||
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 = kcalloc(*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)
|
||||
pr_warn("Failed to get source ACPI device\n");
|
||||
|
||||
result = acpi_bus_get_device(trt->target, &adev);
|
||||
if (result)
|
||||
pr_warn("Failed to get target ACPI device\n");
|
||||
}
|
||||
|
||||
result = 0;
|
||||
|
||||
*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 -ENODEV;
|
||||
|
||||
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 = kcalloc(*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)
|
||||
pr_warn("Failed to get source ACPI device\n");
|
||||
}
|
||||
if (art->target) {
|
||||
result = acpi_bus_get_device(art->target, &adev);
|
||||
if (result)
|
||||
pr_warn("Failed to get target 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 to get device name from acpi 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;
|
||||
int count = 0;
|
||||
char __user *arg = (void __user *)__arg;
|
||||
struct trt *trts = NULL;
|
||||
struct art *arts = NULL;
|
||||
|
||||
switch (cmd) {
|
||||
case ACPI_THERMAL_GET_TRT_COUNT:
|
||||
ret = acpi_parse_trt(acpi_thermal_rel_handle, &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, &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, &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, &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");
|
85
drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.h
Normální soubor
85
drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.h
Normální soubor
@@ -0,0 +1,85 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#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 reserved1;
|
||||
u64 reserved2;
|
||||
u64 reserved3;
|
||||
u64 reserved4;
|
||||
} __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 */
|
382
drivers/thermal/intel/int340x_thermal/int3400_thermal.c
Normální soubor
382
drivers/thermal/intel/int340x_thermal/int3400_thermal.c
Normální soubor
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#define INT3400_THERMAL_TABLE_CHANGED 0x83
|
||||
|
||||
enum int3400_thermal_uuid {
|
||||
INT3400_THERMAL_PASSIVE_1,
|
||||
INT3400_THERMAL_ACTIVE,
|
||||
INT3400_THERMAL_CRITICAL,
|
||||
INT3400_THERMAL_MAXIMUM_UUID,
|
||||
};
|
||||
|
||||
static char *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = {
|
||||
"42A441D6-AE6A-462b-A84B-4A8CE79027D3",
|
||||
"3A95C389-E4B8-4629-A526-C52C88626BAE",
|
||||
"97C68AE7-15FA-499c-B8C9-5DA81D606E0A",
|
||||
};
|
||||
|
||||
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;
|
||||
int current_uuid_index;
|
||||
};
|
||||
|
||||
static ssize_t available_uuids_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
|
||||
int i;
|
||||
int length = 0;
|
||||
|
||||
for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; i++) {
|
||||
if (priv->uuid_bitmap & (1 << i))
|
||||
if (PAGE_SIZE - length > 0)
|
||||
length += snprintf(&buf[length],
|
||||
PAGE_SIZE - length,
|
||||
"%s\n",
|
||||
int3400_thermal_uuids[i]);
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
static ssize_t current_uuid_show(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (priv->uuid_bitmap & (1 << priv->current_uuid_index))
|
||||
return sprintf(buf, "%s\n",
|
||||
int3400_thermal_uuids[priv->current_uuid_index]);
|
||||
else
|
||||
return sprintf(buf, "INVALID\n");
|
||||
}
|
||||
|
||||
static ssize_t current_uuid_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct int3400_thermal_priv *priv = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < INT3400_THERMAL_MAXIMUM_UUID; ++i) {
|
||||
if ((priv->uuid_bitmap & (1 << i)) &&
|
||||
!(strncmp(buf, int3400_thermal_uuids[i],
|
||||
sizeof(int3400_thermal_uuids[i]) - 1))) {
|
||||
priv->current_uuid_index = i;
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(current_uuid);
|
||||
static DEVICE_ATTR_RO(available_uuids);
|
||||
static struct attribute *uuid_attrs[] = {
|
||||
&dev_attr_available_uuids.attr,
|
||||
&dev_attr_current_uuid.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group uuid_attribute_group = {
|
||||
.attrs = uuid_attrs,
|
||||
.name = "uuids"
|
||||
};
|
||||
|
||||
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++) {
|
||||
guid_t guid;
|
||||
|
||||
guid_parse(int3400_thermal_uuids[j], &guid);
|
||||
if (guid_equal((guid_t *)objb->buffer.pointer, &guid)) {
|
||||
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 void int3400_notify(acpi_handle handle,
|
||||
u32 event,
|
||||
void *data)
|
||||
{
|
||||
struct int3400_thermal_priv *priv = data;
|
||||
char *thermal_prop[5];
|
||||
|
||||
if (!priv)
|
||||
return;
|
||||
|
||||
switch (event) {
|
||||
case INT3400_THERMAL_TABLE_CHANGED:
|
||||
thermal_prop[0] = kasprintf(GFP_KERNEL, "NAME=%s",
|
||||
priv->thermal->type);
|
||||
thermal_prop[1] = kasprintf(GFP_KERNEL, "TEMP=%d",
|
||||
priv->thermal->temperature);
|
||||
thermal_prop[2] = kasprintf(GFP_KERNEL, "TRIP=");
|
||||
thermal_prop[3] = kasprintf(GFP_KERNEL, "EVENT=%d",
|
||||
THERMAL_TABLE_CHANGED);
|
||||
thermal_prop[4] = NULL;
|
||||
kobject_uevent_env(&priv->thermal->device.kobj, KOBJ_CHANGE,
|
||||
thermal_prop);
|
||||
break;
|
||||
default:
|
||||
/* Ignore unknown notification codes sent to INT3400 device */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int int3400_thermal_get_temp(struct thermal_zone_device *thermal,
|
||||
int *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;
|
||||
result = int3400_thermal_run_osc(priv->adev->handle,
|
||||
priv->current_uuid_index,
|
||||
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)
|
||||
dev_dbg(&pdev->dev, "_ART table parsing error\n");
|
||||
|
||||
result = acpi_parse_trt(priv->adev->handle, &priv->trt_count,
|
||||
&priv->trts, true);
|
||||
if (result)
|
||||
dev_dbg(&pdev->dev, "_TRT table parsing error\n");
|
||||
|
||||
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_art_trt;
|
||||
}
|
||||
|
||||
priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add(
|
||||
priv->adev->handle);
|
||||
|
||||
result = sysfs_create_group(&pdev->dev.kobj, &uuid_attribute_group);
|
||||
if (result)
|
||||
goto free_rel_misc;
|
||||
|
||||
result = acpi_install_notify_handler(
|
||||
priv->adev->handle, ACPI_DEVICE_NOTIFY, int3400_notify,
|
||||
(void *)priv);
|
||||
if (result)
|
||||
goto free_sysfs;
|
||||
|
||||
return 0;
|
||||
|
||||
free_sysfs:
|
||||
sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group);
|
||||
free_rel_misc:
|
||||
if (!priv->rel_misc_dev_res)
|
||||
acpi_thermal_rel_misc_device_remove(priv->adev->handle);
|
||||
thermal_zone_device_unregister(priv->thermal);
|
||||
free_art_trt:
|
||||
kfree(priv->trts);
|
||||
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);
|
||||
|
||||
acpi_remove_notify_handler(
|
||||
priv->adev->handle, ACPI_DEVICE_NOTIFY,
|
||||
int3400_notify);
|
||||
|
||||
if (!priv->rel_misc_dev_res)
|
||||
acpi_thermal_rel_misc_device_remove(priv->adev->handle);
|
||||
|
||||
sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group);
|
||||
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",
|
||||
.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");
|
108
drivers/thermal/intel/int340x_thermal/int3402_thermal.c
Normální soubor
108
drivers/thermal/intel/int340x_thermal/int3402_thermal.c
Normální soubor
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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>
|
||||
#include "int340x_thermal_zone.h"
|
||||
|
||||
#define INT3402_PERF_CHANGED_EVENT 0x80
|
||||
#define INT3402_THERMAL_EVENT 0x90
|
||||
|
||||
struct int3402_thermal_data {
|
||||
acpi_handle *handle;
|
||||
struct int34x_thermal_zone *int340x_zone;
|
||||
};
|
||||
|
||||
static void int3402_notify(acpi_handle handle, u32 event, void *data)
|
||||
{
|
||||
struct int3402_thermal_data *priv = data;
|
||||
|
||||
if (!priv)
|
||||
return;
|
||||
|
||||
switch (event) {
|
||||
case INT3402_PERF_CHANGED_EVENT:
|
||||
break;
|
||||
case INT3402_THERMAL_EVENT:
|
||||
int340x_thermal_zone_device_update(priv->int340x_zone,
|
||||
THERMAL_TRIP_VIOLATED);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int int3402_thermal_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
|
||||
struct int3402_thermal_data *d;
|
||||
int ret;
|
||||
|
||||
if (!acpi_has_method(adev->handle, "_TMP"))
|
||||
return -ENODEV;
|
||||
|
||||
d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
d->handle = adev->handle;
|
||||
platform_set_drvdata(pdev, d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int3402_thermal_remove(struct platform_device *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);
|
||||
|
||||
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",
|
||||
.acpi_match_table = int3402_thermal_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(int3402_thermal_driver);
|
||||
|
||||
MODULE_DESCRIPTION("INT3402 Thermal driver");
|
||||
MODULE_LICENSE("GPL");
|
311
drivers/thermal/intel/int340x_thermal/int3403_thermal.c
Normální soubor
311
drivers/thermal/intel/int340x_thermal/int3403_thermal.c
Normální soubor
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* 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>
|
||||
#include "int340x_thermal_zone.h"
|
||||
|
||||
#define INT3403_TYPE_SENSOR 0x03
|
||||
#define INT3403_TYPE_CHARGER 0x0B
|
||||
#define INT3403_TYPE_BATTERY 0x0C
|
||||
#define INT3403_PERF_CHANGED_EVENT 0x80
|
||||
#define INT3403_PERF_TRIP_POINT_CHANGED 0x81
|
||||
#define INT3403_THERMAL_EVENT 0x90
|
||||
|
||||
/* Preserved structure for future expandbility */
|
||||
struct int3403_sensor {
|
||||
struct int34x_thermal_zone *int340x_zone;
|
||||
};
|
||||
|
||||
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 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:
|
||||
int340x_thermal_zone_device_update(obj->int340x_zone,
|
||||
THERMAL_TRIP_VIOLATED);
|
||||
break;
|
||||
case INT3403_PERF_TRIP_POINT_CHANGED:
|
||||
int340x_thermal_read_trips(obj->int340x_zone);
|
||||
int340x_thermal_zone_device_update(obj->int340x_zone,
|
||||
THERMAL_TRIP_CHANGED);
|
||||
break;
|
||||
default:
|
||||
dev_err(&priv->pdev->dev, "Unsupported event [0x%x]\n", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int int3403_sensor_add(struct int3403_priv *priv)
|
||||
{
|
||||
int result = 0;
|
||||
struct int3403_sensor *obj;
|
||||
|
||||
obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL);
|
||||
if (!obj)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->priv = obj;
|
||||
|
||||
obj->int340x_zone = int340x_thermal_zone_add(priv->adev, NULL);
|
||||
if (IS_ERR(obj->int340x_zone))
|
||||
return PTR_ERR(obj->int340x_zone);
|
||||
|
||||
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:
|
||||
int340x_thermal_zone_remove(obj->int340x_zone);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int int3403_sensor_remove(struct int3403_priv *priv)
|
||||
{
|
||||
struct int3403_sensor *obj = priv->priv;
|
||||
|
||||
acpi_remove_notify_handler(priv->adev->handle,
|
||||
ACPI_DEVICE_NOTIFY, int3403_notify);
|
||||
int340x_thermal_zone_remove(obj->int340x_zone);
|
||||
|
||||
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");
|
||||
kfree(buf.pointer);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
priv->priv = obj;
|
||||
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);
|
||||
|
||||
kfree(buf.pointer);
|
||||
/* 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)) {
|
||||
unsigned long long tmp;
|
||||
|
||||
status = acpi_evaluate_integer(priv->adev->handle, "_TMP",
|
||||
NULL, &tmp);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
result = -EINVAL;
|
||||
goto err;
|
||||
} else {
|
||||
priv->type = INT3403_TYPE_SENSOR;
|
||||
}
|
||||
}
|
||||
|
||||
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",
|
||||
.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");
|
213
drivers/thermal/intel/int340x_thermal/int3406_thermal.c
Normální soubor
213
drivers/thermal/intel/int340x_thermal/int3406_thermal.c
Normální soubor
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* INT3406 thermal driver for display participant device
|
||||
*
|
||||
* Copyright (C) 2016, 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/backlight.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <acpi/video.h>
|
||||
|
||||
#define INT3406_BRIGHTNESS_LIMITS_CHANGED 0x80
|
||||
|
||||
struct int3406_thermal_data {
|
||||
int upper_limit;
|
||||
int lower_limit;
|
||||
acpi_handle handle;
|
||||
struct acpi_video_device_brightness *br;
|
||||
struct backlight_device *raw_bd;
|
||||
struct thermal_cooling_device *cooling_dev;
|
||||
};
|
||||
|
||||
/*
|
||||
* According to the ACPI spec,
|
||||
* "Each brightness level is represented by a number between 0 and 100,
|
||||
* and can be thought of as a percentage. For example, 50 can be 50%
|
||||
* power consumption or 50% brightness, as defined by the OEM."
|
||||
*
|
||||
* As int3406 device uses this value to communicate with the native
|
||||
* graphics driver, we make the assumption that it represents
|
||||
* the percentage of brightness only
|
||||
*/
|
||||
#define ACPI_TO_RAW(v, d) (d->raw_bd->props.max_brightness * v / 100)
|
||||
#define RAW_TO_ACPI(v, d) (v * 100 / d->raw_bd->props.max_brightness)
|
||||
|
||||
static int
|
||||
int3406_thermal_get_max_state(struct thermal_cooling_device *cooling_dev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct int3406_thermal_data *d = cooling_dev->devdata;
|
||||
|
||||
*state = d->upper_limit - d->lower_limit;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
int3406_thermal_set_cur_state(struct thermal_cooling_device *cooling_dev,
|
||||
unsigned long state)
|
||||
{
|
||||
struct int3406_thermal_data *d = cooling_dev->devdata;
|
||||
int acpi_level, raw_level;
|
||||
|
||||
if (state > d->upper_limit - d->lower_limit)
|
||||
return -EINVAL;
|
||||
|
||||
acpi_level = d->br->levels[d->upper_limit - state];
|
||||
|
||||
raw_level = ACPI_TO_RAW(acpi_level, d);
|
||||
|
||||
return backlight_device_set_brightness(d->raw_bd, raw_level);
|
||||
}
|
||||
|
||||
static int
|
||||
int3406_thermal_get_cur_state(struct thermal_cooling_device *cooling_dev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct int3406_thermal_data *d = cooling_dev->devdata;
|
||||
int acpi_level;
|
||||
int index;
|
||||
|
||||
acpi_level = RAW_TO_ACPI(d->raw_bd->props.brightness, d);
|
||||
|
||||
/*
|
||||
* There is no 1:1 mapping between the firmware interface level
|
||||
* with the raw interface level, we will have to find one that is
|
||||
* right above it.
|
||||
*/
|
||||
for (index = d->lower_limit; index < d->upper_limit; index++) {
|
||||
if (acpi_level <= d->br->levels[index])
|
||||
break;
|
||||
}
|
||||
|
||||
*state = d->upper_limit - index;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct thermal_cooling_device_ops video_cooling_ops = {
|
||||
.get_max_state = int3406_thermal_get_max_state,
|
||||
.get_cur_state = int3406_thermal_get_cur_state,
|
||||
.set_cur_state = int3406_thermal_set_cur_state,
|
||||
};
|
||||
|
||||
static int int3406_thermal_get_index(int *array, int nr, int value)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 2; i < nr; i++) {
|
||||
if (array[i] == value)
|
||||
break;
|
||||
}
|
||||
return i == nr ? -ENOENT : i;
|
||||
}
|
||||
|
||||
static void int3406_thermal_get_limit(struct int3406_thermal_data *d)
|
||||
{
|
||||
acpi_status status;
|
||||
unsigned long long lower_limit, upper_limit;
|
||||
|
||||
status = acpi_evaluate_integer(d->handle, "DDDL", NULL, &lower_limit);
|
||||
if (ACPI_SUCCESS(status))
|
||||
d->lower_limit = int3406_thermal_get_index(d->br->levels,
|
||||
d->br->count, lower_limit);
|
||||
|
||||
status = acpi_evaluate_integer(d->handle, "DDPC", NULL, &upper_limit);
|
||||
if (ACPI_SUCCESS(status))
|
||||
d->upper_limit = int3406_thermal_get_index(d->br->levels,
|
||||
d->br->count, upper_limit);
|
||||
|
||||
/* lower_limit and upper_limit should be always set */
|
||||
d->lower_limit = d->lower_limit > 0 ? d->lower_limit : 2;
|
||||
d->upper_limit = d->upper_limit > 0 ? d->upper_limit : d->br->count - 1;
|
||||
}
|
||||
|
||||
static void int3406_notify(acpi_handle handle, u32 event, void *data)
|
||||
{
|
||||
if (event == INT3406_BRIGHTNESS_LIMITS_CHANGED)
|
||||
int3406_thermal_get_limit(data);
|
||||
}
|
||||
|
||||
static int int3406_thermal_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
|
||||
struct int3406_thermal_data *d;
|
||||
struct backlight_device *bd;
|
||||
int ret;
|
||||
|
||||
if (!ACPI_HANDLE(&pdev->dev))
|
||||
return -ENODEV;
|
||||
|
||||
d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
d->handle = ACPI_HANDLE(&pdev->dev);
|
||||
|
||||
bd = backlight_device_get_by_type(BACKLIGHT_RAW);
|
||||
if (!bd)
|
||||
return -ENODEV;
|
||||
d->raw_bd = bd;
|
||||
|
||||
ret = acpi_video_get_levels(ACPI_COMPANION(&pdev->dev), &d->br, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
int3406_thermal_get_limit(d);
|
||||
|
||||
d->cooling_dev = thermal_cooling_device_register(acpi_device_bid(adev),
|
||||
d, &video_cooling_ops);
|
||||
if (IS_ERR(d->cooling_dev))
|
||||
goto err;
|
||||
|
||||
ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
|
||||
int3406_notify, d);
|
||||
if (ret)
|
||||
goto err_cdev;
|
||||
|
||||
platform_set_drvdata(pdev, d);
|
||||
|
||||
return 0;
|
||||
|
||||
err_cdev:
|
||||
thermal_cooling_device_unregister(d->cooling_dev);
|
||||
err:
|
||||
kfree(d->br);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int int3406_thermal_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct int3406_thermal_data *d = platform_get_drvdata(pdev);
|
||||
|
||||
thermal_cooling_device_unregister(d->cooling_dev);
|
||||
kfree(d->br);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id int3406_thermal_match[] = {
|
||||
{"INT3406", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(acpi, int3406_thermal_match);
|
||||
|
||||
static struct platform_driver int3406_thermal_driver = {
|
||||
.probe = int3406_thermal_probe,
|
||||
.remove = int3406_thermal_remove,
|
||||
.driver = {
|
||||
.name = "int3406 thermal",
|
||||
.acpi_match_table = int3406_thermal_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(int3406_thermal_driver);
|
||||
|
||||
MODULE_DESCRIPTION("INT3406 Thermal driver");
|
||||
MODULE_LICENSE("GPL v2");
|
295
drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c
Normální soubor
295
drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c
Normální soubor
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* int340x_thermal_zone.c
|
||||
* Copyright (c) 2015, 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/acpi.h>
|
||||
#include <linux/thermal.h>
|
||||
#include "int340x_thermal_zone.h"
|
||||
|
||||
static int int340x_thermal_get_zone_temp(struct thermal_zone_device *zone,
|
||||
int *temp)
|
||||
{
|
||||
struct int34x_thermal_zone *d = zone->devdata;
|
||||
unsigned long long tmp;
|
||||
acpi_status status;
|
||||
|
||||
if (d->override_ops && d->override_ops->get_temp)
|
||||
return d->override_ops->get_temp(zone, temp);
|
||||
|
||||
status = acpi_evaluate_integer(d->adev->handle, "_TMP", NULL, &tmp);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
if (d->lpat_table) {
|
||||
int conv_temp;
|
||||
|
||||
conv_temp = acpi_lpat_raw_to_temp(d->lpat_table, (int)tmp);
|
||||
if (conv_temp < 0)
|
||||
return conv_temp;
|
||||
|
||||
*temp = (unsigned long)conv_temp * 10;
|
||||
} else
|
||||
/* _TMP returns the temperature in tenths of degrees Kelvin */
|
||||
*temp = DECI_KELVIN_TO_MILLICELSIUS(tmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int340x_thermal_get_trip_temp(struct thermal_zone_device *zone,
|
||||
int trip, int *temp)
|
||||
{
|
||||
struct int34x_thermal_zone *d = zone->devdata;
|
||||
int i;
|
||||
|
||||
if (d->override_ops && d->override_ops->get_trip_temp)
|
||||
return d->override_ops->get_trip_temp(zone, trip, temp);
|
||||
|
||||
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 < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
|
||||
if (d->act_trips[i].valid &&
|
||||
d->act_trips[i].id == trip) {
|
||||
*temp = d->act_trips[i].temp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int340x_thermal_get_trip_type(struct thermal_zone_device *zone,
|
||||
int trip,
|
||||
enum thermal_trip_type *type)
|
||||
{
|
||||
struct int34x_thermal_zone *d = zone->devdata;
|
||||
int i;
|
||||
|
||||
if (d->override_ops && d->override_ops->get_trip_type)
|
||||
return d->override_ops->get_trip_type(zone, trip, type);
|
||||
|
||||
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 < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
|
||||
if (d->act_trips[i].valid &&
|
||||
d->act_trips[i].id == trip) {
|
||||
*type = THERMAL_TRIP_ACTIVE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone,
|
||||
int trip, int temp)
|
||||
{
|
||||
struct int34x_thermal_zone *d = zone->devdata;
|
||||
acpi_status status;
|
||||
char name[10];
|
||||
|
||||
if (d->override_ops && d->override_ops->set_trip_temp)
|
||||
return d->override_ops->set_trip_temp(zone, trip, temp);
|
||||
|
||||
snprintf(name, sizeof(name), "PAT%d", trip);
|
||||
status = acpi_execute_simple_method(d->adev->handle, name,
|
||||
MILLICELSIUS_TO_DECI_KELVIN(temp));
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
d->aux_trips[trip] = temp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int int340x_thermal_get_trip_hyst(struct thermal_zone_device *zone,
|
||||
int trip, int *temp)
|
||||
{
|
||||
struct int34x_thermal_zone *d = zone->devdata;
|
||||
acpi_status status;
|
||||
unsigned long long hyst;
|
||||
|
||||
if (d->override_ops && d->override_ops->get_trip_hyst)
|
||||
return d->override_ops->get_trip_hyst(zone, trip, temp);
|
||||
|
||||
status = acpi_evaluate_integer(d->adev->handle, "GTSH", NULL, &hyst);
|
||||
if (ACPI_FAILURE(status))
|
||||
*temp = 0;
|
||||
else
|
||||
*temp = hyst * 100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops int340x_thermal_zone_ops = {
|
||||
.get_temp = int340x_thermal_get_zone_temp,
|
||||
.get_trip_temp = int340x_thermal_get_trip_temp,
|
||||
.get_trip_type = int340x_thermal_get_trip_type,
|
||||
.set_trip_temp = int340x_thermal_set_trip_temp,
|
||||
.get_trip_hyst = int340x_thermal_get_trip_hyst,
|
||||
};
|
||||
|
||||
static int int340x_thermal_get_trip_config(acpi_handle handle, char *name,
|
||||
int *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;
|
||||
}
|
||||
|
||||
int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone)
|
||||
{
|
||||
int trip_cnt = int34x_zone->aux_trip_nr;
|
||||
int i;
|
||||
|
||||
int34x_zone->crt_trip_id = -1;
|
||||
if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_CRT",
|
||||
&int34x_zone->crt_temp))
|
||||
int34x_zone->crt_trip_id = trip_cnt++;
|
||||
|
||||
int34x_zone->hot_trip_id = -1;
|
||||
if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_HOT",
|
||||
&int34x_zone->hot_temp))
|
||||
int34x_zone->hot_trip_id = trip_cnt++;
|
||||
|
||||
int34x_zone->psv_trip_id = -1;
|
||||
if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_PSV",
|
||||
&int34x_zone->psv_temp))
|
||||
int34x_zone->psv_trip_id = trip_cnt++;
|
||||
|
||||
for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
|
||||
char name[5] = { '_', 'A', 'C', '0' + i, '\0' };
|
||||
|
||||
if (int340x_thermal_get_trip_config(int34x_zone->adev->handle,
|
||||
name,
|
||||
&int34x_zone->act_trips[i].temp))
|
||||
break;
|
||||
|
||||
int34x_zone->act_trips[i].id = trip_cnt++;
|
||||
int34x_zone->act_trips[i].valid = true;
|
||||
}
|
||||
|
||||
return trip_cnt;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(int340x_thermal_read_trips);
|
||||
|
||||
static struct thermal_zone_params int340x_thermal_params = {
|
||||
.governor_name = "user_space",
|
||||
.no_hwmon = true,
|
||||
};
|
||||
|
||||
struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
|
||||
struct thermal_zone_device_ops *override_ops)
|
||||
{
|
||||
struct int34x_thermal_zone *int34x_thermal_zone;
|
||||
acpi_status status;
|
||||
unsigned long long trip_cnt;
|
||||
int trip_mask = 0;
|
||||
int ret;
|
||||
|
||||
int34x_thermal_zone = kzalloc(sizeof(*int34x_thermal_zone),
|
||||
GFP_KERNEL);
|
||||
if (!int34x_thermal_zone)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
int34x_thermal_zone->adev = adev;
|
||||
int34x_thermal_zone->override_ops = override_ops;
|
||||
|
||||
status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt);
|
||||
if (ACPI_FAILURE(status))
|
||||
trip_cnt = 0;
|
||||
else {
|
||||
int34x_thermal_zone->aux_trips =
|
||||
kcalloc(trip_cnt,
|
||||
sizeof(*int34x_thermal_zone->aux_trips),
|
||||
GFP_KERNEL);
|
||||
if (!int34x_thermal_zone->aux_trips) {
|
||||
ret = -ENOMEM;
|
||||
goto err_trip_alloc;
|
||||
}
|
||||
trip_mask = BIT(trip_cnt) - 1;
|
||||
int34x_thermal_zone->aux_trip_nr = trip_cnt;
|
||||
}
|
||||
|
||||
trip_cnt = int340x_thermal_read_trips(int34x_thermal_zone);
|
||||
|
||||
int34x_thermal_zone->lpat_table = acpi_lpat_get_conversion_table(
|
||||
adev->handle);
|
||||
|
||||
int34x_thermal_zone->zone = thermal_zone_device_register(
|
||||
acpi_device_bid(adev),
|
||||
trip_cnt,
|
||||
trip_mask, int34x_thermal_zone,
|
||||
&int340x_thermal_zone_ops,
|
||||
&int340x_thermal_params,
|
||||
0, 0);
|
||||
if (IS_ERR(int34x_thermal_zone->zone)) {
|
||||
ret = PTR_ERR(int34x_thermal_zone->zone);
|
||||
goto err_thermal_zone;
|
||||
}
|
||||
|
||||
return int34x_thermal_zone;
|
||||
|
||||
err_thermal_zone:
|
||||
acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
|
||||
kfree(int34x_thermal_zone->aux_trips);
|
||||
err_trip_alloc:
|
||||
kfree(int34x_thermal_zone);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(int340x_thermal_zone_add);
|
||||
|
||||
void int340x_thermal_zone_remove(struct int34x_thermal_zone
|
||||
*int34x_thermal_zone)
|
||||
{
|
||||
thermal_zone_device_unregister(int34x_thermal_zone->zone);
|
||||
acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
|
||||
kfree(int34x_thermal_zone->aux_trips);
|
||||
kfree(int34x_thermal_zone);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove);
|
||||
|
||||
MODULE_AUTHOR("Aaron Lu <aaron.lu@intel.com>");
|
||||
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
|
||||
MODULE_DESCRIPTION("Intel INT340x common thermal zone handler");
|
||||
MODULE_LICENSE("GPL v2");
|
70
drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h
Normální soubor
70
drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h
Normální soubor
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* int340x_thermal_zone.h
|
||||
* Copyright (c) 2015, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __INT340X_THERMAL_ZONE_H__
|
||||
#define __INT340X_THERMAL_ZONE_H__
|
||||
|
||||
#include <acpi/acpi_lpat.h>
|
||||
|
||||
#define INT340X_THERMAL_MAX_ACT_TRIP_COUNT 10
|
||||
|
||||
struct active_trip {
|
||||
int temp;
|
||||
int id;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
struct int34x_thermal_zone {
|
||||
struct acpi_device *adev;
|
||||
struct active_trip act_trips[INT340X_THERMAL_MAX_ACT_TRIP_COUNT];
|
||||
unsigned long *aux_trips;
|
||||
int aux_trip_nr;
|
||||
int psv_temp;
|
||||
int psv_trip_id;
|
||||
int crt_temp;
|
||||
int crt_trip_id;
|
||||
int hot_temp;
|
||||
int hot_trip_id;
|
||||
struct thermal_zone_device *zone;
|
||||
struct thermal_zone_device_ops *override_ops;
|
||||
void *priv_data;
|
||||
struct acpi_lpat_conversion_table *lpat_table;
|
||||
};
|
||||
|
||||
struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *,
|
||||
struct thermal_zone_device_ops *override_ops);
|
||||
void int340x_thermal_zone_remove(struct int34x_thermal_zone *);
|
||||
int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone);
|
||||
|
||||
static inline void int340x_thermal_zone_set_priv_data(
|
||||
struct int34x_thermal_zone *tzone, void *priv_data)
|
||||
{
|
||||
tzone->priv_data = priv_data;
|
||||
}
|
||||
|
||||
static inline void *int340x_thermal_zone_get_priv_data(
|
||||
struct int34x_thermal_zone *tzone)
|
||||
{
|
||||
return tzone->priv_data;
|
||||
}
|
||||
|
||||
static inline void int340x_thermal_zone_device_update(
|
||||
struct int34x_thermal_zone *tzone,
|
||||
enum thermal_notify_event event)
|
||||
{
|
||||
thermal_zone_device_update(tzone->zone, event);
|
||||
}
|
||||
|
||||
#endif
|
525
drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
Normální soubor
525
drivers/thermal/intel/int340x_thermal/processor_thermal_device.c
Normální soubor
@@ -0,0 +1,525 @@
|
||||
/*
|
||||
* processor_thermal_device.c
|
||||
* Copyright (c) 2014, 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/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/thermal.h>
|
||||
#include "int340x_thermal_zone.h"
|
||||
#include "../intel_soc_dts_iosf.h"
|
||||
|
||||
/* Broadwell-U/HSB thermal reporting device */
|
||||
#define PCI_DEVICE_ID_PROC_BDW_THERMAL 0x1603
|
||||
#define PCI_DEVICE_ID_PROC_HSB_THERMAL 0x0A03
|
||||
|
||||
/* Skylake thermal reporting device */
|
||||
#define PCI_DEVICE_ID_PROC_SKL_THERMAL 0x1903
|
||||
|
||||
/* CannonLake thermal reporting device */
|
||||
#define PCI_DEVICE_ID_PROC_CNL_THERMAL 0x5a03
|
||||
#define PCI_DEVICE_ID_PROC_CFL_THERMAL 0x3E83
|
||||
|
||||
/* Braswell thermal reporting device */
|
||||
#define PCI_DEVICE_ID_PROC_BSW_THERMAL 0x22DC
|
||||
|
||||
/* Broxton thermal reporting device */
|
||||
#define PCI_DEVICE_ID_PROC_BXT0_THERMAL 0x0A8C
|
||||
#define PCI_DEVICE_ID_PROC_BXT1_THERMAL 0x1A8C
|
||||
#define PCI_DEVICE_ID_PROC_BXTX_THERMAL 0x4A8C
|
||||
#define PCI_DEVICE_ID_PROC_BXTP_THERMAL 0x5A8C
|
||||
|
||||
/* GeminiLake thermal reporting device */
|
||||
#define PCI_DEVICE_ID_PROC_GLK_THERMAL 0x318C
|
||||
|
||||
struct power_config {
|
||||
u32 index;
|
||||
u32 min_uw;
|
||||
u32 max_uw;
|
||||
u32 tmin_us;
|
||||
u32 tmax_us;
|
||||
u32 step_uw;
|
||||
};
|
||||
|
||||
struct proc_thermal_device {
|
||||
struct device *dev;
|
||||
struct acpi_device *adev;
|
||||
struct power_config power_limits[2];
|
||||
struct int34x_thermal_zone *int340x_zone;
|
||||
struct intel_soc_dts_sensors *soc_dts;
|
||||
};
|
||||
|
||||
enum proc_thermal_emum_mode_type {
|
||||
PROC_THERMAL_NONE,
|
||||
PROC_THERMAL_PCI,
|
||||
PROC_THERMAL_PLATFORM_DEV
|
||||
};
|
||||
|
||||
/*
|
||||
* We can have only one type of enumeration, PCI or Platform,
|
||||
* not both. So we don't need instance specific data.
|
||||
*/
|
||||
static enum proc_thermal_emum_mode_type proc_thermal_emum_mode =
|
||||
PROC_THERMAL_NONE;
|
||||
|
||||
#define POWER_LIMIT_SHOW(index, suffix) \
|
||||
static ssize_t power_limit_##index##_##suffix##_show(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct pci_dev *pci_dev; \
|
||||
struct platform_device *pdev; \
|
||||
struct proc_thermal_device *proc_dev; \
|
||||
\
|
||||
if (proc_thermal_emum_mode == PROC_THERMAL_PLATFORM_DEV) { \
|
||||
pdev = to_platform_device(dev); \
|
||||
proc_dev = platform_get_drvdata(pdev); \
|
||||
} else { \
|
||||
pci_dev = to_pci_dev(dev); \
|
||||
proc_dev = pci_get_drvdata(pci_dev); \
|
||||
} \
|
||||
return sprintf(buf, "%lu\n",\
|
||||
(unsigned long)proc_dev->power_limits[index].suffix * 1000); \
|
||||
}
|
||||
|
||||
POWER_LIMIT_SHOW(0, min_uw)
|
||||
POWER_LIMIT_SHOW(0, max_uw)
|
||||
POWER_LIMIT_SHOW(0, step_uw)
|
||||
POWER_LIMIT_SHOW(0, tmin_us)
|
||||
POWER_LIMIT_SHOW(0, tmax_us)
|
||||
|
||||
POWER_LIMIT_SHOW(1, min_uw)
|
||||
POWER_LIMIT_SHOW(1, max_uw)
|
||||
POWER_LIMIT_SHOW(1, step_uw)
|
||||
POWER_LIMIT_SHOW(1, tmin_us)
|
||||
POWER_LIMIT_SHOW(1, tmax_us)
|
||||
|
||||
static DEVICE_ATTR_RO(power_limit_0_min_uw);
|
||||
static DEVICE_ATTR_RO(power_limit_0_max_uw);
|
||||
static DEVICE_ATTR_RO(power_limit_0_step_uw);
|
||||
static DEVICE_ATTR_RO(power_limit_0_tmin_us);
|
||||
static DEVICE_ATTR_RO(power_limit_0_tmax_us);
|
||||
|
||||
static DEVICE_ATTR_RO(power_limit_1_min_uw);
|
||||
static DEVICE_ATTR_RO(power_limit_1_max_uw);
|
||||
static DEVICE_ATTR_RO(power_limit_1_step_uw);
|
||||
static DEVICE_ATTR_RO(power_limit_1_tmin_us);
|
||||
static DEVICE_ATTR_RO(power_limit_1_tmax_us);
|
||||
|
||||
static struct attribute *power_limit_attrs[] = {
|
||||
&dev_attr_power_limit_0_min_uw.attr,
|
||||
&dev_attr_power_limit_1_min_uw.attr,
|
||||
&dev_attr_power_limit_0_max_uw.attr,
|
||||
&dev_attr_power_limit_1_max_uw.attr,
|
||||
&dev_attr_power_limit_0_step_uw.attr,
|
||||
&dev_attr_power_limit_1_step_uw.attr,
|
||||
&dev_attr_power_limit_0_tmin_us.attr,
|
||||
&dev_attr_power_limit_1_tmin_us.attr,
|
||||
&dev_attr_power_limit_0_tmax_us.attr,
|
||||
&dev_attr_power_limit_1_tmax_us.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group power_limit_attribute_group = {
|
||||
.attrs = power_limit_attrs,
|
||||
.name = "power_limits"
|
||||
};
|
||||
|
||||
static int stored_tjmax; /* since it is fixed, we can have local storage */
|
||||
|
||||
static int get_tjmax(void)
|
||||
{
|
||||
u32 eax, edx;
|
||||
u32 val;
|
||||
int err;
|
||||
|
||||
err = rdmsr_safe(MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val = (eax >> 16) & 0xff;
|
||||
if (val)
|
||||
return val;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int read_temp_msr(int *temp)
|
||||
{
|
||||
int cpu;
|
||||
u32 eax, edx;
|
||||
int err;
|
||||
unsigned long curr_temp_off = 0;
|
||||
|
||||
*temp = 0;
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
err = rdmsr_safe_on_cpu(cpu, MSR_IA32_THERM_STATUS, &eax,
|
||||
&edx);
|
||||
if (err)
|
||||
goto err_ret;
|
||||
else {
|
||||
if (eax & 0x80000000) {
|
||||
curr_temp_off = (eax >> 16) & 0x7f;
|
||||
if (!*temp || curr_temp_off < *temp)
|
||||
*temp = curr_temp_off;
|
||||
} else {
|
||||
err = -EINVAL;
|
||||
goto err_ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_ret:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int proc_thermal_get_zone_temp(struct thermal_zone_device *zone,
|
||||
int *temp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = read_temp_msr(temp);
|
||||
if (!ret)
|
||||
*temp = (stored_tjmax - *temp) * 1000;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct thermal_zone_device_ops proc_thermal_local_ops = {
|
||||
.get_temp = proc_thermal_get_zone_temp,
|
||||
};
|
||||
|
||||
static int proc_thermal_read_ppcc(struct proc_thermal_device *proc_priv)
|
||||
{
|
||||
int i;
|
||||
acpi_status status;
|
||||
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *elements, *ppcc;
|
||||
union acpi_object *p;
|
||||
int ret = 0;
|
||||
|
||||
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(proc_priv->dev, "Invalid PPCC data\n");
|
||||
ret = -EFAULT;
|
||||
goto free_buffer;
|
||||
}
|
||||
|
||||
if (!p->package.count) {
|
||||
dev_err(proc_priv->dev, "Invalid PPCC package size\n");
|
||||
ret = -EFAULT;
|
||||
goto free_buffer;
|
||||
}
|
||||
|
||||
for (i = 0; i < min((int)p->package.count - 1, 2); ++i) {
|
||||
elements = &(p->package.elements[i+1]);
|
||||
if (elements->type != ACPI_TYPE_PACKAGE ||
|
||||
elements->package.count != 6) {
|
||||
ret = -EFAULT;
|
||||
goto free_buffer;
|
||||
}
|
||||
ppcc = elements->package.elements;
|
||||
proc_priv->power_limits[i].index = ppcc[0].integer.value;
|
||||
proc_priv->power_limits[i].min_uw = ppcc[1].integer.value;
|
||||
proc_priv->power_limits[i].max_uw = ppcc[2].integer.value;
|
||||
proc_priv->power_limits[i].tmin_us = ppcc[3].integer.value;
|
||||
proc_priv->power_limits[i].tmax_us = ppcc[4].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,
|
||||
THERMAL_DEVICE_POWER_CAPABILITY_CHANGED);
|
||||
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)
|
||||
return ret;
|
||||
|
||||
status = acpi_evaluate_integer(adev->handle, "_TMP", NULL, &tmp);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
/* there is no _TMP method, add local method */
|
||||
stored_tjmax = get_tjmax();
|
||||
if (stored_tjmax > 0)
|
||||
ops = &proc_thermal_local_ops;
|
||||
}
|
||||
|
||||
proc_priv->int340x_zone = int340x_thermal_zone_add(adev, ops);
|
||||
if (IS_ERR(proc_priv->int340x_zone)) {
|
||||
ret = PTR_ERR(proc_priv->int340x_zone);
|
||||
goto remove_group;
|
||||
} else
|
||||
ret = 0;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int int3401_add(struct platform_device *pdev)
|
||||
{
|
||||
struct proc_thermal_device *proc_priv;
|
||||
int ret;
|
||||
|
||||
if (proc_thermal_emum_mode == PROC_THERMAL_PCI) {
|
||||
dev_err(&pdev->dev, "error: enumerated as PCI dev\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = proc_thermal_add(&pdev->dev, &proc_priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, proc_priv);
|
||||
proc_thermal_emum_mode = PROC_THERMAL_PLATFORM_DEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int int3401_remove(struct platform_device *pdev)
|
||||
{
|
||||
proc_thermal_remove(platform_get_drvdata(pdev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t proc_thermal_pci_msi_irq(int irq, void *devid)
|
||||
{
|
||||
struct proc_thermal_device *proc_priv;
|
||||
struct pci_dev *pdev = devid;
|
||||
|
||||
proc_priv = pci_get_drvdata(pdev);
|
||||
|
||||
intel_soc_dts_iosf_interrupt_handler(proc_priv->soc_dts);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int proc_thermal_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *unused)
|
||||
{
|
||||
struct proc_thermal_device *proc_priv;
|
||||
int ret;
|
||||
|
||||
if (proc_thermal_emum_mode == PROC_THERMAL_PLATFORM_DEV) {
|
||||
dev_err(&pdev->dev, "error: enumerated as platform dev\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = pci_enable_device(pdev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "error: could not enable device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = proc_thermal_add(&pdev->dev, &proc_priv);
|
||||
if (ret) {
|
||||
pci_disable_device(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, proc_priv);
|
||||
proc_thermal_emum_mode = PROC_THERMAL_PCI;
|
||||
|
||||
if (pdev->device == PCI_DEVICE_ID_PROC_BSW_THERMAL) {
|
||||
/*
|
||||
* Enumerate additional DTS sensors available via IOSF.
|
||||
* But we are not treating as a failure condition, if
|
||||
* there are no aux DTSs enabled or fails. This driver
|
||||
* already exposes sensors, which can be accessed via
|
||||
* ACPI/MSR. So we don't want to fail for auxiliary DTSs.
|
||||
*/
|
||||
proc_priv->soc_dts = intel_soc_dts_iosf_init(
|
||||
INTEL_SOC_DTS_INTERRUPT_MSI, 2, 0);
|
||||
|
||||
if (proc_priv->soc_dts && pdev->irq) {
|
||||
ret = pci_enable_msi(pdev);
|
||||
if (!ret) {
|
||||
ret = request_threaded_irq(pdev->irq, NULL,
|
||||
proc_thermal_pci_msi_irq,
|
||||
IRQF_ONESHOT, "proc_thermal",
|
||||
pdev);
|
||||
if (ret) {
|
||||
intel_soc_dts_iosf_exit(
|
||||
proc_priv->soc_dts);
|
||||
pci_disable_msi(pdev);
|
||||
proc_priv->soc_dts = NULL;
|
||||
}
|
||||
}
|
||||
} else
|
||||
dev_err(&pdev->dev, "No auxiliary DTSs enabled\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void proc_thermal_pci_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct proc_thermal_device *proc_priv = pci_get_drvdata(pdev);
|
||||
|
||||
if (proc_priv->soc_dts) {
|
||||
intel_soc_dts_iosf_exit(proc_priv->soc_dts);
|
||||
if (pdev->irq) {
|
||||
free_irq(pdev->irq, pdev);
|
||||
pci_disable_msi(pdev);
|
||||
}
|
||||
}
|
||||
proc_thermal_remove(proc_priv);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id proc_thermal_pci_ids[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BDW_THERMAL)},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_HSB_THERMAL)},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_SKL_THERMAL)},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BSW_THERMAL)},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXT0_THERMAL)},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXT1_THERMAL)},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXTX_THERMAL)},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_BXTP_THERMAL)},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_CNL_THERMAL)},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_CFL_THERMAL)},
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PROC_GLK_THERMAL)},
|
||||
{ 0, },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, proc_thermal_pci_ids);
|
||||
|
||||
static struct pci_driver proc_thermal_pci_driver = {
|
||||
.name = "proc_thermal",
|
||||
.probe = proc_thermal_pci_probe,
|
||||
.remove = proc_thermal_pci_remove,
|
||||
.id_table = proc_thermal_pci_ids,
|
||||
};
|
||||
|
||||
static const struct acpi_device_id int3401_device_ids[] = {
|
||||
{"INT3401", 0},
|
||||
{"", 0},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, int3401_device_ids);
|
||||
|
||||
static struct platform_driver int3401_driver = {
|
||||
.probe = int3401_add,
|
||||
.remove = int3401_remove,
|
||||
.driver = {
|
||||
.name = "int3401 thermal",
|
||||
.acpi_match_table = int3401_device_ids,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init proc_thermal_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&int3401_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pci_register_driver(&proc_thermal_pci_driver);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit proc_thermal_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&int3401_driver);
|
||||
pci_unregister_driver(&proc_thermal_pci_driver);
|
||||
}
|
||||
|
||||
module_init(proc_thermal_init);
|
||||
module_exit(proc_thermal_exit);
|
||||
|
||||
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
|
||||
MODULE_DESCRIPTION("Processor Thermal Reporting Device Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
Odkázat v novém úkolu
Zablokovat Uživatele