Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
2707dbd0
Commit
2707dbd0
authored
Dec 13, 2014
by
Zhang Rui
Browse files
Options
Browse Files
Download
Plain Diff
Merge branches 'thermal-core-fix', 'thermal-soc' and 'thermal-int340x' into next
parents
af6c9f16
c89d9954
e413ad7c
Changes
30
Show whitespace changes
Inline
Side-by-side
Showing
30 changed files
with
2744 additions
and
825 deletions
+2744
-825
Documentation/devicetree/bindings/thermal/armada-thermal.txt
Documentation/devicetree/bindings/thermal/armada-thermal.txt
+0
-8
Documentation/devicetree/bindings/thermal/rockchip-thermal.txt
...entation/devicetree/bindings/thermal/rockchip-thermal.txt
+68
-0
Documentation/devicetree/bindings/thermal/tegra-soctherm.txt
Documentation/devicetree/bindings/thermal/tegra-soctherm.txt
+53
-0
arch/arm/boot/dts/tegra124-jetson-tk1.dts
arch/arm/boot/dts/tegra124-jetson-tk1.dts
+44
-0
arch/arm/boot/dts/tegra124.dtsi
arch/arm/boot/dts/tegra124.dtsi
+47
-0
drivers/hwmon/lm75.c
drivers/hwmon/lm75.c
+6
-3
drivers/hwmon/ntc_thermistor.c
drivers/hwmon/ntc_thermistor.c
+5
-1
drivers/hwmon/tmp102.c
drivers/hwmon/tmp102.c
+5
-1
drivers/thermal/Kconfig
drivers/thermal/Kconfig
+32
-0
drivers/thermal/Makefile
drivers/thermal/Makefile
+5
-0
drivers/thermal/armada_thermal.c
drivers/thermal/armada_thermal.c
+0
-20
drivers/thermal/clock_cooling.c
drivers/thermal/clock_cooling.c
+485
-0
drivers/thermal/int340x_thermal/acpi_thermal_rel.c
drivers/thermal/int340x_thermal/acpi_thermal_rel.c
+7
-5
drivers/thermal/int340x_thermal/int3400_thermal.c
drivers/thermal/int340x_thermal/int3400_thermal.c
+78
-2
drivers/thermal/int340x_thermal/int3403_thermal.c
drivers/thermal/int340x_thermal/int3403_thermal.c
+1
-3
drivers/thermal/intel_powerclamp.c
drivers/thermal/intel_powerclamp.c
+1
-0
drivers/thermal/intel_soc_dts_thermal.c
drivers/thermal/intel_soc_dts_thermal.c
+7
-5
drivers/thermal/of-thermal.c
drivers/thermal/of-thermal.c
+108
-40
drivers/thermal/rockchip_thermal.c
drivers/thermal/rockchip_thermal.c
+693
-0
drivers/thermal/samsung/exynos_thermal_common.h
drivers/thermal/samsung/exynos_thermal_common.h
+0
-1
drivers/thermal/samsung/exynos_tmu.c
drivers/thermal/samsung/exynos_tmu.c
+468
-224
drivers/thermal/samsung/exynos_tmu.h
drivers/thermal/samsung/exynos_tmu.h
+9
-114
drivers/thermal/samsung/exynos_tmu_data.c
drivers/thermal/samsung/exynos_tmu_data.c
+8
-231
drivers/thermal/samsung/exynos_tmu_data.h
drivers/thermal/samsung/exynos_tmu_data.h
+0
-159
drivers/thermal/tegra_soctherm.c
drivers/thermal/tegra_soctherm.c
+476
-0
drivers/thermal/thermal_core.h
drivers/thermal/thermal_core.h
+18
-0
drivers/thermal/ti-soc-thermal/ti-thermal-common.c
drivers/thermal/ti-soc-thermal/ti-thermal-common.c
+6
-2
include/dt-bindings/thermal/tegra124-soctherm.h
include/dt-bindings/thermal/tegra124-soctherm.h
+13
-0
include/linux/clock_cooling.h
include/linux/clock_cooling.h
+65
-0
include/linux/thermal.h
include/linux/thermal.h
+36
-6
No files found.
Documentation/devicetree/bindings/thermal/armada-thermal.txt
View file @
2707dbd0
...
...
@@ -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;
...
...
Documentation/devicetree/bindings/thermal/rockchip-thermal.txt
0 → 100644
View file @
2707dbd0
* 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>;
};
};
};
};
Documentation/devicetree/bindings/thermal/tegra-soctherm.txt
0 → 100644
View file @
2707dbd0
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>;
};
};
arch/arm/boot/dts/tegra124-jetson-tk1.dts
View file @
2707dbd0
...
...
@@ -1942,4 +1942,48 @@ sound {
<&
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
*/
};
};
};
};
arch/arm/boot/dts/tegra124.dtsi
View file @
2707dbd0
...
...
@@ -3,6 +3,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"
...
...
@@ -640,6 +641,18 @@ sdhci@0,700b0600 {
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>,
...
...
@@ -881,6 +894,40 @@ cpu@3 {
};
};
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
...
...
drivers/hwmon/lm75.c
View file @
2707dbd0
...
...
@@ -176,6 +176,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 */
...
...
@@ -291,10 +295,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
;
...
...
drivers/hwmon/ntc_thermistor.c
View file @
2707dbd0
...
...
@@ -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
;
...
...
drivers/hwmon/tmp102.c
View file @
2707dbd0
...
...
@@ -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
;
...
...
drivers/thermal/Kconfig
View file @
2707dbd0
...
...
@@ -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
...
...
drivers/thermal/Makefile
View file @
2707dbd0
...
...
@@ -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
drivers/thermal/armada_thermal.c
View file @
2707dbd0
...
...
@@ -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
,
...
...
drivers/thermal/clock_cooling.c
0 → 100644
View file @
2707dbd0
/*
* 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
);
drivers/thermal/int340x_thermal/acpi_thermal_rel.c
View file @
2707dbd0
...
...
@@ -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
);
...
...
drivers/thermal/int340x_thermal/int3400_thermal.c
View file @
2707dbd0
...
...
@@ -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
);
...
...
drivers/thermal/int340x_thermal/int3403_thermal.c
View file @
2707dbd0
...
...
@@ -293,7 +293,6 @@ static int int3403_sensor_add(struct int3403_priv *priv)
return
0
;
err_free_obj:
if
(
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
,
},
};
...
...
drivers/thermal/intel_powerclamp.c
View file @
2707dbd0
...
...
@@ -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
);
...
...
drivers/thermal/intel_soc_dts_thermal.c
View file @
2707dbd0
...
...
@@ -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
;
...
...
drivers/thermal/of-thermal.c
View file @
2707dbd0
...
...
@@ -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
->
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
->
get
_temp
(
data
->
sensor_data
,
temp
);
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
;
...
...
drivers/thermal/rockchip_thermal.c
0 → 100644
View file @
2707dbd0
/*
* Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/thermal.h>
/**
* If the temperature over a period of time High,
* the resulting TSHUT gave CRU module,let it reset the entire chip,
* or via GPIO give PMIC.
*/
enum
tshut_mode
{
TSHUT_MODE_CRU
=
0
,
TSHUT_MODE_GPIO
,
};
/**
* the system Temperature Sensors tshut(tshut) polarity
* the bit 8 is tshut polarity.
* 0: low active, 1: high active
*/
enum
tshut_polarity
{
TSHUT_LOW_ACTIVE
=
0
,
TSHUT_HIGH_ACTIVE
,
};
/**
* The system has three Temperature Sensors. channel 0 is reserved,
* channel 1 is for CPU, and channel 2 is for GPU.
*/
enum
sensor_id
{
SENSOR_CPU
=
1
,
SENSOR_GPU
,
};
struct
rockchip_tsadc_chip
{
/* The hardware-controlled tshut property */
long
tshut_temp
;
enum
tshut_mode
tshut_mode
;
enum
tshut_polarity
tshut_polarity
;
/* Chip-wide methods */
void
(
*
initialize
)(
void
__iomem
*
reg
,
enum
tshut_polarity
p
);
void
(
*
irq_ack
)(
void
__iomem
*
reg
);
void
(
*
control
)(
void
__iomem
*
reg
,
bool
on
);
/* Per-sensor methods */
int
(
*
get_temp
)(
int
chn
,
void
__iomem
*
reg
,
long
*
temp
);
void
(
*
set_tshut_temp
)(
int
chn
,
void
__iomem
*
reg
,
long
temp
);
void
(
*
set_tshut_mode
)(
int
chn
,
void
__iomem
*
reg
,
enum
tshut_mode
m
);
};
struct
rockchip_thermal_sensor
{
struct
rockchip_thermal_data
*
thermal
;
struct
thermal_zone_device
*
tzd
;
enum
sensor_id
id
;
};
#define NUM_SENSORS 2
/* Ignore unused sensor 0 */
struct
rockchip_thermal_data
{
const
struct
rockchip_tsadc_chip
*
chip
;
struct
platform_device
*
pdev
;
struct
reset_control
*
reset
;
struct
rockchip_thermal_sensor
sensors
[
NUM_SENSORS
];
struct
clk
*
clk
;
struct
clk
*
pclk
;
void
__iomem
*
regs
;
long
tshut_temp
;
enum
tshut_mode
tshut_mode
;
enum
tshut_polarity
tshut_polarity
;
};
/* TSADC V2 Sensor info define: */
#define TSADCV2_AUTO_CON 0x04
#define TSADCV2_INT_EN 0x08
#define TSADCV2_INT_PD 0x0c
#define TSADCV2_DATA(chn) (0x20 + (chn) * 0x04)
#define TSADCV2_COMP_SHUT(chn) (0x40 + (chn) * 0x04)
#define TSADCV2_HIGHT_INT_DEBOUNCE 0x60
#define TSADCV2_HIGHT_TSHUT_DEBOUNCE 0x64
#define TSADCV2_AUTO_PERIOD 0x68
#define TSADCV2_AUTO_PERIOD_HT 0x6c
#define TSADCV2_AUTO_EN BIT(0)
#define TSADCV2_AUTO_DISABLE ~BIT(0)
#define TSADCV2_AUTO_SRC_EN(chn) BIT(4 + (chn))
#define TSADCV2_AUTO_TSHUT_POLARITY_HIGH BIT(8)
#define TSADCV2_AUTO_TSHUT_POLARITY_LOW ~BIT(8)
#define TSADCV2_INT_SRC_EN(chn) BIT(chn)
#define TSADCV2_SHUT_2GPIO_SRC_EN(chn) BIT(4 + (chn))
#define TSADCV2_SHUT_2CRU_SRC_EN(chn) BIT(8 + (chn))
#define TSADCV2_INT_PD_CLEAR ~BIT(8)
#define TSADCV2_DATA_MASK 0xfff
#define TSADCV2_HIGHT_INT_DEBOUNCE_COUNT 4
#define TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT 4
#define TSADCV2_AUTO_PERIOD_TIME 250
/* msec */
#define TSADCV2_AUTO_PERIOD_HT_TIME 50
/* msec */
struct
tsadc_table
{
unsigned
long
code
;
long
temp
;
};
static
const
struct
tsadc_table
v2_code_table
[]
=
{
{
TSADCV2_DATA_MASK
,
-
40000
},
{
3800
,
-
40000
},
{
3792
,
-
35000
},
{
3783
,
-
30000
},
{
3774
,
-
25000
},
{
3765
,
-
20000
},
{
3756
,
-
15000
},
{
3747
,
-
10000
},
{
3737
,
-
5000
},
{
3728
,
0
},
{
3718
,
5000
},
{
3708
,
10000
},
{
3698
,
15000
},
{
3688
,
20000
},
{
3678
,
25000
},
{
3667
,
30000
},
{
3656
,
35000
},
{
3645
,
40000
},
{
3634
,
45000
},
{
3623
,
50000
},
{
3611
,
55000
},
{
3600
,
60000
},
{
3588
,
65000
},
{
3575
,
70000
},
{
3563
,
75000
},
{
3550
,
80000
},
{
3537
,
85000
},
{
3524
,
90000
},
{
3510
,
95000
},
{
3496
,
100000
},
{
3482
,
105000
},
{
3467
,
110000
},
{
3452
,
115000
},
{
3437
,
120000
},
{
3421
,
125000
},
{
0
,
125000
},
};
static
u32
rk_tsadcv2_temp_to_code
(
long
temp
)
{
int
high
,
low
,
mid
;
low
=
0
;
high
=
ARRAY_SIZE
(
v2_code_table
)
-
1
;
mid
=
(
high
+
low
)
/
2
;
if
(
temp
<
v2_code_table
[
low
].
temp
||
temp
>
v2_code_table
[
high
].
temp
)
return
0
;
while
(
low
<=
high
)
{
if
(
temp
==
v2_code_table
[
mid
].
temp
)
return
v2_code_table
[
mid
].
code
;
else
if
(
temp
<
v2_code_table
[
mid
].
temp
)
high
=
mid
-
1
;
else
low
=
mid
+
1
;
mid
=
(
low
+
high
)
/
2
;
}
return
0
;
}
static
long
rk_tsadcv2_code_to_temp
(
u32
code
)
{
int
high
,
low
,
mid
;
low
=
0
;
high
=
ARRAY_SIZE
(
v2_code_table
)
-
1
;
mid
=
(
high
+
low
)
/
2
;
if
(
code
>
v2_code_table
[
low
].
code
||
code
<
v2_code_table
[
high
].
code
)
return
125000
;
/* No code available, return max temperature */
while
(
low
<=
high
)
{
if
(
code
>=
v2_code_table
[
mid
].
code
&&
code
<
v2_code_table
[
mid
-
1
].
code
)
return
v2_code_table
[
mid
].
temp
;
else
if
(
code
<
v2_code_table
[
mid
].
code
)
low
=
mid
+
1
;
else
high
=
mid
-
1
;
mid
=
(
low
+
high
)
/
2
;
}
return
125000
;
}
/**
* rk_tsadcv2_initialize - initialize TASDC Controller
* (1) Set TSADCV2_AUTO_PERIOD, configure the interleave between
* every two accessing of TSADC in normal operation.
* (2) Set TSADCV2_AUTO_PERIOD_HT, configure the interleave between
* every two accessing of TSADC after the temperature is higher
* than COM_SHUT or COM_INT.
* (3) Set TSADCV2_HIGH_INT_DEBOUNCE and TSADC_HIGHT_TSHUT_DEBOUNCE,
* if the temperature is higher than COMP_INT or COMP_SHUT for
* "debounce" times, TSADC controller will generate interrupt or TSHUT.
*/
static
void
rk_tsadcv2_initialize
(
void
__iomem
*
regs
,
enum
tshut_polarity
tshut_polarity
)
{
if
(
tshut_polarity
==
TSHUT_HIGH_ACTIVE
)
writel_relaxed
(
0
|
(
TSADCV2_AUTO_TSHUT_POLARITY_HIGH
),
regs
+
TSADCV2_AUTO_CON
);
else
writel_relaxed
(
0
|
(
TSADCV2_AUTO_TSHUT_POLARITY_LOW
),
regs
+
TSADCV2_AUTO_CON
);
writel_relaxed
(
TSADCV2_AUTO_PERIOD_TIME
,
regs
+
TSADCV2_AUTO_PERIOD
);
writel_relaxed
(
TSADCV2_HIGHT_INT_DEBOUNCE_COUNT
,
regs
+
TSADCV2_HIGHT_INT_DEBOUNCE
);
writel_relaxed
(
TSADCV2_AUTO_PERIOD_HT_TIME
,
regs
+
TSADCV2_AUTO_PERIOD_HT
);
writel_relaxed
(
TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT
,
regs
+
TSADCV2_HIGHT_TSHUT_DEBOUNCE
);
}
static
void
rk_tsadcv2_irq_ack
(
void
__iomem
*
regs
)
{
u32
val
;
val
=
readl_relaxed
(
regs
+
TSADCV2_INT_PD
);
writel_relaxed
(
val
&
TSADCV2_INT_PD_CLEAR
,
regs
+
TSADCV2_INT_PD
);
}
static
void
rk_tsadcv2_control
(
void
__iomem
*
regs
,
bool
enable
)
{
u32
val
;
val
=
readl_relaxed
(
regs
+
TSADCV2_AUTO_CON
);
if
(
enable
)
val
|=
TSADCV2_AUTO_EN
;
else
val
&=
~
TSADCV2_AUTO_EN
;
writel_relaxed
(
val
,
regs
+
TSADCV2_AUTO_CON
);
}
static
int
rk_tsadcv2_get_temp
(
int
chn
,
void
__iomem
*
regs
,
long
*
temp
)
{
u32
val
;
/* the A/D value of the channel last conversion need some time */
val
=
readl_relaxed
(
regs
+
TSADCV2_DATA
(
chn
));
if
(
val
==
0
)
return
-
EAGAIN
;
*
temp
=
rk_tsadcv2_code_to_temp
(
val
);
return
0
;
}
static
void
rk_tsadcv2_tshut_temp
(
int
chn
,
void
__iomem
*
regs
,
long
temp
)
{
u32
tshut_value
,
val
;
tshut_value
=
rk_tsadcv2_temp_to_code
(
temp
);
writel_relaxed
(
tshut_value
,
regs
+
TSADCV2_COMP_SHUT
(
chn
));
/* TSHUT will be valid */
val
=
readl_relaxed
(
regs
+
TSADCV2_AUTO_CON
);
writel_relaxed
(
val
|
TSADCV2_AUTO_SRC_EN
(
chn
),
regs
+
TSADCV2_AUTO_CON
);
}
static
void
rk_tsadcv2_tshut_mode
(
int
chn
,
void
__iomem
*
regs
,
enum
tshut_mode
mode
)
{
u32
val
;
val
=
readl_relaxed
(
regs
+
TSADCV2_INT_EN
);
if
(
mode
==
TSHUT_MODE_GPIO
)
{
val
&=
~
TSADCV2_SHUT_2CRU_SRC_EN
(
chn
);
val
|=
TSADCV2_SHUT_2GPIO_SRC_EN
(
chn
);
}
else
{
val
&=
~
TSADCV2_SHUT_2GPIO_SRC_EN
(
chn
);
val
|=
TSADCV2_SHUT_2CRU_SRC_EN
(
chn
);
}
writel_relaxed
(
val
,
regs
+
TSADCV2_INT_EN
);
}
static
const
struct
rockchip_tsadc_chip
rk3288_tsadc_data
=
{
.
tshut_mode
=
TSHUT_MODE_GPIO
,
/* default TSHUT via GPIO give PMIC */
.
tshut_polarity
=
TSHUT_LOW_ACTIVE
,
/* default TSHUT LOW ACTIVE */
.
tshut_temp
=
95000
,
.
initialize
=
rk_tsadcv2_initialize
,
.
irq_ack
=
rk_tsadcv2_irq_ack
,
.
control
=
rk_tsadcv2_control
,
.
get_temp
=
rk_tsadcv2_get_temp
,
.
set_tshut_temp
=
rk_tsadcv2_tshut_temp
,
.
set_tshut_mode
=
rk_tsadcv2_tshut_mode
,
};
static
const
struct
of_device_id
of_rockchip_thermal_match
[]
=
{
{
.
compatible
=
"rockchip,rk3288-tsadc"
,
.
data
=
(
void
*
)
&
rk3288_tsadc_data
,
},
{
/* end */
},
};
MODULE_DEVICE_TABLE
(
of
,
of_rockchip_thermal_match
);
static
void
rockchip_thermal_toggle_sensor
(
struct
rockchip_thermal_sensor
*
sensor
,
bool
on
)
{
struct
thermal_zone_device
*
tzd
=
sensor
->
tzd
;
tzd
->
ops
->
set_mode
(
tzd
,
on
?
THERMAL_DEVICE_ENABLED
:
THERMAL_DEVICE_DISABLED
);
}
static
irqreturn_t
rockchip_thermal_alarm_irq_thread
(
int
irq
,
void
*
dev
)
{
struct
rockchip_thermal_data
*
thermal
=
dev
;
int
i
;
dev_dbg
(
&
thermal
->
pdev
->
dev
,
"thermal alarm
\n
"
);
thermal
->
chip
->
irq_ack
(
thermal
->
regs
);
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
thermal
->
sensors
);
i
++
)
thermal_zone_device_update
(
thermal
->
sensors
[
i
].
tzd
);
return
IRQ_HANDLED
;
}
static
int
rockchip_thermal_get_temp
(
void
*
_sensor
,
long
*
out_temp
)
{
struct
rockchip_thermal_sensor
*
sensor
=
_sensor
;
struct
rockchip_thermal_data
*
thermal
=
sensor
->
thermal
;
const
struct
rockchip_tsadc_chip
*
tsadc
=
sensor
->
thermal
->
chip
;
int
retval
;
retval
=
tsadc
->
get_temp
(
sensor
->
id
,
thermal
->
regs
,
out_temp
);
dev_dbg
(
&
thermal
->
pdev
->
dev
,
"sensor %d - temp: %ld, retval: %d
\n
"
,
sensor
->
id
,
*
out_temp
,
retval
);
return
retval
;
}
static
const
struct
thermal_zone_of_device_ops
rockchip_of_thermal_ops
=
{
.
get_temp
=
rockchip_thermal_get_temp
,
};
static
int
rockchip_configure_from_dt
(
struct
device
*
dev
,
struct
device_node
*
np
,
struct
rockchip_thermal_data
*
thermal
)
{
u32
shut_temp
,
tshut_mode
,
tshut_polarity
;
if
(
of_property_read_u32
(
np
,
"rockchip,hw-tshut-temp"
,
&
shut_temp
))
{
dev_warn
(
dev
,
"Missing tshut temp property, using default %ld
\n
"
,
thermal
->
chip
->
tshut_temp
);
thermal
->
tshut_temp
=
thermal
->
chip
->
tshut_temp
;
}
else
{
thermal
->
tshut_temp
=
shut_temp
;
}
if
(
thermal
->
tshut_temp
>
INT_MAX
)
{
dev_err
(
dev
,
"Invalid tshut temperature specified: %ld
\n
"
,
thermal
->
tshut_temp
);
return
-
ERANGE
;
}
if
(
of_property_read_u32
(
np
,
"rockchip,hw-tshut-mode"
,
&
tshut_mode
))
{
dev_warn
(
dev
,
"Missing tshut mode property, using default (%s)
\n
"
,
thermal
->
chip
->
tshut_mode
==
TSHUT_MODE_GPIO
?
"gpio"
:
"cru"
);
thermal
->
tshut_mode
=
thermal
->
chip
->
tshut_mode
;
}
else
{
thermal
->
tshut_mode
=
tshut_mode
;
}
if
(
thermal
->
tshut_mode
>
1
)
{
dev_err
(
dev
,
"Invalid tshut mode specified: %d
\n
"
,
thermal
->
tshut_mode
);
return
-
EINVAL
;
}
if
(
of_property_read_u32
(
np
,
"rockchip,hw-tshut-polarity"
,
&
tshut_polarity
))
{
dev_warn
(
dev
,
"Missing tshut-polarity property, using default (%s)
\n
"
,
thermal
->
chip
->
tshut_polarity
==
TSHUT_LOW_ACTIVE
?
"low"
:
"high"
);
thermal
->
tshut_polarity
=
thermal
->
chip
->
tshut_polarity
;
}
else
{
thermal
->
tshut_polarity
=
tshut_polarity
;
}
if
(
thermal
->
tshut_polarity
>
1
)
{
dev_err
(
dev
,
"Invalid tshut-polarity specified: %d
\n
"
,
thermal
->
tshut_polarity
);
return
-
EINVAL
;
}
return
0
;
}
static
int
rockchip_thermal_register_sensor
(
struct
platform_device
*
pdev
,
struct
rockchip_thermal_data
*
thermal
,
struct
rockchip_thermal_sensor
*
sensor
,
enum
sensor_id
id
)
{
const
struct
rockchip_tsadc_chip
*
tsadc
=
thermal
->
chip
;
int
error
;
tsadc
->
set_tshut_mode
(
id
,
thermal
->
regs
,
thermal
->
tshut_mode
);
tsadc
->
set_tshut_temp
(
id
,
thermal
->
regs
,
thermal
->
tshut_temp
);
sensor
->
thermal
=
thermal
;
sensor
->
id
=
id
;
sensor
->
tzd
=
thermal_zone_of_sensor_register
(
&
pdev
->
dev
,
id
,
sensor
,
&
rockchip_of_thermal_ops
);
if
(
IS_ERR
(
sensor
->
tzd
))
{
error
=
PTR_ERR
(
sensor
->
tzd
);
dev_err
(
&
pdev
->
dev
,
"failed to register sensor %d: %d
\n
"
,
id
,
error
);
return
error
;
}
return
0
;
}
/*
* Reset TSADC Controller, reset all tsadc registers.
*/
static
void
rockchip_thermal_reset_controller
(
struct
reset_control
*
reset
)
{
reset_control_assert
(
reset
);
usleep_range
(
10
,
20
);
reset_control_deassert
(
reset
);
}
static
int
rockchip_thermal_probe
(
struct
platform_device
*
pdev
)
{
struct
device_node
*
np
=
pdev
->
dev
.
of_node
;
struct
rockchip_thermal_data
*
thermal
;
const
struct
of_device_id
*
match
;
struct
resource
*
res
;
int
irq
;
int
i
;
int
error
;
match
=
of_match_node
(
of_rockchip_thermal_match
,
np
);
if
(
!
match
)
return
-
ENXIO
;
irq
=
platform_get_irq
(
pdev
,
0
);
if
(
irq
<
0
)
{
dev_err
(
&
pdev
->
dev
,
"no irq resource?
\n
"
);
return
-
EINVAL
;
}
thermal
=
devm_kzalloc
(
&
pdev
->
dev
,
sizeof
(
struct
rockchip_thermal_data
),
GFP_KERNEL
);
if
(
!
thermal
)
return
-
ENOMEM
;
thermal
->
pdev
=
pdev
;
thermal
->
chip
=
(
const
struct
rockchip_tsadc_chip
*
)
match
->
data
;
if
(
!
thermal
->
chip
)
return
-
EINVAL
;
res
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
thermal
->
regs
=
devm_ioremap_resource
(
&
pdev
->
dev
,
res
);
if
(
IS_ERR
(
thermal
->
regs
))
return
PTR_ERR
(
thermal
->
regs
);
thermal
->
reset
=
devm_reset_control_get
(
&
pdev
->
dev
,
"tsadc-apb"
);
if
(
IS_ERR
(
thermal
->
reset
))
{
error
=
PTR_ERR
(
thermal
->
reset
);
dev_err
(
&
pdev
->
dev
,
"failed to get tsadc reset: %d
\n
"
,
error
);
return
error
;
}
thermal
->
clk
=
devm_clk_get
(
&
pdev
->
dev
,
"tsadc"
);
if
(
IS_ERR
(
thermal
->
clk
))
{
error
=
PTR_ERR
(
thermal
->
clk
);
dev_err
(
&
pdev
->
dev
,
"failed to get tsadc clock: %d
\n
"
,
error
);
return
error
;
}
thermal
->
pclk
=
devm_clk_get
(
&
pdev
->
dev
,
"apb_pclk"
);
if
(
IS_ERR
(
thermal
->
pclk
))
{
error
=
PTR_ERR
(
thermal
->
clk
);
dev_err
(
&
pdev
->
dev
,
"failed to get apb_pclk clock: %d
\n
"
,
error
);
return
error
;
}
error
=
clk_prepare_enable
(
thermal
->
clk
);
if
(
error
)
{
dev_err
(
&
pdev
->
dev
,
"failed to enable converter clock: %d
\n
"
,
error
);
return
error
;
}
error
=
clk_prepare_enable
(
thermal
->
pclk
);
if
(
error
)
{
dev_err
(
&
pdev
->
dev
,
"failed to enable pclk: %d
\n
"
,
error
);
goto
err_disable_clk
;
}
rockchip_thermal_reset_controller
(
thermal
->
reset
);
error
=
rockchip_configure_from_dt
(
&
pdev
->
dev
,
np
,
thermal
);
if
(
error
)
{
dev_err
(
&
pdev
->
dev
,
"failed to parse device tree data: %d
\n
"
,
error
);
goto
err_disable_pclk
;
}
thermal
->
chip
->
initialize
(
thermal
->
regs
,
thermal
->
tshut_polarity
);
error
=
rockchip_thermal_register_sensor
(
pdev
,
thermal
,
&
thermal
->
sensors
[
0
],
SENSOR_CPU
);
if
(
error
)
{
dev_err
(
&
pdev
->
dev
,
"failed to register CPU thermal sensor: %d
\n
"
,
error
);
goto
err_disable_pclk
;
}
error
=
rockchip_thermal_register_sensor
(
pdev
,
thermal
,
&
thermal
->
sensors
[
1
],
SENSOR_GPU
);
if
(
error
)
{
dev_err
(
&
pdev
->
dev
,
"failed to register GPU thermal sensor: %d
\n
"
,
error
);
goto
err_unregister_cpu_sensor
;
}
error
=
devm_request_threaded_irq
(
&
pdev
->
dev
,
irq
,
NULL
,
&
rockchip_thermal_alarm_irq_thread
,
IRQF_ONESHOT
,
"rockchip_thermal"
,
thermal
);
if
(
error
)
{
dev_err
(
&
pdev
->
dev
,
"failed to request tsadc irq: %d
\n
"
,
error
);
goto
err_unregister_gpu_sensor
;
}
thermal
->
chip
->
control
(
thermal
->
regs
,
true
);
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
thermal
->
sensors
);
i
++
)
rockchip_thermal_toggle_sensor
(
&
thermal
->
sensors
[
i
],
true
);
platform_set_drvdata
(
pdev
,
thermal
);
return
0
;
err_unregister_gpu_sensor:
thermal_zone_of_sensor_unregister
(
&
pdev
->
dev
,
thermal
->
sensors
[
1
].
tzd
);
err_unregister_cpu_sensor:
thermal_zone_of_sensor_unregister
(
&
pdev
->
dev
,
thermal
->
sensors
[
0
].
tzd
);
err_disable_pclk:
clk_disable_unprepare
(
thermal
->
pclk
);
err_disable_clk:
clk_disable_unprepare
(
thermal
->
clk
);
return
error
;
}
static
int
rockchip_thermal_remove
(
struct
platform_device
*
pdev
)
{
struct
rockchip_thermal_data
*
thermal
=
platform_get_drvdata
(
pdev
);
int
i
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
thermal
->
sensors
);
i
++
)
{
struct
rockchip_thermal_sensor
*
sensor
=
&
thermal
->
sensors
[
i
];
rockchip_thermal_toggle_sensor
(
sensor
,
false
);
thermal_zone_of_sensor_unregister
(
&
pdev
->
dev
,
sensor
->
tzd
);
}
thermal
->
chip
->
control
(
thermal
->
regs
,
false
);
clk_disable_unprepare
(
thermal
->
pclk
);
clk_disable_unprepare
(
thermal
->
clk
);
return
0
;
}
static
int
__maybe_unused
rockchip_thermal_suspend
(
struct
device
*
dev
)
{
struct
platform_device
*
pdev
=
to_platform_device
(
dev
);
struct
rockchip_thermal_data
*
thermal
=
platform_get_drvdata
(
pdev
);
int
i
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
thermal
->
sensors
);
i
++
)
rockchip_thermal_toggle_sensor
(
&
thermal
->
sensors
[
i
],
false
);
thermal
->
chip
->
control
(
thermal
->
regs
,
false
);
clk_disable
(
thermal
->
pclk
);
clk_disable
(
thermal
->
clk
);
return
0
;
}
static
int
__maybe_unused
rockchip_thermal_resume
(
struct
device
*
dev
)
{
struct
platform_device
*
pdev
=
to_platform_device
(
dev
);
struct
rockchip_thermal_data
*
thermal
=
platform_get_drvdata
(
pdev
);
int
i
;
int
error
;
error
=
clk_enable
(
thermal
->
clk
);
if
(
error
)
return
error
;
error
=
clk_enable
(
thermal
->
pclk
);
if
(
error
)
return
error
;
rockchip_thermal_reset_controller
(
thermal
->
reset
);
thermal
->
chip
->
initialize
(
thermal
->
regs
,
thermal
->
tshut_polarity
);
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
thermal
->
sensors
);
i
++
)
{
enum
sensor_id
id
=
thermal
->
sensors
[
i
].
id
;
thermal
->
chip
->
set_tshut_mode
(
id
,
thermal
->
regs
,
thermal
->
tshut_mode
);
thermal
->
chip
->
set_tshut_temp
(
id
,
thermal
->
regs
,
thermal
->
tshut_temp
);
}
thermal
->
chip
->
control
(
thermal
->
regs
,
true
);
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
thermal
->
sensors
);
i
++
)
rockchip_thermal_toggle_sensor
(
&
thermal
->
sensors
[
i
],
true
);
return
0
;
}
static
SIMPLE_DEV_PM_OPS
(
rockchip_thermal_pm_ops
,
rockchip_thermal_suspend
,
rockchip_thermal_resume
);
static
struct
platform_driver
rockchip_thermal_driver
=
{
.
driver
=
{
.
name
=
"rockchip-thermal"
,
.
owner
=
THIS_MODULE
,
.
pm
=
&
rockchip_thermal_pm_ops
,
.
of_match_table
=
of_rockchip_thermal_match
,
},
.
probe
=
rockchip_thermal_probe
,
.
remove
=
rockchip_thermal_remove
,
};
module_platform_driver
(
rockchip_thermal_driver
);
MODULE_DESCRIPTION
(
"ROCKCHIP THERMAL Driver"
);
MODULE_AUTHOR
(
"Rockchip, Inc."
);
MODULE_LICENSE
(
"GPL v2"
);
MODULE_ALIAS
(
"platform:rockchip-thermal"
);
drivers/thermal/samsung/exynos_thermal_common.h
View file @
2707dbd0
...
...
@@ -27,7 +27,6 @@
#define SENSOR_NAME_LEN 16
#define MAX_TRIP_COUNT 8
#define MAX_COOLING_DEVICE 4
#define MAX_TRIMINFO_CTRL_REG 2
#define ACTIVE_INTERVAL 500
#define IDLE_INTERVAL 10000
...
...
drivers/thermal/samsung/exynos_tmu.c
View file @
2707dbd0
...
...
@@ -33,7 +33,87 @@
#include "exynos_thermal_common.h"
#include "exynos_tmu.h"
#include "exynos_tmu_data.h"
/* Exynos generic registers */
#define EXYNOS_TMU_REG_TRIMINFO 0x0
#define EXYNOS_TMU_REG_CONTROL 0x20
#define EXYNOS_TMU_REG_STATUS 0x28
#define EXYNOS_TMU_REG_CURRENT_TEMP 0x40
#define EXYNOS_TMU_REG_INTEN 0x70
#define EXYNOS_TMU_REG_INTSTAT 0x74
#define EXYNOS_TMU_REG_INTCLEAR 0x78
#define EXYNOS_TMU_TEMP_MASK 0xff
#define EXYNOS_TMU_REF_VOLTAGE_SHIFT 24
#define EXYNOS_TMU_REF_VOLTAGE_MASK 0x1f
#define EXYNOS_TMU_BUF_SLOPE_SEL_MASK 0xf
#define EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT 8
#define EXYNOS_TMU_CORE_EN_SHIFT 0
/* Exynos3250 specific registers */
#define EXYNOS_TMU_TRIMINFO_CON1 0x10
/* Exynos4210 specific registers */
#define EXYNOS4210_TMU_REG_THRESHOLD_TEMP 0x44
#define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50
/* Exynos5250, Exynos4412, Exynos3250 specific registers */
#define EXYNOS_TMU_TRIMINFO_CON2 0x14
#define EXYNOS_THD_TEMP_RISE 0x50
#define EXYNOS_THD_TEMP_FALL 0x54
#define EXYNOS_EMUL_CON 0x80
#define EXYNOS_TRIMINFO_RELOAD_ENABLE 1
#define EXYNOS_TRIMINFO_25_SHIFT 0
#define EXYNOS_TRIMINFO_85_SHIFT 8
#define EXYNOS_TMU_TRIP_MODE_SHIFT 13
#define EXYNOS_TMU_TRIP_MODE_MASK 0x7
#define EXYNOS_TMU_THERM_TRIP_EN_SHIFT 12
#define EXYNOS_TMU_INTEN_RISE0_SHIFT 0
#define EXYNOS_TMU_INTEN_RISE1_SHIFT 4
#define EXYNOS_TMU_INTEN_RISE2_SHIFT 8
#define EXYNOS_TMU_INTEN_RISE3_SHIFT 12
#define EXYNOS_TMU_INTEN_FALL0_SHIFT 16
#define EXYNOS_EMUL_TIME 0x57F0
#define EXYNOS_EMUL_TIME_MASK 0xffff
#define EXYNOS_EMUL_TIME_SHIFT 16
#define EXYNOS_EMUL_DATA_SHIFT 8
#define EXYNOS_EMUL_DATA_MASK 0xFF
#define EXYNOS_EMUL_ENABLE 0x1
/* Exynos5260 specific */
#define EXYNOS5260_TMU_REG_INTEN 0xC0
#define EXYNOS5260_TMU_REG_INTSTAT 0xC4
#define EXYNOS5260_TMU_REG_INTCLEAR 0xC8
#define EXYNOS5260_EMUL_CON 0x100
/* Exynos4412 specific */
#define EXYNOS4412_MUX_ADDR_VALUE 6
#define EXYNOS4412_MUX_ADDR_SHIFT 20
/*exynos5440 specific registers*/
#define EXYNOS5440_TMU_S0_7_TRIM 0x000
#define EXYNOS5440_TMU_S0_7_CTRL 0x020
#define EXYNOS5440_TMU_S0_7_DEBUG 0x040
#define EXYNOS5440_TMU_S0_7_TEMP 0x0f0
#define EXYNOS5440_TMU_S0_7_TH0 0x110
#define EXYNOS5440_TMU_S0_7_TH1 0x130
#define EXYNOS5440_TMU_S0_7_TH2 0x150
#define EXYNOS5440_TMU_S0_7_IRQEN 0x210
#define EXYNOS5440_TMU_S0_7_IRQ 0x230
/* exynos5440 common registers */
#define EXYNOS5440_TMU_IRQ_STATUS 0x000
#define EXYNOS5440_TMU_PMIN 0x004
#define EXYNOS5440_TMU_INTEN_RISE0_SHIFT 0
#define EXYNOS5440_TMU_INTEN_RISE1_SHIFT 1
#define EXYNOS5440_TMU_INTEN_RISE2_SHIFT 2
#define EXYNOS5440_TMU_INTEN_RISE3_SHIFT 3
#define EXYNOS5440_TMU_INTEN_FALL0_SHIFT 4
#define EXYNOS5440_TMU_TH_RISE4_SHIFT 24
#define EXYNOS5440_EFUSE_SWAP_OFFSET 8
/**
* struct exynos_tmu_data : A structure to hold the private data of the TMU
...
...
@@ -52,6 +132,11 @@
* @temp_error2: fused value of the second point trim.
* @regulator: pointer to the TMU regulator structure.
* @reg_conf: pointer to structure to register with core thermal.
* @tmu_initialize: SoC specific TMU initialization method
* @tmu_control: SoC specific TMU control method
* @tmu_read: SoC specific TMU temperature read method
* @tmu_set_emulation: SoC specific TMU emulation setting method
* @tmu_clear_irqs: SoC specific TMU interrupts clearing method
*/
struct
exynos_tmu_data
{
int
id
;
...
...
@@ -66,6 +151,12 @@ struct exynos_tmu_data {
u8
temp_error1
,
temp_error2
;
struct
regulator
*
regulator
;
struct
thermal_sensor_conf
*
reg_conf
;
int
(
*
tmu_initialize
)(
struct
platform_device
*
pdev
);
void
(
*
tmu_control
)(
struct
platform_device
*
pdev
,
bool
on
);
int
(
*
tmu_read
)(
struct
exynos_tmu_data
*
data
);
void
(
*
tmu_set_emulation
)(
struct
exynos_tmu_data
*
data
,
unsigned
long
temp
);
void
(
*
tmu_clear_irqs
)(
struct
exynos_tmu_data
*
data
);
};
/*
...
...
@@ -122,252 +213,350 @@ static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code)
return
temp
;
}
static
void
exynos_tmu_clear_irqs
(
struct
exynos_tmu_data
*
data
)
static
void
sanitize_temp_error
(
struct
exynos_tmu_data
*
data
,
u32
trim_info
)
{
const
struct
exynos_tmu_registers
*
reg
=
data
->
pdata
->
registers
;
unsigned
int
val_irq
;
struct
exynos_tmu_platform_data
*
pdata
=
data
->
pdata
;
val_irq
=
readl
(
data
->
base
+
reg
->
tmu_intstat
);
/*
* Clear the interrupts. Please note that the documentation for
* Exynos3250, Exynos4412, Exynos5250 and Exynos5260 incorrectly
* states that INTCLEAR register has a different placing of bits
* responsible for FALL IRQs than INTSTAT register. Exynos5420
* and Exynos5440 documentation is correct (Exynos4210 doesn't
* support FALL IRQs at all).
*/
writel
(
val_irq
,
data
->
base
+
reg
->
tmu_intclear
);
data
->
temp_error1
=
trim_info
&
EXYNOS_TMU_TEMP_MASK
;
data
->
temp_error2
=
((
trim_info
>>
EXYNOS_TRIMINFO_85_SHIFT
)
&
EXYNOS_TMU_TEMP_MASK
);
if
(
!
data
->
temp_error1
||
(
pdata
->
min_efuse_value
>
data
->
temp_error1
)
||
(
data
->
temp_error1
>
pdata
->
max_efuse_value
))
data
->
temp_error1
=
pdata
->
efuse_value
&
EXYNOS_TMU_TEMP_MASK
;
if
(
!
data
->
temp_error2
)
data
->
temp_error2
=
(
pdata
->
efuse_value
>>
EXYNOS_TRIMINFO_85_SHIFT
)
&
EXYNOS_TMU_TEMP_MASK
;
}
static
u32
get_th_reg
(
struct
exynos_tmu_data
*
data
,
u32
threshold
,
bool
falling
)
{
struct
exynos_tmu_platform_data
*
pdata
=
data
->
pdata
;
int
i
;
for
(
i
=
0
;
i
<
pdata
->
non_hw_trigger_levels
;
i
++
)
{
u8
temp
=
pdata
->
trigger_levels
[
i
];
if
(
falling
)
temp
-=
pdata
->
threshold_falling
;
else
threshold
&=
~
(
0xff
<<
8
*
i
);
threshold
|=
temp_to_code
(
data
,
temp
)
<<
8
*
i
;
}
return
threshold
;
}
static
int
exynos_tmu_initialize
(
struct
platform_device
*
pdev
)
{
struct
exynos_tmu_data
*
data
=
platform_get_drvdata
(
pdev
);
struct
exynos_tmu_platform_data
*
pdata
=
data
->
pdata
;
const
struct
exynos_tmu_registers
*
reg
=
pdata
->
registers
;
unsigned
int
status
,
trim_info
=
0
,
con
,
ctrl
;
unsigned
int
rising_threshold
=
0
,
falling_threshold
=
0
;
int
ret
=
0
,
threshold_code
,
i
;
int
ret
;
mutex_lock
(
&
data
->
lock
);
clk_enable
(
data
->
clk
);
if
(
!
IS_ERR
(
data
->
clk_sec
))
clk_enable
(
data
->
clk_sec
);
ret
=
data
->
tmu_initialize
(
pdev
);
clk_disable
(
data
->
clk
);
mutex_unlock
(
&
data
->
lock
);
if
(
!
IS_ERR
(
data
->
clk_sec
))
clk_disable
(
data
->
clk_sec
);
return
ret
;
}
static
u32
get_con_reg
(
struct
exynos_tmu_data
*
data
,
u32
con
)
{
struct
exynos_tmu_platform_data
*
pdata
=
data
->
pdata
;
if
(
data
->
soc
==
SOC_ARCH_EXYNOS4412
||
data
->
soc
==
SOC_ARCH_EXYNOS3250
)
con
|=
(
EXYNOS4412_MUX_ADDR_VALUE
<<
EXYNOS4412_MUX_ADDR_SHIFT
);
con
&=
~
(
EXYNOS_TMU_REF_VOLTAGE_MASK
<<
EXYNOS_TMU_REF_VOLTAGE_SHIFT
);
con
|=
pdata
->
reference_voltage
<<
EXYNOS_TMU_REF_VOLTAGE_SHIFT
;
con
&=
~
(
EXYNOS_TMU_BUF_SLOPE_SEL_MASK
<<
EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT
);
con
|=
(
pdata
->
gain
<<
EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT
);
if
(
pdata
->
noise_cancel_mode
)
{
con
&=
~
(
EXYNOS_TMU_TRIP_MODE_MASK
<<
EXYNOS_TMU_TRIP_MODE_SHIFT
);
con
|=
(
pdata
->
noise_cancel_mode
<<
EXYNOS_TMU_TRIP_MODE_SHIFT
);
}
return
con
;
}
static
void
exynos_tmu_control
(
struct
platform_device
*
pdev
,
bool
on
)
{
struct
exynos_tmu_data
*
data
=
platform_get_drvdata
(
pdev
);
if
(
TMU_SUPPORTS
(
pdata
,
READY_STATUS
))
{
status
=
readb
(
data
->
base
+
reg
->
tmu_status
);
mutex_lock
(
&
data
->
lock
);
clk_enable
(
data
->
clk
);
data
->
tmu_control
(
pdev
,
on
);
clk_disable
(
data
->
clk
);
mutex_unlock
(
&
data
->
lock
);
}
static
int
exynos4210_tmu_initialize
(
struct
platform_device
*
pdev
)
{
struct
exynos_tmu_data
*
data
=
platform_get_drvdata
(
pdev
);
struct
exynos_tmu_platform_data
*
pdata
=
data
->
pdata
;
unsigned
int
status
;
int
ret
=
0
,
threshold_code
,
i
;
status
=
readb
(
data
->
base
+
EXYNOS_TMU_REG_STATUS
);
if
(
!
status
)
{
ret
=
-
EBUSY
;
goto
out
;
}
sanitize_temp_error
(
data
,
readl
(
data
->
base
+
EXYNOS_TMU_REG_TRIMINFO
));
/* Write temperature code for threshold */
threshold_code
=
temp_to_code
(
data
,
pdata
->
threshold
);
writeb
(
threshold_code
,
data
->
base
+
EXYNOS4210_TMU_REG_THRESHOLD_TEMP
);
for
(
i
=
0
;
i
<
pdata
->
non_hw_trigger_levels
;
i
++
)
writeb
(
pdata
->
trigger_levels
[
i
],
data
->
base
+
EXYNOS4210_TMU_REG_TRIG_LEVEL0
+
i
*
4
);
data
->
tmu_clear_irqs
(
data
);
out:
return
ret
;
}
static
int
exynos4412_tmu_initialize
(
struct
platform_device
*
pdev
)
{
struct
exynos_tmu_data
*
data
=
platform_get_drvdata
(
pdev
);
struct
exynos_tmu_platform_data
*
pdata
=
data
->
pdata
;
unsigned
int
status
,
trim_info
,
con
,
ctrl
,
rising_threshold
;
int
ret
=
0
,
threshold_code
,
i
;
status
=
readb
(
data
->
base
+
EXYNOS_TMU_REG_STATUS
);
if
(
!
status
)
{
ret
=
-
EBUSY
;
goto
out
;
}
if
(
TMU_SUPPORTS
(
pdata
,
TRIM_RELOAD
))
{
for
(
i
=
0
;
i
<
reg
->
triminfo_ctrl_count
;
i
++
)
{
if
(
pdata
->
triminfo_reload
[
i
])
{
ctrl
=
readl
(
data
->
base
+
reg
->
triminfo_ctrl
[
i
]);
ctrl
|=
pdata
->
triminfo_reload
[
i
];
writel
(
ctrl
,
data
->
base
+
reg
->
triminfo_ctrl
[
i
]);
if
(
data
->
soc
==
SOC_ARCH_EXYNOS3250
||
data
->
soc
==
SOC_ARCH_EXYNOS4412
||
data
->
soc
==
SOC_ARCH_EXYNOS5250
)
{
if
(
data
->
soc
==
SOC_ARCH_EXYNOS3250
)
{
ctrl
=
readl
(
data
->
base
+
EXYNOS_TMU_TRIMINFO_CON1
);
ctrl
|=
EXYNOS_TRIMINFO_RELOAD_ENABLE
;
writel
(
ctrl
,
data
->
base
+
EXYNOS_TMU_TRIMINFO_CON1
);
}
ctrl
=
readl
(
data
->
base
+
EXYNOS_TMU_TRIMINFO_CON2
);
ctrl
|=
EXYNOS_TRIMINFO_RELOAD_ENABLE
;
writel
(
ctrl
,
data
->
base
+
EXYNOS_TMU_TRIMINFO_CON2
);
}
/* On exynos5420 the triminfo register is in the shared space */
if
(
data
->
soc
==
SOC_ARCH_EXYNOS5420_TRIMINFO
)
trim_info
=
readl
(
data
->
base_second
+
EXYNOS_TMU_REG_TRIMINFO
);
else
trim_info
=
readl
(
data
->
base
+
EXYNOS_TMU_REG_TRIMINFO
);
sanitize_temp_error
(
data
,
trim_info
);
/* Write temperature code for rising and falling threshold */
rising_threshold
=
readl
(
data
->
base
+
EXYNOS_THD_TEMP_RISE
);
rising_threshold
=
get_th_reg
(
data
,
rising_threshold
,
false
);
writel
(
rising_threshold
,
data
->
base
+
EXYNOS_THD_TEMP_RISE
);
writel
(
get_th_reg
(
data
,
0
,
true
),
data
->
base
+
EXYNOS_THD_TEMP_FALL
);
data
->
tmu_clear_irqs
(
data
);
/* if last threshold limit is also present */
i
=
pdata
->
max_trigger_level
-
1
;
if
(
pdata
->
trigger_levels
[
i
]
&&
pdata
->
trigger_type
[
i
]
==
HW_TRIP
)
{
threshold_code
=
temp_to_code
(
data
,
pdata
->
trigger_levels
[
i
]);
/* 1-4 level to be assigned in th0 reg */
rising_threshold
&=
~
(
0xff
<<
8
*
i
);
rising_threshold
|=
threshold_code
<<
8
*
i
;
writel
(
rising_threshold
,
data
->
base
+
EXYNOS_THD_TEMP_RISE
);
con
=
readl
(
data
->
base
+
EXYNOS_TMU_REG_CONTROL
);
con
|=
(
1
<<
EXYNOS_TMU_THERM_TRIP_EN_SHIFT
);
writel
(
con
,
data
->
base
+
EXYNOS_TMU_REG_CONTROL
);
}
out:
return
ret
;
}
static
int
exynos5440_tmu_initialize
(
struct
platform_device
*
pdev
)
{
struct
exynos_tmu_data
*
data
=
platform_get_drvdata
(
pdev
);
struct
exynos_tmu_platform_data
*
pdata
=
data
->
pdata
;
unsigned
int
trim_info
=
0
,
con
,
rising_threshold
;
int
ret
=
0
,
threshold_code
,
i
;
/* Save trimming info in order to perform calibration */
if
(
data
->
soc
==
SOC_ARCH_EXYNOS5440
)
{
/*
* For exynos5440 soc triminfo value is swapped between TMU0 and
* TMU2, so the below logic is needed.
*/
switch
(
data
->
id
)
{
case
0
:
trim_info
=
readl
(
data
->
base
+
EXYNOS5440_EFUSE_SWAP_OFFSET
+
reg
->
triminfo_data
);
trim_info
=
readl
(
data
->
base
+
EXYNOS5440_EFUSE_SWAP_OFFSET
+
EXYNOS5440_TMU_S0_7_TRIM
);
break
;
case
1
:
trim_info
=
readl
(
data
->
base
+
reg
->
triminfo_data
);
trim_info
=
readl
(
data
->
base
+
EXYNOS5440_TMU_S0_7_TRIM
);
break
;
case
2
:
trim_info
=
readl
(
data
->
base
-
EXYNOS5440_EFUSE_SWAP_OFFSET
+
reg
->
triminfo_data
);
trim_info
=
readl
(
data
->
base
-
EXYNOS5440_EFUSE_SWAP_OFFSET
+
EXYNOS5440_TMU_S0_7_TRIM
);
}
}
else
{
/* On exynos5420 the triminfo register is in the shared space */
if
(
data
->
soc
==
SOC_ARCH_EXYNOS5420_TRIMINFO
)
trim_info
=
readl
(
data
->
base_second
+
reg
->
triminfo_data
);
else
trim_info
=
readl
(
data
->
base
+
reg
->
triminfo_data
);
}
data
->
temp_error1
=
trim_info
&
EXYNOS_TMU_TEMP_MASK
;
data
->
temp_error2
=
((
trim_info
>>
EXYNOS_TRIMINFO_85_SHIFT
)
&
EXYNOS_TMU_TEMP_MASK
);
sanitize_temp_error
(
data
,
trim_info
);
if
(
!
data
->
temp_error1
||
(
pdata
->
min_efuse_value
>
data
->
temp_error1
)
||
(
data
->
temp_error1
>
pdata
->
max_efuse_value
))
data
->
temp_error1
=
pdata
->
efuse_value
&
EXYNOS_TMU_TEMP_MASK
;
if
(
!
data
->
temp_error2
)
data
->
temp_error2
=
(
pdata
->
efuse_value
>>
EXYNOS_TRIMINFO_85_SHIFT
)
&
EXYNOS_TMU_TEMP_MASK
;
rising_threshold
=
readl
(
data
->
base
+
reg
->
threshold_th0
);
if
(
data
->
soc
==
SOC_ARCH_EXYNOS4210
)
{
/* Write temperature code for threshold */
threshold_code
=
temp_to_code
(
data
,
pdata
->
threshold
);
writeb
(
threshold_code
,
data
->
base
+
reg
->
threshold_temp
);
for
(
i
=
0
;
i
<
pdata
->
non_hw_trigger_levels
;
i
++
)
writeb
(
pdata
->
trigger_levels
[
i
],
data
->
base
+
reg
->
threshold_th0
+
i
*
sizeof
(
reg
->
threshold_th0
));
exynos_tmu_clear_irqs
(
data
);
}
else
{
/* Write temperature code for rising and falling threshold */
for
(
i
=
0
;
i
<
pdata
->
non_hw_trigger_levels
;
i
++
)
{
threshold_code
=
temp_to_code
(
data
,
pdata
->
trigger_levels
[
i
]);
rising_threshold
&=
~
(
0xff
<<
8
*
i
);
rising_threshold
|=
threshold_code
<<
8
*
i
;
if
(
pdata
->
threshold_falling
)
{
threshold_code
=
temp_to_code
(
data
,
pdata
->
trigger_levels
[
i
]
-
pdata
->
threshold_falling
);
falling_threshold
|=
threshold_code
<<
8
*
i
;
}
}
rising_threshold
=
readl
(
data
->
base
+
EXYNOS5440_TMU_S0_7_TH0
);
rising_threshold
=
get_th_reg
(
data
,
rising_threshold
,
false
);
writel
(
rising_threshold
,
data
->
base
+
EXYNOS5440_TMU_S0_7_TH0
);
writel
(
0
,
data
->
base
+
EXYNOS5440_TMU_S0_7_TH1
);
writel
(
rising_threshold
,
data
->
base
+
reg
->
threshold_th0
);
writel
(
falling_threshold
,
data
->
base
+
reg
->
threshold_th1
);
exynos_tmu_clear_irqs
(
data
);
data
->
tmu_clear_irqs
(
data
);
/* if last threshold limit is also present */
i
=
pdata
->
max_trigger_level
-
1
;
if
(
pdata
->
trigger_levels
[
i
]
&&
(
pdata
->
trigger_type
[
i
]
==
HW_TRIP
))
{
threshold_code
=
temp_to_code
(
data
,
pdata
->
trigger_levels
[
i
]);
if
(
i
==
EXYNOS_MAX_TRIGGER_PER_REG
-
1
)
{
/* 1-4 level to be assigned in th0 reg */
rising_threshold
&=
~
(
0xff
<<
8
*
i
);
rising_threshold
|=
threshold_code
<<
8
*
i
;
writel
(
rising_threshold
,
data
->
base
+
reg
->
threshold_th0
);
}
else
if
(
i
==
EXYNOS_MAX_TRIGGER_PER_REG
)
{
if
(
pdata
->
trigger_levels
[
i
]
&&
pdata
->
trigger_type
[
i
]
==
HW_TRIP
)
{
threshold_code
=
temp_to_code
(
data
,
pdata
->
trigger_levels
[
i
]);
/* 5th level to be assigned in th2 reg */
rising_threshold
=
threshold_code
<<
reg
->
threshold_th3_l0_shift
;
writel
(
rising_threshold
,
data
->
base
+
reg
->
threshold_th2
);
}
con
=
readl
(
data
->
base
+
reg
->
tmu_ctrl
);
con
|=
(
1
<<
reg
->
therm_trip_en_shift
);
writel
(
con
,
data
->
base
+
reg
->
tmu_ctrl
);
}
}
/*Clear the PMIN in the common TMU register*/
if
(
reg
->
tmu_pmin
&&
!
data
->
id
)
writel
(
0
,
data
->
base_second
+
reg
->
tmu_pmin
);
out:
clk_disable
(
data
->
clk
);
mutex_unlock
(
&
data
->
lock
);
if
(
!
IS_ERR
(
data
->
clk_sec
))
clk_disable
(
data
->
clk_sec
);
threshold_code
<<
EXYNOS5440_TMU_TH_RISE4_SHIFT
;
writel
(
rising_threshold
,
data
->
base
+
EXYNOS5440_TMU_S0_7_TH2
);
con
=
readl
(
data
->
base
+
EXYNOS5440_TMU_S0_7_CTRL
);
con
|=
(
1
<<
EXYNOS_TMU_THERM_TRIP_EN_SHIFT
);
writel
(
con
,
data
->
base
+
EXYNOS5440_TMU_S0_7_CTRL
);
}
/* Clear the PMIN in the common TMU register */
if
(
!
data
->
id
)
writel
(
0
,
data
->
base_second
+
EXYNOS5440_TMU_PMIN
);
return
ret
;
}
static
void
exynos_tmu_control
(
struct
platform_device
*
pdev
,
bool
on
)
static
void
exynos
4210
_tmu_control
(
struct
platform_device
*
pdev
,
bool
on
)
{
struct
exynos_tmu_data
*
data
=
platform_get_drvdata
(
pdev
);
struct
exynos_tmu_platform_data
*
pdata
=
data
->
pdata
;
const
struct
exynos_tmu_registers
*
reg
=
pdata
->
registers
;
unsigned
int
con
,
interrupt_en
;
mutex_lock
(
&
data
->
lock
);
clk_enable
(
data
->
clk
);
con
=
readl
(
data
->
base
+
reg
->
tmu_ctrl
);
if
(
pdata
->
test_mux
)
con
|=
(
pdata
->
test_mux
<<
reg
->
test_mux_addr_shift
);
con
=
get_con_reg
(
data
,
readl
(
data
->
base
+
EXYNOS_TMU_REG_CONTROL
));
con
&=
~
(
EXYNOS_TMU_REF_VOLTAGE_MASK
<<
EXYNOS_TMU_REF_VOLTAGE_SHIFT
);
con
|=
pdata
->
reference_voltage
<<
EXYNOS_TMU_REF_VOLTAGE_SHIFT
;
if
(
on
)
{
con
|=
(
1
<<
EXYNOS_TMU_CORE_EN_SHIFT
);
interrupt_en
=
pdata
->
trigger_enable
[
3
]
<<
EXYNOS_TMU_INTEN_RISE3_SHIFT
|
pdata
->
trigger_enable
[
2
]
<<
EXYNOS_TMU_INTEN_RISE2_SHIFT
|
pdata
->
trigger_enable
[
1
]
<<
EXYNOS_TMU_INTEN_RISE1_SHIFT
|
pdata
->
trigger_enable
[
0
]
<<
EXYNOS_TMU_INTEN_RISE0_SHIFT
;
if
(
data
->
soc
!=
SOC_ARCH_EXYNOS4210
)
interrupt_en
|=
interrupt_en
<<
EXYNOS_TMU_INTEN_FALL0_SHIFT
;
}
else
{
con
&=
~
(
1
<<
EXYNOS_TMU_CORE_EN_SHIFT
);
interrupt_en
=
0
;
/* Disable all interrupts */
}
writel
(
interrupt_en
,
data
->
base
+
EXYNOS_TMU_REG_INTEN
);
writel
(
con
,
data
->
base
+
EXYNOS_TMU_REG_CONTROL
);
}
con
&=
~
(
EXYNOS_TMU_BUF_SLOPE_SEL_MASK
<<
EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT
);
con
|=
(
pdata
->
gain
<<
EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT
);
static
void
exynos5440_tmu_control
(
struct
platform_device
*
pdev
,
bool
on
)
{
struct
exynos_tmu_data
*
data
=
platform_get_drvdata
(
pdev
);
struct
exynos_tmu_platform_data
*
pdata
=
data
->
pdata
;
unsigned
int
con
,
interrupt_en
;
if
(
pdata
->
noise_cancel_mode
)
{
con
&=
~
(
reg
->
therm_trip_mode_mask
<<
reg
->
therm_trip_mode_shift
);
con
|=
(
pdata
->
noise_cancel_mode
<<
reg
->
therm_trip_mode_shift
);
}
con
=
get_con_reg
(
data
,
readl
(
data
->
base
+
EXYNOS5440_TMU_S0_7_CTRL
));
if
(
on
)
{
con
|=
(
1
<<
EXYNOS_TMU_CORE_EN_SHIFT
);
interrupt_en
=
pdata
->
trigger_enable
[
3
]
<<
reg
->
inten_rise3_shift
|
pdata
->
trigger_enable
[
2
]
<<
reg
->
inten_rise2_shift
|
pdata
->
trigger_enable
[
1
]
<<
reg
->
inten_rise1_shift
|
pdata
->
trigger_enable
[
0
]
<<
reg
->
inten_rise0_shift
;
if
(
TMU_SUPPORTS
(
pdata
,
FALLING_TRIP
))
interrupt_en
|=
interrupt_en
<<
reg
->
inten_fall0_shift
;
pdata
->
trigger_enable
[
3
]
<<
EXYNOS5440_TMU_INTEN_RISE3_SHIFT
|
pdata
->
trigger_enable
[
2
]
<<
EXYNOS5440_TMU_INTEN_RISE2_SHIFT
|
pdata
->
trigger_enable
[
1
]
<<
EXYNOS5440_TMU_INTEN_RISE1_SHIFT
|
pdata
->
trigger_enable
[
0
]
<<
EXYNOS5440_TMU_INTEN_RISE0_SHIFT
;
interrupt_en
|=
interrupt_en
<<
EXYNOS5440_TMU_INTEN_FALL0_SHIFT
;
}
else
{
con
&=
~
(
1
<<
EXYNOS_TMU_CORE_EN_SHIFT
);
interrupt_en
=
0
;
/* Disable all interrupts */
}
writel
(
interrupt_en
,
data
->
base
+
reg
->
tmu_inten
);
writel
(
con
,
data
->
base
+
reg
->
tmu_ctrl
);
clk_disable
(
data
->
clk
);
mutex_unlock
(
&
data
->
lock
);
writel
(
interrupt_en
,
data
->
base
+
EXYNOS5440_TMU_S0_7_IRQEN
);
writel
(
con
,
data
->
base
+
EXYNOS5440_TMU_S0_7_CTRL
);
}
static
int
exynos_tmu_read
(
struct
exynos_tmu_data
*
data
)
{
struct
exynos_tmu_platform_data
*
pdata
=
data
->
pdata
;
const
struct
exynos_tmu_registers
*
reg
=
pdata
->
registers
;
u8
temp_code
;
int
temp
;
int
ret
;
mutex_lock
(
&
data
->
lock
);
clk_enable
(
data
->
clk
);
ret
=
data
->
tmu_read
(
data
);
if
(
ret
>=
0
)
ret
=
code_to_temp
(
data
,
ret
);
clk_disable
(
data
->
clk
);
mutex_unlock
(
&
data
->
lock
);
temp_code
=
readb
(
data
->
base
+
reg
->
tmu_cur_temp
);
return
ret
;
}
if
(
data
->
soc
==
SOC_ARCH_EXYNOS4210
)
/* temp_code should range between 75 and 175 */
if
(
temp_code
<
75
||
temp_code
>
175
)
{
temp
=
-
ENODATA
;
goto
out
;
#ifdef CONFIG_THERMAL_EMULATION
static
u32
get_emul_con_reg
(
struct
exynos_tmu_data
*
data
,
unsigned
int
val
,
unsigned
long
temp
)
{
if
(
temp
)
{
temp
/=
MCELSIUS
;
if
(
data
->
soc
!=
SOC_ARCH_EXYNOS5440
)
{
val
&=
~
(
EXYNOS_EMUL_TIME_MASK
<<
EXYNOS_EMUL_TIME_SHIFT
);
val
|=
(
EXYNOS_EMUL_TIME
<<
EXYNOS_EMUL_TIME_SHIFT
);
}
val
&=
~
(
EXYNOS_EMUL_DATA_MASK
<<
EXYNOS_EMUL_DATA_SHIFT
);
val
|=
(
temp_to_code
(
data
,
temp
)
<<
EXYNOS_EMUL_DATA_SHIFT
)
|
EXYNOS_EMUL_ENABLE
;
}
else
{
val
&=
~
EXYNOS_EMUL_ENABLE
;
}
temp
=
code_to_temp
(
data
,
temp_code
);
out:
clk_disable
(
data
->
clk
);
mutex_unlock
(
&
data
->
lock
);
return
val
;
}
return
temp
;
static
void
exynos4412_tmu_set_emulation
(
struct
exynos_tmu_data
*
data
,
unsigned
long
temp
)
{
unsigned
int
val
;
u32
emul_con
;
if
(
data
->
soc
==
SOC_ARCH_EXYNOS5260
)
emul_con
=
EXYNOS5260_EMUL_CON
;
else
emul_con
=
EXYNOS_EMUL_CON
;
val
=
readl
(
data
->
base
+
emul_con
);
val
=
get_emul_con_reg
(
data
,
val
,
temp
);
writel
(
val
,
data
->
base
+
emul_con
);
}
static
void
exynos5440_tmu_set_emulation
(
struct
exynos_tmu_data
*
data
,
unsigned
long
temp
)
{
unsigned
int
val
;
val
=
readl
(
data
->
base
+
EXYNOS5440_TMU_S0_7_DEBUG
);
val
=
get_emul_con_reg
(
data
,
val
,
temp
);
writel
(
val
,
data
->
base
+
EXYNOS5440_TMU_S0_7_DEBUG
);
}
#ifdef CONFIG_THERMAL_EMULATION
static
int
exynos_tmu_set_emulation
(
void
*
drv_data
,
unsigned
long
temp
)
{
struct
exynos_tmu_data
*
data
=
drv_data
;
struct
exynos_tmu_platform_data
*
pdata
=
data
->
pdata
;
const
struct
exynos_tmu_registers
*
reg
=
pdata
->
registers
;
unsigned
int
val
;
int
ret
=
-
EINVAL
;
if
(
!
TMU_SUPPORTS
(
pdata
,
EMULATION
)
)
if
(
data
->
soc
==
SOC_ARCH_EXYNOS4210
)
goto
out
;
if
(
temp
&&
temp
<
MCELSIUS
)
...
...
@@ -375,25 +564,7 @@ static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp)
mutex_lock
(
&
data
->
lock
);
clk_enable
(
data
->
clk
);
val
=
readl
(
data
->
base
+
reg
->
emul_con
);
if
(
temp
)
{
temp
/=
MCELSIUS
;
if
(
TMU_SUPPORTS
(
pdata
,
EMUL_TIME
))
{
val
&=
~
(
EXYNOS_EMUL_TIME_MASK
<<
reg
->
emul_time_shift
);
val
|=
(
EXYNOS_EMUL_TIME
<<
reg
->
emul_time_shift
);
}
val
&=
~
(
EXYNOS_EMUL_DATA_MASK
<<
reg
->
emul_temp_shift
);
val
|=
(
temp_to_code
(
data
,
temp
)
<<
reg
->
emul_temp_shift
)
|
EXYNOS_EMUL_ENABLE
;
}
else
{
val
&=
~
EXYNOS_EMUL_ENABLE
;
}
writel
(
val
,
data
->
base
+
reg
->
emul_con
);
data
->
tmu_set_emulation
(
data
,
temp
);
clk_disable
(
data
->
clk
);
mutex_unlock
(
&
data
->
lock
);
return
0
;
...
...
@@ -401,23 +572,41 @@ static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp)
return
ret
;
}
#else
#define exynos4412_tmu_set_emulation NULL
#define exynos5440_tmu_set_emulation NULL
static
int
exynos_tmu_set_emulation
(
void
*
drv_data
,
unsigned
long
temp
)
{
return
-
EINVAL
;
}
#endif
/*CONFIG_THERMAL_EMULATION*/
static
int
exynos4210_tmu_read
(
struct
exynos_tmu_data
*
data
)
{
int
ret
=
readb
(
data
->
base
+
EXYNOS_TMU_REG_CURRENT_TEMP
);
/* "temp_code" should range between 75 and 175 */
return
(
ret
<
75
||
ret
>
175
)
?
-
ENODATA
:
ret
;
}
static
int
exynos4412_tmu_read
(
struct
exynos_tmu_data
*
data
)
{
return
readb
(
data
->
base
+
EXYNOS_TMU_REG_CURRENT_TEMP
);
}
static
int
exynos5440_tmu_read
(
struct
exynos_tmu_data
*
data
)
{
return
readb
(
data
->
base
+
EXYNOS5440_TMU_S0_7_TEMP
);
}
static
void
exynos_tmu_work
(
struct
work_struct
*
work
)
{
struct
exynos_tmu_data
*
data
=
container_of
(
work
,
struct
exynos_tmu_data
,
irq_work
);
struct
exynos_tmu_platform_data
*
pdata
=
data
->
pdata
;
const
struct
exynos_tmu_registers
*
reg
=
pdata
->
registers
;
unsigned
int
val_type
;
if
(
!
IS_ERR
(
data
->
clk_sec
))
clk_enable
(
data
->
clk_sec
);
/* Find which sensor generated this interrupt */
if
(
reg
->
tmu_irqstatus
)
{
val_type
=
readl
(
data
->
base_second
+
reg
->
tmu_irqstatus
);
if
(
data
->
soc
==
SOC_ARCH_EXYNOS5440
)
{
val_type
=
readl
(
data
->
base_second
+
EXYNOS5440_TMU_IRQ_STATUS
);
if
(
!
((
val_type
>>
data
->
id
)
&
0x1
))
goto
out
;
}
...
...
@@ -429,7 +618,7 @@ static void exynos_tmu_work(struct work_struct *work)
clk_enable
(
data
->
clk
);
/* TODO: take action based on particular interrupt */
exynos_
tmu_clear_irqs
(
data
);
data
->
tmu_clear_irqs
(
data
);
clk_disable
(
data
->
clk
);
mutex_unlock
(
&
data
->
lock
);
...
...
@@ -437,6 +626,40 @@ static void exynos_tmu_work(struct work_struct *work)
enable_irq
(
data
->
irq
);
}
static
void
exynos4210_tmu_clear_irqs
(
struct
exynos_tmu_data
*
data
)
{
unsigned
int
val_irq
;
u32
tmu_intstat
,
tmu_intclear
;
if
(
data
->
soc
==
SOC_ARCH_EXYNOS5260
)
{
tmu_intstat
=
EXYNOS5260_TMU_REG_INTSTAT
;
tmu_intclear
=
EXYNOS5260_TMU_REG_INTCLEAR
;
}
else
{
tmu_intstat
=
EXYNOS_TMU_REG_INTSTAT
;
tmu_intclear
=
EXYNOS_TMU_REG_INTCLEAR
;
}
val_irq
=
readl
(
data
->
base
+
tmu_intstat
);
/*
* Clear the interrupts. Please note that the documentation for
* Exynos3250, Exynos4412, Exynos5250 and Exynos5260 incorrectly
* states that INTCLEAR register has a different placing of bits
* responsible for FALL IRQs than INTSTAT register. Exynos5420
* and Exynos5440 documentation is correct (Exynos4210 doesn't
* support FALL IRQs at all).
*/
writel
(
val_irq
,
data
->
base
+
tmu_intclear
);
}
static
void
exynos5440_tmu_clear_irqs
(
struct
exynos_tmu_data
*
data
)
{
unsigned
int
val_irq
;
val_irq
=
readl
(
data
->
base
+
EXYNOS5440_TMU_S0_7_IRQ
);
/* clear the interrupts */
writel
(
val_irq
,
data
->
base
+
EXYNOS5440_TMU_S0_7_IRQ
);
}
static
irqreturn_t
exynos_tmu_irq
(
int
irq
,
void
*
id
)
{
struct
exynos_tmu_data
*
data
=
id
;
...
...
@@ -450,35 +673,35 @@ static irqreturn_t exynos_tmu_irq(int irq, void *id)
static
const
struct
of_device_id
exynos_tmu_match
[]
=
{
{
.
compatible
=
"samsung,exynos3250-tmu"
,
.
data
=
(
void
*
)
EXYNOS3250_TMU_DRV_DATA
,
.
data
=
&
exynos3250_default_tmu_data
,
},
{
.
compatible
=
"samsung,exynos4210-tmu"
,
.
data
=
(
void
*
)
EXYNOS4210_TMU_DRV_DATA
,
.
data
=
&
exynos4210_default_tmu_data
,
},
{
.
compatible
=
"samsung,exynos4412-tmu"
,
.
data
=
(
void
*
)
EXYNOS4412_TMU_DRV_DATA
,
.
data
=
&
exynos4412_default_tmu_data
,
},
{
.
compatible
=
"samsung,exynos5250-tmu"
,
.
data
=
(
void
*
)
EXYNOS5250_TMU_DRV_DATA
,
.
data
=
&
exynos5250_default_tmu_data
,
},
{
.
compatible
=
"samsung,exynos5260-tmu"
,
.
data
=
(
void
*
)
EXYNOS5260_TMU_DRV_DATA
,
.
data
=
&
exynos5260_default_tmu_data
,
},
{
.
compatible
=
"samsung,exynos5420-tmu"
,
.
data
=
(
void
*
)
EXYNOS5420_TMU_DRV_DATA
,
.
data
=
&
exynos5420_default_tmu_data
,
},
{
.
compatible
=
"samsung,exynos5420-tmu-ext-triminfo"
,
.
data
=
(
void
*
)
EXYNOS5420_TMU_DRV_DATA
,
.
data
=
&
exynos5420_default_tmu_data
,
},
{
.
compatible
=
"samsung,exynos5440-tmu"
,
.
data
=
(
void
*
)
EXYNOS5440_TMU_DRV_DATA
,
.
data
=
&
exynos5440_default_tmu_data
,
},
{},
};
...
...
@@ -553,12 +776,47 @@ static int exynos_map_dt_data(struct platform_device *pdev)
dev_err
(
&
pdev
->
dev
,
"No platform init data supplied.
\n
"
);
return
-
ENODEV
;
}
data
->
pdata
=
pdata
;
data
->
soc
=
pdata
->
type
;
switch
(
data
->
soc
)
{
case
SOC_ARCH_EXYNOS4210
:
data
->
tmu_initialize
=
exynos4210_tmu_initialize
;
data
->
tmu_control
=
exynos4210_tmu_control
;
data
->
tmu_read
=
exynos4210_tmu_read
;
data
->
tmu_clear_irqs
=
exynos4210_tmu_clear_irqs
;
break
;
case
SOC_ARCH_EXYNOS3250
:
case
SOC_ARCH_EXYNOS4412
:
case
SOC_ARCH_EXYNOS5250
:
case
SOC_ARCH_EXYNOS5260
:
case
SOC_ARCH_EXYNOS5420
:
case
SOC_ARCH_EXYNOS5420_TRIMINFO
:
data
->
tmu_initialize
=
exynos4412_tmu_initialize
;
data
->
tmu_control
=
exynos4210_tmu_control
;
data
->
tmu_read
=
exynos4412_tmu_read
;
data
->
tmu_set_emulation
=
exynos4412_tmu_set_emulation
;
data
->
tmu_clear_irqs
=
exynos4210_tmu_clear_irqs
;
break
;
case
SOC_ARCH_EXYNOS5440
:
data
->
tmu_initialize
=
exynos5440_tmu_initialize
;
data
->
tmu_control
=
exynos5440_tmu_control
;
data
->
tmu_read
=
exynos5440_tmu_read
;
data
->
tmu_set_emulation
=
exynos5440_tmu_set_emulation
;
data
->
tmu_clear_irqs
=
exynos5440_tmu_clear_irqs
;
break
;
default:
dev_err
(
&
pdev
->
dev
,
"Platform not supported
\n
"
);
return
-
EINVAL
;
}
/*
* Check if the TMU shares some registers and then try to map the
* memory of common registers.
*/
if
(
!
TMU_SUPPORTS
(
pdata
,
ADDRESS_MULTIPLE
))
if
(
data
->
soc
!=
SOC_ARCH_EXYNOS5420_TRIMINFO
&&
data
->
soc
!=
SOC_ARCH_EXYNOS5440
)
return
0
;
if
(
of_address_to_resource
(
pdev
->
dev
.
of_node
,
1
,
&
res
))
{
...
...
@@ -625,20 +883,6 @@ static int exynos_tmu_probe(struct platform_device *pdev)
goto
err_clk_sec
;
}
if
(
pdata
->
type
==
SOC_ARCH_EXYNOS3250
||
pdata
->
type
==
SOC_ARCH_EXYNOS4210
||
pdata
->
type
==
SOC_ARCH_EXYNOS4412
||
pdata
->
type
==
SOC_ARCH_EXYNOS5250
||
pdata
->
type
==
SOC_ARCH_EXYNOS5260
||
pdata
->
type
==
SOC_ARCH_EXYNOS5420_TRIMINFO
||
pdata
->
type
==
SOC_ARCH_EXYNOS5440
)
data
->
soc
=
pdata
->
type
;
else
{
ret
=
-
EINVAL
;
dev_err
(
&
pdev
->
dev
,
"Platform not supported
\n
"
);
goto
err_clk
;
}
ret
=
exynos_tmu_initialize
(
pdev
);
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"Failed to initialize TMU
\n
"
);
...
...
drivers/thermal/samsung/exynos_tmu.h
View file @
2707dbd0
...
...
@@ -40,114 +40,11 @@ enum soc_type {
SOC_ARCH_EXYNOS4412
,
SOC_ARCH_EXYNOS5250
,
SOC_ARCH_EXYNOS5260
,
SOC_ARCH_EXYNOS5420
,
SOC_ARCH_EXYNOS5420_TRIMINFO
,
SOC_ARCH_EXYNOS5440
,
};
/**
* EXYNOS TMU supported features.
* TMU_SUPPORT_EMULATION - This features is used to set user defined
* temperature to the TMU controller.
* TMU_SUPPORT_MULTI_INST - This features denotes that the soc
* has many instances of TMU.
* TMU_SUPPORT_TRIM_RELOAD - This features shows that trimming can
* be reloaded.
* TMU_SUPPORT_FALLING_TRIP - This features shows that interrupt can
* be registered for falling trips also.
* TMU_SUPPORT_READY_STATUS - This feature tells that the TMU current
* state(active/idle) can be checked.
* TMU_SUPPORT_EMUL_TIME - This features allows to set next temp emulation
* sample time.
* TMU_SUPPORT_ADDRESS_MULTIPLE - This feature tells that the different TMU
* sensors shares some common registers.
* TMU_SUPPORT - macro to compare the above features with the supplied.
*/
#define TMU_SUPPORT_EMULATION BIT(0)
#define TMU_SUPPORT_MULTI_INST BIT(1)
#define TMU_SUPPORT_TRIM_RELOAD BIT(2)
#define TMU_SUPPORT_FALLING_TRIP BIT(3)
#define TMU_SUPPORT_READY_STATUS BIT(4)
#define TMU_SUPPORT_EMUL_TIME BIT(5)
#define TMU_SUPPORT_ADDRESS_MULTIPLE BIT(6)
#define TMU_SUPPORTS(a, b) (a->features & TMU_SUPPORT_ ## b)
/**
* struct exynos_tmu_register - register descriptors to access registers and
* bitfields. The register validity, offsets and bitfield values may vary
* slightly across different exynos SOC's.
* @triminfo_data: register containing 2 pont trimming data
* @triminfo_ctrl: trim info controller register.
* @triminfo_ctrl_count: the number of trim info controller register.
* @tmu_ctrl: TMU main controller register.
* @test_mux_addr_shift: shift bits of test mux address.
* @therm_trip_mode_shift: shift bits of tripping mode in tmu_ctrl register.
* @therm_trip_mode_mask: mask bits of tripping mode in tmu_ctrl register.
* @therm_trip_en_shift: shift bits of tripping enable in tmu_ctrl register.
* @tmu_status: register drescribing the TMU status.
* @tmu_cur_temp: register containing the current temperature of the TMU.
* @threshold_temp: register containing the base threshold level.
* @threshold_th0: Register containing first set of rising levels.
* @threshold_th1: Register containing second set of rising levels.
* @threshold_th2: Register containing third set of rising levels.
* @threshold_th3_l0_shift: shift bits of level0 threshold temperature.
* @tmu_inten: register containing the different threshold interrupt
enable bits.
* @inten_rise0_shift: shift bits of rising 0 interrupt bits.
* @inten_rise1_shift: shift bits of rising 1 interrupt bits.
* @inten_rise2_shift: shift bits of rising 2 interrupt bits.
* @inten_rise3_shift: shift bits of rising 3 interrupt bits.
* @inten_fall0_shift: shift bits of falling 0 interrupt bits.
* @tmu_intstat: Register containing the interrupt status values.
* @tmu_intclear: Register for clearing the raised interrupt status.
* @emul_con: TMU emulation controller register.
* @emul_temp_shift: shift bits of emulation temperature.
* @emul_time_shift: shift bits of emulation time.
* @tmu_irqstatus: register to find which TMU generated interrupts.
* @tmu_pmin: register to get/set the Pmin value.
*/
struct
exynos_tmu_registers
{
u32
triminfo_data
;
u32
triminfo_ctrl
[
MAX_TRIMINFO_CTRL_REG
];
u32
triminfo_ctrl_count
;
u32
tmu_ctrl
;
u32
test_mux_addr_shift
;
u32
therm_trip_mode_shift
;
u32
therm_trip_mode_mask
;
u32
therm_trip_en_shift
;
u32
tmu_status
;
u32
tmu_cur_temp
;
u32
threshold_temp
;
u32
threshold_th0
;
u32
threshold_th1
;
u32
threshold_th2
;
u32
threshold_th3_l0_shift
;
u32
tmu_inten
;
u32
inten_rise0_shift
;
u32
inten_rise1_shift
;
u32
inten_rise2_shift
;
u32
inten_rise3_shift
;
u32
inten_fall0_shift
;
u32
tmu_intstat
;
u32
tmu_intclear
;
u32
emul_con
;
u32
emul_temp_shift
;
u32
emul_time_shift
;
u32
tmu_irqstatus
;
u32
tmu_pmin
;
};
/**
* struct exynos_tmu_platform_data
* @threshold: basic temperature for generating interrupt
...
...
@@ -192,16 +89,10 @@ struct exynos_tmu_registers {
* @first_point_trim: temp value of the first point trimming
* @second_point_trim: temp value of the second point trimming
* @default_temp_offset: default temperature offset in case of no trimming
* @test_mux; information if SoC supports test MUX
* @triminfo_reload: reload value to read TRIMINFO register
* @cal_type: calibration type for temperature
* @freq_clip_table: Table representing frequency reduction percentage.
* @freq_tab_count: Count of the above table as frequency reduction may
* applicable to only some of the trigger levels.
* @registers: Pointer to structure containing all the TMU controller registers
* and bitfields shifts and masks.
* @features: a bitfield value indicating the features supported in SOC like
* emulation, multi instance etc
*
* This structure is required for configuration of exynos_tmu driver.
*/
...
...
@@ -223,15 +114,11 @@ struct exynos_tmu_platform_data {
u8
first_point_trim
;
u8
second_point_trim
;
u8
default_temp_offset
;
u8
test_mux
;
u8
triminfo_reload
[
MAX_TRIMINFO_CTRL_REG
];
enum
calibration_type
cal_type
;
enum
soc_type
type
;
struct
freq_clip_table
freq_tab
[
4
];
unsigned
int
freq_tab_count
;
const
struct
exynos_tmu_registers
*
registers
;
unsigned
int
features
;
};
/**
...
...
@@ -246,4 +133,12 @@ struct exynos_tmu_init_data {
struct
exynos_tmu_platform_data
tmu_data
[];
};
extern
struct
exynos_tmu_init_data
const
exynos3250_default_tmu_data
;
extern
struct
exynos_tmu_init_data
const
exynos4210_default_tmu_data
;
extern
struct
exynos_tmu_init_data
const
exynos4412_default_tmu_data
;
extern
struct
exynos_tmu_init_data
const
exynos5250_default_tmu_data
;
extern
struct
exynos_tmu_init_data
const
exynos5260_default_tmu_data
;
extern
struct
exynos_tmu_init_data
const
exynos5420_default_tmu_data
;
extern
struct
exynos_tmu_init_data
const
exynos5440_default_tmu_data
;
#endif
/* _EXYNOS_TMU_H */
drivers/thermal/samsung/exynos_tmu_data.c
View file @
2707dbd0
...
...
@@ -22,24 +22,6 @@
#include "exynos_thermal_common.h"
#include "exynos_tmu.h"
#include "exynos_tmu_data.h"
#if defined(CONFIG_CPU_EXYNOS4210)
static
const
struct
exynos_tmu_registers
exynos4210_tmu_registers
=
{
.
triminfo_data
=
EXYNOS_TMU_REG_TRIMINFO
,
.
tmu_ctrl
=
EXYNOS_TMU_REG_CONTROL
,
.
tmu_status
=
EXYNOS_TMU_REG_STATUS
,
.
tmu_cur_temp
=
EXYNOS_TMU_REG_CURRENT_TEMP
,
.
threshold_temp
=
EXYNOS4210_TMU_REG_THRESHOLD_TEMP
,
.
threshold_th0
=
EXYNOS4210_TMU_REG_TRIG_LEVEL0
,
.
tmu_inten
=
EXYNOS_TMU_REG_INTEN
,
.
inten_rise0_shift
=
EXYNOS_TMU_INTEN_RISE0_SHIFT
,
.
inten_rise1_shift
=
EXYNOS_TMU_INTEN_RISE1_SHIFT
,
.
inten_rise2_shift
=
EXYNOS_TMU_INTEN_RISE2_SHIFT
,
.
inten_rise3_shift
=
EXYNOS_TMU_INTEN_RISE3_SHIFT
,
.
tmu_intstat
=
EXYNOS_TMU_REG_INTSTAT
,
.
tmu_intclear
=
EXYNOS_TMU_REG_INTCLEAR
,
};
struct
exynos_tmu_init_data
const
exynos4210_default_tmu_data
=
{
.
tmu_data
=
{
...
...
@@ -75,40 +57,10 @@ struct exynos_tmu_init_data const exynos4210_default_tmu_data = {
},
.
freq_tab_count
=
2
,
.
type
=
SOC_ARCH_EXYNOS4210
,
.
registers
=
&
exynos4210_tmu_registers
,
.
features
=
TMU_SUPPORT_READY_STATUS
,
},
},
.
tmu_count
=
1
,
};
#endif
#if defined(CONFIG_SOC_EXYNOS3250)
static
const
struct
exynos_tmu_registers
exynos3250_tmu_registers
=
{
.
triminfo_data
=
EXYNOS_TMU_REG_TRIMINFO
,
.
triminfo_ctrl
[
0
]
=
EXYNOS_TMU_TRIMINFO_CON1
,
.
triminfo_ctrl
[
1
]
=
EXYNOS_TMU_TRIMINFO_CON2
,
.
triminfo_ctrl_count
=
2
,
.
tmu_ctrl
=
EXYNOS_TMU_REG_CONTROL
,
.
test_mux_addr_shift
=
EXYNOS4412_MUX_ADDR_SHIFT
,
.
therm_trip_mode_shift
=
EXYNOS_TMU_TRIP_MODE_SHIFT
,
.
therm_trip_mode_mask
=
EXYNOS_TMU_TRIP_MODE_MASK
,
.
therm_trip_en_shift
=
EXYNOS_TMU_THERM_TRIP_EN_SHIFT
,
.
tmu_status
=
EXYNOS_TMU_REG_STATUS
,
.
tmu_cur_temp
=
EXYNOS_TMU_REG_CURRENT_TEMP
,
.
threshold_th0
=
EXYNOS_THD_TEMP_RISE
,
.
threshold_th1
=
EXYNOS_THD_TEMP_FALL
,
.
tmu_inten
=
EXYNOS_TMU_REG_INTEN
,
.
inten_rise0_shift
=
EXYNOS_TMU_INTEN_RISE0_SHIFT
,
.
inten_rise1_shift
=
EXYNOS_TMU_INTEN_RISE1_SHIFT
,
.
inten_rise2_shift
=
EXYNOS_TMU_INTEN_RISE2_SHIFT
,
.
inten_fall0_shift
=
EXYNOS_TMU_INTEN_FALL0_SHIFT
,
.
tmu_intstat
=
EXYNOS_TMU_REG_INTSTAT
,
.
tmu_intclear
=
EXYNOS_TMU_REG_INTCLEAR
,
.
emul_con
=
EXYNOS_EMUL_CON
,
.
emul_temp_shift
=
EXYNOS_EMUL_DATA_SHIFT
,
.
emul_time_shift
=
EXYNOS_EMUL_TIME_SHIFT
,
};
#define EXYNOS3250_TMU_DATA \
.threshold_falling = 10, \
...
...
@@ -144,54 +96,17 @@ static const struct exynos_tmu_registers exynos3250_tmu_registers = {
.freq_clip_max = 400 * 1000, \
.temp_level = 95, \
}, \
.freq_tab_count = 2, \
.triminfo_reload[0] = EXYNOS_TRIMINFO_RELOAD_ENABLE, \
.triminfo_reload[1] = EXYNOS_TRIMINFO_RELOAD_ENABLE, \
.registers = &exynos3250_tmu_registers, \
.features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_TRIM_RELOAD | \
TMU_SUPPORT_FALLING_TRIP | TMU_SUPPORT_READY_STATUS | \
TMU_SUPPORT_EMUL_TIME)
#endif
.freq_tab_count = 2
#if defined(CONFIG_SOC_EXYNOS3250)
struct
exynos_tmu_init_data
const
exynos3250_default_tmu_data
=
{
.
tmu_data
=
{
{
EXYNOS3250_TMU_DATA
,
.
type
=
SOC_ARCH_EXYNOS3250
,
.
test_mux
=
EXYNOS4412_MUX_ADDR_VALUE
,
},
},
.
tmu_count
=
1
,
};
#endif
#if defined(CONFIG_SOC_EXYNOS4412) || defined(CONFIG_SOC_EXYNOS5250)
static
const
struct
exynos_tmu_registers
exynos4412_tmu_registers
=
{
.
triminfo_data
=
EXYNOS_TMU_REG_TRIMINFO
,
.
triminfo_ctrl
[
0
]
=
EXYNOS_TMU_TRIMINFO_CON2
,
.
triminfo_ctrl_count
=
1
,
.
tmu_ctrl
=
EXYNOS_TMU_REG_CONTROL
,
.
test_mux_addr_shift
=
EXYNOS4412_MUX_ADDR_SHIFT
,
.
therm_trip_mode_shift
=
EXYNOS_TMU_TRIP_MODE_SHIFT
,
.
therm_trip_mode_mask
=
EXYNOS_TMU_TRIP_MODE_MASK
,
.
therm_trip_en_shift
=
EXYNOS_TMU_THERM_TRIP_EN_SHIFT
,
.
tmu_status
=
EXYNOS_TMU_REG_STATUS
,
.
tmu_cur_temp
=
EXYNOS_TMU_REG_CURRENT_TEMP
,
.
threshold_th0
=
EXYNOS_THD_TEMP_RISE
,
.
threshold_th1
=
EXYNOS_THD_TEMP_FALL
,
.
tmu_inten
=
EXYNOS_TMU_REG_INTEN
,
.
inten_rise0_shift
=
EXYNOS_TMU_INTEN_RISE0_SHIFT
,
.
inten_rise1_shift
=
EXYNOS_TMU_INTEN_RISE1_SHIFT
,
.
inten_rise2_shift
=
EXYNOS_TMU_INTEN_RISE2_SHIFT
,
.
inten_rise3_shift
=
EXYNOS_TMU_INTEN_RISE3_SHIFT
,
.
inten_fall0_shift
=
EXYNOS_TMU_INTEN_FALL0_SHIFT
,
.
tmu_intstat
=
EXYNOS_TMU_REG_INTSTAT
,
.
tmu_intclear
=
EXYNOS_TMU_REG_INTCLEAR
,
.
emul_con
=
EXYNOS_EMUL_CON
,
.
emul_temp_shift
=
EXYNOS_EMUL_DATA_SHIFT
,
.
emul_time_shift
=
EXYNOS_EMUL_TIME_SHIFT
,
};
#define EXYNOS4412_TMU_DATA \
.threshold_falling = 10, \
...
...
@@ -227,28 +142,18 @@ static const struct exynos_tmu_registers exynos4412_tmu_registers = {
.freq_clip_max = 400 * 1000, \
.temp_level = 95, \
}, \
.freq_tab_count = 2, \
.triminfo_reload[0] = EXYNOS_TRIMINFO_RELOAD_ENABLE, \
.registers = &exynos4412_tmu_registers, \
.features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_TRIM_RELOAD | \
TMU_SUPPORT_FALLING_TRIP | TMU_SUPPORT_READY_STATUS | \
TMU_SUPPORT_EMUL_TIME)
#endif
.freq_tab_count = 2
#if defined(CONFIG_SOC_EXYNOS4412)
struct
exynos_tmu_init_data
const
exynos4412_default_tmu_data
=
{
.
tmu_data
=
{
{
EXYNOS4412_TMU_DATA
,
.
type
=
SOC_ARCH_EXYNOS4412
,
.
test_mux
=
EXYNOS4412_MUX_ADDR_VALUE
,
},
},
.
tmu_count
=
1
,
};
#endif
#if defined(CONFIG_SOC_EXYNOS5250)
struct
exynos_tmu_init_data
const
exynos5250_default_tmu_data
=
{
.
tmu_data
=
{
{
...
...
@@ -258,31 +163,6 @@ struct exynos_tmu_init_data const exynos5250_default_tmu_data = {
},
.
tmu_count
=
1
,
};
#endif
#if defined(CONFIG_SOC_EXYNOS5260)
static
const
struct
exynos_tmu_registers
exynos5260_tmu_registers
=
{
.
triminfo_data
=
EXYNOS_TMU_REG_TRIMINFO
,
.
tmu_ctrl
=
EXYNOS_TMU_REG_CONTROL
,
.
therm_trip_mode_shift
=
EXYNOS_TMU_TRIP_MODE_SHIFT
,
.
therm_trip_mode_mask
=
EXYNOS_TMU_TRIP_MODE_MASK
,
.
therm_trip_en_shift
=
EXYNOS_TMU_THERM_TRIP_EN_SHIFT
,
.
tmu_status
=
EXYNOS_TMU_REG_STATUS
,
.
tmu_cur_temp
=
EXYNOS_TMU_REG_CURRENT_TEMP
,
.
threshold_th0
=
EXYNOS_THD_TEMP_RISE
,
.
threshold_th1
=
EXYNOS_THD_TEMP_FALL
,
.
tmu_inten
=
EXYNOS5260_TMU_REG_INTEN
,
.
inten_rise0_shift
=
EXYNOS_TMU_INTEN_RISE0_SHIFT
,
.
inten_rise1_shift
=
EXYNOS_TMU_INTEN_RISE1_SHIFT
,
.
inten_rise2_shift
=
EXYNOS_TMU_INTEN_RISE2_SHIFT
,
.
inten_rise3_shift
=
EXYNOS_TMU_INTEN_RISE3_SHIFT
,
.
inten_fall0_shift
=
EXYNOS_TMU_INTEN_FALL0_SHIFT
,
.
tmu_intstat
=
EXYNOS5260_TMU_REG_INTSTAT
,
.
tmu_intclear
=
EXYNOS5260_TMU_REG_INTCLEAR
,
.
emul_con
=
EXYNOS5260_EMUL_CON
,
.
emul_temp_shift
=
EXYNOS_EMUL_DATA_SHIFT
,
.
emul_time_shift
=
EXYNOS_EMUL_TIME_SHIFT
,
};
#define __EXYNOS5260_TMU_DATA \
.threshold_falling = 10, \
...
...
@@ -319,13 +199,10 @@ static const struct exynos_tmu_registers exynos5260_tmu_registers = {
.temp_level = 103, \
}, \
.freq_tab_count = 2, \
.registers = &exynos5260_tmu_registers, \
#define EXYNOS5260_TMU_DATA \
__EXYNOS5260_TMU_DATA \
.type = SOC_ARCH_EXYNOS5260, \
.features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_FALLING_TRIP | \
TMU_SUPPORT_READY_STATUS | TMU_SUPPORT_EMUL_TIME)
.type = SOC_ARCH_EXYNOS5260
struct
exynos_tmu_init_data
const
exynos5260_default_tmu_data
=
{
.
tmu_data
=
{
...
...
@@ -337,82 +214,14 @@ struct exynos_tmu_init_data const exynos5260_default_tmu_data = {
},
.
tmu_count
=
5
,
};
#endif
#if defined(CONFIG_SOC_EXYNOS5420)
static
const
struct
exynos_tmu_registers
exynos5420_tmu_registers
=
{
.
triminfo_data
=
EXYNOS_TMU_REG_TRIMINFO
,
.
tmu_ctrl
=
EXYNOS_TMU_REG_CONTROL
,
.
therm_trip_mode_shift
=
EXYNOS_TMU_TRIP_MODE_SHIFT
,
.
therm_trip_mode_mask
=
EXYNOS_TMU_TRIP_MODE_MASK
,
.
therm_trip_en_shift
=
EXYNOS_TMU_THERM_TRIP_EN_SHIFT
,
.
tmu_status
=
EXYNOS_TMU_REG_STATUS
,
.
tmu_cur_temp
=
EXYNOS_TMU_REG_CURRENT_TEMP
,
.
threshold_th0
=
EXYNOS_THD_TEMP_RISE
,
.
threshold_th1
=
EXYNOS_THD_TEMP_FALL
,
.
tmu_inten
=
EXYNOS_TMU_REG_INTEN
,
.
inten_rise0_shift
=
EXYNOS_TMU_INTEN_RISE0_SHIFT
,
.
inten_rise1_shift
=
EXYNOS_TMU_INTEN_RISE1_SHIFT
,
.
inten_rise2_shift
=
EXYNOS_TMU_INTEN_RISE2_SHIFT
,
/* INTEN_RISE3 Not availble in exynos5420 */
.
inten_rise3_shift
=
EXYNOS_TMU_INTEN_RISE3_SHIFT
,
.
inten_fall0_shift
=
EXYNOS_TMU_INTEN_FALL0_SHIFT
,
.
tmu_intstat
=
EXYNOS_TMU_REG_INTSTAT
,
.
tmu_intclear
=
EXYNOS_TMU_REG_INTCLEAR
,
.
emul_con
=
EXYNOS_EMUL_CON
,
.
emul_temp_shift
=
EXYNOS_EMUL_DATA_SHIFT
,
.
emul_time_shift
=
EXYNOS_EMUL_TIME_SHIFT
,
};
#define __EXYNOS5420_TMU_DATA \
.threshold_falling = 10, \
.trigger_levels[0] = 85, \
.trigger_levels[1] = 103, \
.trigger_levels[2] = 110, \
.trigger_levels[3] = 120, \
.trigger_enable[0] = true, \
.trigger_enable[1] = true, \
.trigger_enable[2] = true, \
.trigger_enable[3] = false, \
.trigger_type[0] = THROTTLE_ACTIVE, \
.trigger_type[1] = THROTTLE_ACTIVE, \
.trigger_type[2] = SW_TRIP, \
.trigger_type[3] = HW_TRIP, \
.max_trigger_level = 4, \
.non_hw_trigger_levels = 3, \
.gain = 8, \
.reference_voltage = 16, \
.noise_cancel_mode = 4, \
.cal_type = TYPE_ONE_POINT_TRIMMING, \
.efuse_value = 55, \
.min_efuse_value = 40, \
.max_efuse_value = 100, \
.first_point_trim = 25, \
.second_point_trim = 85, \
.default_temp_offset = 50, \
.freq_tab[0] = { \
.freq_clip_max = 800 * 1000, \
.temp_level = 85, \
}, \
.freq_tab[1] = { \
.freq_clip_max = 200 * 1000, \
.temp_level = 103, \
}, \
.freq_tab_count = 2, \
.registers = &exynos5420_tmu_registers, \
#define EXYNOS5420_TMU_DATA \
__EXYNOS5420_TMU_DATA \
.type = SOC_ARCH_EXYNOS5250, \
.features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_FALLING_TRIP | \
TMU_SUPPORT_READY_STATUS | TMU_SUPPORT_EMUL_TIME)
__EXYNOS5260_TMU_DATA \
.type = SOC_ARCH_EXYNOS5420
#define EXYNOS5420_TMU_DATA_SHARED \
__EXYNOS5420_TMU_DATA \
.type = SOC_ARCH_EXYNOS5420_TRIMINFO, \
.features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_FALLING_TRIP | \
TMU_SUPPORT_READY_STATUS | TMU_SUPPORT_EMUL_TIME | \
TMU_SUPPORT_ADDRESS_MULTIPLE)
__EXYNOS5260_TMU_DATA \
.type = SOC_ARCH_EXYNOS5420_TRIMINFO
struct
exynos_tmu_init_data
const
exynos5420_default_tmu_data
=
{
.
tmu_data
=
{
...
...
@@ -424,34 +233,6 @@ struct exynos_tmu_init_data const exynos5420_default_tmu_data = {
},
.
tmu_count
=
5
,
};
#endif
#if defined(CONFIG_SOC_EXYNOS5440)
static
const
struct
exynos_tmu_registers
exynos5440_tmu_registers
=
{
.
triminfo_data
=
EXYNOS5440_TMU_S0_7_TRIM
,
.
tmu_ctrl
=
EXYNOS5440_TMU_S0_7_CTRL
,
.
therm_trip_mode_shift
=
EXYNOS_TMU_TRIP_MODE_SHIFT
,
.
therm_trip_mode_mask
=
EXYNOS_TMU_TRIP_MODE_MASK
,
.
therm_trip_en_shift
=
EXYNOS_TMU_THERM_TRIP_EN_SHIFT
,
.
tmu_status
=
EXYNOS5440_TMU_S0_7_STATUS
,
.
tmu_cur_temp
=
EXYNOS5440_TMU_S0_7_TEMP
,
.
threshold_th0
=
EXYNOS5440_TMU_S0_7_TH0
,
.
threshold_th1
=
EXYNOS5440_TMU_S0_7_TH1
,
.
threshold_th2
=
EXYNOS5440_TMU_S0_7_TH2
,
.
threshold_th3_l0_shift
=
EXYNOS5440_TMU_TH_RISE4_SHIFT
,
.
tmu_inten
=
EXYNOS5440_TMU_S0_7_IRQEN
,
.
inten_rise0_shift
=
EXYNOS5440_TMU_INTEN_RISE0_SHIFT
,
.
inten_rise1_shift
=
EXYNOS5440_TMU_INTEN_RISE1_SHIFT
,
.
inten_rise2_shift
=
EXYNOS5440_TMU_INTEN_RISE2_SHIFT
,
.
inten_rise3_shift
=
EXYNOS5440_TMU_INTEN_RISE3_SHIFT
,
.
inten_fall0_shift
=
EXYNOS5440_TMU_INTEN_FALL0_SHIFT
,
.
tmu_intstat
=
EXYNOS5440_TMU_S0_7_IRQ
,
.
tmu_intclear
=
EXYNOS5440_TMU_S0_7_IRQ
,
.
tmu_irqstatus
=
EXYNOS5440_TMU_IRQ_STATUS
,
.
emul_con
=
EXYNOS5440_TMU_S0_7_DEBUG
,
.
emul_temp_shift
=
EXYNOS_EMUL_DATA_SHIFT
,
.
tmu_pmin
=
EXYNOS5440_TMU_PMIN
,
};
#define EXYNOS5440_TMU_DATA \
.trigger_levels[0] = 100, \
...
...
@@ -471,10 +252,7 @@ static const struct exynos_tmu_registers exynos5440_tmu_registers = {
.first_point_trim = 25, \
.second_point_trim = 70, \
.default_temp_offset = 25, \
.type = SOC_ARCH_EXYNOS5440, \
.registers = &exynos5440_tmu_registers, \
.features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_FALLING_TRIP | \
TMU_SUPPORT_MULTI_INST | TMU_SUPPORT_ADDRESS_MULTIPLE),
.type = SOC_ARCH_EXYNOS5440
struct
exynos_tmu_init_data
const
exynos5440_default_tmu_data
=
{
.
tmu_data
=
{
...
...
@@ -484,4 +262,3 @@ struct exynos_tmu_init_data const exynos5440_default_tmu_data = {
},
.
tmu_count
=
3
,
};
#endif
drivers/thermal/samsung/exynos_tmu_data.h
deleted
100644 → 0
View file @
af6c9f16
/*
* exynos_tmu_data.h - Samsung EXYNOS tmu data header file
*
* Copyright (C) 2013 Samsung Electronics
* Amit Daniel Kachhap <amit.daniel@samsung.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; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef _EXYNOS_TMU_DATA_H
#define _EXYNOS_TMU_DATA_H
/* Exynos generic registers */
#define EXYNOS_TMU_REG_TRIMINFO 0x0
#define EXYNOS_TMU_REG_CONTROL 0x20
#define EXYNOS_TMU_REG_STATUS 0x28
#define EXYNOS_TMU_REG_CURRENT_TEMP 0x40
#define EXYNOS_TMU_REG_INTEN 0x70
#define EXYNOS_TMU_REG_INTSTAT 0x74
#define EXYNOS_TMU_REG_INTCLEAR 0x78
#define EXYNOS_TMU_TEMP_MASK 0xff
#define EXYNOS_TMU_REF_VOLTAGE_SHIFT 24
#define EXYNOS_TMU_REF_VOLTAGE_MASK 0x1f
#define EXYNOS_TMU_BUF_SLOPE_SEL_MASK 0xf
#define EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT 8
#define EXYNOS_TMU_CORE_EN_SHIFT 0
/* Exynos3250 specific registers */
#define EXYNOS_TMU_TRIMINFO_CON1 0x10
/* Exynos4210 specific registers */
#define EXYNOS4210_TMU_REG_THRESHOLD_TEMP 0x44
#define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50
/* Exynos5250, Exynos4412, Exynos3250 specific registers */
#define EXYNOS_TMU_TRIMINFO_CON2 0x14
#define EXYNOS_THD_TEMP_RISE 0x50
#define EXYNOS_THD_TEMP_FALL 0x54
#define EXYNOS_EMUL_CON 0x80
#define EXYNOS_TRIMINFO_RELOAD_ENABLE 1
#define EXYNOS_TRIMINFO_25_SHIFT 0
#define EXYNOS_TRIMINFO_85_SHIFT 8
#define EXYNOS_TMU_TRIP_MODE_SHIFT 13
#define EXYNOS_TMU_TRIP_MODE_MASK 0x7
#define EXYNOS_TMU_THERM_TRIP_EN_SHIFT 12
#define EXYNOS_TMU_INTEN_RISE0_SHIFT 0
#define EXYNOS_TMU_INTEN_RISE1_SHIFT 4
#define EXYNOS_TMU_INTEN_RISE2_SHIFT 8
#define EXYNOS_TMU_INTEN_RISE3_SHIFT 12
#define EXYNOS_TMU_INTEN_FALL0_SHIFT 16
#define EXYNOS_EMUL_TIME 0x57F0
#define EXYNOS_EMUL_TIME_MASK 0xffff
#define EXYNOS_EMUL_TIME_SHIFT 16
#define EXYNOS_EMUL_DATA_SHIFT 8
#define EXYNOS_EMUL_DATA_MASK 0xFF
#define EXYNOS_EMUL_ENABLE 0x1
#define EXYNOS_MAX_TRIGGER_PER_REG 4
/* Exynos5260 specific */
#define EXYNOS5260_TMU_REG_INTEN 0xC0
#define EXYNOS5260_TMU_REG_INTSTAT 0xC4
#define EXYNOS5260_TMU_REG_INTCLEAR 0xC8
#define EXYNOS5260_EMUL_CON 0x100
/* Exynos4412 specific */
#define EXYNOS4412_MUX_ADDR_VALUE 6
#define EXYNOS4412_MUX_ADDR_SHIFT 20
/*exynos5440 specific registers*/
#define EXYNOS5440_TMU_S0_7_TRIM 0x000
#define EXYNOS5440_TMU_S0_7_CTRL 0x020
#define EXYNOS5440_TMU_S0_7_DEBUG 0x040
#define EXYNOS5440_TMU_S0_7_STATUS 0x060
#define EXYNOS5440_TMU_S0_7_TEMP 0x0f0
#define EXYNOS5440_TMU_S0_7_TH0 0x110
#define EXYNOS5440_TMU_S0_7_TH1 0x130
#define EXYNOS5440_TMU_S0_7_TH2 0x150
#define EXYNOS5440_TMU_S0_7_IRQEN 0x210
#define EXYNOS5440_TMU_S0_7_IRQ 0x230
/* exynos5440 common registers */
#define EXYNOS5440_TMU_IRQ_STATUS 0x000
#define EXYNOS5440_TMU_PMIN 0x004
#define EXYNOS5440_TMU_INTEN_RISE0_SHIFT 0
#define EXYNOS5440_TMU_INTEN_RISE1_SHIFT 1
#define EXYNOS5440_TMU_INTEN_RISE2_SHIFT 2
#define EXYNOS5440_TMU_INTEN_RISE3_SHIFT 3
#define EXYNOS5440_TMU_INTEN_FALL0_SHIFT 4
#define EXYNOS5440_TMU_TH_RISE4_SHIFT 24
#define EXYNOS5440_EFUSE_SWAP_OFFSET 8
#if defined(CONFIG_SOC_EXYNOS3250)
extern
struct
exynos_tmu_init_data
const
exynos3250_default_tmu_data
;
#define EXYNOS3250_TMU_DRV_DATA (&exynos3250_default_tmu_data)
#else
#define EXYNOS3250_TMU_DRV_DATA (NULL)
#endif
#if defined(CONFIG_CPU_EXYNOS4210)
extern
struct
exynos_tmu_init_data
const
exynos4210_default_tmu_data
;
#define EXYNOS4210_TMU_DRV_DATA (&exynos4210_default_tmu_data)
#else
#define EXYNOS4210_TMU_DRV_DATA (NULL)
#endif
#if defined(CONFIG_SOC_EXYNOS4412)
extern
struct
exynos_tmu_init_data
const
exynos4412_default_tmu_data
;
#define EXYNOS4412_TMU_DRV_DATA (&exynos4412_default_tmu_data)
#else
#define EXYNOS4412_TMU_DRV_DATA (NULL)
#endif
#if defined(CONFIG_SOC_EXYNOS5250)
extern
struct
exynos_tmu_init_data
const
exynos5250_default_tmu_data
;
#define EXYNOS5250_TMU_DRV_DATA (&exynos5250_default_tmu_data)
#else
#define EXYNOS5250_TMU_DRV_DATA (NULL)
#endif
#if defined(CONFIG_SOC_EXYNOS5260)
extern
struct
exynos_tmu_init_data
const
exynos5260_default_tmu_data
;
#define EXYNOS5260_TMU_DRV_DATA (&exynos5260_default_tmu_data)
#else
#define EXYNOS5260_TMU_DRV_DATA (NULL)
#endif
#if defined(CONFIG_SOC_EXYNOS5420)
extern
struct
exynos_tmu_init_data
const
exynos5420_default_tmu_data
;
#define EXYNOS5420_TMU_DRV_DATA (&exynos5420_default_tmu_data)
#else
#define EXYNOS5420_TMU_DRV_DATA (NULL)
#endif
#if defined(CONFIG_SOC_EXYNOS5440)
extern
struct
exynos_tmu_init_data
const
exynos5440_default_tmu_data
;
#define EXYNOS5440_TMU_DRV_DATA (&exynos5440_default_tmu_data)
#else
#define EXYNOS5440_TMU_DRV_DATA (NULL)
#endif
#endif
/*_EXYNOS_TMU_DATA_H*/
drivers/thermal/tegra_soctherm.c
0 → 100644
View file @
2707dbd0
/*
* Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
*
* Author:
* Mikko Perttunen <mperttunen@nvidia.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/bitops.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/thermal.h>
#include <soc/tegra/fuse.h>
#define SENSOR_CONFIG0 0
#define SENSOR_CONFIG0_STOP BIT(0)
#define SENSOR_CONFIG0_TALL_SHIFT 8
#define SENSOR_CONFIG0_TCALC_OVER BIT(4)
#define SENSOR_CONFIG0_OVER BIT(3)
#define SENSOR_CONFIG0_CPTR_OVER BIT(2)
#define SENSOR_CONFIG1 4
#define SENSOR_CONFIG1_TSAMPLE_SHIFT 0
#define SENSOR_CONFIG1_TIDDQ_EN_SHIFT 15
#define SENSOR_CONFIG1_TEN_COUNT_SHIFT 24
#define SENSOR_CONFIG1_TEMP_ENABLE BIT(31)
#define SENSOR_CONFIG2 8
#define SENSOR_CONFIG2_THERMA_SHIFT 16
#define SENSOR_CONFIG2_THERMB_SHIFT 0
#define SENSOR_PDIV 0x1c0
#define SENSOR_PDIV_T124 0x8888
#define SENSOR_HOTSPOT_OFF 0x1c4
#define SENSOR_HOTSPOT_OFF_T124 0x00060600
#define SENSOR_TEMP1 0x1c8
#define SENSOR_TEMP2 0x1cc
#define SENSOR_TEMP_MASK 0xffff
#define READBACK_VALUE_MASK 0xff00
#define READBACK_VALUE_SHIFT 8
#define READBACK_ADD_HALF BIT(7)
#define READBACK_NEGATE BIT(1)
#define FUSE_TSENSOR8_CALIB 0x180
#define FUSE_SPARE_REALIGNMENT_REG_0 0x1fc
#define FUSE_TSENSOR_CALIB_CP_TS_BASE_MASK 0x1fff
#define FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK (0x1fff << 13)
#define FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT 13
#define FUSE_TSENSOR8_CALIB_CP_TS_BASE_MASK 0x3ff
#define FUSE_TSENSOR8_CALIB_FT_TS_BASE_MASK (0x7ff << 10)
#define FUSE_TSENSOR8_CALIB_FT_TS_BASE_SHIFT 10
#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_CP_MASK 0x3f
#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_MASK (0x1f << 21)
#define FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT 21
#define NOMINAL_CALIB_FT_T124 105
#define NOMINAL_CALIB_CP_T124 25
struct
tegra_tsensor_configuration
{
u32
tall
,
tsample
,
tiddq_en
,
ten_count
,
pdiv
,
tsample_ate
,
pdiv_ate
;
};
struct
tegra_tsensor
{
const
struct
tegra_tsensor_configuration
*
config
;
u32
base
,
calib_fuse_offset
;
/* Correction values used to modify values read from calibration fuses */
s32
fuse_corr_alpha
,
fuse_corr_beta
;
};
struct
tegra_thermctl_zone
{
void
__iomem
*
reg
;
unsigned
int
shift
;
};
static
const
struct
tegra_tsensor_configuration
t124_tsensor_config
=
{
.
tall
=
16300
,
.
tsample
=
120
,
.
tiddq_en
=
1
,
.
ten_count
=
1
,
.
pdiv
=
8
,
.
tsample_ate
=
480
,
.
pdiv_ate
=
8
};
static
const
struct
tegra_tsensor
t124_tsensors
[]
=
{
{
.
config
=
&
t124_tsensor_config
,
.
base
=
0xc0
,
.
calib_fuse_offset
=
0x098
,
.
fuse_corr_alpha
=
1135400
,
.
fuse_corr_beta
=
-
6266900
,
},
{
.
config
=
&
t124_tsensor_config
,
.
base
=
0xe0
,
.
calib_fuse_offset
=
0x084
,
.
fuse_corr_alpha
=
1122220
,
.
fuse_corr_beta
=
-
5700700
,
},
{
.
config
=
&
t124_tsensor_config
,
.
base
=
0x100
,
.
calib_fuse_offset
=
0x088
,
.
fuse_corr_alpha
=
1127000
,
.
fuse_corr_beta
=
-
6768200
,
},
{
.
config
=
&
t124_tsensor_config
,
.
base
=
0x120
,
.
calib_fuse_offset
=
0x12c
,
.
fuse_corr_alpha
=
1110900
,
.
fuse_corr_beta
=
-
6232000
,
},
{
.
config
=
&
t124_tsensor_config
,
.
base
=
0x140
,
.
calib_fuse_offset
=
0x158
,
.
fuse_corr_alpha
=
1122300
,
.
fuse_corr_beta
=
-
5936400
,
},
{
.
config
=
&
t124_tsensor_config
,
.
base
=
0x160
,
.
calib_fuse_offset
=
0x15c
,
.
fuse_corr_alpha
=
1145700
,
.
fuse_corr_beta
=
-
7124600
,
},
{
.
config
=
&
t124_tsensor_config
,
.
base
=
0x180
,
.
calib_fuse_offset
=
0x154
,
.
fuse_corr_alpha
=
1120100
,
.
fuse_corr_beta
=
-
6000500
,
},
{
.
config
=
&
t124_tsensor_config
,
.
base
=
0x1a0
,
.
calib_fuse_offset
=
0x160
,
.
fuse_corr_alpha
=
1106500
,
.
fuse_corr_beta
=
-
6729300
,
},
};
struct
tegra_soctherm
{
struct
reset_control
*
reset
;
struct
clk
*
clock_tsensor
;
struct
clk
*
clock_soctherm
;
void
__iomem
*
regs
;
struct
thermal_zone_device
*
thermctl_tzs
[
4
];
};
struct
tsensor_shared_calibration
{
u32
base_cp
,
base_ft
;
u32
actual_temp_cp
,
actual_temp_ft
;
};
static
int
calculate_shared_calibration
(
struct
tsensor_shared_calibration
*
r
)
{
u32
val
,
shifted_cp
,
shifted_ft
;
int
err
;
err
=
tegra_fuse_readl
(
FUSE_TSENSOR8_CALIB
,
&
val
);
if
(
err
)
return
err
;
r
->
base_cp
=
val
&
FUSE_TSENSOR8_CALIB_CP_TS_BASE_MASK
;
r
->
base_ft
=
(
val
&
FUSE_TSENSOR8_CALIB_FT_TS_BASE_MASK
)
>>
FUSE_TSENSOR8_CALIB_FT_TS_BASE_SHIFT
;
val
=
((
val
&
FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_MASK
)
>>
FUSE_SPARE_REALIGNMENT_REG_SHIFT_FT_SHIFT
);
shifted_ft
=
sign_extend32
(
val
,
4
);
err
=
tegra_fuse_readl
(
FUSE_SPARE_REALIGNMENT_REG_0
,
&
val
);
if
(
err
)
return
err
;
shifted_cp
=
sign_extend32
(
val
,
5
);
r
->
actual_temp_cp
=
2
*
NOMINAL_CALIB_CP_T124
+
shifted_cp
;
r
->
actual_temp_ft
=
2
*
NOMINAL_CALIB_FT_T124
+
shifted_ft
;
return
0
;
}
static
s64
div64_s64_precise
(
s64
a
,
s64
b
)
{
s64
r
,
al
;
/* Scale up for increased precision division */
al
=
a
<<
16
;
r
=
div64_s64
(
al
*
2
+
1
,
2
*
b
);
return
r
>>
16
;
}
static
int
calculate_tsensor_calibration
(
const
struct
tegra_tsensor
*
sensor
,
const
struct
tsensor_shared_calibration
*
shared
,
u32
*
calib
)
{
u32
val
;
s32
actual_tsensor_ft
,
actual_tsensor_cp
,
delta_sens
,
delta_temp
,
mult
,
div
;
s16
therma
,
thermb
;
s64
tmp
;
int
err
;
err
=
tegra_fuse_readl
(
sensor
->
calib_fuse_offset
,
&
val
);
if
(
err
)
return
err
;
actual_tsensor_cp
=
(
shared
->
base_cp
*
64
)
+
sign_extend32
(
val
,
12
);
val
=
(
val
&
FUSE_TSENSOR_CALIB_FT_TS_BASE_MASK
)
>>
FUSE_TSENSOR_CALIB_FT_TS_BASE_SHIFT
;
actual_tsensor_ft
=
(
shared
->
base_ft
*
32
)
+
sign_extend32
(
val
,
12
);
delta_sens
=
actual_tsensor_ft
-
actual_tsensor_cp
;
delta_temp
=
shared
->
actual_temp_ft
-
shared
->
actual_temp_cp
;
mult
=
sensor
->
config
->
pdiv
*
sensor
->
config
->
tsample_ate
;
div
=
sensor
->
config
->
tsample
*
sensor
->
config
->
pdiv_ate
;
therma
=
div64_s64_precise
((
s64
)
delta_temp
*
(
1LL
<<
13
)
*
mult
,
(
s64
)
delta_sens
*
div
);
tmp
=
(
s64
)
actual_tsensor_ft
*
shared
->
actual_temp_cp
-
(
s64
)
actual_tsensor_cp
*
shared
->
actual_temp_ft
;
thermb
=
div64_s64_precise
(
tmp
,
(
s64
)
delta_sens
);
therma
=
div64_s64_precise
((
s64
)
therma
*
sensor
->
fuse_corr_alpha
,
(
s64
)
1000000LL
);
thermb
=
div64_s64_precise
((
s64
)
thermb
*
sensor
->
fuse_corr_alpha
+
sensor
->
fuse_corr_beta
,
(
s64
)
1000000LL
);
*
calib
=
((
u16
)
therma
<<
SENSOR_CONFIG2_THERMA_SHIFT
)
|
((
u16
)
thermb
<<
SENSOR_CONFIG2_THERMB_SHIFT
);
return
0
;
}
static
int
enable_tsensor
(
struct
tegra_soctherm
*
tegra
,
const
struct
tegra_tsensor
*
sensor
,
const
struct
tsensor_shared_calibration
*
shared
)
{
void
__iomem
*
base
=
tegra
->
regs
+
sensor
->
base
;
unsigned
int
val
;
u32
calib
;
int
err
;
err
=
calculate_tsensor_calibration
(
sensor
,
shared
,
&
calib
);
if
(
err
)
return
err
;
val
=
sensor
->
config
->
tall
<<
SENSOR_CONFIG0_TALL_SHIFT
;
writel
(
val
,
base
+
SENSOR_CONFIG0
);
val
=
(
sensor
->
config
->
tsample
-
1
)
<<
SENSOR_CONFIG1_TSAMPLE_SHIFT
;
val
|=
sensor
->
config
->
tiddq_en
<<
SENSOR_CONFIG1_TIDDQ_EN_SHIFT
;
val
|=
sensor
->
config
->
ten_count
<<
SENSOR_CONFIG1_TEN_COUNT_SHIFT
;
val
|=
SENSOR_CONFIG1_TEMP_ENABLE
;
writel
(
val
,
base
+
SENSOR_CONFIG1
);
writel
(
calib
,
base
+
SENSOR_CONFIG2
);
return
0
;
}
/*
* Translate from soctherm readback format to millicelsius.
* The soctherm readback format in bits is as follows:
* TTTTTTTT H______N
* where T's contain the temperature in Celsius,
* H denotes an addition of 0.5 Celsius and N denotes negation
* of the final value.
*/
static
long
translate_temp
(
u16
val
)
{
long
t
;
t
=
((
val
&
READBACK_VALUE_MASK
)
>>
READBACK_VALUE_SHIFT
)
*
1000
;
if
(
val
&
READBACK_ADD_HALF
)
t
+=
500
;
if
(
val
&
READBACK_NEGATE
)
t
*=
-
1
;
return
t
;
}
static
int
tegra_thermctl_get_temp
(
void
*
data
,
long
*
out_temp
)
{
struct
tegra_thermctl_zone
*
zone
=
data
;
u32
val
;
val
=
(
readl
(
zone
->
reg
)
>>
zone
->
shift
)
&
SENSOR_TEMP_MASK
;
*
out_temp
=
translate_temp
(
val
);
return
0
;
}
static
const
struct
thermal_zone_of_device_ops
tegra_of_thermal_ops
=
{
.
get_temp
=
tegra_thermctl_get_temp
,
};
static
const
struct
of_device_id
tegra_soctherm_of_match
[]
=
{
{
.
compatible
=
"nvidia,tegra124-soctherm"
},
{
},
};
MODULE_DEVICE_TABLE
(
of
,
tegra_soctherm_of_match
);
struct
thermctl_zone_desc
{
unsigned
int
offset
;
unsigned
int
shift
;
};
static
const
struct
thermctl_zone_desc
t124_thermctl_temp_zones
[]
=
{
{
SENSOR_TEMP1
,
16
},
{
SENSOR_TEMP2
,
16
},
{
SENSOR_TEMP1
,
0
},
{
SENSOR_TEMP2
,
0
}
};
static
int
tegra_soctherm_probe
(
struct
platform_device
*
pdev
)
{
struct
tegra_soctherm
*
tegra
;
struct
thermal_zone_device
*
tz
;
struct
tsensor_shared_calibration
shared_calib
;
struct
resource
*
res
;
unsigned
int
i
;
int
err
;
const
struct
tegra_tsensor
*
tsensors
=
t124_tsensors
;
tegra
=
devm_kzalloc
(
&
pdev
->
dev
,
sizeof
(
*
tegra
),
GFP_KERNEL
);
if
(
!
tegra
)
return
-
ENOMEM
;
res
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
tegra
->
regs
=
devm_ioremap_resource
(
&
pdev
->
dev
,
res
);
if
(
IS_ERR
(
tegra
->
regs
))
return
PTR_ERR
(
tegra
->
regs
);
tegra
->
reset
=
devm_reset_control_get
(
&
pdev
->
dev
,
"soctherm"
);
if
(
IS_ERR
(
tegra
->
reset
))
{
dev_err
(
&
pdev
->
dev
,
"can't get soctherm reset
\n
"
);
return
PTR_ERR
(
tegra
->
reset
);
}
tegra
->
clock_tsensor
=
devm_clk_get
(
&
pdev
->
dev
,
"tsensor"
);
if
(
IS_ERR
(
tegra
->
clock_tsensor
))
{
dev_err
(
&
pdev
->
dev
,
"can't get tsensor clock
\n
"
);
return
PTR_ERR
(
tegra
->
clock_tsensor
);
}
tegra
->
clock_soctherm
=
devm_clk_get
(
&
pdev
->
dev
,
"soctherm"
);
if
(
IS_ERR
(
tegra
->
clock_soctherm
))
{
dev_err
(
&
pdev
->
dev
,
"can't get soctherm clock
\n
"
);
return
PTR_ERR
(
tegra
->
clock_soctherm
);
}
reset_control_assert
(
tegra
->
reset
);
err
=
clk_prepare_enable
(
tegra
->
clock_soctherm
);
if
(
err
)
return
err
;
err
=
clk_prepare_enable
(
tegra
->
clock_tsensor
);
if
(
err
)
{
clk_disable_unprepare
(
tegra
->
clock_soctherm
);
return
err
;
}
reset_control_deassert
(
tegra
->
reset
);
/* Initialize raw sensors */
err
=
calculate_shared_calibration
(
&
shared_calib
);
if
(
err
)
goto
disable_clocks
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
t124_tsensors
);
++
i
)
{
err
=
enable_tsensor
(
tegra
,
tsensors
+
i
,
&
shared_calib
);
if
(
err
)
goto
disable_clocks
;
}
writel
(
SENSOR_PDIV_T124
,
tegra
->
regs
+
SENSOR_PDIV
);
writel
(
SENSOR_HOTSPOT_OFF_T124
,
tegra
->
regs
+
SENSOR_HOTSPOT_OFF
);
/* Initialize thermctl sensors */
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
tegra
->
thermctl_tzs
);
++
i
)
{
struct
tegra_thermctl_zone
*
zone
=
devm_kzalloc
(
&
pdev
->
dev
,
sizeof
(
*
zone
),
GFP_KERNEL
);
if
(
!
zone
)
{
err
=
-
ENOMEM
;
goto
unregister_tzs
;
}
zone
->
reg
=
tegra
->
regs
+
t124_thermctl_temp_zones
[
i
].
offset
;
zone
->
shift
=
t124_thermctl_temp_zones
[
i
].
shift
;
tz
=
thermal_zone_of_sensor_register
(
&
pdev
->
dev
,
i
,
zone
,
&
tegra_of_thermal_ops
);
if
(
IS_ERR
(
tz
))
{
err
=
PTR_ERR
(
tz
);
dev_err
(
&
pdev
->
dev
,
"failed to register sensor: %d
\n
"
,
err
);
goto
unregister_tzs
;
}
tegra
->
thermctl_tzs
[
i
]
=
tz
;
}
return
0
;
unregister_tzs:
while
(
i
--
)
thermal_zone_of_sensor_unregister
(
&
pdev
->
dev
,
tegra
->
thermctl_tzs
[
i
]);
disable_clocks:
clk_disable_unprepare
(
tegra
->
clock_tsensor
);
clk_disable_unprepare
(
tegra
->
clock_soctherm
);
return
err
;
}
static
int
tegra_soctherm_remove
(
struct
platform_device
*
pdev
)
{
struct
tegra_soctherm
*
tegra
=
platform_get_drvdata
(
pdev
);
unsigned
int
i
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
tegra
->
thermctl_tzs
);
++
i
)
{
thermal_zone_of_sensor_unregister
(
&
pdev
->
dev
,
tegra
->
thermctl_tzs
[
i
]);
}
clk_disable_unprepare
(
tegra
->
clock_tsensor
);
clk_disable_unprepare
(
tegra
->
clock_soctherm
);
return
0
;
}
static
struct
platform_driver
tegra_soctherm_driver
=
{
.
probe
=
tegra_soctherm_probe
,
.
remove
=
tegra_soctherm_remove
,
.
driver
=
{
.
name
=
"tegra-soctherm"
,
.
of_match_table
=
tegra_soctherm_of_match
,
},
};
module_platform_driver
(
tegra_soctherm_driver
);
MODULE_AUTHOR
(
"Mikko Perttunen <mperttunen@nvidia.com>"
);
MODULE_DESCRIPTION
(
"NVIDIA Tegra SOCTHERM thermal management driver"
);
MODULE_LICENSE
(
"GPL v2"
);
drivers/thermal/thermal_core.h
View file @
2707dbd0
...
...
@@ -89,9 +89,27 @@ static inline void thermal_gov_user_space_unregister(void) {}
#ifdef CONFIG_THERMAL_OF
int
of_parse_thermal_zones
(
void
);
void
of_thermal_destroy_zones
(
void
);
int
of_thermal_get_ntrips
(
struct
thermal_zone_device
*
);
bool
of_thermal_is_trip_valid
(
struct
thermal_zone_device
*
,
int
);
const
struct
thermal_trip
*
const
of_thermal_get_trip_points
(
struct
thermal_zone_device
*
);
#else
static
inline
int
of_parse_thermal_zones
(
void
)
{
return
0
;
}
static
inline
void
of_thermal_destroy_zones
(
void
)
{
}
static
inline
int
of_thermal_get_ntrips
(
struct
thermal_zone_device
*
tz
)
{
return
0
;
}
static
inline
bool
of_thermal_is_trip_valid
(
struct
thermal_zone_device
*
tz
,
int
trip
)
{
return
0
;
}
static
inline
const
struct
thermal_trip
*
const
of_thermal_get_trip_points
(
struct
thermal_zone_device
*
tz
)
{
return
NULL
;
}
#endif
#endif
/* __THERMAL_CORE_H__ */
drivers/thermal/ti-soc-thermal/ti-thermal-common.c
View file @
2707dbd0
...
...
@@ -286,6 +286,11 @@ static int ti_thermal_get_crit_temp(struct thermal_zone_device *thermal,
return
ti_thermal_get_trip_temp
(
thermal
,
OMAP_TRIP_NUMBER
-
1
,
temp
);
}
static
const
struct
thermal_zone_of_device_ops
ti_of_thermal_ops
=
{
.
get_temp
=
__ti_thermal_get_temp
,
.
get_trend
=
__ti_thermal_get_trend
,
};
static
struct
thermal_zone_device_ops
ti_thermal_ops
=
{
.
get_temp
=
ti_thermal_get_temp
,
.
get_trend
=
ti_thermal_get_trend
,
...
...
@@ -333,8 +338,7 @@ int ti_thermal_expose_sensor(struct ti_bandgap *bgp, int id,
/* in case this is specified by DT */
data
->
ti_thermal
=
thermal_zone_of_sensor_register
(
bgp
->
dev
,
id
,
data
,
__ti_thermal_get_temp
,
__ti_thermal_get_trend
);
data
,
&
ti_of_thermal_ops
);
if
(
IS_ERR
(
data
->
ti_thermal
))
{
/* Create thermal zone */
data
->
ti_thermal
=
thermal_zone_device_register
(
domain
,
...
...
include/dt-bindings/thermal/tegra124-soctherm.h
0 → 100644
View file @
2707dbd0
/*
* This header provides constants for binding nvidia,tegra124-soctherm.
*/
#ifndef _DT_BINDINGS_THERMAL_TEGRA124_SOCTHERM_H
#define _DT_BINDINGS_THERMAL_TEGRA124_SOCTHERM_H
#define TEGRA124_SOCTHERM_SENSOR_CPU 0
#define TEGRA124_SOCTHERM_SENSOR_MEM 1
#define TEGRA124_SOCTHERM_SENSOR_GPU 2
#define TEGRA124_SOCTHERM_SENSOR_PLLX 3
#endif
include/linux/clock_cooling.h
0 → 100644
View file @
2707dbd0
/*
* linux/include/linux/clock_cooling.h
*
* 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.
*/
#ifndef __CPU_COOLING_H__
#define __CPU_COOLING_H__
#include <linux/of.h>
#include <linux/thermal.h>
#include <linux/cpumask.h>
#ifdef CONFIG_CLOCK_THERMAL
/**
* 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.
*/
struct
thermal_cooling_device
*
clock_cooling_register
(
struct
device
*
dev
,
const
char
*
clock_name
);
/**
* clock_cooling_unregister - function to remove clock cooling device.
* @cdev: thermal cooling device pointer.
*/
void
clock_cooling_unregister
(
struct
thermal_cooling_device
*
cdev
);
unsigned
long
clock_cooling_get_level
(
struct
thermal_cooling_device
*
cdev
,
unsigned
long
freq
);
#else
/* !CONFIG_CLOCK_THERMAL */
static
inline
struct
thermal_cooling_device
*
clock_cooling_register
(
struct
device
*
dev
,
const
char
*
clock_name
)
{
return
NULL
;
}
static
inline
void
clock_cooling_unregister
(
struct
thermal_cooling_device
*
cdev
)
{
}
static
inline
unsigned
long
clock_cooling_get_level
(
struct
thermal_cooling_device
*
cdev
,
unsigned
long
freq
)
{
return
THERMAL_CSTATE_INVALID
;
}
#endif
/* CONFIG_CLOCK_THERMAL */
#endif
/* __CPU_COOLING_H__ */
include/linux/thermal.h
View file @
2707dbd0
...
...
@@ -260,19 +260,49 @@ struct thermal_genl_event {
enum
events
event
;
};
/**
* struct thermal_zone_of_device_ops - scallbacks for handling DT based zones
*
* Mandatory:
* @get_temp: a pointer to a function that reads the sensor temperature.
*
* Optional:
* @get_trend: a pointer to a function that reads the sensor temperature trend.
* @set_emul_temp: a pointer to a function that sets sensor emulated
* temperature.
*/
struct
thermal_zone_of_device_ops
{
int
(
*
get_temp
)(
void
*
,
long
*
);
int
(
*
get_trend
)(
void
*
,
long
*
);
int
(
*
set_emul_temp
)(
void
*
,
unsigned
long
);
};
/**
* 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
;
};
/* Function declarations */
#ifdef CONFIG_THERMAL_OF
struct
thermal_zone_device
*
thermal_zone_of_sensor_register
(
struct
device
*
dev
,
int
id
,
void
*
data
,
int
(
*
get_temp
)(
void
*
,
long
*
),
int
(
*
get_trend
)(
void
*
,
long
*
));
thermal_zone_of_sensor_register
(
struct
device
*
dev
,
int
id
,
void
*
data
,
const
struct
thermal_zone_of_device_ops
*
ops
);
void
thermal_zone_of_sensor_unregister
(
struct
device
*
dev
,
struct
thermal_zone_device
*
tz
);
#else
static
inline
struct
thermal_zone_device
*
thermal_zone_of_sensor_register
(
struct
device
*
dev
,
int
id
,
void
*
data
,
int
(
*
get_temp
)(
void
*
,
long
*
),
int
(
*
get_trend
)(
void
*
,
long
*
))
thermal_zone_of_sensor_register
(
struct
device
*
dev
,
int
id
,
void
*
data
,
const
struct
thermal_zone_of_device_ops
*
ops
)
{
return
NULL
;
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment