mirror of
https://github.com/armbian/linux.git
synced 2026-01-06 10:13:00 -08:00
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux
Pull thermal management update from Zhang Rui:
"Summary:
- of-thermal extension to allow drivers to register and use its
functionality in a better way, without exploiting thermal core.
From Lukasz Majewski.
- Fix a bug in intel_soc_dts_thermal driver which calls a sleep
function in interrupt handler. From Maurice Petallo.
- add a thermal UAPI header file for exporting the thermal generic
netlink information to user-space. From Florian Fainelli.
- First round of refactoring in Exynos driver. Bartlomiej and Lukasz
are attempting to make it lean and easier to understand.
- New thermal driver for Rockchip (rk3288), with support for DT
thermal. From Caesar Wang.
- New thermal driver for Nvidia, Tegra124 SOCTHERM driver, with
support for DT thermal. From Mikko Perttunen.
- New cooling device, based on common clock framework. From Eduardo
Valentin.
- a couple of small fixes in thermal core framework. From Srinivas
Pandruvada, Javi Merino, Luis Henriques.
- Dropping Armada A375-Z1 SoC thermal support as the chip is not in
the market, armada folks decided to drop its support.
- a couple of small fixes and cleanups in int340x thermal driver"
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux: (58 commits)
thermal: provide an UAPI header file
Thermal/int340x: Clear the error value of the last acpi_bus_get_device() call
thermal/powerclamp: add id for braswell cpu
thermal: Intel SoC DTS: Don't do thermal zone update inside spin_lock
Thermal: fix platform_no_drv_owner.cocci warnings
Thermal/int340x: avoid unnecessary pointer casting
thermal: int3403: Delete a check before thermal_zone_device_unregister()
thermal/int3400: export uuids
thermal: of: Extend current of-thermal.c code to allow setting emulated temp
thermal: of: Extend of-thermal to export table of trip points
thermal: of: Rename struct __thermal_trip to struct thermal_trip
thermal: of: Extend of-thermal.c to provide check if trip point is valid
thermal: of: Extend of-thermal.c to provide number of trip points
thermal: Fix error path in thermal_init()
thermal: lock the thermal zone when switching governors
thermal: core: ignore invalid trip temperature
thermal: armada: Remove support for A375-Z1 SoC
thermal: rockchip: add driver for thermal
dt-bindings: document Rockchip thermal
thermal: exynos: remove exynos_tmu_data.h include
...
This commit is contained in:
@@ -5,17 +5,9 @@ Required properties:
|
||||
- compatible: Should be set to one of the following:
|
||||
marvell,armada370-thermal
|
||||
marvell,armada375-thermal
|
||||
marvell,armada375-z1-thermal
|
||||
marvell,armada380-thermal
|
||||
marvell,armadaxp-thermal
|
||||
|
||||
Note: As the name suggests, "marvell,armada375-z1-thermal"
|
||||
applies for the SoC Z1 stepping only. On such stepping
|
||||
some quirks need to be done and the register offset differs
|
||||
from the one in the A0 stepping.
|
||||
The operating system may auto-detect the SoC stepping and
|
||||
update the compatible and register offsets at runtime.
|
||||
|
||||
- reg: Device's register space.
|
||||
Two entries are expected, see the examples below.
|
||||
The first one is required for the sensor register;
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
* Temperature Sensor ADC (TSADC) on rockchip SoCs
|
||||
|
||||
Required properties:
|
||||
- compatible : "rockchip,rk3288-tsadc"
|
||||
- reg : physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts : The interrupt number to the cpu. The interrupt specifier format
|
||||
depends on the interrupt controller.
|
||||
- clocks : Must contain an entry for each entry in clock-names.
|
||||
- clock-names : Shall be "tsadc" for the converter-clock, and "apb_pclk" for
|
||||
the peripheral clock.
|
||||
- resets : Must contain an entry for each entry in reset-names.
|
||||
See ../reset/reset.txt for details.
|
||||
- reset-names : Must include the name "tsadc-apb".
|
||||
- #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description.
|
||||
- rockchip,hw-tshut-temp : The hardware-controlled shutdown temperature value.
|
||||
- rockchip,hw-tshut-mode : The hardware-controlled shutdown mode 0:CRU 1:GPIO.
|
||||
- rockchip,hw-tshut-polarity : The hardware-controlled active polarity 0:LOW
|
||||
1:HIGH.
|
||||
|
||||
Exiample:
|
||||
tsadc: tsadc@ff280000 {
|
||||
compatible = "rockchip,rk3288-tsadc";
|
||||
reg = <0xff280000 0x100>;
|
||||
interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cru SCLK_TSADC>, <&cru PCLK_TSADC>;
|
||||
clock-names = "tsadc", "apb_pclk";
|
||||
resets = <&cru SRST_TSADC>;
|
||||
reset-names = "tsadc-apb";
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&otp_out>;
|
||||
#thermal-sensor-cells = <1>;
|
||||
rockchip,hw-tshut-temp = <95000>;
|
||||
rockchip,hw-tshut-mode = <0>;
|
||||
rockchip,hw-tshut-polarity = <0>;
|
||||
};
|
||||
|
||||
Example: referring to thermal sensors:
|
||||
thermal-zones {
|
||||
cpu_thermal: cpu_thermal {
|
||||
polling-delay-passive = <1000>; /* milliseconds */
|
||||
polling-delay = <5000>; /* milliseconds */
|
||||
|
||||
/* sensor ID */
|
||||
thermal-sensors = <&tsadc 1>;
|
||||
|
||||
trips {
|
||||
cpu_alert0: cpu_alert {
|
||||
temperature = <70000>; /* millicelsius */
|
||||
hysteresis = <2000>; /* millicelsius */
|
||||
type = "passive";
|
||||
};
|
||||
cpu_crit: cpu_crit {
|
||||
temperature = <90000>; /* millicelsius */
|
||||
hysteresis = <2000>; /* millicelsius */
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
|
||||
cooling-maps {
|
||||
map0 {
|
||||
trip = <&cpu_alert0>;
|
||||
cooling-device =
|
||||
<&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
53
Documentation/devicetree/bindings/thermal/tegra-soctherm.txt
Normal file
53
Documentation/devicetree/bindings/thermal/tegra-soctherm.txt
Normal file
@@ -0,0 +1,53 @@
|
||||
Tegra124 SOCTHERM thermal management system
|
||||
|
||||
The SOCTHERM IP block contains thermal sensors, support for polled
|
||||
or interrupt-based thermal monitoring, CPU and GPU throttling based
|
||||
on temperature trip points, and handling external overcurrent
|
||||
notifications. It is also used to manage emergency shutdown in an
|
||||
overheating situation.
|
||||
|
||||
Required properties :
|
||||
- compatible : "nvidia,tegra124-soctherm".
|
||||
- reg : Should contain 1 entry:
|
||||
- SOCTHERM register set
|
||||
- interrupts : Defines the interrupt used by SOCTHERM
|
||||
- clocks : Must contain an entry for each entry in clock-names.
|
||||
See ../clocks/clock-bindings.txt for details.
|
||||
- clock-names : Must include the following entries:
|
||||
- tsensor
|
||||
- soctherm
|
||||
- resets : Must contain an entry for each entry in reset-names.
|
||||
See ../reset/reset.txt for details.
|
||||
- reset-names : Must include the following entries:
|
||||
- soctherm
|
||||
- #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description
|
||||
of this property. See <dt-bindings/thermal/tegra124-soctherm.h> for a
|
||||
list of valid values when referring to thermal sensors.
|
||||
|
||||
|
||||
Example :
|
||||
|
||||
soctherm@0,700e2000 {
|
||||
compatible = "nvidia,tegra124-soctherm";
|
||||
reg = <0x0 0x700e2000 0x0 0x1000>;
|
||||
interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&tegra_car TEGRA124_CLK_TSENSOR>,
|
||||
<&tegra_car TEGRA124_CLK_SOC_THERM>;
|
||||
clock-names = "tsensor", "soctherm";
|
||||
resets = <&tegra_car 78>;
|
||||
reset-names = "soctherm";
|
||||
|
||||
#thermal-sensor-cells = <1>;
|
||||
};
|
||||
|
||||
Example: referring to thermal sensors :
|
||||
|
||||
thermal-zones {
|
||||
cpu {
|
||||
polling-delay-passive = <1000>;
|
||||
polling-delay = <1000>;
|
||||
|
||||
thermal-sensors =
|
||||
<&soctherm TEGRA124_SOCTHERM_SENSOR_CPU>;
|
||||
};
|
||||
};
|
||||
@@ -9516,6 +9516,7 @@ Q: https://patchwork.kernel.org/project/linux-pm/list/
|
||||
S: Supported
|
||||
F: drivers/thermal/
|
||||
F: include/linux/thermal.h
|
||||
F: include/uapi/linux/thermal.h
|
||||
F: include/linux/cpu_cooling.h
|
||||
F: Documentation/devicetree/bindings/thermal/
|
||||
|
||||
|
||||
@@ -1942,4 +1942,48 @@
|
||||
<&tegra_car TEGRA124_CLK_EXTERN1>;
|
||||
clock-names = "pll_a", "pll_a_out0", "mclk";
|
||||
};
|
||||
|
||||
thermal-zones {
|
||||
cpu {
|
||||
trips {
|
||||
trip@0 {
|
||||
temperature = <101000>;
|
||||
hysteresis = <0>;
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
|
||||
cooling-maps {
|
||||
/* There are currently no cooling maps because there are no cooling devices */
|
||||
};
|
||||
};
|
||||
|
||||
mem {
|
||||
trips {
|
||||
trip@0 {
|
||||
temperature = <101000>;
|
||||
hysteresis = <0>;
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
|
||||
cooling-maps {
|
||||
/* There are currently no cooling maps because there are no cooling devices */
|
||||
};
|
||||
};
|
||||
|
||||
gpu {
|
||||
trips {
|
||||
trip@0 {
|
||||
temperature = <101000>;
|
||||
hysteresis = <0>;
|
||||
type = "critical";
|
||||
};
|
||||
};
|
||||
|
||||
cooling-maps {
|
||||
/* There are currently no cooling maps because there are no cooling devices */
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <dt-bindings/pinctrl/pinctrl-tegra.h>
|
||||
#include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/thermal/tegra124-soctherm.h>
|
||||
|
||||
#include "skeleton.dtsi"
|
||||
|
||||
@@ -657,6 +658,18 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
soctherm: thermal-sensor@0,700e2000 {
|
||||
compatible = "nvidia,tegra124-soctherm";
|
||||
reg = <0x0 0x700e2000 0x0 0x1000>;
|
||||
interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&tegra_car TEGRA124_CLK_TSENSOR>,
|
||||
<&tegra_car TEGRA124_CLK_SOC_THERM>;
|
||||
clock-names = "tsensor", "soctherm";
|
||||
resets = <&tegra_car 78>;
|
||||
reset-names = "soctherm";
|
||||
#thermal-sensor-cells = <1>;
|
||||
};
|
||||
|
||||
ahub@0,70300000 {
|
||||
compatible = "nvidia,tegra124-ahub";
|
||||
reg = <0x0 0x70300000 0x0 0x200>,
|
||||
@@ -898,6 +911,40 @@
|
||||
};
|
||||
};
|
||||
|
||||
thermal-zones {
|
||||
cpu {
|
||||
polling-delay-passive = <1000>;
|
||||
polling-delay = <1000>;
|
||||
|
||||
thermal-sensors =
|
||||
<&soctherm TEGRA124_SOCTHERM_SENSOR_CPU>;
|
||||
};
|
||||
|
||||
mem {
|
||||
polling-delay-passive = <1000>;
|
||||
polling-delay = <1000>;
|
||||
|
||||
thermal-sensors =
|
||||
<&soctherm TEGRA124_SOCTHERM_SENSOR_MEM>;
|
||||
};
|
||||
|
||||
gpu {
|
||||
polling-delay-passive = <1000>;
|
||||
polling-delay = <1000>;
|
||||
|
||||
thermal-sensors =
|
||||
<&soctherm TEGRA124_SOCTHERM_SENSOR_GPU>;
|
||||
};
|
||||
|
||||
pllx {
|
||||
polling-delay-passive = <1000>;
|
||||
polling-delay = <1000>;
|
||||
|
||||
thermal-sensors =
|
||||
<&soctherm TEGRA124_SOCTHERM_SENSOR_PLLX>;
|
||||
};
|
||||
};
|
||||
|
||||
timer {
|
||||
compatible = "arm,armv7-timer";
|
||||
interrupts = <GIC_PPI 13
|
||||
|
||||
@@ -177,6 +177,10 @@ static struct attribute *lm75_attrs[] = {
|
||||
};
|
||||
ATTRIBUTE_GROUPS(lm75);
|
||||
|
||||
static const struct thermal_zone_of_device_ops lm75_of_thermal_ops = {
|
||||
.get_temp = lm75_read_temp,
|
||||
};
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
/* device probe and removal */
|
||||
@@ -296,10 +300,9 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
if (IS_ERR(data->hwmon_dev))
|
||||
return PTR_ERR(data->hwmon_dev);
|
||||
|
||||
data->tz = thermal_zone_of_sensor_register(data->hwmon_dev,
|
||||
0,
|
||||
data->tz = thermal_zone_of_sensor_register(data->hwmon_dev, 0,
|
||||
data->hwmon_dev,
|
||||
lm75_read_temp, NULL);
|
||||
&lm75_of_thermal_ops);
|
||||
if (IS_ERR(data->tz))
|
||||
data->tz = NULL;
|
||||
|
||||
|
||||
@@ -486,6 +486,10 @@ static const struct attribute_group ntc_attr_group = {
|
||||
.attrs = ntc_attributes,
|
||||
};
|
||||
|
||||
static const struct thermal_zone_of_device_ops ntc_of_thermal_ops = {
|
||||
.get_temp = ntc_read_temp,
|
||||
};
|
||||
|
||||
static int ntc_thermistor_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *of_id =
|
||||
@@ -579,7 +583,7 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
|
||||
pdev_id->name);
|
||||
|
||||
data->tz = thermal_zone_of_sensor_register(data->dev, 0, data->dev,
|
||||
ntc_read_temp, NULL);
|
||||
&ntc_of_thermal_ops);
|
||||
if (IS_ERR(data->tz)) {
|
||||
dev_dbg(&pdev->dev, "Failed to register to thermal fw.\n");
|
||||
data->tz = NULL;
|
||||
|
||||
@@ -158,6 +158,10 @@ ATTRIBUTE_GROUPS(tmp102);
|
||||
#define TMP102_CONFIG (TMP102_CONF_TM | TMP102_CONF_EM | TMP102_CONF_CR1)
|
||||
#define TMP102_CONFIG_RD_ONLY (TMP102_CONF_R0 | TMP102_CONF_R1 | TMP102_CONF_AL)
|
||||
|
||||
static const struct thermal_zone_of_device_ops tmp102_of_thermal_ops = {
|
||||
.get_temp = tmp102_read_temp,
|
||||
};
|
||||
|
||||
static int tmp102_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
@@ -215,7 +219,7 @@ static int tmp102_probe(struct i2c_client *client,
|
||||
}
|
||||
tmp102->hwmon_dev = hwmon_dev;
|
||||
tmp102->tz = thermal_zone_of_sensor_register(hwmon_dev, 0, hwmon_dev,
|
||||
tmp102_read_temp, NULL);
|
||||
&tmp102_of_thermal_ops);
|
||||
if (IS_ERR(tmp102->tz))
|
||||
tmp102->tz = NULL;
|
||||
|
||||
|
||||
@@ -112,6 +112,18 @@ config CPU_THERMAL
|
||||
|
||||
If you want this support, you should say Y here.
|
||||
|
||||
config CLOCK_THERMAL
|
||||
bool "Generic clock cooling support"
|
||||
depends on COMMON_CLK
|
||||
depends on PM_OPP
|
||||
help
|
||||
This entry implements the generic clock cooling mechanism through
|
||||
frequency clipping. Typically used to cool off co-processors. The
|
||||
device that is configured to use this cooling mechanism will be
|
||||
controlled to reduce clock frequency whenever temperature is high.
|
||||
|
||||
If you want this support, you should say Y here.
|
||||
|
||||
config THERMAL_EMULATION
|
||||
bool "Thermal emulation mode support"
|
||||
help
|
||||
@@ -143,6 +155,16 @@ config SPEAR_THERMAL
|
||||
Enable this to plug the SPEAr thermal sensor driver into the Linux
|
||||
thermal framework.
|
||||
|
||||
config ROCKCHIP_THERMAL
|
||||
tristate "Rockchip thermal driver"
|
||||
depends on ARCH_ROCKCHIP
|
||||
depends on RESET_CONTROLLER
|
||||
help
|
||||
Rockchip thermal driver provides support for Temperature sensor
|
||||
ADC (TS-ADC) found on Rockchip SoCs. It supports one critical
|
||||
trip point. Cpufreq is used as the cooling device and will throttle
|
||||
CPUs when the Temperature crosses the passive trip point.
|
||||
|
||||
config RCAR_THERMAL
|
||||
tristate "Renesas R-Car thermal driver"
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
@@ -185,6 +207,16 @@ config ARMADA_THERMAL
|
||||
Enable this option if you want to have support for thermal management
|
||||
controller present in Armada 370 and Armada XP SoC.
|
||||
|
||||
config TEGRA_SOCTHERM
|
||||
tristate "Tegra SOCTHERM thermal management"
|
||||
depends on ARCH_TEGRA
|
||||
help
|
||||
Enable this option for integrated thermal management support on NVIDIA
|
||||
Tegra124 systems-on-chip. The driver supports four thermal zones
|
||||
(CPU, GPU, MEM, PLLX). Cooling devices can be bound to the thermal
|
||||
zones to manage temperatures. This option is also required for the
|
||||
emergency thermal reset (thermtrip) feature to function.
|
||||
|
||||
config DB8500_CPUFREQ_COOLING
|
||||
tristate "DB8500 cpufreq cooling"
|
||||
depends on ARCH_U8500
|
||||
|
||||
@@ -18,8 +18,12 @@ thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE) += user_space.o
|
||||
# cpufreq cooling
|
||||
thermal_sys-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
|
||||
|
||||
# clock cooling
|
||||
thermal_sys-$(CONFIG_CLOCK_THERMAL) += clock_cooling.o
|
||||
|
||||
# platform thermal drivers
|
||||
obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
|
||||
obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o
|
||||
obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o
|
||||
obj-$(CONFIG_KIRKWOOD_THERMAL) += kirkwood_thermal.o
|
||||
obj-y += samsung/
|
||||
@@ -34,3 +38,4 @@ obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o
|
||||
obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
|
||||
obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/
|
||||
obj-$(CONFIG_ST_THERMAL) += st/
|
||||
obj-$(CONFIG_TEGRA_SOCTHERM) += tegra_soctherm.o
|
||||
|
||||
@@ -35,10 +35,6 @@
|
||||
#define PMU_TDC0_OTF_CAL_MASK (0x1 << 30)
|
||||
#define PMU_TDC0_START_CAL_MASK (0x1 << 25)
|
||||
|
||||
#define A375_Z1_CAL_RESET_LSB 0x8011e214
|
||||
#define A375_Z1_CAL_RESET_MSB 0x30a88019
|
||||
#define A375_Z1_WORKAROUND_BIT BIT(9)
|
||||
|
||||
#define A375_UNIT_CONTROL_SHIFT 27
|
||||
#define A375_UNIT_CONTROL_MASK 0x7
|
||||
#define A375_READOUT_INVERT BIT(15)
|
||||
@@ -124,24 +120,12 @@ static void armada375_init_sensor(struct platform_device *pdev,
|
||||
struct armada_thermal_priv *priv)
|
||||
{
|
||||
unsigned long reg;
|
||||
bool quirk_needed =
|
||||
!!of_device_is_compatible(pdev->dev.of_node,
|
||||
"marvell,armada375-z1-thermal");
|
||||
|
||||
if (quirk_needed) {
|
||||
/* Ensure these registers have the default (reset) values */
|
||||
writel(A375_Z1_CAL_RESET_LSB, priv->control);
|
||||
writel(A375_Z1_CAL_RESET_MSB, priv->control + 0x4);
|
||||
}
|
||||
|
||||
reg = readl(priv->control + 4);
|
||||
reg &= ~(A375_UNIT_CONTROL_MASK << A375_UNIT_CONTROL_SHIFT);
|
||||
reg &= ~A375_READOUT_INVERT;
|
||||
reg &= ~A375_HW_RESETn;
|
||||
|
||||
if (quirk_needed)
|
||||
reg |= A375_Z1_WORKAROUND_BIT;
|
||||
|
||||
writel(reg, priv->control + 4);
|
||||
mdelay(20);
|
||||
|
||||
@@ -259,10 +243,6 @@ static const struct of_device_id armada_thermal_id_table[] = {
|
||||
.compatible = "marvell,armada375-thermal",
|
||||
.data = &armada375_data,
|
||||
},
|
||||
{
|
||||
.compatible = "marvell,armada375-z1-thermal",
|
||||
.data = &armada375_data,
|
||||
},
|
||||
{
|
||||
.compatible = "marvell,armada380-thermal",
|
||||
.data = &armada380_data,
|
||||
|
||||
485
drivers/thermal/clock_cooling.c
Normal file
485
drivers/thermal/clock_cooling.c
Normal file
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
* drivers/thermal/clock_cooling.c
|
||||
*
|
||||
* Copyright (C) 2014 Eduardo Valentin <edubezval@gmail.com>
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments Inc.
|
||||
* Contact: Eduardo Valentin <eduardo.valentin@ti.com>
|
||||
*
|
||||
* Highly based on cpu_cooling.c.
|
||||
* Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com)
|
||||
* Copyright (C) 2012 Amit Daniel <amit.kachhap@linaro.org>
|
||||
*
|
||||
* 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 of the License.
|
||||
*
|
||||
* 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/clk.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/clock_cooling.h>
|
||||
|
||||
/**
|
||||
* struct clock_cooling_device - data for cooling device with clock
|
||||
* @id: unique integer value corresponding to each clock_cooling_device
|
||||
* registered.
|
||||
* @dev: struct device pointer to the device being used to cool off using
|
||||
* clock frequencies.
|
||||
* @cdev: thermal_cooling_device pointer to keep track of the
|
||||
* registered cooling device.
|
||||
* @clk_rate_change_nb: reference to notifier block used to receive clock
|
||||
* rate changes.
|
||||
* @freq_table: frequency table used to keep track of available frequencies.
|
||||
* @clock_state: integer value representing the current state of clock
|
||||
* cooling devices.
|
||||
* @clock_val: integer value representing the absolute value of the clipped
|
||||
* frequency.
|
||||
* @clk: struct clk reference used to enforce clock limits.
|
||||
* @lock: mutex lock to protect this struct.
|
||||
*
|
||||
* This structure is required for keeping information of each
|
||||
* clock_cooling_device registered. In order to prevent corruption of this a
|
||||
* mutex @lock is used.
|
||||
*/
|
||||
struct clock_cooling_device {
|
||||
int id;
|
||||
struct device *dev;
|
||||
struct thermal_cooling_device *cdev;
|
||||
struct notifier_block clk_rate_change_nb;
|
||||
struct cpufreq_frequency_table *freq_table;
|
||||
unsigned long clock_state;
|
||||
unsigned long clock_val;
|
||||
struct clk *clk;
|
||||
struct mutex lock; /* lock to protect the content of this struct */
|
||||
};
|
||||
#define to_clock_cooling_device(x) \
|
||||
container_of(x, struct clock_cooling_device, clk_rate_change_nb)
|
||||
static DEFINE_IDR(clock_idr);
|
||||
static DEFINE_MUTEX(cooling_clock_lock);
|
||||
|
||||
/**
|
||||
* clock_cooling_get_idr - function to get an unique id.
|
||||
* @id: int * value generated by this function.
|
||||
*
|
||||
* This function will populate @id with an unique
|
||||
* id, using the idr API.
|
||||
*
|
||||
* Return: 0 on success, an error code on failure.
|
||||
*/
|
||||
static int clock_cooling_get_idr(int *id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&cooling_clock_lock);
|
||||
ret = idr_alloc(&clock_idr, NULL, 0, 0, GFP_KERNEL);
|
||||
mutex_unlock(&cooling_clock_lock);
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
*id = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* release_idr - function to free the unique id.
|
||||
* @id: int value representing the unique id.
|
||||
*/
|
||||
static void release_idr(int id)
|
||||
{
|
||||
mutex_lock(&cooling_clock_lock);
|
||||
idr_remove(&clock_idr, id);
|
||||
mutex_unlock(&cooling_clock_lock);
|
||||
}
|
||||
|
||||
/* Below code defines functions to be used for clock as cooling device */
|
||||
|
||||
enum clock_cooling_property {
|
||||
GET_LEVEL,
|
||||
GET_FREQ,
|
||||
GET_MAXL,
|
||||
};
|
||||
|
||||
/**
|
||||
* clock_cooling_get_property - fetch a property of interest for a give cpu.
|
||||
* @ccdev: clock cooling device reference
|
||||
* @input: query parameter
|
||||
* @output: query return
|
||||
* @property: type of query (frequency, level, max level)
|
||||
*
|
||||
* This is the common function to
|
||||
* 1. get maximum clock cooling states
|
||||
* 2. translate frequency to cooling state
|
||||
* 3. translate cooling state to frequency
|
||||
* Note that the code may be not in good shape
|
||||
* but it is written in this way in order to:
|
||||
* a) reduce duplicate code as most of the code can be shared.
|
||||
* b) make sure the logic is consistent when translating between
|
||||
* cooling states and frequencies.
|
||||
*
|
||||
* Return: 0 on success, -EINVAL when invalid parameters are passed.
|
||||
*/
|
||||
static int clock_cooling_get_property(struct clock_cooling_device *ccdev,
|
||||
unsigned long input,
|
||||
unsigned long *output,
|
||||
enum clock_cooling_property property)
|
||||
{
|
||||
int i;
|
||||
unsigned long max_level = 0, level = 0;
|
||||
unsigned int freq = CPUFREQ_ENTRY_INVALID;
|
||||
int descend = -1;
|
||||
struct cpufreq_frequency_table *pos, *table = ccdev->freq_table;
|
||||
|
||||
if (!output)
|
||||
return -EINVAL;
|
||||
|
||||
if (!table)
|
||||
return -EINVAL;
|
||||
|
||||
cpufreq_for_each_valid_entry(pos, table) {
|
||||
/* ignore duplicate entry */
|
||||
if (freq == pos->frequency)
|
||||
continue;
|
||||
|
||||
/* get the frequency order */
|
||||
if (freq != CPUFREQ_ENTRY_INVALID && descend == -1)
|
||||
descend = freq > pos->frequency;
|
||||
|
||||
freq = pos->frequency;
|
||||
max_level++;
|
||||
}
|
||||
|
||||
/* No valid cpu frequency entry */
|
||||
if (max_level == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* max_level is an index, not a counter */
|
||||
max_level--;
|
||||
|
||||
/* get max level */
|
||||
if (property == GET_MAXL) {
|
||||
*output = max_level;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (property == GET_FREQ)
|
||||
level = descend ? input : (max_level - input);
|
||||
|
||||
i = 0;
|
||||
cpufreq_for_each_valid_entry(pos, table) {
|
||||
/* ignore duplicate entry */
|
||||
if (freq == pos->frequency)
|
||||
continue;
|
||||
|
||||
/* now we have a valid frequency entry */
|
||||
freq = pos->frequency;
|
||||
|
||||
if (property == GET_LEVEL && (unsigned int)input == freq) {
|
||||
/* get level by frequency */
|
||||
*output = descend ? i : (max_level - i);
|
||||
return 0;
|
||||
}
|
||||
if (property == GET_FREQ && level == i) {
|
||||
/* get frequency by level */
|
||||
*output = freq;
|
||||
return 0;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* clock_cooling_get_level - return the cooling level of given clock cooling.
|
||||
* @cdev: reference of a thermal cooling device of used as clock cooling device
|
||||
* @freq: the frequency of interest
|
||||
*
|
||||
* This function will match the cooling level corresponding to the
|
||||
* requested @freq and return it.
|
||||
*
|
||||
* Return: The matched cooling level on success or THERMAL_CSTATE_INVALID
|
||||
* otherwise.
|
||||
*/
|
||||
unsigned long clock_cooling_get_level(struct thermal_cooling_device *cdev,
|
||||
unsigned long freq)
|
||||
{
|
||||
struct clock_cooling_device *ccdev = cdev->devdata;
|
||||
unsigned long val;
|
||||
|
||||
if (clock_cooling_get_property(ccdev, (unsigned long)freq, &val,
|
||||
GET_LEVEL))
|
||||
return THERMAL_CSTATE_INVALID;
|
||||
|
||||
return val;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clock_cooling_get_level);
|
||||
|
||||
/**
|
||||
* clock_cooling_get_frequency - get the absolute value of frequency from level.
|
||||
* @ccdev: clock cooling device reference
|
||||
* @level: cooling level
|
||||
*
|
||||
* This function matches cooling level with frequency. Based on a cooling level
|
||||
* of frequency, equals cooling state of cpu cooling device, it will return
|
||||
* the corresponding frequency.
|
||||
* e.g level=0 --> 1st MAX FREQ, level=1 ---> 2nd MAX FREQ, .... etc
|
||||
*
|
||||
* Return: 0 on error, the corresponding frequency otherwise.
|
||||
*/
|
||||
static unsigned long
|
||||
clock_cooling_get_frequency(struct clock_cooling_device *ccdev,
|
||||
unsigned long level)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long freq;
|
||||
|
||||
ret = clock_cooling_get_property(ccdev, level, &freq, GET_FREQ);
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
/**
|
||||
* clock_cooling_apply - function to apply frequency clipping.
|
||||
* @ccdev: clock_cooling_device pointer containing frequency clipping data.
|
||||
* @cooling_state: value of the cooling state.
|
||||
*
|
||||
* Function used to make sure the clock layer is aware of current thermal
|
||||
* limits. The limits are applied by updating the clock rate in case it is
|
||||
* higher than the corresponding frequency based on the requested cooling_state.
|
||||
*
|
||||
* Return: 0 on success, an error code otherwise (-EINVAL in case wrong
|
||||
* cooling state).
|
||||
*/
|
||||
static int clock_cooling_apply(struct clock_cooling_device *ccdev,
|
||||
unsigned long cooling_state)
|
||||
{
|
||||
unsigned long clip_freq, cur_freq;
|
||||
int ret = 0;
|
||||
|
||||
/* Here we write the clipping */
|
||||
/* Check if the old cooling action is same as new cooling action */
|
||||
if (ccdev->clock_state == cooling_state)
|
||||
return 0;
|
||||
|
||||
clip_freq = clock_cooling_get_frequency(ccdev, cooling_state);
|
||||
if (!clip_freq)
|
||||
return -EINVAL;
|
||||
|
||||
cur_freq = clk_get_rate(ccdev->clk);
|
||||
|
||||
mutex_lock(&ccdev->lock);
|
||||
ccdev->clock_state = cooling_state;
|
||||
ccdev->clock_val = clip_freq;
|
||||
/* enforce clock level */
|
||||
if (cur_freq > clip_freq)
|
||||
ret = clk_set_rate(ccdev->clk, clip_freq);
|
||||
mutex_unlock(&ccdev->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* clock_cooling_clock_notifier - notifier callback on clock rate changes.
|
||||
* @nb: struct notifier_block * with callback info.
|
||||
* @event: value showing clock event for which this function invoked.
|
||||
* @data: callback-specific data
|
||||
*
|
||||
* Callback to hijack the notification on clock transition.
|
||||
* Every time there is a clock change, we intercept all pre change events
|
||||
* and block the transition in case the new rate infringes thermal limits.
|
||||
*
|
||||
* Return: NOTIFY_DONE (success) or NOTIFY_BAD (new_rate > thermal limit).
|
||||
*/
|
||||
static int clock_cooling_clock_notifier(struct notifier_block *nb,
|
||||
unsigned long event, void *data)
|
||||
{
|
||||
struct clk_notifier_data *ndata = data;
|
||||
struct clock_cooling_device *ccdev = to_clock_cooling_device(nb);
|
||||
|
||||
switch (event) {
|
||||
case PRE_RATE_CHANGE:
|
||||
/*
|
||||
* checks on current state
|
||||
* TODO: current method is not best we can find as it
|
||||
* allows possibly voltage transitions, in case DVFS
|
||||
* layer is also hijacking clock pre notifications.
|
||||
*/
|
||||
if (ndata->new_rate > ccdev->clock_val)
|
||||
return NOTIFY_BAD;
|
||||
/* fall through */
|
||||
case POST_RATE_CHANGE:
|
||||
case ABORT_RATE_CHANGE:
|
||||
default:
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
/* clock cooling device thermal callback functions are defined below */
|
||||
|
||||
/**
|
||||
* clock_cooling_get_max_state - callback function to get the max cooling state.
|
||||
* @cdev: thermal cooling device pointer.
|
||||
* @state: fill this variable with the max cooling state.
|
||||
*
|
||||
* Callback for the thermal cooling device to return the clock
|
||||
* max cooling state.
|
||||
*
|
||||
* Return: 0 on success, an error code otherwise.
|
||||
*/
|
||||
static int clock_cooling_get_max_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct clock_cooling_device *ccdev = cdev->devdata;
|
||||
unsigned long count = 0;
|
||||
int ret;
|
||||
|
||||
ret = clock_cooling_get_property(ccdev, 0, &count, GET_MAXL);
|
||||
if (!ret)
|
||||
*state = count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* clock_cooling_get_cur_state - function to get the current cooling state.
|
||||
* @cdev: thermal cooling device pointer.
|
||||
* @state: fill this variable with the current cooling state.
|
||||
*
|
||||
* Callback for the thermal cooling device to return the clock
|
||||
* current cooling state.
|
||||
*
|
||||
* Return: 0 (success)
|
||||
*/
|
||||
static int clock_cooling_get_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct clock_cooling_device *ccdev = cdev->devdata;
|
||||
|
||||
*state = ccdev->clock_state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* clock_cooling_set_cur_state - function to set the current cooling state.
|
||||
* @cdev: thermal cooling device pointer.
|
||||
* @state: set this variable to the current cooling state.
|
||||
*
|
||||
* Callback for the thermal cooling device to change the clock cooling
|
||||
* current cooling state.
|
||||
*
|
||||
* Return: 0 on success, an error code otherwise.
|
||||
*/
|
||||
static int clock_cooling_set_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long state)
|
||||
{
|
||||
struct clock_cooling_device *clock_device = cdev->devdata;
|
||||
|
||||
return clock_cooling_apply(clock_device, state);
|
||||
}
|
||||
|
||||
/* Bind clock callbacks to thermal cooling device ops */
|
||||
static struct thermal_cooling_device_ops const clock_cooling_ops = {
|
||||
.get_max_state = clock_cooling_get_max_state,
|
||||
.get_cur_state = clock_cooling_get_cur_state,
|
||||
.set_cur_state = clock_cooling_set_cur_state,
|
||||
};
|
||||
|
||||
/**
|
||||
* clock_cooling_register - function to create clock cooling device.
|
||||
* @dev: struct device pointer to the device used as clock cooling device.
|
||||
* @clock_name: string containing the clock used as cooling mechanism.
|
||||
*
|
||||
* This interface function registers the clock cooling device with the name
|
||||
* "thermal-clock-%x". The cooling device is based on clock frequencies.
|
||||
* The struct device is assumed to be capable of DVFS transitions.
|
||||
* The OPP layer is used to fetch and fill the available frequencies for
|
||||
* the referred device. The ordered frequency table is used to control
|
||||
* the clock cooling device cooling states and to limit clock transitions
|
||||
* based on the cooling state requested by the thermal framework.
|
||||
*
|
||||
* Return: a valid struct thermal_cooling_device pointer on success,
|
||||
* on failure, it returns a corresponding ERR_PTR().
|
||||
*/
|
||||
struct thermal_cooling_device *
|
||||
clock_cooling_register(struct device *dev, const char *clock_name)
|
||||
{
|
||||
struct thermal_cooling_device *cdev;
|
||||
struct clock_cooling_device *ccdev = NULL;
|
||||
char dev_name[THERMAL_NAME_LENGTH];
|
||||
int ret = 0;
|
||||
|
||||
ccdev = devm_kzalloc(dev, sizeof(*ccdev), GFP_KERNEL);
|
||||
if (!ccdev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ccdev->dev = dev;
|
||||
ccdev->clk = devm_clk_get(dev, clock_name);
|
||||
if (IS_ERR(ccdev->clk))
|
||||
return ERR_CAST(ccdev->clk);
|
||||
|
||||
ret = clock_cooling_get_idr(&ccdev->id);
|
||||
if (ret)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
snprintf(dev_name, sizeof(dev_name), "thermal-clock-%d", ccdev->id);
|
||||
|
||||
cdev = thermal_cooling_device_register(dev_name, ccdev,
|
||||
&clock_cooling_ops);
|
||||
if (IS_ERR(cdev)) {
|
||||
release_idr(ccdev->id);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
ccdev->cdev = cdev;
|
||||
ccdev->clk_rate_change_nb.notifier_call = clock_cooling_clock_notifier;
|
||||
|
||||
/* Assuming someone has already filled the opp table for this device */
|
||||
ret = dev_pm_opp_init_cpufreq_table(dev, &ccdev->freq_table);
|
||||
if (ret) {
|
||||
release_idr(ccdev->id);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
ccdev->clock_state = 0;
|
||||
ccdev->clock_val = clock_cooling_get_frequency(ccdev, 0);
|
||||
|
||||
clk_notifier_register(ccdev->clk, &ccdev->clk_rate_change_nb);
|
||||
|
||||
return cdev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clock_cooling_register);
|
||||
|
||||
/**
|
||||
* clock_cooling_unregister - function to remove clock cooling device.
|
||||
* @cdev: thermal cooling device pointer.
|
||||
*
|
||||
* This interface function unregisters the "thermal-clock-%x" cooling device.
|
||||
*/
|
||||
void clock_cooling_unregister(struct thermal_cooling_device *cdev)
|
||||
{
|
||||
struct clock_cooling_device *ccdev;
|
||||
|
||||
if (!cdev)
|
||||
return;
|
||||
|
||||
ccdev = cdev->devdata;
|
||||
|
||||
clk_notifier_unregister(ccdev->clk, &ccdev->clk_rate_change_nb);
|
||||
dev_pm_opp_free_cpufreq_table(ccdev->dev, &ccdev->freq_table);
|
||||
|
||||
thermal_cooling_device_unregister(ccdev->cdev);
|
||||
release_idr(ccdev->id);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clock_cooling_unregister);
|
||||
@@ -131,6 +131,8 @@ int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp,
|
||||
pr_warn("Failed to get target ACPI device\n");
|
||||
}
|
||||
|
||||
result = 0;
|
||||
|
||||
*trtp = trts;
|
||||
/* don't count bad entries */
|
||||
*trt_count -= nr_bad_entries;
|
||||
@@ -317,21 +319,21 @@ static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long length = 0;
|
||||
unsigned long count = 0;
|
||||
int count = 0;
|
||||
char __user *arg = (void __user *)__arg;
|
||||
struct trt *trts;
|
||||
struct art *arts;
|
||||
|
||||
switch (cmd) {
|
||||
case ACPI_THERMAL_GET_TRT_COUNT:
|
||||
ret = acpi_parse_trt(acpi_thermal_rel_handle, (int *)&count,
|
||||
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, (int *)&count,
|
||||
ret = acpi_parse_trt(acpi_thermal_rel_handle, &count,
|
||||
&trts, false);
|
||||
kfree(trts);
|
||||
length = count * sizeof(union trt_object);
|
||||
@@ -341,14 +343,14 @@ static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,
|
||||
case ACPI_THERMAL_GET_TRT:
|
||||
return fill_trt(arg);
|
||||
case ACPI_THERMAL_GET_ART_COUNT:
|
||||
ret = acpi_parse_art(acpi_thermal_rel_handle, (int *)&count,
|
||||
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, (int *)&count,
|
||||
ret = acpi_parse_art(acpi_thermal_rel_handle, &count,
|
||||
&arts, false);
|
||||
kfree(arts);
|
||||
length = count * sizeof(union art_object);
|
||||
|
||||
@@ -43,6 +43,74 @@ struct int3400_thermal_priv {
|
||||
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 platform_device *pdev = to_platform_device(dev);
|
||||
struct int3400_thermal_priv *priv = platform_get_drvdata(pdev);
|
||||
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 platform_device *pdev = to_platform_device(dev);
|
||||
struct int3400_thermal_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
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 platform_device *pdev = to_platform_device(dev);
|
||||
struct int3400_thermal_priv *priv = platform_get_drvdata(pdev);
|
||||
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(current_uuid, 0644, current_uuid_show, current_uuid_store);
|
||||
static DEVICE_ATTR_RO(available_uuids);
|
||||
static struct attribute *uuid_attrs[] = {
|
||||
&dev_attr_available_uuids.attr,
|
||||
&dev_attr_current_uuid.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group uuid_attribute_group = {
|
||||
.attrs = uuid_attrs,
|
||||
.name = "uuids"
|
||||
};
|
||||
|
||||
static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv)
|
||||
@@ -160,9 +228,9 @@ static int int3400_thermal_set_mode(struct thermal_zone_device *thermal,
|
||||
|
||||
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);
|
||||
priv->current_uuid_index,
|
||||
enable);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -223,7 +291,14 @@ static int int3400_thermal_probe(struct platform_device *pdev)
|
||||
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_zone;
|
||||
|
||||
return 0;
|
||||
|
||||
free_zone:
|
||||
thermal_zone_device_unregister(priv->thermal);
|
||||
free_trt:
|
||||
kfree(priv->trts);
|
||||
free_art:
|
||||
@@ -240,6 +315,7 @@ static int int3400_thermal_remove(struct platform_device *pdev)
|
||||
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);
|
||||
|
||||
@@ -293,8 +293,7 @@ static int int3403_sensor_add(struct int3403_priv *priv)
|
||||
return 0;
|
||||
|
||||
err_free_obj:
|
||||
if (obj->tzone)
|
||||
thermal_zone_device_unregister(obj->tzone);
|
||||
thermal_zone_device_unregister(obj->tzone);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -471,7 +470,6 @@ static struct platform_driver int3403_driver = {
|
||||
.remove = int3403_remove,
|
||||
.driver = {
|
||||
.name = "int3403 thermal",
|
||||
.owner = THIS_MODULE,
|
||||
.acpi_match_table = int3403_device_ids,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -689,6 +689,7 @@ static const struct x86_cpu_id intel_powerclamp_ids[] = {
|
||||
{ X86_VENDOR_INTEL, 6, 0x3f},
|
||||
{ X86_VENDOR_INTEL, 6, 0x45},
|
||||
{ X86_VENDOR_INTEL, 6, 0x46},
|
||||
{ X86_VENDOR_INTEL, 6, 0x4c},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, intel_powerclamp_ids);
|
||||
|
||||
@@ -360,6 +360,9 @@ static void proc_thermal_interrupt(void)
|
||||
u32 sticky_out;
|
||||
int status;
|
||||
u32 ptmc_out;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&intr_notify_lock, flags);
|
||||
|
||||
/* Clear APIC interrupt */
|
||||
status = iosf_mbi_read(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_READ,
|
||||
@@ -378,21 +381,20 @@ static void proc_thermal_interrupt(void)
|
||||
/* reset sticky bit */
|
||||
status = iosf_mbi_write(BT_MBI_UNIT_PMC, BT_MBI_BUNIT_WRITE,
|
||||
SOC_DTS_OFFSET_PTTSS, sticky_out);
|
||||
spin_unlock_irqrestore(&intr_notify_lock, flags);
|
||||
|
||||
for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
|
||||
pr_debug("TZD update for zone %d\n", i);
|
||||
thermal_zone_device_update(soc_dts[i]->tzone);
|
||||
}
|
||||
}
|
||||
} else
|
||||
spin_unlock_irqrestore(&intr_notify_lock, flags);
|
||||
|
||||
}
|
||||
|
||||
static irqreturn_t soc_irq_thread_fn(int irq, void *dev_data)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&intr_notify_lock, flags);
|
||||
proc_thermal_interrupt();
|
||||
spin_unlock_irqrestore(&intr_notify_lock, flags);
|
||||
pr_debug("proc_thermal_interrupt\n");
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
||||
@@ -30,26 +30,12 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include "thermal_core.h"
|
||||
|
||||
/*** Private data structures to represent thermal device tree data ***/
|
||||
|
||||
/**
|
||||
* struct __thermal_trip - representation of a point in temperature domain
|
||||
* @np: pointer to struct device_node that this trip point was created from
|
||||
* @temperature: temperature value in miliCelsius
|
||||
* @hysteresis: relative hysteresis in miliCelsius
|
||||
* @type: trip point type
|
||||
*/
|
||||
|
||||
struct __thermal_trip {
|
||||
struct device_node *np;
|
||||
unsigned long int temperature;
|
||||
unsigned long int hysteresis;
|
||||
enum thermal_trip_type type;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct __thermal_bind_param - a match between trip and cooling device
|
||||
* @cooling_device: a pointer to identify the referred cooling device
|
||||
@@ -77,8 +63,7 @@ struct __thermal_bind_params {
|
||||
* @num_tbps: number of thermal bind params
|
||||
* @tbps: an array of thermal bind params (0..num_tbps - 1)
|
||||
* @sensor_data: sensor private data used while reading temperature and trend
|
||||
* @get_temp: sensor callback to read temperature
|
||||
* @get_trend: sensor callback to read temperature trend
|
||||
* @ops: set of callbacks to handle the thermal zone based on DT
|
||||
*/
|
||||
|
||||
struct __thermal_zone {
|
||||
@@ -88,7 +73,7 @@ struct __thermal_zone {
|
||||
|
||||
/* trip data */
|
||||
int ntrips;
|
||||
struct __thermal_trip *trips;
|
||||
struct thermal_trip *trips;
|
||||
|
||||
/* cooling binding data */
|
||||
int num_tbps;
|
||||
@@ -96,8 +81,7 @@ struct __thermal_zone {
|
||||
|
||||
/* sensor interface */
|
||||
void *sensor_data;
|
||||
int (*get_temp)(void *, long *);
|
||||
int (*get_trend)(void *, long *);
|
||||
const struct thermal_zone_of_device_ops *ops;
|
||||
};
|
||||
|
||||
/*** DT thermal zone device callbacks ***/
|
||||
@@ -107,10 +91,96 @@ static int of_thermal_get_temp(struct thermal_zone_device *tz,
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
if (!data->get_temp)
|
||||
if (!data->ops->get_temp)
|
||||
return -EINVAL;
|
||||
|
||||
return data->get_temp(data->sensor_data, temp);
|
||||
return data->ops->get_temp(data->sensor_data, temp);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_thermal_get_ntrips - function to export number of available trip
|
||||
* points.
|
||||
* @tz: pointer to a thermal zone
|
||||
*
|
||||
* This function is a globally visible wrapper to get number of trip points
|
||||
* stored in the local struct __thermal_zone
|
||||
*
|
||||
* Return: number of available trip points, -ENODEV when data not available
|
||||
*/
|
||||
int of_thermal_get_ntrips(struct thermal_zone_device *tz)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
if (!data || IS_ERR(data))
|
||||
return -ENODEV;
|
||||
|
||||
return data->ntrips;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_thermal_get_ntrips);
|
||||
|
||||
/**
|
||||
* of_thermal_is_trip_valid - function to check if trip point is valid
|
||||
*
|
||||
* @tz: pointer to a thermal zone
|
||||
* @trip: trip point to evaluate
|
||||
*
|
||||
* This function is responsible for checking if passed trip point is valid
|
||||
*
|
||||
* Return: true if trip point is valid, false otherwise
|
||||
*/
|
||||
bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, int trip)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
if (!data || trip >= data->ntrips || trip < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_thermal_is_trip_valid);
|
||||
|
||||
/**
|
||||
* of_thermal_get_trip_points - function to get access to a globally exported
|
||||
* trip points
|
||||
*
|
||||
* @tz: pointer to a thermal zone
|
||||
*
|
||||
* This function provides a pointer to trip points table
|
||||
*
|
||||
* Return: pointer to trip points table, NULL otherwise
|
||||
*/
|
||||
const struct thermal_trip * const
|
||||
of_thermal_get_trip_points(struct thermal_zone_device *tz)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
return data->trips;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_thermal_get_trip_points);
|
||||
|
||||
/**
|
||||
* of_thermal_set_emul_temp - function to set emulated temperature
|
||||
*
|
||||
* @tz: pointer to a thermal zone
|
||||
* @temp: temperature to set
|
||||
*
|
||||
* This function gives the ability to set emulated value of temperature,
|
||||
* which is handy for debugging
|
||||
*
|
||||
* Return: zero on success, error code otherwise
|
||||
*/
|
||||
static int of_thermal_set_emul_temp(struct thermal_zone_device *tz,
|
||||
unsigned long temp)
|
||||
{
|
||||
struct __thermal_zone *data = tz->devdata;
|
||||
|
||||
if (!data->ops || !data->ops->set_emul_temp)
|
||||
return -EINVAL;
|
||||
|
||||
return data->ops->set_emul_temp(data->sensor_data, temp);
|
||||
}
|
||||
|
||||
static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
|
||||
@@ -120,10 +190,10 @@ static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
|
||||
long dev_trend;
|
||||
int r;
|
||||
|
||||
if (!data->get_trend)
|
||||
if (!data->ops->get_trend)
|
||||
return -EINVAL;
|
||||
|
||||
r = data->get_trend(data->sensor_data, &dev_trend);
|
||||
r = data->ops->get_trend(data->sensor_data, &dev_trend);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
@@ -324,8 +394,7 @@ static struct thermal_zone_device_ops of_thermal_ops = {
|
||||
static struct thermal_zone_device *
|
||||
thermal_zone_of_add_sensor(struct device_node *zone,
|
||||
struct device_node *sensor, void *data,
|
||||
int (*get_temp)(void *, long *),
|
||||
int (*get_trend)(void *, long *))
|
||||
const struct thermal_zone_of_device_ops *ops)
|
||||
{
|
||||
struct thermal_zone_device *tzd;
|
||||
struct __thermal_zone *tz;
|
||||
@@ -336,13 +405,16 @@ thermal_zone_of_add_sensor(struct device_node *zone,
|
||||
|
||||
tz = tzd->devdata;
|
||||
|
||||
if (!ops)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
mutex_lock(&tzd->lock);
|
||||
tz->get_temp = get_temp;
|
||||
tz->get_trend = get_trend;
|
||||
tz->ops = ops;
|
||||
tz->sensor_data = data;
|
||||
|
||||
tzd->ops->get_temp = of_thermal_get_temp;
|
||||
tzd->ops->get_trend = of_thermal_get_trend;
|
||||
tzd->ops->set_emul_temp = of_thermal_set_emul_temp;
|
||||
mutex_unlock(&tzd->lock);
|
||||
|
||||
return tzd;
|
||||
@@ -356,8 +428,7 @@ thermal_zone_of_add_sensor(struct device_node *zone,
|
||||
* than one sensors
|
||||
* @data: a private pointer (owned by the caller) that will be passed
|
||||
* back, when a temperature reading is needed.
|
||||
* @get_temp: a pointer to a function that reads the sensor temperature.
|
||||
* @get_trend: a pointer to a function that reads the sensor temperature trend.
|
||||
* @ops: struct thermal_zone_of_device_ops *. Must contain at least .get_temp.
|
||||
*
|
||||
* This function will search the list of thermal zones described in device
|
||||
* tree and look for the zone that refer to the sensor device pointed by
|
||||
@@ -382,9 +453,8 @@ thermal_zone_of_add_sensor(struct device_node *zone,
|
||||
* check the return value with help of IS_ERR() helper.
|
||||
*/
|
||||
struct thermal_zone_device *
|
||||
thermal_zone_of_sensor_register(struct device *dev, int sensor_id,
|
||||
void *data, int (*get_temp)(void *, long *),
|
||||
int (*get_trend)(void *, long *))
|
||||
thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data,
|
||||
const struct thermal_zone_of_device_ops *ops)
|
||||
{
|
||||
struct device_node *np, *child, *sensor_np;
|
||||
struct thermal_zone_device *tzd = ERR_PTR(-ENODEV);
|
||||
@@ -426,9 +496,7 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id,
|
||||
|
||||
if (sensor_specs.np == sensor_np && id == sensor_id) {
|
||||
tzd = thermal_zone_of_add_sensor(child, sensor_np,
|
||||
data,
|
||||
get_temp,
|
||||
get_trend);
|
||||
data, ops);
|
||||
of_node_put(sensor_specs.np);
|
||||
of_node_put(child);
|
||||
goto exit;
|
||||
@@ -475,9 +543,9 @@ void thermal_zone_of_sensor_unregister(struct device *dev,
|
||||
mutex_lock(&tzd->lock);
|
||||
tzd->ops->get_temp = NULL;
|
||||
tzd->ops->get_trend = NULL;
|
||||
tzd->ops->set_emul_temp = NULL;
|
||||
|
||||
tz->get_temp = NULL;
|
||||
tz->get_trend = NULL;
|
||||
tz->ops = NULL;
|
||||
tz->sensor_data = NULL;
|
||||
mutex_unlock(&tzd->lock);
|
||||
}
|
||||
@@ -501,7 +569,7 @@ EXPORT_SYMBOL_GPL(thermal_zone_of_sensor_unregister);
|
||||
*/
|
||||
static int thermal_of_populate_bind_params(struct device_node *np,
|
||||
struct __thermal_bind_params *__tbp,
|
||||
struct __thermal_trip *trips,
|
||||
struct thermal_trip *trips,
|
||||
int ntrips)
|
||||
{
|
||||
struct of_phandle_args cooling_spec;
|
||||
@@ -604,7 +672,7 @@ static int thermal_of_get_trip_type(struct device_node *np,
|
||||
* Return: 0 on success, proper error code otherwise
|
||||
*/
|
||||
static int thermal_of_populate_trip(struct device_node *np,
|
||||
struct __thermal_trip *trip)
|
||||
struct thermal_trip *trip)
|
||||
{
|
||||
int prop;
|
||||
int ret;
|
||||
|
||||
693
drivers/thermal/rockchip_thermal.c
Normal file
693
drivers/thermal/rockchip_thermal.c
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user