Merge branch 'for_3.12/exynos' of git://git.kernel.org/pub/scm/linux/kernel/git/evalenti/linux-soc-thermal into exynos

This commit is contained in:
Zhang Rui 2013-08-15 13:56:24 +08:00
commit 9c532d1710
14 changed files with 2143 additions and 1196 deletions

View File

@ -0,0 +1,55 @@
* Exynos Thermal Management Unit (TMU)
** Required properties:
- compatible : One of the following:
"samsung,exynos4412-tmu"
"samsung,exynos4210-tmu"
"samsung,exynos5250-tmu"
"samsung,exynos5440-tmu"
- interrupt-parent : The phandle for the interrupt controller
- reg : Address range of the thermal registers. For soc's which has multiple
instances of TMU and some registers are shared across all TMU's like
interrupt related then 2 set of register has to supplied. First set
belongs to each instance of TMU and second set belongs to common TMU
registers.
- interrupts : Should contain interrupt for thermal system
- clocks : The main clock for TMU device
- clock-names : Thermal system clock name
- vtmu-supply: This entry is optional and provides the regulator node supplying
voltage to TMU. If needed this entry can be placed inside
board/platform specific dts file.
Example 1):
tmu@100C0000 {
compatible = "samsung,exynos4412-tmu";
interrupt-parent = <&combiner>;
reg = <0x100C0000 0x100>;
interrupts = <2 4>;
clocks = <&clock 383>;
clock-names = "tmu_apbif";
status = "disabled";
vtmu-supply = <&tmu_regulator_node>;
};
Example 2):
tmuctrl_0: tmuctrl@160118 {
compatible = "samsung,exynos5440-tmu";
reg = <0x160118 0x230>, <0x160368 0x10>;
interrupts = <0 58 0>;
clocks = <&clock 21>;
clock-names = "tmu_apbif";
};
Note: For multi-instance tmu each instance should have an alias correctly
numbered in "aliases" node.
Example:
aliases {
tmuctrl0 = &tmuctrl_0;
tmuctrl1 = &tmuctrl_1;
tmuctrl2 = &tmuctrl_2;
};

View File

@ -1,17 +1,17 @@
Kernel driver exynos4_tmu Kernel driver exynos_tmu
================= =================
Supported chips: Supported chips:
* ARM SAMSUNG EXYNOS4 series of SoC * ARM SAMSUNG EXYNOS4, EXYNOS5 series of SoC
Prefix: 'exynos4-tmu'
Datasheet: Not publicly available Datasheet: Not publicly available
Authors: Donggeun Kim <dg77.kim@samsung.com> Authors: Donggeun Kim <dg77.kim@samsung.com>
Authors: Amit Daniel <amit.daniel@samsung.com>
Description TMU controller Description:
----------- ---------------------------
This driver allows to read temperature inside SAMSUNG EXYNOS4 series of SoC. This driver allows to read temperature inside SAMSUNG EXYNOS4/5 series of SoC.
The chip only exposes the measured 8-bit temperature code value The chip only exposes the measured 8-bit temperature code value
through a register. through a register.
@ -34,9 +34,9 @@ The three equations are:
TI2: Trimming info for 85 degree Celsius (stored at TRIMINFO register) TI2: Trimming info for 85 degree Celsius (stored at TRIMINFO register)
Temperature code measured at 85 degree Celsius which is unchanged Temperature code measured at 85 degree Celsius which is unchanged
TMU(Thermal Management Unit) in EXYNOS4 generates interrupt TMU(Thermal Management Unit) in EXYNOS4/5 generates interrupt
when temperature exceeds pre-defined levels. when temperature exceeds pre-defined levels.
The maximum number of configurable threshold is four. The maximum number of configurable threshold is five.
The threshold levels are defined as follows: The threshold levels are defined as follows:
Level_0: current temperature > trigger_level_0 + threshold Level_0: current temperature > trigger_level_0 + threshold
Level_1: current temperature > trigger_level_1 + threshold Level_1: current temperature > trigger_level_1 + threshold
@ -47,6 +47,31 @@ The threshold levels are defined as follows:
through the corresponding registers. through the corresponding registers.
When an interrupt occurs, this driver notify kernel thermal framework When an interrupt occurs, this driver notify kernel thermal framework
with the function exynos4_report_trigger. with the function exynos_report_trigger.
Although an interrupt condition for level_0 can be set, Although an interrupt condition for level_0 can be set,
it can be used to synchronize the cooling action. it can be used to synchronize the cooling action.
TMU driver description:
-----------------------
The exynos thermal driver is structured as,
Kernel Core thermal framework
(thermal_core.c, step_wise.c, cpu_cooling.c)
^
|
|
TMU configuration data -------> TMU Driver <------> Exynos Core thermal wrapper
(exynos_tmu_data.c) (exynos_tmu.c) (exynos_thermal_common.c)
(exynos_tmu_data.h) (exynos_tmu.h) (exynos_thermal_common.h)
a) TMU configuration data: This consist of TMU register offsets/bitfields
described through structure exynos_tmu_registers. Also several
other platform data (struct exynos_tmu_platform_data) members
are used to configure the TMU.
b) TMU driver: This component initialises the TMU controller and sets different
thresholds. It invokes core thermal implementation with the call
exynos_report_trigger.
c) Exynos Core thermal wrapper: This provides 3 wrapper function to use the
Kernel core thermal framework. They are exynos_unregister_thermal,
exynos_register_thermal and exynos_report_trigger.

View File

@ -114,14 +114,6 @@ config KIRKWOOD_THERMAL
Support for the Kirkwood thermal sensor driver into the Linux thermal Support for the Kirkwood thermal sensor driver into the Linux thermal
framework. Only kirkwood 88F6282 and 88F6283 have this sensor. framework. Only kirkwood 88F6282 and 88F6283 have this sensor.
config EXYNOS_THERMAL
tristate "Temperature sensor on Samsung EXYNOS"
depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5)
depends on CPU_THERMAL
help
If you say yes here you get support for TMU (Thermal Management
Unit) on SAMSUNG EXYNOS series of SoC.
config DOVE_THERMAL config DOVE_THERMAL
tristate "Temperature sensor on Marvell Dove SoCs" tristate "Temperature sensor on Marvell Dove SoCs"
depends on ARCH_DOVE depends on ARCH_DOVE
@ -184,4 +176,9 @@ menu "Texas Instruments thermal drivers"
source "drivers/thermal/ti-soc-thermal/Kconfig" source "drivers/thermal/ti-soc-thermal/Kconfig"
endmenu endmenu
menu "Samsung thermal drivers"
depends on PLAT_SAMSUNG
source "drivers/thermal/samsung/Kconfig"
endmenu
endif endif

View File

@ -17,7 +17,7 @@ thermal_sys-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o
obj-$(CONFIG_KIRKWOOD_THERMAL) += kirkwood_thermal.o obj-$(CONFIG_KIRKWOOD_THERMAL) += kirkwood_thermal.o
obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o obj-y += samsung/
obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o obj-$(CONFIG_DOVE_THERMAL) += dove_thermal.o
obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o obj-$(CONFIG_DB8500_THERMAL) += db8500_thermal.o
obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
config EXYNOS_THERMAL
tristate "Exynos thermal management unit driver"
depends on ARCH_HAS_BANDGAP
help
If you say yes here you get support for the TMU (Thermal Management
Unit) driver for SAMSUNG EXYNOS series of soc. This driver initialises
the TMU, reports temperature and handles cooling action if defined.
This driver uses the exynos core thermal API's and TMU configuration
data from the supported soc's.
config EXYNOS_THERMAL_CORE
bool "Core thermal framework support for EXYNOS SOC's"
depends on EXYNOS_THERMAL
help
If you say yes here you get support for EXYNOS TMU
(Thermal Management Unit) common registration/unregistration
functions to the core thermal layer and also to use the generic
cpu cooling API's.

View File

@ -0,0 +1,7 @@
#
# Samsung thermal specific Makefile
#
obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o
exynos_thermal-y := exynos_tmu.o
exynos_thermal-y += exynos_tmu_data.o
exynos_thermal-$(CONFIG_EXYNOS_THERMAL_CORE) += exynos_thermal_common.o

View File

@ -0,0 +1,432 @@
/*
* exynos_thermal_common.c - Samsung EXYNOS common thermal 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
*
*/
#include <linux/cpu_cooling.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/thermal.h>
#include "exynos_thermal_common.h"
struct exynos_thermal_zone {
enum thermal_device_mode mode;
struct thermal_zone_device *therm_dev;
struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE];
unsigned int cool_dev_size;
struct platform_device *exynos4_dev;
struct thermal_sensor_conf *sensor_conf;
bool bind;
};
/* Get mode callback functions for thermal zone */
static int exynos_get_mode(struct thermal_zone_device *thermal,
enum thermal_device_mode *mode)
{
struct exynos_thermal_zone *th_zone = thermal->devdata;
if (th_zone)
*mode = th_zone->mode;
return 0;
}
/* Set mode callback functions for thermal zone */
static int exynos_set_mode(struct thermal_zone_device *thermal,
enum thermal_device_mode mode)
{
struct exynos_thermal_zone *th_zone = thermal->devdata;
if (!th_zone) {
dev_err(th_zone->sensor_conf->dev,
"thermal zone not registered\n");
return 0;
}
mutex_lock(&thermal->lock);
if (mode == THERMAL_DEVICE_ENABLED &&
!th_zone->sensor_conf->trip_data.trigger_falling)
thermal->polling_delay = IDLE_INTERVAL;
else
thermal->polling_delay = 0;
mutex_unlock(&thermal->lock);
th_zone->mode = mode;
thermal_zone_device_update(thermal);
dev_dbg(th_zone->sensor_conf->dev,
"thermal polling set for duration=%d msec\n",
thermal->polling_delay);
return 0;
}
/* Get trip type callback functions for thermal zone */
static int exynos_get_trip_type(struct thermal_zone_device *thermal, int trip,
enum thermal_trip_type *type)
{
struct exynos_thermal_zone *th_zone = thermal->devdata;
int max_trip = th_zone->sensor_conf->trip_data.trip_count;
int trip_type;
if (trip < 0 || trip >= max_trip)
return -EINVAL;
trip_type = th_zone->sensor_conf->trip_data.trip_type[trip];
if (trip_type == SW_TRIP)
*type = THERMAL_TRIP_CRITICAL;
else if (trip_type == THROTTLE_ACTIVE)
*type = THERMAL_TRIP_ACTIVE;
else if (trip_type == THROTTLE_PASSIVE)
*type = THERMAL_TRIP_PASSIVE;
else
return -EINVAL;
return 0;
}
/* Get trip temperature callback functions for thermal zone */
static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip,
unsigned long *temp)
{
struct exynos_thermal_zone *th_zone = thermal->devdata;
int max_trip = th_zone->sensor_conf->trip_data.trip_count;
if (trip < 0 || trip >= max_trip)
return -EINVAL;
*temp = th_zone->sensor_conf->trip_data.trip_val[trip];
/* convert the temperature into millicelsius */
*temp = *temp * MCELSIUS;
return 0;
}
/* Get critical temperature callback functions for thermal zone */
static int exynos_get_crit_temp(struct thermal_zone_device *thermal,
unsigned long *temp)
{
struct exynos_thermal_zone *th_zone = thermal->devdata;
int max_trip = th_zone->sensor_conf->trip_data.trip_count;
/* Get the temp of highest trip*/
return exynos_get_trip_temp(thermal, max_trip - 1, temp);
}
/* Bind callback functions for thermal zone */
static int exynos_bind(struct thermal_zone_device *thermal,
struct thermal_cooling_device *cdev)
{
int ret = 0, i, tab_size, level;
struct freq_clip_table *tab_ptr, *clip_data;
struct exynos_thermal_zone *th_zone = thermal->devdata;
struct thermal_sensor_conf *data = th_zone->sensor_conf;
tab_ptr = (struct freq_clip_table *)data->cooling_data.freq_data;
tab_size = data->cooling_data.freq_clip_count;
if (tab_ptr == NULL || tab_size == 0)
return 0;
/* find the cooling device registered*/
for (i = 0; i < th_zone->cool_dev_size; i++)
if (cdev == th_zone->cool_dev[i])
break;
/* No matching cooling device */
if (i == th_zone->cool_dev_size)
return 0;
/* Bind the thermal zone to the cpufreq cooling device */
for (i = 0; i < tab_size; i++) {
clip_data = (struct freq_clip_table *)&(tab_ptr[i]);
level = cpufreq_cooling_get_level(0, clip_data->freq_clip_max);
if (level == THERMAL_CSTATE_INVALID)
return 0;
switch (GET_ZONE(i)) {
case MONITOR_ZONE:
case WARN_ZONE:
if (thermal_zone_bind_cooling_device(thermal, i, cdev,
level, 0)) {
dev_err(data->dev,
"error unbinding cdev inst=%d\n", i);
ret = -EINVAL;
}
th_zone->bind = true;
break;
default:
ret = -EINVAL;
}
}
return ret;
}
/* Unbind callback functions for thermal zone */
static int exynos_unbind(struct thermal_zone_device *thermal,
struct thermal_cooling_device *cdev)
{
int ret = 0, i, tab_size;
struct exynos_thermal_zone *th_zone = thermal->devdata;
struct thermal_sensor_conf *data = th_zone->sensor_conf;
if (th_zone->bind == false)
return 0;
tab_size = data->cooling_data.freq_clip_count;
if (tab_size == 0)
return 0;
/* find the cooling device registered*/
for (i = 0; i < th_zone->cool_dev_size; i++)
if (cdev == th_zone->cool_dev[i])
break;
/* No matching cooling device */
if (i == th_zone->cool_dev_size)
return 0;
/* Bind the thermal zone to the cpufreq cooling device */
for (i = 0; i < tab_size; i++) {
switch (GET_ZONE(i)) {
case MONITOR_ZONE:
case WARN_ZONE:
if (thermal_zone_unbind_cooling_device(thermal, i,
cdev)) {
dev_err(data->dev,
"error unbinding cdev inst=%d\n", i);
ret = -EINVAL;
}
th_zone->bind = false;
break;
default:
ret = -EINVAL;
}
}
return ret;
}
/* Get temperature callback functions for thermal zone */
static int exynos_get_temp(struct thermal_zone_device *thermal,
unsigned long *temp)
{
struct exynos_thermal_zone *th_zone = thermal->devdata;
void *data;
if (!th_zone->sensor_conf) {
dev_err(th_zone->sensor_conf->dev,
"Temperature sensor not initialised\n");
return -EINVAL;
}
data = th_zone->sensor_conf->driver_data;
*temp = th_zone->sensor_conf->read_temperature(data);
/* convert the temperature into millicelsius */
*temp = *temp * MCELSIUS;
return 0;
}
/* Get temperature callback functions for thermal zone */
static int exynos_set_emul_temp(struct thermal_zone_device *thermal,
unsigned long temp)
{
void *data;
int ret = -EINVAL;
struct exynos_thermal_zone *th_zone = thermal->devdata;
if (!th_zone->sensor_conf) {
dev_err(th_zone->sensor_conf->dev,
"Temperature sensor not initialised\n");
return -EINVAL;
}
data = th_zone->sensor_conf->driver_data;
if (th_zone->sensor_conf->write_emul_temp)
ret = th_zone->sensor_conf->write_emul_temp(data, temp);
return ret;
}
/* Get the temperature trend */
static int exynos_get_trend(struct thermal_zone_device *thermal,
int trip, enum thermal_trend *trend)
{
int ret;
unsigned long trip_temp;
ret = exynos_get_trip_temp(thermal, trip, &trip_temp);
if (ret < 0)
return ret;
if (thermal->temperature >= trip_temp)
*trend = THERMAL_TREND_RAISE_FULL;
else
*trend = THERMAL_TREND_DROP_FULL;
return 0;
}
/* Operation callback functions for thermal zone */
static struct thermal_zone_device_ops const exynos_dev_ops = {
.bind = exynos_bind,
.unbind = exynos_unbind,
.get_temp = exynos_get_temp,
.set_emul_temp = exynos_set_emul_temp,
.get_trend = exynos_get_trend,
.get_mode = exynos_get_mode,
.set_mode = exynos_set_mode,
.get_trip_type = exynos_get_trip_type,
.get_trip_temp = exynos_get_trip_temp,
.get_crit_temp = exynos_get_crit_temp,
};
/*
* This function may be called from interrupt based temperature sensor
* when threshold is changed.
*/
void exynos_report_trigger(struct thermal_sensor_conf *conf)
{
unsigned int i;
char data[10];
char *envp[] = { data, NULL };
struct exynos_thermal_zone *th_zone;
if (!conf || !conf->pzone_data) {
pr_err("Invalid temperature sensor configuration data\n");
return;
}
th_zone = conf->pzone_data;
if (th_zone->therm_dev)
return;
if (th_zone->bind == false) {
for (i = 0; i < th_zone->cool_dev_size; i++) {
if (!th_zone->cool_dev[i])
continue;
exynos_bind(th_zone->therm_dev,
th_zone->cool_dev[i]);
}
}
thermal_zone_device_update(th_zone->therm_dev);
mutex_lock(&th_zone->therm_dev->lock);
/* Find the level for which trip happened */
for (i = 0; i < th_zone->sensor_conf->trip_data.trip_count; i++) {
if (th_zone->therm_dev->last_temperature <
th_zone->sensor_conf->trip_data.trip_val[i] * MCELSIUS)
break;
}
if (th_zone->mode == THERMAL_DEVICE_ENABLED &&
!th_zone->sensor_conf->trip_data.trigger_falling) {
if (i > 0)
th_zone->therm_dev->polling_delay = ACTIVE_INTERVAL;
else
th_zone->therm_dev->polling_delay = IDLE_INTERVAL;
}
snprintf(data, sizeof(data), "%u", i);
kobject_uevent_env(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE, envp);
mutex_unlock(&th_zone->therm_dev->lock);
}
/* Register with the in-kernel thermal management */
int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf)
{
int ret;
struct cpumask mask_val;
struct exynos_thermal_zone *th_zone;
if (!sensor_conf || !sensor_conf->read_temperature) {
pr_err("Temperature sensor not initialised\n");
return -EINVAL;
}
th_zone = devm_kzalloc(sensor_conf->dev,
sizeof(struct exynos_thermal_zone), GFP_KERNEL);
if (!th_zone)
return -ENOMEM;
th_zone->sensor_conf = sensor_conf;
/*
* TODO: 1) Handle multiple cooling devices in a thermal zone
* 2) Add a flag/name in cooling info to map to specific
* sensor
*/
if (sensor_conf->cooling_data.freq_clip_count > 0) {
cpumask_set_cpu(0, &mask_val);
th_zone->cool_dev[th_zone->cool_dev_size] =
cpufreq_cooling_register(&mask_val);
if (IS_ERR(th_zone->cool_dev[th_zone->cool_dev_size])) {
dev_err(sensor_conf->dev,
"Failed to register cpufreq cooling device\n");
ret = -EINVAL;
goto err_unregister;
}
th_zone->cool_dev_size++;
}
th_zone->therm_dev = thermal_zone_device_register(
sensor_conf->name, sensor_conf->trip_data.trip_count,
0, th_zone, &exynos_dev_ops, NULL, 0,
sensor_conf->trip_data.trigger_falling ? 0 :
IDLE_INTERVAL);
if (IS_ERR(th_zone->therm_dev)) {
dev_err(sensor_conf->dev,
"Failed to register thermal zone device\n");
ret = PTR_ERR(th_zone->therm_dev);
goto err_unregister;
}
th_zone->mode = THERMAL_DEVICE_ENABLED;
sensor_conf->pzone_data = th_zone;
dev_info(sensor_conf->dev,
"Exynos: Thermal zone(%s) registered\n", sensor_conf->name);
return 0;
err_unregister:
exynos_unregister_thermal(sensor_conf);
return ret;
}
/* Un-Register with the in-kernel thermal management */
void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf)
{
int i;
struct exynos_thermal_zone *th_zone;
if (!sensor_conf || !sensor_conf->pzone_data) {
pr_err("Invalid temperature sensor configuration data\n");
return;
}
th_zone = sensor_conf->pzone_data;
if (th_zone->therm_dev)
thermal_zone_device_unregister(th_zone->therm_dev);
for (i = 0; i < th_zone->cool_dev_size; i++) {
if (th_zone->cool_dev[i])
cpufreq_cooling_unregister(th_zone->cool_dev[i]);
}
dev_info(sensor_conf->dev,
"Exynos: Kernel Thermal management unregistered\n");
}

View File

@ -0,0 +1,107 @@
/*
* exynos_thermal_common.h - Samsung EXYNOS common 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_THERMAL_COMMON_H
#define _EXYNOS_THERMAL_COMMON_H
/* In-kernel thermal framework related macros & definations */
#define SENSOR_NAME_LEN 16
#define MAX_TRIP_COUNT 8
#define MAX_COOLING_DEVICE 4
#define MAX_THRESHOLD_LEVS 5
#define ACTIVE_INTERVAL 500
#define IDLE_INTERVAL 10000
#define MCELSIUS 1000
/* CPU Zone information */
#define PANIC_ZONE 4
#define WARN_ZONE 3
#define MONITOR_ZONE 2
#define SAFE_ZONE 1
#define GET_ZONE(trip) (trip + 2)
#define GET_TRIP(zone) (zone - 2)
enum trigger_type {
THROTTLE_ACTIVE = 1,
THROTTLE_PASSIVE,
SW_TRIP,
HW_TRIP,
};
/**
* struct freq_clip_table
* @freq_clip_max: maximum frequency allowed for this cooling state.
* @temp_level: Temperature level at which the temperature clipping will
* happen.
* @mask_val: cpumask of the allowed cpu's where the clipping will take place.
*
* This structure is required to be filled and passed to the
* cpufreq_cooling_unregister function.
*/
struct freq_clip_table {
unsigned int freq_clip_max;
unsigned int temp_level;
const struct cpumask *mask_val;
};
struct thermal_trip_point_conf {
int trip_val[MAX_TRIP_COUNT];
int trip_type[MAX_TRIP_COUNT];
int trip_count;
unsigned char trigger_falling;
};
struct thermal_cooling_conf {
struct freq_clip_table freq_data[MAX_TRIP_COUNT];
int freq_clip_count;
};
struct thermal_sensor_conf {
char name[SENSOR_NAME_LEN];
int (*read_temperature)(void *data);
int (*write_emul_temp)(void *drv_data, unsigned long temp);
struct thermal_trip_point_conf trip_data;
struct thermal_cooling_conf cooling_data;
void *driver_data;
void *pzone_data;
struct device *dev;
};
/*Functions used exynos based thermal sensor driver*/
#ifdef CONFIG_EXYNOS_THERMAL_CORE
void exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf);
int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf);
void exynos_report_trigger(struct thermal_sensor_conf *sensor_conf);
#else
static inline void
exynos_unregister_thermal(struct thermal_sensor_conf *sensor_conf) { return; }
static inline int
exynos_register_thermal(struct thermal_sensor_conf *sensor_conf) { return 0; }
static inline void
exynos_report_trigger(struct thermal_sensor_conf *sensor_conf) { return; }
#endif /* CONFIG_EXYNOS_THERMAL_CORE */
#endif /* _EXYNOS_THERMAL_COMMON_H */

View File

@ -0,0 +1,768 @@
/*
* exynos_tmu.c - Samsung EXYNOS TMU (Thermal Management Unit)
*
* Copyright (C) 2011 Samsung Electronics
* Donggeun Kim <dg77.kim@samsung.com>
* Amit Daniel Kachhap <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; 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
*
*/
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/interrupt.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/regulator/consumer.h>
#include "exynos_thermal_common.h"
#include "exynos_tmu.h"
#include "exynos_tmu_data.h"
/**
* struct exynos_tmu_data : A structure to hold the private data of the TMU
driver
* @id: identifier of the one instance of the TMU controller.
* @pdata: pointer to the tmu platform/configuration data
* @base: base address of the single instance of the TMU controller.
* @base_common: base address of the common registers of the TMU controller.
* @irq: irq number of the TMU controller.
* @soc: id of the SOC type.
* @irq_work: pointer to the irq work structure.
* @lock: lock to implement synchronization.
* @clk: pointer to the clock structure.
* @temp_error1: fused value of the first point trim.
* @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.
*/
struct exynos_tmu_data {
int id;
struct exynos_tmu_platform_data *pdata;
void __iomem *base;
void __iomem *base_common;
int irq;
enum soc_type soc;
struct work_struct irq_work;
struct mutex lock;
struct clk *clk;
u8 temp_error1, temp_error2;
struct regulator *regulator;
struct thermal_sensor_conf *reg_conf;
};
/*
* TMU treats temperature as a mapped temperature code.
* The temperature is converted differently depending on the calibration type.
*/
static int temp_to_code(struct exynos_tmu_data *data, u8 temp)
{
struct exynos_tmu_platform_data *pdata = data->pdata;
int temp_code;
if (pdata->cal_mode == HW_MODE)
return temp;
if (data->soc == SOC_ARCH_EXYNOS4210)
/* temp should range between 25 and 125 */
if (temp < 25 || temp > 125) {
temp_code = -EINVAL;
goto out;
}
switch (pdata->cal_type) {
case TYPE_TWO_POINT_TRIMMING:
temp_code = (temp - pdata->first_point_trim) *
(data->temp_error2 - data->temp_error1) /
(pdata->second_point_trim - pdata->first_point_trim) +
data->temp_error1;
break;
case TYPE_ONE_POINT_TRIMMING:
temp_code = temp + data->temp_error1 - pdata->first_point_trim;
break;
default:
temp_code = temp + pdata->default_temp_offset;
break;
}
out:
return temp_code;
}
/*
* Calculate a temperature value from a temperature code.
* The unit of the temperature is degree Celsius.
*/
static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code)
{
struct exynos_tmu_platform_data *pdata = data->pdata;
int temp;
if (pdata->cal_mode == HW_MODE)
return temp_code;
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;
}
switch (pdata->cal_type) {
case TYPE_TWO_POINT_TRIMMING:
temp = (temp_code - data->temp_error1) *
(pdata->second_point_trim - pdata->first_point_trim) /
(data->temp_error2 - data->temp_error1) +
pdata->first_point_trim;
break;
case TYPE_ONE_POINT_TRIMMING:
temp = temp_code - data->temp_error1 + pdata->first_point_trim;
break;
default:
temp = temp_code - pdata->default_temp_offset;
break;
}
out:
return temp;
}
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;
unsigned int rising_threshold = 0, falling_threshold = 0;
int ret = 0, threshold_code, i, trigger_levs = 0;
mutex_lock(&data->lock);
clk_enable(data->clk);
if (TMU_SUPPORTS(pdata, READY_STATUS)) {
status = readb(data->base + reg->tmu_status);
if (!status) {
ret = -EBUSY;
goto out;
}
}
if (TMU_SUPPORTS(pdata, TRIM_RELOAD))
__raw_writel(1, data->base + reg->triminfo_ctrl);
if (pdata->cal_mode == HW_MODE)
goto skip_calib_data;
/* 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);
break;
case 1:
trim_info = readl(data->base + reg->triminfo_data);
break;
case 2:
trim_info = readl(data->base -
EXYNOS5440_EFUSE_SWAP_OFFSET + 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 >> reg->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 >> reg->triminfo_85_shift) &
EXYNOS_TMU_TEMP_MASK;
skip_calib_data:
if (pdata->max_trigger_level > MAX_THRESHOLD_LEVS) {
dev_err(&pdev->dev, "Invalid max trigger level\n");
goto out;
}
for (i = 0; i < pdata->max_trigger_level; i++) {
if (!pdata->trigger_levels[i])
continue;
if ((pdata->trigger_type[i] == HW_TRIP) &&
(!pdata->trigger_levels[pdata->max_trigger_level - 1])) {
dev_err(&pdev->dev, "Invalid hw trigger level\n");
ret = -EINVAL;
goto out;
}
/* Count trigger levels except the HW trip*/
if (!(pdata->trigger_type[i] == HW_TRIP))
trigger_levs++;
}
if (data->soc == SOC_ARCH_EXYNOS4210) {
/* Write temperature code for threshold */
threshold_code = temp_to_code(data, pdata->threshold);
if (threshold_code < 0) {
ret = threshold_code;
goto out;
}
writeb(threshold_code,
data->base + reg->threshold_temp);
for (i = 0; i < trigger_levs; i++)
writeb(pdata->trigger_levels[i], data->base +
reg->threshold_th0 + i * sizeof(reg->threshold_th0));
writel(reg->inten_rise_mask, data->base + reg->tmu_intclear);
} else {
/* Write temperature code for rising and falling threshold */
for (i = 0;
i < trigger_levs && i < EXYNOS_MAX_TRIGGER_PER_REG; i++) {
threshold_code = temp_to_code(data,
pdata->trigger_levels[i]);
if (threshold_code < 0) {
ret = threshold_code;
goto out;
}
rising_threshold |= threshold_code << 8 * i;
if (pdata->threshold_falling) {
threshold_code = temp_to_code(data,
pdata->trigger_levels[i] -
pdata->threshold_falling);
if (threshold_code > 0)
falling_threshold |=
threshold_code << 8 * i;
}
}
writel(rising_threshold,
data->base + reg->threshold_th0);
writel(falling_threshold,
data->base + reg->threshold_th1);
writel((reg->inten_rise_mask << reg->inten_rise_shift) |
(reg->inten_fall_mask << reg->inten_fall_shift),
data->base + reg->tmu_intclear);
/* 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 (threshold_code < 0) {
ret = threshold_code;
goto out;
}
if (i == EXYNOS_MAX_TRIGGER_PER_REG - 1) {
/* 1-4 level to be assigned in th0 reg */
rising_threshold |= threshold_code << 8 * i;
writel(rising_threshold,
data->base + reg->threshold_th0);
} else if (i == EXYNOS_MAX_TRIGGER_PER_REG) {
/* 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_common + reg->tmu_pmin);
out:
clk_disable(data->clk);
mutex_unlock(&data->lock);
return ret;
}
static void exynos_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, cal_val;
mutex_lock(&data->lock);
clk_enable(data->clk);
con = readl(data->base + reg->tmu_ctrl);
if (pdata->reference_voltage) {
con &= ~(reg->buf_vref_sel_mask << reg->buf_vref_sel_shift);
con |= pdata->reference_voltage << reg->buf_vref_sel_shift;
}
if (pdata->gain) {
con &= ~(reg->buf_slope_sel_mask << reg->buf_slope_sel_shift);
con |= (pdata->gain << reg->buf_slope_sel_shift);
}
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);
}
if (pdata->cal_mode == HW_MODE) {
con &= ~(reg->calib_mode_mask << reg->calib_mode_shift);
cal_val = 0;
switch (pdata->cal_type) {
case TYPE_TWO_POINT_TRIMMING:
cal_val = 3;
break;
case TYPE_ONE_POINT_TRIMMING_85:
cal_val = 2;
break;
case TYPE_ONE_POINT_TRIMMING_25:
cal_val = 1;
break;
case TYPE_NONE:
break;
default:
dev_err(&pdev->dev, "Invalid calibration type, using none\n");
}
con |= cal_val << reg->calib_mode_shift;
}
if (on) {
con |= (1 << reg->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;
} else {
con &= ~(1 << reg->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);
}
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;
mutex_lock(&data->lock);
clk_enable(data->clk);
temp_code = readb(data->base + reg->tmu_cur_temp);
temp = code_to_temp(data, temp_code);
clk_disable(data->clk);
mutex_unlock(&data->lock);
return temp;
}
#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))
goto out;
if (temp && temp < MCELSIUS)
goto out;
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);
clk_disable(data->clk);
mutex_unlock(&data->lock);
return 0;
out:
return ret;
}
#else
static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp)
{ return -EINVAL; }
#endif/*CONFIG_THERMAL_EMULATION*/
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_irq, val_type;
/* Find which sensor generated this interrupt */
if (reg->tmu_irqstatus) {
val_type = readl(data->base_common + reg->tmu_irqstatus);
if (!((val_type >> data->id) & 0x1))
goto out;
}
exynos_report_trigger(data->reg_conf);
mutex_lock(&data->lock);
clk_enable(data->clk);
/* TODO: take action based on particular interrupt */
val_irq = readl(data->base + reg->tmu_intstat);
/* clear the interrupts */
writel(val_irq, data->base + reg->tmu_intclear);
clk_disable(data->clk);
mutex_unlock(&data->lock);
out:
enable_irq(data->irq);
}
static irqreturn_t exynos_tmu_irq(int irq, void *id)
{
struct exynos_tmu_data *data = id;
disable_irq_nosync(irq);
schedule_work(&data->irq_work);
return IRQ_HANDLED;
}
#ifdef CONFIG_OF
static const struct of_device_id exynos_tmu_match[] = {
{
.compatible = "samsung,exynos4210-tmu",
.data = (void *)EXYNOS4210_TMU_DRV_DATA,
},
{
.compatible = "samsung,exynos4412-tmu",
.data = (void *)EXYNOS5250_TMU_DRV_DATA,
},
{
.compatible = "samsung,exynos5250-tmu",
.data = (void *)EXYNOS5250_TMU_DRV_DATA,
},
{
.compatible = "samsung,exynos5440-tmu",
.data = (void *)EXYNOS5440_TMU_DRV_DATA,
},
{},
};
MODULE_DEVICE_TABLE(of, exynos_tmu_match);
#endif
static inline struct exynos_tmu_platform_data *exynos_get_driver_data(
struct platform_device *pdev, int id)
{
#ifdef CONFIG_OF
struct exynos_tmu_init_data *data_table;
struct exynos_tmu_platform_data *tmu_data;
if (pdev->dev.of_node) {
const struct of_device_id *match;
match = of_match_node(exynos_tmu_match, pdev->dev.of_node);
if (!match)
return NULL;
data_table = (struct exynos_tmu_init_data *) match->data;
if (!data_table || id >= data_table->tmu_count)
return NULL;
tmu_data = data_table->tmu_data;
return (struct exynos_tmu_platform_data *) (tmu_data + id);
}
#endif
return NULL;
}
static int exynos_map_dt_data(struct platform_device *pdev)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
struct exynos_tmu_platform_data *pdata;
struct resource res;
int ret;
if (!data)
return -ENODEV;
/*
* Try enabling the regulator if found
* TODO: Add regulator as an SOC feature, so that regulator enable
* is a compulsory call.
*/
data->regulator = devm_regulator_get(&pdev->dev, "vtmu");
if (!IS_ERR(data->regulator)) {
ret = regulator_enable(data->regulator);
if (ret) {
dev_err(&pdev->dev, "failed to enable vtmu\n");
return ret;
}
} else {
dev_info(&pdev->dev, "Regulator node (vtmu) not found\n");
}
data->id = of_alias_get_id(pdev->dev.of_node, "tmuctrl");
if (data->id < 0)
data->id = 0;
data->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
if (data->irq <= 0) {
dev_err(&pdev->dev, "failed to get IRQ\n");
return -ENODEV;
}
if (of_address_to_resource(pdev->dev.of_node, 0, &res)) {
dev_err(&pdev->dev, "failed to get Resource 0\n");
return -ENODEV;
}
data->base = devm_ioremap(&pdev->dev, res.start, resource_size(&res));
if (!data->base) {
dev_err(&pdev->dev, "Failed to ioremap memory\n");
return -EADDRNOTAVAIL;
}
pdata = exynos_get_driver_data(pdev, data->id);
if (!pdata) {
dev_err(&pdev->dev, "No platform init data supplied.\n");
return -ENODEV;
}
data->pdata = pdata;
/*
* Check if the TMU shares some registers and then try to map the
* memory of common registers.
*/
if (!TMU_SUPPORTS(pdata, SHARED_MEMORY))
return 0;
if (of_address_to_resource(pdev->dev.of_node, 1, &res)) {
dev_err(&pdev->dev, "failed to get Resource 1\n");
return -ENODEV;
}
data->base_common = devm_ioremap(&pdev->dev, res.start,
resource_size(&res));
if (!data->base) {
dev_err(&pdev->dev, "Failed to ioremap memory\n");
return -ENOMEM;
}
return 0;
}
static int exynos_tmu_probe(struct platform_device *pdev)
{
struct exynos_tmu_data *data;
struct exynos_tmu_platform_data *pdata;
struct thermal_sensor_conf *sensor_conf;
int ret, i;
data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data),
GFP_KERNEL);
if (!data) {
dev_err(&pdev->dev, "Failed to allocate driver structure\n");
return -ENOMEM;
}
platform_set_drvdata(pdev, data);
mutex_init(&data->lock);
ret = exynos_map_dt_data(pdev);
if (ret)
return ret;
pdata = data->pdata;
INIT_WORK(&data->irq_work, exynos_tmu_work);
data->clk = devm_clk_get(&pdev->dev, "tmu_apbif");
if (IS_ERR(data->clk)) {
dev_err(&pdev->dev, "Failed to get clock\n");
return PTR_ERR(data->clk);
}
ret = clk_prepare(data->clk);
if (ret)
return ret;
if (pdata->type == SOC_ARCH_EXYNOS ||
pdata->type == SOC_ARCH_EXYNOS4210 ||
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");
goto err_clk;
}
exynos_tmu_control(pdev, true);
/* Allocate a structure to register with the exynos core thermal */
sensor_conf = devm_kzalloc(&pdev->dev,
sizeof(struct thermal_sensor_conf), GFP_KERNEL);
if (!sensor_conf) {
dev_err(&pdev->dev, "Failed to allocate registration struct\n");
ret = -ENOMEM;
goto err_clk;
}
sprintf(sensor_conf->name, "therm_zone%d", data->id);
sensor_conf->read_temperature = (int (*)(void *))exynos_tmu_read;
sensor_conf->write_emul_temp =
(int (*)(void *, unsigned long))exynos_tmu_set_emulation;
sensor_conf->driver_data = data;
sensor_conf->trip_data.trip_count = pdata->trigger_enable[0] +
pdata->trigger_enable[1] + pdata->trigger_enable[2]+
pdata->trigger_enable[3];
for (i = 0; i < sensor_conf->trip_data.trip_count; i++) {
sensor_conf->trip_data.trip_val[i] =
pdata->threshold + pdata->trigger_levels[i];
sensor_conf->trip_data.trip_type[i] =
pdata->trigger_type[i];
}
sensor_conf->trip_data.trigger_falling = pdata->threshold_falling;
sensor_conf->cooling_data.freq_clip_count = pdata->freq_tab_count;
for (i = 0; i < pdata->freq_tab_count; i++) {
sensor_conf->cooling_data.freq_data[i].freq_clip_max =
pdata->freq_tab[i].freq_clip_max;
sensor_conf->cooling_data.freq_data[i].temp_level =
pdata->freq_tab[i].temp_level;
}
sensor_conf->dev = &pdev->dev;
/* Register the sensor with thermal management interface */
ret = exynos_register_thermal(sensor_conf);
if (ret) {
dev_err(&pdev->dev, "Failed to register thermal interface\n");
goto err_clk;
}
data->reg_conf = sensor_conf;
ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq,
IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data);
if (ret) {
dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
goto err_clk;
}
return 0;
err_clk:
clk_unprepare(data->clk);
return ret;
}
static int exynos_tmu_remove(struct platform_device *pdev)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
exynos_tmu_control(pdev, false);
exynos_unregister_thermal(data->reg_conf);
clk_unprepare(data->clk);
if (!IS_ERR(data->regulator))
regulator_disable(data->regulator);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int exynos_tmu_suspend(struct device *dev)
{
exynos_tmu_control(to_platform_device(dev), false);
return 0;
}
static int exynos_tmu_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
exynos_tmu_initialize(pdev);
exynos_tmu_control(pdev, true);
return 0;
}
static SIMPLE_DEV_PM_OPS(exynos_tmu_pm,
exynos_tmu_suspend, exynos_tmu_resume);
#define EXYNOS_TMU_PM (&exynos_tmu_pm)
#else
#define EXYNOS_TMU_PM NULL
#endif
static struct platform_driver exynos_tmu_driver = {
.driver = {
.name = "exynos-tmu",
.owner = THIS_MODULE,
.pm = EXYNOS_TMU_PM,
.of_match_table = of_match_ptr(exynos_tmu_match),
},
.probe = exynos_tmu_probe,
.remove = exynos_tmu_remove,
};
module_platform_driver(exynos_tmu_driver);
MODULE_DESCRIPTION("EXYNOS TMU Driver");
MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:exynos-tmu");

View File

@ -0,0 +1,311 @@
/*
* exynos_tmu.h - Samsung EXYNOS TMU (Thermal Management Unit)
*
* Copyright (C) 2011 Samsung Electronics
* Donggeun Kim <dg77.kim@samsung.com>
* 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_H
#define _EXYNOS_TMU_H
#include <linux/cpu_cooling.h>
#include "exynos_thermal_common.h"
enum calibration_type {
TYPE_ONE_POINT_TRIMMING,
TYPE_ONE_POINT_TRIMMING_25,
TYPE_ONE_POINT_TRIMMING_85,
TYPE_TWO_POINT_TRIMMING,
TYPE_NONE,
};
enum calibration_mode {
SW_MODE,
HW_MODE,
};
enum soc_type {
SOC_ARCH_EXYNOS4210 = 1,
SOC_ARCH_EXYNOS,
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_SHARED_MEMORY - 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_SHARED_MEMORY 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_25_shift: shift bit of the 25 C trim value in triminfo_data reg.
* @triminfo_85_shift: shift bit of the 85 C trim value in triminfo_data reg.
* @triminfo_ctrl: trim info controller register.
* @triminfo_reload_shift: shift of triminfo reload enable bit in triminfo_ctrl
reg.
* @tmu_ctrl: TMU main controller register.
* @buf_vref_sel_shift: shift bits of reference voltage in tmu_ctrl register.
* @buf_vref_sel_mask: mask bits of reference voltage in tmu_ctrl register.
* @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.
* @buf_slope_sel_shift: shift bits of amplifier gain value in tmu_ctrl
register.
* @buf_slope_sel_mask: mask bits of amplifier gain value in tmu_ctrl register.
* @calib_mode_shift: shift bits of calibration mode value in tmu_ctrl
register.
* @calib_mode_mask: mask bits of calibration mode value in tmu_ctrl
register.
* @therm_trip_tq_en_shift: shift bits of thermal trip enable by TQ pin in
tmu_ctrl register.
* @core_en_shift: shift bits of TMU core enable bit in tmu_ctrl register.
* @tmu_status: register drescribing the TMU status.
* @tmu_cur_temp: register containing the current temperature of the TMU.
* @tmu_cur_temp_shift: shift bits of current temp value in tmu_cur_temp
register.
* @threshold_temp: register containing the base threshold level.
* @threshold_th0: Register containing first set of rising levels.
* @threshold_th0_l0_shift: shift bits of level0 threshold temperature.
* @threshold_th0_l1_shift: shift bits of level1 threshold temperature.
* @threshold_th0_l2_shift: shift bits of level2 threshold temperature.
* @threshold_th0_l3_shift: shift bits of level3 threshold temperature.
* @threshold_th1: Register containing second set of rising levels.
* @threshold_th1_l0_shift: shift bits of level0 threshold temperature.
* @threshold_th1_l1_shift: shift bits of level1 threshold temperature.
* @threshold_th1_l2_shift: shift bits of level2 threshold temperature.
* @threshold_th1_l3_shift: shift bits of level3 threshold temperature.
* @threshold_th2: Register containing third set of rising levels.
* @threshold_th2_l0_shift: shift bits of level0 threshold temperature.
* @threshold_th3: Register containing fourth 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_rise_shift: shift bits of all rising interrupt bits.
* @inten_rise_mask: mask bits of all rising interrupt bits.
* @inten_fall_shift: shift bits of all rising interrupt bits.
* @inten_fall_mask: mask bits of all rising interrupt 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.
* @inten_fall1_shift: shift bits of falling 1 interrupt bits.
* @inten_fall2_shift: shift bits of falling 2 interrupt bits.
* @inten_fall3_shift: shift bits of falling 3 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.
* @emul_time_mask: mask 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_25_shift;
u32 triminfo_85_shift;
u32 triminfo_ctrl;
u32 triminfo_reload_shift;
u32 tmu_ctrl;
u32 buf_vref_sel_shift;
u32 buf_vref_sel_mask;
u32 therm_trip_mode_shift;
u32 therm_trip_mode_mask;
u32 therm_trip_en_shift;
u32 buf_slope_sel_shift;
u32 buf_slope_sel_mask;
u32 calib_mode_shift;
u32 calib_mode_mask;
u32 therm_trip_tq_en_shift;
u32 core_en_shift;
u32 tmu_status;
u32 tmu_cur_temp;
u32 tmu_cur_temp_shift;
u32 threshold_temp;
u32 threshold_th0;
u32 threshold_th0_l0_shift;
u32 threshold_th0_l1_shift;
u32 threshold_th0_l2_shift;
u32 threshold_th0_l3_shift;
u32 threshold_th1;
u32 threshold_th1_l0_shift;
u32 threshold_th1_l1_shift;
u32 threshold_th1_l2_shift;
u32 threshold_th1_l3_shift;
u32 threshold_th2;
u32 threshold_th2_l0_shift;
u32 threshold_th3;
u32 threshold_th3_l0_shift;
u32 tmu_inten;
u32 inten_rise_shift;
u32 inten_rise_mask;
u32 inten_fall_shift;
u32 inten_fall_mask;
u32 inten_rise0_shift;
u32 inten_rise1_shift;
u32 inten_rise2_shift;
u32 inten_rise3_shift;
u32 inten_fall0_shift;
u32 inten_fall1_shift;
u32 inten_fall2_shift;
u32 inten_fall3_shift;
u32 tmu_intstat;
u32 tmu_intclear;
u32 emul_con;
u32 emul_temp_shift;
u32 emul_time_shift;
u32 emul_time_mask;
u32 tmu_irqstatus;
u32 tmu_pmin;
};
/**
* struct exynos_tmu_platform_data
* @threshold: basic temperature for generating interrupt
* 25 <= threshold <= 125 [unit: degree Celsius]
* @threshold_falling: differntial value for setting threshold
* of temperature falling interrupt.
* @trigger_levels: array for each interrupt levels
* [unit: degree Celsius]
* 0: temperature for trigger_level0 interrupt
* condition for trigger_level0 interrupt:
* current temperature > threshold + trigger_levels[0]
* 1: temperature for trigger_level1 interrupt
* condition for trigger_level1 interrupt:
* current temperature > threshold + trigger_levels[1]
* 2: temperature for trigger_level2 interrupt
* condition for trigger_level2 interrupt:
* current temperature > threshold + trigger_levels[2]
* 3: temperature for trigger_level3 interrupt
* condition for trigger_level3 interrupt:
* current temperature > threshold + trigger_levels[3]
* @trigger_type: defines the type of trigger. Possible values are,
* THROTTLE_ACTIVE trigger type
* THROTTLE_PASSIVE trigger type
* SW_TRIP trigger type
* HW_TRIP
* @trigger_enable[]: array to denote which trigger levels are enabled.
* 1 = enable trigger_level[] interrupt,
* 0 = disable trigger_level[] interrupt
* @max_trigger_level: max trigger level supported by the TMU
* @gain: gain of amplifier in the positive-TC generator block
* 0 <= gain <= 15
* @reference_voltage: reference voltage of amplifier
* in the positive-TC generator block
* 0 <= reference_voltage <= 31
* @noise_cancel_mode: noise cancellation mode
* 000, 100, 101, 110 and 111 can be different modes
* @type: determines the type of SOC
* @efuse_value: platform defined fuse value
* @min_efuse_value: minimum valid trimming data
* @max_efuse_value: maximum valid trimming data
* @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
* @cal_type: calibration type for temperature
* @cal_mode: calibration mode 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.
*/
struct exynos_tmu_platform_data {
u8 threshold;
u8 threshold_falling;
u8 trigger_levels[MAX_TRIP_COUNT];
enum trigger_type trigger_type[MAX_TRIP_COUNT];
bool trigger_enable[MAX_TRIP_COUNT];
u8 max_trigger_level;
u8 gain;
u8 reference_voltage;
u8 noise_cancel_mode;
u32 efuse_value;
u32 min_efuse_value;
u32 max_efuse_value;
u8 first_point_trim;
u8 second_point_trim;
u8 default_temp_offset;
enum calibration_type cal_type;
enum calibration_mode cal_mode;
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;
};
/**
* struct exynos_tmu_init_data
* @tmu_count: number of TMU instances.
* @tmu_data: platform data of all TMU instances.
* This structure is required to store data for multi-instance exynos tmu
* driver.
*/
struct exynos_tmu_init_data {
int tmu_count;
struct exynos_tmu_platform_data tmu_data[];
};
#endif /* _EXYNOS_TMU_H */

View File

@ -0,0 +1,250 @@
/*
* exynos_tmu_data.c - Samsung EXYNOS tmu data 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
*
*/
#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,
.triminfo_25_shift = EXYNOS_TRIMINFO_25_SHIFT,
.triminfo_85_shift = EXYNOS_TRIMINFO_85_SHIFT,
.tmu_ctrl = EXYNOS_TMU_REG_CONTROL,
.buf_vref_sel_shift = EXYNOS_TMU_REF_VOLTAGE_SHIFT,
.buf_vref_sel_mask = EXYNOS_TMU_REF_VOLTAGE_MASK,
.buf_slope_sel_shift = EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT,
.buf_slope_sel_mask = EXYNOS_TMU_BUF_SLOPE_SEL_MASK,
.core_en_shift = EXYNOS_TMU_CORE_EN_SHIFT,
.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_rise_mask = EXYNOS4210_TMU_TRIG_LEVEL_MASK,
.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 = {
{
.threshold = 80,
.trigger_levels[0] = 5,
.trigger_levels[1] = 20,
.trigger_levels[2] = 30,
.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,
.max_trigger_level = 4,
.gain = 15,
.reference_voltage = 7,
.cal_type = TYPE_ONE_POINT_TRIMMING,
.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 = 100,
},
.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_EXYNOS5250) || defined(CONFIG_SOC_EXYNOS4412)
static const struct exynos_tmu_registers exynos5250_tmu_registers = {
.triminfo_data = EXYNOS_TMU_REG_TRIMINFO,
.triminfo_25_shift = EXYNOS_TRIMINFO_25_SHIFT,
.triminfo_85_shift = EXYNOS_TRIMINFO_85_SHIFT,
.triminfo_ctrl = EXYNOS_TMU_TRIMINFO_CON,
.triminfo_reload_shift = EXYNOS_TRIMINFO_RELOAD_SHIFT,
.tmu_ctrl = EXYNOS_TMU_REG_CONTROL,
.buf_vref_sel_shift = EXYNOS_TMU_REF_VOLTAGE_SHIFT,
.buf_vref_sel_mask = EXYNOS_TMU_REF_VOLTAGE_MASK,
.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,
.buf_slope_sel_shift = EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT,
.buf_slope_sel_mask = EXYNOS_TMU_BUF_SLOPE_SEL_MASK,
.core_en_shift = EXYNOS_TMU_CORE_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_rise_mask = EXYNOS_TMU_RISE_INT_MASK,
.inten_rise_shift = EXYNOS_TMU_RISE_INT_SHIFT,
.inten_fall_mask = EXYNOS_TMU_FALL_INT_MASK,
.inten_fall_shift = EXYNOS_TMU_FALL_INT_SHIFT,
.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,
.emul_time_mask = EXYNOS_EMUL_TIME_MASK,
};
#define EXYNOS5250_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, \
.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, \
.type = SOC_ARCH_EXYNOS, \
.registers = &exynos5250_tmu_registers, \
.features = (TMU_SUPPORT_EMULATION | TMU_SUPPORT_TRIM_RELOAD | \
TMU_SUPPORT_FALLING_TRIP | TMU_SUPPORT_READY_STATUS | \
TMU_SUPPORT_EMUL_TIME)
struct exynos_tmu_init_data const exynos5250_default_tmu_data = {
.tmu_data = {
{ EXYNOS5250_TMU_DATA },
},
.tmu_count = 1,
};
#endif
#if defined(CONFIG_SOC_EXYNOS5440)
static const struct exynos_tmu_registers exynos5440_tmu_registers = {
.triminfo_data = EXYNOS5440_TMU_S0_7_TRIM,
.triminfo_25_shift = EXYNOS_TRIMINFO_25_SHIFT,
.triminfo_85_shift = EXYNOS_TRIMINFO_85_SHIFT,
.tmu_ctrl = EXYNOS5440_TMU_S0_7_CTRL,
.buf_vref_sel_shift = EXYNOS_TMU_REF_VOLTAGE_SHIFT,
.buf_vref_sel_mask = EXYNOS_TMU_REF_VOLTAGE_MASK,
.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,
.buf_slope_sel_shift = EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT,
.buf_slope_sel_mask = EXYNOS_TMU_BUF_SLOPE_SEL_MASK,
.calib_mode_shift = EXYNOS_TMU_CALIB_MODE_SHIFT,
.calib_mode_mask = EXYNOS_TMU_CALIB_MODE_MASK,
.core_en_shift = EXYNOS_TMU_CORE_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_rise_mask = EXYNOS5440_TMU_RISE_INT_MASK,
.inten_rise_shift = EXYNOS5440_TMU_RISE_INT_SHIFT,
.inten_fall_mask = EXYNOS5440_TMU_FALL_INT_MASK,
.inten_fall_shift = EXYNOS5440_TMU_FALL_INT_SHIFT,
.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, \
.trigger_levels[4] = 105, \
.trigger_enable[0] = 1, \
.trigger_type[0] = SW_TRIP, \
.trigger_type[4] = HW_TRIP, \
.max_trigger_level = 5, \
.gain = 5, \
.reference_voltage = 16, \
.noise_cancel_mode = 4, \
.cal_type = TYPE_ONE_POINT_TRIMMING, \
.cal_mode = 0, \
.efuse_value = 0x5b2d, \
.min_efuse_value = 16, \
.max_efuse_value = 76, \
.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_SHARED_MEMORY),
struct exynos_tmu_init_data const exynos5440_default_tmu_data = {
.tmu_data = {
{ EXYNOS5440_TMU_DATA } ,
{ EXYNOS5440_TMU_DATA } ,
{ EXYNOS5440_TMU_DATA } ,
},
.tmu_count = 3,
};
#endif

View File

@ -0,0 +1,155 @@
/*
* 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
/* Exynos4210 specific registers */
#define EXYNOS4210_TMU_REG_THRESHOLD_TEMP 0x44
#define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50
#define EXYNOS4210_TMU_REG_TRIG_LEVEL1 0x54
#define EXYNOS4210_TMU_REG_TRIG_LEVEL2 0x58
#define EXYNOS4210_TMU_REG_TRIG_LEVEL3 0x5C
#define EXYNOS4210_TMU_REG_PAST_TEMP0 0x60
#define EXYNOS4210_TMU_REG_PAST_TEMP1 0x64
#define EXYNOS4210_TMU_REG_PAST_TEMP2 0x68
#define EXYNOS4210_TMU_REG_PAST_TEMP3 0x6C
#define EXYNOS4210_TMU_TRIG_LEVEL0_MASK 0x1
#define EXYNOS4210_TMU_TRIG_LEVEL1_MASK 0x10
#define EXYNOS4210_TMU_TRIG_LEVEL2_MASK 0x100
#define EXYNOS4210_TMU_TRIG_LEVEL3_MASK 0x1000
#define EXYNOS4210_TMU_TRIG_LEVEL_MASK 0x1111
#define EXYNOS4210_TMU_INTCLEAR_VAL 0x1111
/* Exynos5250 and Exynos4412 specific registers */
#define EXYNOS_TMU_TRIMINFO_CON 0x14
#define EXYNOS_THD_TEMP_RISE 0x50
#define EXYNOS_THD_TEMP_FALL 0x54
#define EXYNOS_EMUL_CON 0x80
#define EXYNOS_TRIMINFO_RELOAD_SHIFT 1
#define EXYNOS_TRIMINFO_25_SHIFT 0
#define EXYNOS_TRIMINFO_85_SHIFT 8
#define EXYNOS_TMU_RISE_INT_MASK 0x111
#define EXYNOS_TMU_RISE_INT_SHIFT 0
#define EXYNOS_TMU_FALL_INT_MASK 0x111
#define EXYNOS_TMU_FALL_INT_SHIFT 12
#define EXYNOS_TMU_CLEAR_RISE_INT 0x111
#define EXYNOS_TMU_CLEAR_FALL_INT (0x111 << 12)
#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_CALIB_MODE_SHIFT 4
#define EXYNOS_TMU_CALIB_MODE_MASK 0x3
#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_TMU_INTEN_FALL1_SHIFT 20
#define EXYNOS_TMU_INTEN_FALL2_SHIFT 24
#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
/*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_EVTEN 0x1F0
#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_TEMP 0x008
#define EXYNOS5440_TMU_RISE_INT_MASK 0xf
#define EXYNOS5440_TMU_RISE_INT_SHIFT 0
#define EXYNOS5440_TMU_FALL_INT_MASK 0xf
#define EXYNOS5440_TMU_FALL_INT_SHIFT 4
#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_INTEN_FALL1_SHIFT 5
#define EXYNOS5440_TMU_INTEN_FALL2_SHIFT 6
#define EXYNOS5440_TMU_INTEN_FALL3_SHIFT 7
#define EXYNOS5440_TMU_TH_RISE0_SHIFT 0
#define EXYNOS5440_TMU_TH_RISE1_SHIFT 8
#define EXYNOS5440_TMU_TH_RISE2_SHIFT 16
#define EXYNOS5440_TMU_TH_RISE3_SHIFT 24
#define EXYNOS5440_TMU_TH_RISE4_SHIFT 24
#define EXYNOS5440_EFUSE_SWAP_OFFSET 8
#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_EXYNOS5250) || defined(CONFIG_SOC_EXYNOS4412))
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_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*/

View File

@ -1,119 +0,0 @@
/*
* exynos_thermal.h - Samsung EXYNOS TMU (Thermal Management Unit)
*
* Copyright (C) 2011 Samsung Electronics
* Donggeun Kim <dg77.kim@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 _LINUX_EXYNOS_THERMAL_H
#define _LINUX_EXYNOS_THERMAL_H
#include <linux/cpu_cooling.h>
enum calibration_type {
TYPE_ONE_POINT_TRIMMING,
TYPE_TWO_POINT_TRIMMING,
TYPE_NONE,
};
enum soc_type {
SOC_ARCH_EXYNOS4210 = 1,
SOC_ARCH_EXYNOS,
};
/**
* struct freq_clip_table
* @freq_clip_max: maximum frequency allowed for this cooling state.
* @temp_level: Temperature level at which the temperature clipping will
* happen.
* @mask_val: cpumask of the allowed cpu's where the clipping will take place.
*
* This structure is required to be filled and passed to the
* cpufreq_cooling_unregister function.
*/
struct freq_clip_table {
unsigned int freq_clip_max;
unsigned int temp_level;
const struct cpumask *mask_val;
};
/**
* struct exynos_tmu_platform_data
* @threshold: basic temperature for generating interrupt
* 25 <= threshold <= 125 [unit: degree Celsius]
* @threshold_falling: differntial value for setting threshold
* of temperature falling interrupt.
* @trigger_levels: array for each interrupt levels
* [unit: degree Celsius]
* 0: temperature for trigger_level0 interrupt
* condition for trigger_level0 interrupt:
* current temperature > threshold + trigger_levels[0]
* 1: temperature for trigger_level1 interrupt
* condition for trigger_level1 interrupt:
* current temperature > threshold + trigger_levels[1]
* 2: temperature for trigger_level2 interrupt
* condition for trigger_level2 interrupt:
* current temperature > threshold + trigger_levels[2]
* 3: temperature for trigger_level3 interrupt
* condition for trigger_level3 interrupt:
* current temperature > threshold + trigger_levels[3]
* @trigger_level0_en:
* 1 = enable trigger_level0 interrupt,
* 0 = disable trigger_level0 interrupt
* @trigger_level1_en:
* 1 = enable trigger_level1 interrupt,
* 0 = disable trigger_level1 interrupt
* @trigger_level2_en:
* 1 = enable trigger_level2 interrupt,
* 0 = disable trigger_level2 interrupt
* @trigger_level3_en:
* 1 = enable trigger_level3 interrupt,
* 0 = disable trigger_level3 interrupt
* @gain: gain of amplifier in the positive-TC generator block
* 0 <= gain <= 15
* @reference_voltage: reference voltage of amplifier
* in the positive-TC generator block
* 0 <= reference_voltage <= 31
* @noise_cancel_mode: noise cancellation mode
* 000, 100, 101, 110 and 111 can be different modes
* @type: determines the type of SOC
* @efuse_value: platform defined fuse value
* @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.
*
* This structure is required for configuration of exynos_tmu driver.
*/
struct exynos_tmu_platform_data {
u8 threshold;
u8 threshold_falling;
u8 trigger_levels[4];
bool trigger_level0_en;
bool trigger_level1_en;
bool trigger_level2_en;
bool trigger_level3_en;
u8 gain;
u8 reference_voltage;
u8 noise_cancel_mode;
u32 efuse_value;
enum calibration_type cal_type;
enum soc_type type;
struct freq_clip_table freq_tab[4];
unsigned int freq_tab_count;
};
#endif /* _LINUX_EXYNOS_THERMAL_H */