Commit 8264fce6 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux

Pull thermal management updates from Zhang Rui:
 "Sorry that I missed the merge window as there is a bug found in the
  last minute, and I have to fix it and wait for the code to be tested
  in linux-next tree for a few days.  Now the buggy patch has been
  dropped entirely from my next branch.  Thus I hope those changes can
  still be merged in 3.18-rc2 as most of them are platform thermal
  driver changes.

  Specifics:

   - introduce ACPI INT340X thermal drivers.

     Newer laptops and tablets may have thermal sensors and other
     devices with thermal control capabilities that are exposed for the
     OS to use via the ACPI INT340x device objects.  Several drivers are
     introduced to expose the temperature information and cooling
     ability from these objects to user-space via the normal thermal
     framework.

     From: Lu Aaron, Lan Tianyu, Jacob Pan and Zhang Rui.

   - introduce a new thermal governor, which just uses a hysteresis to
     switch abruptly on/off a cooling device.  This governor can be used
     to control certain fan devices that can not be throttled but just
     switched on or off.  From: Peter Feuerer.

   - introduce support for some new thermal interrupt functions on
     i.MX6SX, in IMX thermal driver.  From: Anson, Huang.

   - introduce tracing support on thermal framework.  From: Punit
     Agrawal.

   - small fixes in OF thermal and thermal step_wise governor"

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux: (25 commits)
  Thermal: int340x thermal: select ACPI fan driver
  Thermal: int3400_thermal: use acpi_thermal_rel parsing APIs
  Thermal: int340x_thermal: expose acpi thermal relationship tables
  Thermal: introduce int3403 thermal driver
  Thermal: introduce INT3402 thermal driver
  Thermal: move the KELVIN_TO_MILLICELSIUS macro to thermal.h
  ACPI / Fan: support INT3404 thermal device
  ACPI / Fan: add ACPI 4.0 style fan support
  ACPI / fan: convert to platform driver
  ACPI / fan: use acpi_device_xxx_power instead of acpi_bus equivelant
  ACPI / fan: remove no need check for device pointer
  ACPI / fan: remove unused macro
  Thermal: int3400 thermal: register to thermal framework
  Thermal: int3400 thermal: add capability to detect supporting UUIDs
  Thermal: introduce int3400 thermal driver
  ACPI: add ACPI_TYPE_LOCAL_REFERENCE support to acpi_extract_package()
  ACPI: make acpi_create_platform_device() an external API
  thermal: step_wise: fix: Prevent from binary overflow when trend is dropping
  ACPI: introduce ACPI int340x thermal scan handler
  thermal: Added Bang-bang thermal governor
  ...
parents 816fb417 6ceaf58a
* Temperature Monitor (TEMPMON) on Freescale i.MX SoCs * Temperature Monitor (TEMPMON) on Freescale i.MX SoCs
Required properties: Required properties:
- compatible : "fsl,imx6q-thermal" - compatible : "fsl,imx6q-tempmon" for i.MX6Q, "fsl,imx6sx-tempmon" for i.MX6SX.
i.MX6SX has two more IRQs than i.MX6Q, one is IRQ_LOW and the other is IRQ_PANIC,
when temperature is below than low threshold, IRQ_LOW will be triggered, when temperature
is higher than panic threshold, system will auto reboot by SRC module.
- fsl,tempmon : phandle pointer to system controller that contains TEMPMON - fsl,tempmon : phandle pointer to system controller that contains TEMPMON
control registers, e.g. ANATOP on imx6q. control registers, e.g. ANATOP on imx6q.
- fsl,tempmon-data : phandle pointer to fuse controller that contains TEMPMON - fsl,tempmon-data : phandle pointer to fuse controller that contains TEMPMON
......
...@@ -144,7 +144,7 @@ config ACPI_VIDEO ...@@ -144,7 +144,7 @@ config ACPI_VIDEO
config ACPI_FAN config ACPI_FAN
tristate "Fan" tristate "Fan"
select THERMAL depends on THERMAL
default y default y
help help
This driver supports ACPI fan devices, allowing user-mode This driver supports ACPI fan devices, allowing user-mode
......
...@@ -43,6 +43,7 @@ acpi-y += pci_root.o pci_link.o pci_irq.o ...@@ -43,6 +43,7 @@ acpi-y += pci_root.o pci_link.o pci_irq.o
acpi-y += acpi_lpss.o acpi-y += acpi_lpss.o
acpi-y += acpi_platform.o acpi-y += acpi_platform.o
acpi-y += acpi_pnp.o acpi-y += acpi_pnp.o
acpi-y += int340x_thermal.o
acpi-y += power.o acpi-y += power.o
acpi-y += event.o acpi-y += event.o
acpi-y += sysfs.o acpi-y += sysfs.o
......
...@@ -113,3 +113,4 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) ...@@ -113,3 +113,4 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev)
kfree(resources); kfree(resources);
return pdev; return pdev;
} }
EXPORT_SYMBOL_GPL(acpi_create_platform_device);
...@@ -343,6 +343,7 @@ int acpi_device_update_power(struct acpi_device *device, int *state_p) ...@@ -343,6 +343,7 @@ int acpi_device_update_power(struct acpi_device *device, int *state_p)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(acpi_device_update_power);
int acpi_bus_update_power(acpi_handle handle, int *state_p) int acpi_bus_update_power(acpi_handle handle, int *state_p)
{ {
......
This diff is collapsed.
/*
* ACPI support for int340x thermal drivers
*
* 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/acpi.h>
#include <linux/module.h>
#include "internal.h"
#define DO_ENUMERATION 0x01
static const struct acpi_device_id int340x_thermal_device_ids[] = {
{"INT3400", DO_ENUMERATION },
{"INT3401"},
{"INT3402"},
{"INT3403"},
{"INT3404"},
{"INT3406"},
{"INT3407"},
{"INT3408"},
{"INT3409"},
{"INT340A"},
{"INT340B"},
{""},
};
static int int340x_thermal_handler_attach(struct acpi_device *adev,
const struct acpi_device_id *id)
{
#if defined(CONFIG_INT340X_THERMAL) || defined(CONFIG_INT340X_THERMAL_MODULE)
if (id->driver_data == DO_ENUMERATION)
acpi_create_platform_device(adev);
#endif
return 1;
}
static struct acpi_scan_handler int340x_thermal_handler = {
.ids = int340x_thermal_device_ids,
.attach = int340x_thermal_handler_attach,
};
void __init acpi_int340x_thermal_init(void)
{
acpi_scan_add_handler(&int340x_thermal_handler);
}
...@@ -31,6 +31,7 @@ void acpi_pci_link_init(void); ...@@ -31,6 +31,7 @@ void acpi_pci_link_init(void);
void acpi_processor_init(void); void acpi_processor_init(void);
void acpi_platform_init(void); void acpi_platform_init(void);
void acpi_pnp_init(void); void acpi_pnp_init(void);
void acpi_int340x_thermal_init(void);
int acpi_sysfs_init(void); int acpi_sysfs_init(void);
void acpi_container_init(void); void acpi_container_init(void);
void acpi_memory_hotplug_init(void); void acpi_memory_hotplug_init(void);
...@@ -103,8 +104,6 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state); ...@@ -103,8 +104,6 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state);
int acpi_power_on_resources(struct acpi_device *device, int state); int acpi_power_on_resources(struct acpi_device *device, int state);
int acpi_power_transition(struct acpi_device *device, int state); int acpi_power_transition(struct acpi_device *device, int state);
int acpi_device_update_power(struct acpi_device *device, int *state_p);
int acpi_wakeup_device_init(void); int acpi_wakeup_device_init(void);
#ifdef CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC #ifdef CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC
...@@ -167,13 +166,6 @@ static inline int suspend_nvs_save(void) { return 0; } ...@@ -167,13 +166,6 @@ static inline int suspend_nvs_save(void) { return 0; }
static inline void suspend_nvs_restore(void) {} static inline void suspend_nvs_restore(void) {}
#endif #endif
/*--------------------------------------------------------------------------
Platform bus support
-------------------------------------------------------------------------- */
struct platform_device;
struct platform_device *acpi_create_platform_device(struct acpi_device *adev);
/*-------------------------------------------------------------------------- /*--------------------------------------------------------------------------
Video Video
-------------------------------------------------------------------------- */ -------------------------------------------------------------------------- */
......
...@@ -2315,6 +2315,7 @@ int __init acpi_scan_init(void) ...@@ -2315,6 +2315,7 @@ int __init acpi_scan_init(void)
acpi_container_init(); acpi_container_init();
acpi_memory_hotplug_init(); acpi_memory_hotplug_init();
acpi_pnp_init(); acpi_pnp_init();
acpi_int340x_thermal_init();
mutex_lock(&acpi_scan_lock); mutex_lock(&acpi_scan_lock);
/* /*
......
...@@ -528,7 +528,6 @@ static void acpi_thermal_check(void *data) ...@@ -528,7 +528,6 @@ static void acpi_thermal_check(void *data)
} }
/* sys I/F for generic thermal sysfs support */ /* sys I/F for generic thermal sysfs support */
#define KELVIN_TO_MILLICELSIUS(t, off) (((t) - (off)) * 100)
static int thermal_get_temp(struct thermal_zone_device *thermal, static int thermal_get_temp(struct thermal_zone_device *thermal,
unsigned long *temp) unsigned long *temp)
...@@ -543,7 +542,8 @@ static int thermal_get_temp(struct thermal_zone_device *thermal, ...@@ -543,7 +542,8 @@ static int thermal_get_temp(struct thermal_zone_device *thermal,
if (result) if (result)
return result; return result;
*temp = KELVIN_TO_MILLICELSIUS(tz->temperature, tz->kelvin_offset); *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(tz->temperature,
tz->kelvin_offset);
return 0; return 0;
} }
...@@ -647,7 +647,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, ...@@ -647,7 +647,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
if (tz->trips.critical.flags.valid) { if (tz->trips.critical.flags.valid) {
if (!trip) { if (!trip) {
*temp = KELVIN_TO_MILLICELSIUS( *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
tz->trips.critical.temperature, tz->trips.critical.temperature,
tz->kelvin_offset); tz->kelvin_offset);
return 0; return 0;
...@@ -657,7 +657,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, ...@@ -657,7 +657,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
if (tz->trips.hot.flags.valid) { if (tz->trips.hot.flags.valid) {
if (!trip) { if (!trip) {
*temp = KELVIN_TO_MILLICELSIUS( *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
tz->trips.hot.temperature, tz->trips.hot.temperature,
tz->kelvin_offset); tz->kelvin_offset);
return 0; return 0;
...@@ -667,7 +667,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, ...@@ -667,7 +667,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
if (tz->trips.passive.flags.valid) { if (tz->trips.passive.flags.valid) {
if (!trip) { if (!trip) {
*temp = KELVIN_TO_MILLICELSIUS( *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
tz->trips.passive.temperature, tz->trips.passive.temperature,
tz->kelvin_offset); tz->kelvin_offset);
return 0; return 0;
...@@ -678,7 +678,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, ...@@ -678,7 +678,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
tz->trips.active[i].flags.valid; i++) { tz->trips.active[i].flags.valid; i++) {
if (!trip) { if (!trip) {
*temp = KELVIN_TO_MILLICELSIUS( *temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
tz->trips.active[i].temperature, tz->trips.active[i].temperature,
tz->kelvin_offset); tz->kelvin_offset);
return 0; return 0;
...@@ -694,7 +694,7 @@ static int thermal_get_crit_temp(struct thermal_zone_device *thermal, ...@@ -694,7 +694,7 @@ static int thermal_get_crit_temp(struct thermal_zone_device *thermal,
struct acpi_thermal *tz = thermal->devdata; struct acpi_thermal *tz = thermal->devdata;
if (tz->trips.critical.flags.valid) { if (tz->trips.critical.flags.valid) {
*temperature = KELVIN_TO_MILLICELSIUS( *temperature = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
tz->trips.critical.temperature, tz->trips.critical.temperature,
tz->kelvin_offset); tz->kelvin_offset);
return 0; return 0;
...@@ -714,8 +714,8 @@ static int thermal_get_trend(struct thermal_zone_device *thermal, ...@@ -714,8 +714,8 @@ static int thermal_get_trend(struct thermal_zone_device *thermal,
if (type == THERMAL_TRIP_ACTIVE) { if (type == THERMAL_TRIP_ACTIVE) {
unsigned long trip_temp; unsigned long trip_temp;
unsigned long temp = KELVIN_TO_MILLICELSIUS(tz->temperature, unsigned long temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
tz->kelvin_offset); tz->temperature, tz->kelvin_offset);
if (thermal_get_trip_temp(thermal, trip, &trip_temp)) if (thermal_get_trip_temp(thermal, trip, &trip_temp))
return -EINVAL; return -EINVAL;
......
...@@ -149,6 +149,21 @@ acpi_extract_package(union acpi_object *package, ...@@ -149,6 +149,21 @@ acpi_extract_package(union acpi_object *package,
break; break;
} }
break; break;
case ACPI_TYPE_LOCAL_REFERENCE:
switch (format_string[i]) {
case 'R':
size_required += sizeof(void *);
tail_offset += sizeof(void *);
break;
default:
printk(KERN_WARNING PREFIX "Invalid package element"
" [%d] got reference,"
" expecting [%c]\n",
i, format_string[i]);
return AE_BAD_DATA;
break;
}
break;
case ACPI_TYPE_PACKAGE: case ACPI_TYPE_PACKAGE:
default: default:
...@@ -247,7 +262,18 @@ acpi_extract_package(union acpi_object *package, ...@@ -247,7 +262,18 @@ acpi_extract_package(union acpi_object *package,
break; break;
} }
break; break;
case ACPI_TYPE_LOCAL_REFERENCE:
switch (format_string[i]) {
case 'R':
*(void **)head =
(void *)element->reference.handle;
head += sizeof(void *);
break;
default:
/* Should never get here */
break;
}
break;
case ACPI_TYPE_PACKAGE: case ACPI_TYPE_PACKAGE:
/* TBD: handle nested packages... */ /* TBD: handle nested packages... */
default: default:
......
...@@ -84,6 +84,16 @@ config THERMAL_GOV_STEP_WISE ...@@ -84,6 +84,16 @@ config THERMAL_GOV_STEP_WISE
Enable this to manage platform thermals using a simple linear Enable this to manage platform thermals using a simple linear
governor. governor.
config THERMAL_GOV_BANG_BANG
bool "Bang Bang thermal governor"
default n
help
Enable this to manage platform thermals using bang bang governor.
Say 'Y' here if you want to use two point temperature regulation
used for fans without throttling. Some fan drivers depend on this
governor to be enabled (e.g. acerhdf).
config THERMAL_GOV_USER_SPACE config THERMAL_GOV_USER_SPACE
bool "User_space thermal governor" bool "User_space thermal governor"
help help
...@@ -207,21 +217,6 @@ config X86_PKG_TEMP_THERMAL ...@@ -207,21 +217,6 @@ config X86_PKG_TEMP_THERMAL
two trip points which can be set by user to get notifications via thermal two trip points which can be set by user to get notifications via thermal
notification methods. notification methods.
config ACPI_INT3403_THERMAL
tristate "ACPI INT3403 thermal driver"
depends on X86 && ACPI
help
Newer laptops and tablets that use ACPI may have thermal sensors
outside the core CPU/SOC for thermal safety reasons. These
temperature sensors are also exposed for the OS to use via the so
called INT3403 ACPI object. This driver will, on devices that have
such sensors, expose the temperature information from these sensors
to userspace via the normal thermal framework. This means that a wide
range of applications and GUI widgets can show this 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 INTEL_SOC_DTS_THERMAL config INTEL_SOC_DTS_THERMAL
tristate "Intel SoCs DTS thermal driver" tristate "Intel SoCs DTS thermal driver"
depends on X86 && IOSF_MBI depends on X86 && IOSF_MBI
...@@ -234,6 +229,30 @@ config INTEL_SOC_DTS_THERMAL ...@@ -234,6 +229,30 @@ config INTEL_SOC_DTS_THERMAL
notification methods.The other trip is a critical trip point, which notification methods.The other trip is a critical trip point, which
was set by the driver based on the TJ MAX temperature. was set by the driver based on the TJ MAX temperature.
config INT340X_THERMAL
tristate "ACPI INT340X thermal drivers"
depends on X86 && ACPI
select THERMAL_GOV_USER_SPACE
select ACPI_THERMAL_REL
select ACPI_FAN
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
menu "Texas Instruments thermal drivers" menu "Texas Instruments thermal drivers"
source "drivers/thermal/ti-soc-thermal/Kconfig" source "drivers/thermal/ti-soc-thermal/Kconfig"
endmenu endmenu
......
...@@ -11,6 +11,7 @@ thermal_sys-$(CONFIG_THERMAL_OF) += of-thermal.o ...@@ -11,6 +11,7 @@ thermal_sys-$(CONFIG_THERMAL_OF) += of-thermal.o
# governors # governors
thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE) += fair_share.o
thermal_sys-$(CONFIG_THERMAL_GOV_BANG_BANG) += gov_bang_bang.o
thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE) += step_wise.o thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE) += step_wise.o
thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o
...@@ -31,5 +32,5 @@ obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o ...@@ -31,5 +32,5 @@ obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o
obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/
obj-$(CONFIG_ST_THERMAL) += st/ obj-$(CONFIG_ST_THERMAL) += st/
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
*/ */
#include <linux/thermal.h> #include <linux/thermal.h>
#include <trace/events/thermal.h>
#include "thermal_core.h" #include "thermal_core.h"
...@@ -34,6 +35,7 @@ static int get_trip_level(struct thermal_zone_device *tz) ...@@ -34,6 +35,7 @@ static int get_trip_level(struct thermal_zone_device *tz)
{ {
int count = 0; int count = 0;
unsigned long trip_temp; unsigned long trip_temp;
enum thermal_trip_type trip_type;
if (tz->trips == 0 || !tz->ops->get_trip_temp) if (tz->trips == 0 || !tz->ops->get_trip_temp)
return 0; return 0;
...@@ -43,6 +45,16 @@ static int get_trip_level(struct thermal_zone_device *tz) ...@@ -43,6 +45,16 @@ static int get_trip_level(struct thermal_zone_device *tz)
if (tz->temperature < trip_temp) if (tz->temperature < trip_temp)
break; break;
} }
/*
* count > 0 only if temperature is greater than first trip
* point, in which case, trip_point = count - 1
*/
if (count > 0) {
tz->ops->get_trip_type(tz, count - 1, &trip_type);
trace_thermal_zone_trip(tz, count - 1, trip_type);
}
return count; return count;
} }
......
/*
* gov_bang_bang.c - A simple thermal throttling governor using hysteresis
*
* Copyright (C) 2014 Peter Feuerer <peter@piie.net>
*
* Based on step_wise.c with following Copyrights:
* Copyright (C) 2012 Intel Corp
* Copyright (C) 2012 Durgadoss R <durgadoss.r@intel.com>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
*/
#include <linux/thermal.h>
#include "thermal_core.h"
static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
{
long trip_temp;
unsigned long trip_hyst;
struct thermal_instance *instance;
tz->ops->get_trip_temp(tz, trip, &trip_temp);
tz->ops->get_trip_hyst(tz, trip, &trip_hyst);
dev_dbg(&tz->device, "Trip%d[temp=%ld]:temp=%d:hyst=%ld\n",
trip, trip_temp, tz->temperature,
trip_hyst);
mutex_lock(&tz->lock);
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
if (instance->trip != trip)
continue;
/* in case fan is in initial state, switch the fan off */
if (instance->target == THERMAL_NO_TARGET)
instance->target = 0;
/* in case fan is neither on nor off set the fan to active */
if (instance->target != 0 && instance->target != 1) {
pr_warn("Thermal instance %s controlled by bang-bang has unexpected state: %ld\n",
instance->name, instance->target);
instance->target = 1;
}
/*
* enable fan when temperature exceeds trip_temp and disable
* the fan in case it falls below trip_temp minus hysteresis
*/
if (instance->target == 0 && tz->temperature >= trip_temp)
instance->target = 1;
else if (instance->target == 1 &&
tz->temperature < trip_temp - trip_hyst)
instance->target = 0;
dev_dbg(&instance->cdev->device, "target=%d\n",
(int)instance->target);
instance->cdev->updated = false; /* cdev needs update */
}
mutex_unlock(&tz->lock);
}
/**
* bang_bang_control - controls devices associated with the given zone
* @tz - thermal_zone_device
* @trip - the trip point
*
* Regulation Logic: a two point regulation, deliver cooling state depending
* on the previous state shown in this diagram:
*
* Fan: OFF ON
*
* |
* |
* trip_temp: +---->+
* | | ^
* | | |
* | | Temperature
* (trip_temp - hyst): +<----+
* |
* |
* |
*
* * If the fan is not running and temperature exceeds trip_temp, the fan
* gets turned on.
* * In case the fan is running, temperature must fall below
* (trip_temp - hyst) so that the fan gets turned off again.
*
*/
static int bang_bang_control(struct thermal_zone_device *tz, int trip)
{
struct thermal_instance *instance;
thermal_zone_trip_update(tz, trip);
mutex_lock(&tz->lock);
list_for_each_entry(instance, &tz->thermal_instances, tz_node)
thermal_cdev_update(instance->cdev);
mutex_unlock(&tz->lock);
return 0;
}
static struct thermal_governor thermal_gov_bang_bang = {
.name = "bang_bang",
.throttle = bang_bang_control,
};
int thermal_gov_bang_bang_register(void)
{
return thermal_register_governor(&thermal_gov_bang_bang);
}
void thermal_gov_bang_bang_unregister(void)
{
thermal_unregister_governor(&thermal_gov_bang_bang);
}
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -31,6 +32,11 @@ ...@@ -31,6 +32,11 @@
#define MISC0 0x0150 #define MISC0 0x0150
#define MISC0_REFTOP_SELBIASOFF (1 << 3) #define MISC0_REFTOP_SELBIASOFF (1 << 3)
#define MISC1 0x0160
#define MISC1_IRQ_TEMPHIGH (1 << 29)
/* Below LOW and PANIC bits are only for TEMPMON_IMX6SX */
#define MISC1_IRQ_TEMPLOW (1 << 28)
#define MISC1_IRQ_TEMPPANIC (1 << 27)
#define TEMPSENSE0 0x0180 #define TEMPSENSE0 0x0180
#define TEMPSENSE0_ALARM_VALUE_SHIFT 20 #define TEMPSENSE0_ALARM_VALUE_SHIFT 20
...@@ -43,6 +49,12 @@ ...@@ -43,6 +49,12 @@
#define TEMPSENSE1 0x0190 #define TEMPSENSE1 0x0190
#define TEMPSENSE1_MEASURE_FREQ 0xffff #define TEMPSENSE1_MEASURE_FREQ 0xffff
/* Below TEMPSENSE2 is only for TEMPMON_IMX6SX */
#define TEMPSENSE2 0x0290
#define TEMPSENSE2_LOW_VALUE_SHIFT 0
#define TEMPSENSE2_LOW_VALUE_MASK 0xfff
#define TEMPSENSE2_PANIC_VALUE_SHIFT 16
#define TEMPSENSE2_PANIC_VALUE_MASK 0xfff0000
#define OCOTP_ANA1 0x04e0 #define OCOTP_ANA1 0x04e0
...@@ -66,6 +78,21 @@ enum imx_thermal_trip { ...@@ -66,6 +78,21 @@ enum imx_thermal_trip {
#define FACTOR1 15976 #define FACTOR1 15976
#define FACTOR2 4297157 #define FACTOR2 4297157
#define TEMPMON_IMX6Q 1
#define TEMPMON_IMX6SX 2
struct thermal_soc_data {
u32 version;
};
static struct thermal_soc_data thermal_imx6q_data = {
.version = TEMPMON_IMX6Q,
};
static struct thermal_soc_data thermal_imx6sx_data = {
.version = TEMPMON_IMX6SX,
};
struct imx_thermal_data { struct imx_thermal_data {
struct thermal_zone_device *tz; struct thermal_zone_device *tz;
struct thermal_cooling_device *cdev; struct thermal_cooling_device *cdev;
...@@ -79,8 +106,21 @@ struct imx_thermal_data { ...@@ -79,8 +106,21 @@ struct imx_thermal_data {
bool irq_enabled; bool irq_enabled;
int irq; int irq;
struct clk *thermal_clk; struct clk *thermal_clk;
const struct thermal_soc_data *socdata;
}; };
static void imx_set_panic_temp(struct imx_thermal_data *data,
signed long panic_temp)
{
struct regmap *map = data->tempmon;
int critical_value;
critical_value = (data->c2 - panic_temp) / data->c1;
regmap_write(map, TEMPSENSE2 + REG_CLR, TEMPSENSE2_PANIC_VALUE_MASK);
regmap_write(map, TEMPSENSE2 + REG_SET, critical_value <<
TEMPSENSE2_PANIC_VALUE_SHIFT);
}
static void imx_set_alarm_temp(struct imx_thermal_data *data, static void imx_set_alarm_temp(struct imx_thermal_data *data,
signed long alarm_temp) signed long alarm_temp)
{ {
...@@ -142,13 +182,17 @@ static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp) ...@@ -142,13 +182,17 @@ static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
/* See imx_get_sensor_data() for formula derivation */ /* See imx_get_sensor_data() for formula derivation */
*temp = data->c2 - n_meas * data->c1; *temp = data->c2 - n_meas * data->c1;
/* Update alarm value to next higher trip point */ /* Update alarm value to next higher trip point for TEMPMON_IMX6Q */
if (data->alarm_temp == data->temp_passive && *temp >= data->temp_passive) if (data->socdata->version == TEMPMON_IMX6Q) {
imx_set_alarm_temp(data, data->temp_critical); if (data->alarm_temp == data->temp_passive &&
if (data->alarm_temp == data->temp_critical && *temp < data->temp_passive) { *temp >= data->temp_passive)
imx_set_alarm_temp(data, data->temp_passive); imx_set_alarm_temp(data, data->temp_critical);
dev_dbg(&tz->device, "thermal alarm off: T < %lu\n", if (data->alarm_temp == data->temp_critical &&
data->alarm_temp / 1000); *temp < data->temp_passive) {
imx_set_alarm_temp(data, data->temp_passive);
dev_dbg(&tz->device, "thermal alarm off: T < %lu\n",
data->alarm_temp / 1000);
}
} }
if (*temp != data->last_temp) { if (*temp != data->last_temp) {
...@@ -398,8 +442,17 @@ static irqreturn_t imx_thermal_alarm_irq_thread(int irq, void *dev) ...@@ -398,8 +442,17 @@ static irqreturn_t imx_thermal_alarm_irq_thread(int irq, void *dev)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static const struct of_device_id of_imx_thermal_match[] = {
{ .compatible = "fsl,imx6q-tempmon", .data = &thermal_imx6q_data, },
{ .compatible = "fsl,imx6sx-tempmon", .data = &thermal_imx6sx_data, },
{ /* end */ }
};
MODULE_DEVICE_TABLE(of, of_imx_thermal_match);
static int imx_thermal_probe(struct platform_device *pdev) static int imx_thermal_probe(struct platform_device *pdev)
{ {
const struct of_device_id *of_id =
of_match_device(of_imx_thermal_match, &pdev->dev);
struct imx_thermal_data *data; struct imx_thermal_data *data;
struct cpumask clip_cpus; struct cpumask clip_cpus;
struct regmap *map; struct regmap *map;
...@@ -418,6 +471,20 @@ static int imx_thermal_probe(struct platform_device *pdev) ...@@ -418,6 +471,20 @@ static int imx_thermal_probe(struct platform_device *pdev)
} }
data->tempmon = map; data->tempmon = map;
data->socdata = of_id->data;
/* make sure the IRQ flag is clear before enabling irq on i.MX6SX */
if (data->socdata->version == TEMPMON_IMX6SX) {
regmap_write(map, MISC1 + REG_CLR, MISC1_IRQ_TEMPHIGH |
MISC1_IRQ_TEMPLOW | MISC1_IRQ_TEMPPANIC);
/*
* reset value of LOW ALARM is incorrect, set it to lowest
* value to avoid false trigger of low alarm.
*/
regmap_write(map, TEMPSENSE2 + REG_SET,
TEMPSENSE2_LOW_VALUE_MASK);
}
data->irq = platform_get_irq(pdev, 0); data->irq = platform_get_irq(pdev, 0);
if (data->irq < 0) if (data->irq < 0)
return data->irq; return data->irq;
...@@ -489,6 +556,10 @@ static int imx_thermal_probe(struct platform_device *pdev) ...@@ -489,6 +556,10 @@ static int imx_thermal_probe(struct platform_device *pdev)
measure_freq = DIV_ROUND_UP(32768, 10); /* 10 Hz */ measure_freq = DIV_ROUND_UP(32768, 10); /* 10 Hz */
regmap_write(map, TEMPSENSE1 + REG_SET, measure_freq); regmap_write(map, TEMPSENSE1 + REG_SET, measure_freq);
imx_set_alarm_temp(data, data->temp_passive); imx_set_alarm_temp(data, data->temp_passive);
if (data->socdata->version == TEMPMON_IMX6SX)
imx_set_panic_temp(data, data->temp_critical);
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN); regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP); regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
...@@ -550,12 +621,6 @@ static int imx_thermal_resume(struct device *dev) ...@@ -550,12 +621,6 @@ static int imx_thermal_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(imx_thermal_pm_ops, static SIMPLE_DEV_PM_OPS(imx_thermal_pm_ops,
imx_thermal_suspend, imx_thermal_resume); imx_thermal_suspend, imx_thermal_resume);
static const struct of_device_id of_imx_thermal_match[] = {
{ .compatible = "fsl,imx6q-tempmon", },
{ /* end */ }
};
MODULE_DEVICE_TABLE(of, of_imx_thermal_match);
static struct platform_driver imx_thermal = { static struct platform_driver imx_thermal = {
.driver = { .driver = {
.name = "imx_thermal", .name = "imx_thermal",
......
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
This diff is collapsed.
#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 */
/*
* 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");
/*
* 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");
...@@ -401,6 +401,10 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, ...@@ -401,6 +401,10 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id,
struct of_phandle_args sensor_specs; struct of_phandle_args sensor_specs;
int ret, id; int ret, id;
/* Check whether child is enabled or not */
if (!of_device_is_available(child))
continue;
/* For now, thermal framework supports only 1 sensor per zone */ /* For now, thermal framework supports only 1 sensor per zone */
ret = of_parse_phandle_with_args(child, "thermal-sensors", ret = of_parse_phandle_with_args(child, "thermal-sensors",
"#thermal-sensor-cells", "#thermal-sensor-cells",
...@@ -771,6 +775,10 @@ int __init of_parse_thermal_zones(void) ...@@ -771,6 +775,10 @@ int __init of_parse_thermal_zones(void)
struct thermal_zone_device *zone; struct thermal_zone_device *zone;
struct thermal_zone_params *tzp; struct thermal_zone_params *tzp;
/* Check whether child is enabled or not */
if (!of_device_is_available(child))
continue;
tz = thermal_of_build_thermal_zone(child); tz = thermal_of_build_thermal_zone(child);
if (IS_ERR(tz)) { if (IS_ERR(tz)) {
pr_err("failed to build thermal zone %s: %ld\n", pr_err("failed to build thermal zone %s: %ld\n",
...@@ -838,6 +846,10 @@ void of_thermal_destroy_zones(void) ...@@ -838,6 +846,10 @@ void of_thermal_destroy_zones(void)
for_each_child_of_node(np, child) { for_each_child_of_node(np, child) {
struct thermal_zone_device *zone; struct thermal_zone_device *zone;
/* Check whether child is enabled or not */
if (!of_device_is_available(child))
continue;
zone = thermal_zone_get_zone_by_name(child->name); zone = thermal_zone_get_zone_by_name(child->name);
if (IS_ERR(zone)) if (IS_ERR(zone))
continue; continue;
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
*/ */
#include <linux/thermal.h> #include <linux/thermal.h>
#include <trace/events/thermal.h>
#include "thermal_core.h" #include "thermal_core.h"
...@@ -76,7 +77,7 @@ static unsigned long get_target_state(struct thermal_instance *instance, ...@@ -76,7 +77,7 @@ static unsigned long get_target_state(struct thermal_instance *instance,
next_target = instance->upper; next_target = instance->upper;
break; break;
case THERMAL_TREND_DROPPING: case THERMAL_TREND_DROPPING:
if (cur_state == instance->lower) { if (cur_state <= instance->lower) {
if (!throttle) if (!throttle)
next_target = THERMAL_NO_TARGET; next_target = THERMAL_NO_TARGET;
} else { } else {
...@@ -129,8 +130,10 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) ...@@ -129,8 +130,10 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
trend = get_tz_trend(tz, trip); trend = get_tz_trend(tz, trip);
if (tz->temperature >= trip_temp) if (tz->temperature >= trip_temp) {
throttle = true; throttle = true;
trace_thermal_zone_trip(tz, trip, trip_type);
}
dev_dbg(&tz->device, "Trip%d[type=%d,temp=%ld]:trend=%d,throttle=%d\n", dev_dbg(&tz->device, "Trip%d[type=%d,temp=%ld]:trend=%d,throttle=%d\n",
trip, trip_type, trip_temp, trend, throttle); trip, trip_type, trip_temp, trend, throttle);
......
...@@ -38,6 +38,9 @@ ...@@ -38,6 +38,9 @@
#include <net/netlink.h> #include <net/netlink.h>
#include <net/genetlink.h> #include <net/genetlink.h>
#define CREATE_TRACE_POINTS
#include <trace/events/thermal.h>
#include "thermal_core.h" #include "thermal_core.h"
#include "thermal_hwmon.h" #include "thermal_hwmon.h"
...@@ -368,6 +371,8 @@ static void handle_critical_trips(struct thermal_zone_device *tz, ...@@ -368,6 +371,8 @@ static void handle_critical_trips(struct thermal_zone_device *tz,
if (tz->temperature < trip_temp) if (tz->temperature < trip_temp)
return; return;
trace_thermal_zone_trip(tz, trip, trip_type);
if (tz->ops->notify) if (tz->ops->notify)
tz->ops->notify(tz, trip, trip_type); tz->ops->notify(tz, trip, trip_type);
...@@ -463,6 +468,7 @@ static void update_temperature(struct thermal_zone_device *tz) ...@@ -463,6 +468,7 @@ static void update_temperature(struct thermal_zone_device *tz)
tz->temperature = temp; tz->temperature = temp;
mutex_unlock(&tz->lock); mutex_unlock(&tz->lock);
trace_thermal_temperature(tz);
dev_dbg(&tz->device, "last_temperature=%d, current_temperature=%d\n", dev_dbg(&tz->device, "last_temperature=%d, current_temperature=%d\n",
tz->last_temperature, tz->temperature); tz->last_temperature, tz->temperature);
} }
...@@ -1287,6 +1293,7 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev) ...@@ -1287,6 +1293,7 @@ void thermal_cdev_update(struct thermal_cooling_device *cdev)
mutex_unlock(&cdev->lock); mutex_unlock(&cdev->lock);
cdev->ops->set_cur_state(cdev, target); cdev->ops->set_cur_state(cdev, target);
cdev->updated = true; cdev->updated = true;
trace_cdev_update(cdev, target);
dev_dbg(&cdev->device, "set to state %lu\n", target); dev_dbg(&cdev->device, "set to state %lu\n", target);
} }
EXPORT_SYMBOL(thermal_cdev_update); EXPORT_SYMBOL(thermal_cdev_update);
...@@ -1790,6 +1797,10 @@ static int __init thermal_register_governors(void) ...@@ -1790,6 +1797,10 @@ static int __init thermal_register_governors(void)
if (result) if (result)
return result; return result;
result = thermal_gov_bang_bang_register();
if (result)
return result;
return thermal_gov_user_space_register(); return thermal_gov_user_space_register();
} }
...@@ -1797,6 +1808,7 @@ static void thermal_unregister_governors(void) ...@@ -1797,6 +1808,7 @@ static void thermal_unregister_governors(void)
{ {
thermal_gov_step_wise_unregister(); thermal_gov_step_wise_unregister();
thermal_gov_fair_share_unregister(); thermal_gov_fair_share_unregister();
thermal_gov_bang_bang_unregister();
thermal_gov_user_space_unregister(); thermal_gov_user_space_unregister();
} }
......
...@@ -69,6 +69,14 @@ static inline int thermal_gov_fair_share_register(void) { return 0; } ...@@ -69,6 +69,14 @@ static inline int thermal_gov_fair_share_register(void) { return 0; }
static inline void thermal_gov_fair_share_unregister(void) {} static inline void thermal_gov_fair_share_unregister(void) {}
#endif /* CONFIG_THERMAL_GOV_FAIR_SHARE */ #endif /* CONFIG_THERMAL_GOV_FAIR_SHARE */
#ifdef CONFIG_THERMAL_GOV_BANG_BANG
int thermal_gov_bang_bang_register(void);
void thermal_gov_bang_bang_unregister(void);
#else
static inline int thermal_gov_bang_bang_register(void) { return 0; }
static inline void thermal_gov_bang_bang_unregister(void) {}
#endif /* CONFIG_THERMAL_GOV_BANG_BANG */
#ifdef CONFIG_THERMAL_GOV_USER_SPACE #ifdef CONFIG_THERMAL_GOV_USER_SPACE
int thermal_gov_user_space_register(void); int thermal_gov_user_space_register(void);
void thermal_gov_user_space_unregister(void); void thermal_gov_user_space_unregister(void);
......
...@@ -433,6 +433,7 @@ int acpi_device_set_power(struct acpi_device *device, int state); ...@@ -433,6 +433,7 @@ int acpi_device_set_power(struct acpi_device *device, int state);
int acpi_bus_init_power(struct acpi_device *device); int acpi_bus_init_power(struct acpi_device *device);
int acpi_device_fix_up_power(struct acpi_device *device); int acpi_device_fix_up_power(struct acpi_device *device);
int acpi_bus_update_power(acpi_handle handle, int *state_p); int acpi_bus_update_power(acpi_handle handle, int *state_p);
int acpi_device_update_power(struct acpi_device *device, int *state_p);
bool acpi_bus_power_manageable(acpi_handle handle); bool acpi_bus_power_manageable(acpi_handle handle);
#ifdef CONFIG_PM #ifdef CONFIG_PM
......
...@@ -432,6 +432,7 @@ static inline bool acpi_driver_match_device(struct device *dev, ...@@ -432,6 +432,7 @@ static inline bool acpi_driver_match_device(struct device *dev,
int acpi_device_uevent_modalias(struct device *, struct kobj_uevent_env *); int acpi_device_uevent_modalias(struct device *, struct kobj_uevent_env *);
int acpi_device_modalias(struct device *, char *, int); int acpi_device_modalias(struct device *, char *, int);
struct platform_device *acpi_create_platform_device(struct acpi_device *);
#define ACPI_PTR(_ptr) (_ptr) #define ACPI_PTR(_ptr) (_ptr)
#else /* !CONFIG_ACPI */ #else /* !CONFIG_ACPI */
......
...@@ -44,6 +44,10 @@ ...@@ -44,6 +44,10 @@
#define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732 >= 0) ? \ #define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732 >= 0) ? \
((long)t-2732+5)/10 : ((long)t-2732-5)/10) ((long)t-2732+5)/10 : ((long)t-2732-5)/10)
#define CELSIUS_TO_KELVIN(t) ((t)*10+2732) #define CELSIUS_TO_KELVIN(t) ((t)*10+2732)
#define DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(t, off) (((t) - (off)) * 100)
#define DECI_KELVIN_TO_MILLICELSIUS(t) DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(t, 2732)
#define MILLICELSIUS_TO_DECI_KELVIN_WITH_OFFSET(t, off) (((t) / 100) + (off))
#define MILLICELSIUS_TO_DECI_KELVIN(t) MILLICELSIUS_TO_DECI_KELVIN_WITH_OFFSET(t, 2732)
/* Adding event notification support elements */ /* Adding event notification support elements */
#define THERMAL_GENL_FAMILY_NAME "thermal_event" #define THERMAL_GENL_FAMILY_NAME "thermal_event"
......
#undef TRACE_SYSTEM
#define TRACE_SYSTEM thermal
#if !defined(_TRACE_THERMAL_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_THERMAL_H
#include <linux/thermal.h>
#include <linux/tracepoint.h>
TRACE_EVENT(thermal_temperature,
TP_PROTO(struct thermal_zone_device *tz),
TP_ARGS(tz),
TP_STRUCT__entry(
__string(thermal_zone, tz->type)
__field(int, id)
__field(int, temp_prev)
__field(int, temp)
),
TP_fast_assign(
__assign_str(thermal_zone, tz->type);
__entry->id = tz->id;
__entry->temp_prev = tz->last_temperature;
__entry->temp = tz->temperature;
),
TP_printk("thermal_zone=%s id=%d temp_prev=%d temp=%d",
__get_str(thermal_zone), __entry->id, __entry->temp_prev,
__entry->temp)
);
TRACE_EVENT(cdev_update,
TP_PROTO(struct thermal_cooling_device *cdev, unsigned long target),
TP_ARGS(cdev, target),
TP_STRUCT__entry(
__string(type, cdev->type)
__field(unsigned long, target)
),
TP_fast_assign(
__assign_str(type, cdev->type);
__entry->target = target;
),
TP_printk("type=%s target=%lu", __get_str(type), __entry->target)
);
TRACE_EVENT(thermal_zone_trip,
TP_PROTO(struct thermal_zone_device *tz, int trip,
enum thermal_trip_type trip_type),
TP_ARGS(tz, trip, trip_type),
TP_STRUCT__entry(
__string(thermal_zone, tz->type)
__field(int, id)
__field(int, trip)
__field(enum thermal_trip_type, trip_type)
),
TP_fast_assign(
__assign_str(thermal_zone, tz->type);
__entry->id = tz->id;
__entry->trip = trip;
__entry->trip_type = trip_type;
),
TP_printk("thermal_zone=%s id=%d trip=%d trip_type=%d",
__get_str(thermal_zone), __entry->id, __entry->trip,
__entry->trip_type)
);
#endif /* _TRACE_THERMAL_H */
/* This part must be outside protection */
#include <trace/define_trace.h>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment