提交 5fd41f2a 编写于 作者: L Linus Torvalds

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

Pull thermal updates from Zhang Rui:
 "Specifics:

   - adds full support for 2 types of Thermal Controllers produced by
     STMicroelectronics.  One is a more traditional memory mapped
     variant, the other is controlled solely by system configuration
     registers.  From Lee Jones.

   - add TMU (Thermal Management Unit) support for Exynos3250 Soc.
     From Chanwoo Choi.

   - add critical and passive trip point support for int3403 thermal
     driver.  From Srinivas Pandruvada.

   - a couple of small fixes/cleanups from Javi Merino, and Geert
     Uytterhoeven"

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux:
  thermal: document struct thermal_zone_device and thermal_governor
  thermal: cpu_cooling: fix typo highjack -> hijack
  thermal: rcar: Document SoC-specific bindings
  thermal: samsung: Add TMU support for Exynos3250 SoC
  thermal: exynos: fix ordering in exynos_tmu_remove()
  thermal: allow building dove_thermal with mvebu
  thermal: sti: Add support for ST's Memory Mapped based Thermal controller
  thermal: sti: Add support for ST's System Config Register based Thermal controller
  thermal: sti: Introduce ST Thermal core code
  thermal: sti: Supply Device Tree documentation
  Thermal: int3403: Add CRT and PSV trip
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
** Required properties: ** Required properties:
- compatible : One of the following: - compatible : One of the following:
"samsung,exynos3250-tmu"
"samsung,exynos4412-tmu" "samsung,exynos4412-tmu"
"samsung,exynos4210-tmu" "samsung,exynos4210-tmu"
"samsung,exynos5250-tmu" "samsung,exynos5250-tmu"
......
* Renesas R-Car Thermal * Renesas R-Car Thermal
Required properties: Required properties:
- compatible : "renesas,rcar-thermal" - compatible : "renesas,thermal-<soctype>", "renesas,rcar-thermal"
as fallback.
Examples with soctypes are:
- "renesas,thermal-r8a73a4" (R-Mobile AP6)
- "renesas,thermal-r8a7779" (R-Car H1)
- "renesas,thermal-r8a7790" (R-Car H2)
- "renesas,thermal-r8a7791" (R-Car M2)
- reg : Address range of the thermal registers. - reg : Address range of the thermal registers.
The 1st reg will be recognized as common register The 1st reg will be recognized as common register
if it has "interrupts". if it has "interrupts".
...@@ -12,18 +18,18 @@ Option properties: ...@@ -12,18 +18,18 @@ Option properties:
Example (non interrupt support): Example (non interrupt support):
thermal@e61f0100 { thermal@ffc48000 {
compatible = "renesas,rcar-thermal"; compatible = "renesas,thermal-r8a7779", "renesas,rcar-thermal";
reg = <0xe61f0100 0x38>; reg = <0xffc48000 0x38>;
}; };
Example (interrupt support): Example (interrupt support):
thermal@e61f0000 { thermal@e61f0000 {
compatible = "renesas,rcar-thermal"; compatible = "renesas,thermal-r8a73a4", "renesas,rcar-thermal";
reg = <0xe61f0000 0x14 reg = <0xe61f0000 0x14
0xe61f0100 0x38 0xe61f0100 0x38
0xe61f0200 0x38 0xe61f0200 0x38
0xe61f0300 0x38>; 0xe61f0300 0x38>;
interrupts = <0 69 4>; interrupts = <0 69 IRQ_TYPE_LEVEL_HIGH>;
}; };
Binding for Thermal Sensor driver for STMicroelectronics STi series of SoCs.
Required parameters:
-------------------
compatible : st,<SoC>-<module>-thermal; should be one of:
"st,stih415-sas-thermal",
"st,stih415-mpe-thermal",
"st,stih416-sas-thermal"
"st,stih416-mpe-thermal"
"st,stid127-thermal" or
"st,stih407-thermal"
according to the SoC type (stih415, stih416, stid127, stih407)
and module type (sas or mpe). On stid127 & stih407 there is only
one die/module, so there is no module type in the compatible
string.
clock-names : Should be "thermal".
See: Documentation/devicetree/bindings/resource-names.txt
clocks : Phandle of the clock used by the thermal sensor.
See: Documentation/devicetree/bindings/clock/clock-bindings.txt
Optional parameters:
-------------------
reg : For non-sysconf based sensors, this should be the physical base
address and length of the sensor's registers.
interrupts : Standard way to define interrupt number.
Interrupt is mandatory to be defined when compatible is
"stih416-mpe-thermal".
NB: For thermal sensor's for which no interrupt has been
defined, a polling delay of 1000ms will be used to read the
temperature from device.
Example:
temp1@fdfe8000 {
compatible = "st,stih416-mpe-thermal";
reg = <0xfdfe8000 0x10>;
clock-names = "thermal";
clocks = <&clk_m_mpethsens>;
interrupts = <GIC_SPI 23 IRQ_TYPE_NONE>;
};
...@@ -151,7 +151,7 @@ config KIRKWOOD_THERMAL ...@@ -151,7 +151,7 @@ config KIRKWOOD_THERMAL
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 || MACH_DOVE
depends on OF depends on OF
help help
Support for the Dove thermal sensor driver in the Linux thermal Support for the Dove thermal sensor driver in the Linux thermal
...@@ -243,4 +243,9 @@ depends on ARCH_EXYNOS ...@@ -243,4 +243,9 @@ depends on ARCH_EXYNOS
source "drivers/thermal/samsung/Kconfig" source "drivers/thermal/samsung/Kconfig"
endmenu endmenu
menu "STMicroelectronics thermal drivers"
depends on ARCH_STI && OF
source "drivers/thermal/st/Kconfig"
endmenu
endif endif
...@@ -32,3 +32,4 @@ obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o ...@@ -32,3 +32,4 @@ obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o
obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o
obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o
obj-$(CONFIG_ST_THERMAL) += st/
...@@ -305,7 +305,7 @@ static int cpufreq_apply_cooling(struct cpufreq_cooling_device *cpufreq_device, ...@@ -305,7 +305,7 @@ static int cpufreq_apply_cooling(struct cpufreq_cooling_device *cpufreq_device,
* @event: value showing cpufreq event for which this function invoked. * @event: value showing cpufreq event for which this function invoked.
* @data: callback-specific data * @data: callback-specific data
* *
* Callback to highjack the notification on cpufreq policy transition. * Callback to hijack the notification on cpufreq policy transition.
* Every time there is a change in policy, we will intercept and * Every time there is a change in policy, we will intercept and
* update the cpufreq policy with thermal constraints. * update the cpufreq policy with thermal constraints.
* *
......
...@@ -33,6 +33,10 @@ ...@@ -33,6 +33,10 @@
struct int3403_sensor { struct int3403_sensor {
struct thermal_zone_device *tzone; struct thermal_zone_device *tzone;
unsigned long *thresholds; unsigned long *thresholds;
unsigned long crit_temp;
int crit_trip_id;
unsigned long psv_temp;
int psv_trip_id;
}; };
static int sys_get_curr_temp(struct thermal_zone_device *tzone, static int sys_get_curr_temp(struct thermal_zone_device *tzone,
...@@ -79,12 +83,18 @@ static int sys_get_trip_temp(struct thermal_zone_device *tzone, ...@@ -79,12 +83,18 @@ static int sys_get_trip_temp(struct thermal_zone_device *tzone,
struct acpi_device *device = tzone->devdata; struct acpi_device *device = tzone->devdata;
struct int3403_sensor *obj = acpi_driver_data(device); struct int3403_sensor *obj = acpi_driver_data(device);
/* if (trip == obj->crit_trip_id)
* get_trip_temp is a mandatory callback but *temp = obj->crit_temp;
* PATx method doesn't return any value, so return else if (trip == obj->psv_trip_id)
* cached value, which was last set from user space. *temp = obj->psv_temp;
*/ else {
*temp = obj->thresholds[trip]; /*
* get_trip_temp is a mandatory callback but
* PATx method doesn't return any value, so return
* cached value, which was last set from user space.
*/
*temp = obj->thresholds[trip];
}
return 0; return 0;
} }
...@@ -92,8 +102,14 @@ static int sys_get_trip_temp(struct thermal_zone_device *tzone, ...@@ -92,8 +102,14 @@ static int sys_get_trip_temp(struct thermal_zone_device *tzone,
static int sys_get_trip_type(struct thermal_zone_device *thermal, static int sys_get_trip_type(struct thermal_zone_device *thermal,
int trip, enum thermal_trip_type *type) int trip, enum thermal_trip_type *type)
{ {
struct acpi_device *device = thermal->devdata;
struct int3403_sensor *obj = acpi_driver_data(device);
/* Mandatory callback, may not mean much here */ /* Mandatory callback, may not mean much here */
*type = THERMAL_TRIP_PASSIVE; if (trip == obj->crit_trip_id)
*type = THERMAL_TRIP_CRITICAL;
else
*type = THERMAL_TRIP_PASSIVE;
return 0; return 0;
} }
...@@ -155,6 +171,34 @@ static void acpi_thermal_notify(struct acpi_device *device, u32 event) ...@@ -155,6 +171,34 @@ static void acpi_thermal_notify(struct acpi_device *device, u32 event)
} }
} }
static int sys_get_trip_crt(struct acpi_device *device, unsigned long *temp)
{
unsigned long long crt;
acpi_status status;
status = acpi_evaluate_integer(device->handle, "_CRT", NULL, &crt);
if (ACPI_FAILURE(status))
return -EIO;
*temp = DECI_KELVIN_TO_MILLI_CELSIUS(crt, KELVIN_OFFSET);
return 0;
}
static int sys_get_trip_psv(struct acpi_device *device, unsigned long *temp)
{
unsigned long long psv;
acpi_status status;
status = acpi_evaluate_integer(device->handle, "_PSV", NULL, &psv);
if (ACPI_FAILURE(status))
return -EIO;
*temp = DECI_KELVIN_TO_MILLI_CELSIUS(psv, KELVIN_OFFSET);
return 0;
}
static int acpi_int3403_add(struct acpi_device *device) static int acpi_int3403_add(struct acpi_device *device)
{ {
int result = 0; int result = 0;
...@@ -194,6 +238,15 @@ static int acpi_int3403_add(struct acpi_device *device) ...@@ -194,6 +238,15 @@ static int acpi_int3403_add(struct acpi_device *device)
return -ENOMEM; return -ENOMEM;
trip_mask = BIT(trip_cnt) - 1; trip_mask = BIT(trip_cnt) - 1;
} }
obj->psv_trip_id = -1;
if (!sys_get_trip_psv(device, &obj->psv_temp))
obj->psv_trip_id = trip_cnt++;
obj->crit_trip_id = -1;
if (!sys_get_trip_crt(device, &obj->crit_temp))
obj->crit_trip_id = trip_cnt++;
obj->tzone = thermal_zone_device_register(acpi_device_bid(device), obj->tzone = thermal_zone_device_register(acpi_device_bid(device),
trip_cnt, trip_mask, device, &tzone_ops, trip_cnt, trip_mask, device, &tzone_ops,
NULL, 0, 0); NULL, 0, 0);
......
...@@ -504,6 +504,10 @@ static irqreturn_t exynos_tmu_irq(int irq, void *id) ...@@ -504,6 +504,10 @@ static irqreturn_t exynos_tmu_irq(int irq, void *id)
} }
static const struct of_device_id exynos_tmu_match[] = { static const struct of_device_id exynos_tmu_match[] = {
{
.compatible = "samsung,exynos3250-tmu",
.data = (void *)EXYNOS3250_TMU_DRV_DATA,
},
{ {
.compatible = "samsung,exynos4210-tmu", .compatible = "samsung,exynos4210-tmu",
.data = (void *)EXYNOS4210_TMU_DRV_DATA, .data = (void *)EXYNOS4210_TMU_DRV_DATA,
...@@ -677,7 +681,8 @@ static int exynos_tmu_probe(struct platform_device *pdev) ...@@ -677,7 +681,8 @@ static int exynos_tmu_probe(struct platform_device *pdev)
goto err_clk_sec; goto err_clk_sec;
} }
if (pdata->type == SOC_ARCH_EXYNOS4210 || if (pdata->type == SOC_ARCH_EXYNOS3250 ||
pdata->type == SOC_ARCH_EXYNOS4210 ||
pdata->type == SOC_ARCH_EXYNOS4412 || pdata->type == SOC_ARCH_EXYNOS4412 ||
pdata->type == SOC_ARCH_EXYNOS5250 || pdata->type == SOC_ARCH_EXYNOS5250 ||
pdata->type == SOC_ARCH_EXYNOS5260 || pdata->type == SOC_ARCH_EXYNOS5260 ||
...@@ -759,10 +764,10 @@ static int exynos_tmu_remove(struct platform_device *pdev) ...@@ -759,10 +764,10 @@ static int exynos_tmu_remove(struct platform_device *pdev)
{ {
struct exynos_tmu_data *data = platform_get_drvdata(pdev); struct exynos_tmu_data *data = platform_get_drvdata(pdev);
exynos_tmu_control(pdev, false);
exynos_unregister_thermal(data->reg_conf); exynos_unregister_thermal(data->reg_conf);
exynos_tmu_control(pdev, false);
clk_unprepare(data->clk); clk_unprepare(data->clk);
if (!IS_ERR(data->clk_sec)) if (!IS_ERR(data->clk_sec))
clk_unprepare(data->clk_sec); clk_unprepare(data->clk_sec);
......
...@@ -40,7 +40,8 @@ enum calibration_mode { ...@@ -40,7 +40,8 @@ enum calibration_mode {
}; };
enum soc_type { enum soc_type {
SOC_ARCH_EXYNOS4210 = 1, SOC_ARCH_EXYNOS3250 = 1,
SOC_ARCH_EXYNOS4210,
SOC_ARCH_EXYNOS4412, SOC_ARCH_EXYNOS4412,
SOC_ARCH_EXYNOS5250, SOC_ARCH_EXYNOS5250,
SOC_ARCH_EXYNOS5260, SOC_ARCH_EXYNOS5260,
......
...@@ -90,6 +90,95 @@ struct exynos_tmu_init_data const exynos4210_default_tmu_data = { ...@@ -90,6 +90,95 @@ struct exynos_tmu_init_data const exynos4210_default_tmu_data = {
}; };
#endif #endif
#if defined(CONFIG_SOC_EXYNOS3250)
static const struct exynos_tmu_registers exynos3250_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,
.test_mux_addr_shift = EXYNOS4412_MUX_ADDR_SHIFT,
.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_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,
.intclr_fall_shift = EXYNOS_TMU_CLEAR_FALL_INT_SHIFT,
.intclr_rise_shift = EXYNOS_TMU_RISE_INT_SHIFT,
.intclr_rise_mask = EXYNOS_TMU_RISE_INT_MASK,
.intclr_fall_mask = EXYNOS_TMU_FALL_INT_MASK,
.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 EXYNOS3250_TMU_DATA \
.threshold_falling = 10, \
.trigger_levels[0] = 70, \
.trigger_levels[1] = 95, \
.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_TWO_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 = 70, \
}, \
.freq_tab[1] = { \
.freq_clip_max = 400 * 1000, \
.temp_level = 95, \
}, \
.freq_tab_count = 2, \
.registers = &exynos3250_tmu_registers, \
.features = (TMU_SUPPORT_EMULATION | \
TMU_SUPPORT_FALLING_TRIP | TMU_SUPPORT_READY_STATUS | \
TMU_SUPPORT_EMUL_TIME)
#endif
#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) #if defined(CONFIG_SOC_EXYNOS4412) || defined(CONFIG_SOC_EXYNOS5250)
static const struct exynos_tmu_registers exynos4412_tmu_registers = { static const struct exynos_tmu_registers exynos4412_tmu_registers = {
.triminfo_data = EXYNOS_TMU_REG_TRIMINFO, .triminfo_data = EXYNOS_TMU_REG_TRIMINFO,
......
...@@ -148,6 +148,13 @@ ...@@ -148,6 +148,13 @@
#define EXYNOS5440_TMU_TH_RISE4_SHIFT 24 #define EXYNOS5440_TMU_TH_RISE4_SHIFT 24
#define EXYNOS5440_EFUSE_SWAP_OFFSET 8 #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) #if defined(CONFIG_CPU_EXYNOS4210)
extern struct exynos_tmu_init_data const exynos4210_default_tmu_data; extern struct exynos_tmu_init_data const exynos4210_default_tmu_data;
#define EXYNOS4210_TMU_DRV_DATA (&exynos4210_default_tmu_data) #define EXYNOS4210_TMU_DRV_DATA (&exynos4210_default_tmu_data)
......
config ST_THERMAL
tristate "Thermal sensors on STMicroelectronics STi series of SoCs"
help
Support for thermal sensors on STMicroelectronics STi series of SoCs.
config ST_THERMAL_SYSCFG
select ST_THERMAL
tristate "STi series syscfg register access based thermal sensors"
config ST_THERMAL_MEMMAP
select ST_THERMAL
tristate "STi series memory mapped access based thermal sensors"
obj-$(CONFIG_ST_THERMAL) := st_thermal.o
obj-$(CONFIG_ST_THERMAL_SYSCFG) += st_thermal_syscfg.o
obj-$(CONFIG_ST_THERMAL_MEMMAP) += st_thermal_memmap.o
/*
* ST Thermal Sensor Driver core routines
* Author: Ajit Pal Singh <ajitpal.singh@st.com>
*
* Copyright (C) 2003-2014 STMicroelectronics (R&D) Limited
*
* 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.
*
*/
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include "st_thermal.h"
/* The Thermal Framework expects millidegrees */
#define mcelsius(temp) ((temp) * 1000)
/*
* Function to allocate regfields which are common
* between syscfg and memory mapped based sensors
*/
int st_thermal_alloc_regfields(struct st_thermal_sensor *sensor)
{
struct device *dev = sensor->dev;
struct regmap *regmap = sensor->regmap;
const struct reg_field *reg_fields = sensor->cdata->reg_fields;
sensor->dcorrect = devm_regmap_field_alloc(dev, regmap,
reg_fields[DCORRECT]);
sensor->overflow = devm_regmap_field_alloc(dev, regmap,
reg_fields[OVERFLOW]);
sensor->temp_data = devm_regmap_field_alloc(dev, regmap,
reg_fields[DATA]);
if (IS_ERR(sensor->dcorrect) ||
IS_ERR(sensor->overflow) ||
IS_ERR(sensor->temp_data)) {
dev_err(dev, "failed to allocate common regfields\n");
return -EINVAL;
}
return sensor->ops->alloc_regfields(sensor);
}
static int st_thermal_sensor_on(struct st_thermal_sensor *sensor)
{
int ret;
struct device *dev = sensor->dev;
ret = clk_prepare_enable(sensor->clk);
if (ret) {
dev_err(dev, "failed to enable clk\n");
return ret;
}
ret = sensor->ops->power_ctrl(sensor, POWER_ON);
if (ret) {
dev_err(dev, "failed to power on sensor\n");
clk_disable_unprepare(sensor->clk);
}
return ret;
}
static int st_thermal_sensor_off(struct st_thermal_sensor *sensor)
{
int ret;
ret = sensor->ops->power_ctrl(sensor, POWER_OFF);
if (ret)
return ret;
clk_disable_unprepare(sensor->clk);
return 0;
}
static int st_thermal_calibration(struct st_thermal_sensor *sensor)
{
int ret;
unsigned int val;
struct device *dev = sensor->dev;
/* Check if sensor calibration data is already written */
ret = regmap_field_read(sensor->dcorrect, &val);
if (ret) {
dev_err(dev, "failed to read calibration data\n");
return ret;
}
if (!val) {
/*
* Sensor calibration value not set by bootloader,
* default calibration data to be used
*/
ret = regmap_field_write(sensor->dcorrect,
sensor->cdata->calibration_val);
if (ret)
dev_err(dev, "failed to set calibration data\n");
}
return ret;
}
/* Callback to get temperature from HW*/
static int st_thermal_get_temp(struct thermal_zone_device *th,
unsigned long *temperature)
{
struct st_thermal_sensor *sensor = th->devdata;
struct device *dev = sensor->dev;
unsigned int temp;
unsigned int overflow;
int ret;
ret = regmap_field_read(sensor->overflow, &overflow);
if (ret)
return ret;
if (overflow)
return -EIO;
ret = regmap_field_read(sensor->temp_data, &temp);
if (ret)
return ret;
temp += sensor->cdata->temp_adjust_val;
temp = mcelsius(temp);
dev_dbg(dev, "temperature: %d\n", temp);
*temperature = temp;
return 0;
}
static int st_thermal_get_trip_type(struct thermal_zone_device *th,
int trip, enum thermal_trip_type *type)
{
struct st_thermal_sensor *sensor = th->devdata;
struct device *dev = sensor->dev;
switch (trip) {
case 0:
*type = THERMAL_TRIP_CRITICAL;
break;
default:
dev_err(dev, "invalid trip point\n");
return -EINVAL;
}
return 0;
}
static int st_thermal_get_trip_temp(struct thermal_zone_device *th,
int trip, unsigned long *temp)
{
struct st_thermal_sensor *sensor = th->devdata;
struct device *dev = sensor->dev;
switch (trip) {
case 0:
*temp = mcelsius(sensor->cdata->crit_temp);
break;
default:
dev_err(dev, "Invalid trip point\n");
return -EINVAL;
}
return 0;
}
static struct thermal_zone_device_ops st_tz_ops = {
.get_temp = st_thermal_get_temp,
.get_trip_type = st_thermal_get_trip_type,
.get_trip_temp = st_thermal_get_trip_temp,
};
int st_thermal_register(struct platform_device *pdev,
const struct of_device_id *st_thermal_of_match)
{
struct st_thermal_sensor *sensor;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
const struct of_device_id *match;
int polling_delay;
int ret;
if (!np) {
dev_err(dev, "device tree node not found\n");
return -EINVAL;
}
sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
if (!sensor)
return -ENOMEM;
sensor->dev = dev;
match = of_match_device(st_thermal_of_match, dev);
if (!(match && match->data))
return -EINVAL;
sensor->cdata = match->data;
if (!sensor->cdata->ops)
return -EINVAL;
sensor->ops = sensor->cdata->ops;
ret = sensor->ops->regmap_init(sensor);
if (ret)
return ret;
ret = st_thermal_alloc_regfields(sensor);
if (ret)
return ret;
sensor->clk = devm_clk_get(dev, "thermal");
if (IS_ERR(sensor->clk)) {
dev_err(dev, "failed to fetch clock\n");
return PTR_ERR(sensor->clk);
}
if (sensor->ops->register_enable_irq) {
ret = sensor->ops->register_enable_irq(sensor);
if (ret)
return ret;
}
ret = st_thermal_sensor_on(sensor);
if (ret)
return ret;
ret = st_thermal_calibration(sensor);
if (ret)
goto sensor_off;
polling_delay = sensor->ops->register_enable_irq ? 0 : 1000;
sensor->thermal_dev =
thermal_zone_device_register(dev_name(dev), 1, 0, sensor,
&st_tz_ops, NULL, 0, polling_delay);
if (IS_ERR(sensor->thermal_dev)) {
dev_err(dev, "failed to register thermal zone device\n");
ret = PTR_ERR(sensor->thermal_dev);
goto sensor_off;
}
platform_set_drvdata(pdev, sensor);
return 0;
sensor_off:
st_thermal_sensor_off(sensor);
return ret;
}
EXPORT_SYMBOL_GPL(st_thermal_register);
int st_thermal_unregister(struct platform_device *pdev)
{
struct st_thermal_sensor *sensor = platform_get_drvdata(pdev);
st_thermal_sensor_off(sensor);
thermal_zone_device_unregister(sensor->thermal_dev);
return 0;
}
EXPORT_SYMBOL_GPL(st_thermal_unregister);
static int st_thermal_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct st_thermal_sensor *sensor = platform_get_drvdata(pdev);
return st_thermal_sensor_off(sensor);
}
static int st_thermal_resume(struct device *dev)
{
int ret;
struct platform_device *pdev = to_platform_device(dev);
struct st_thermal_sensor *sensor = platform_get_drvdata(pdev);
ret = st_thermal_sensor_on(sensor);
if (ret)
return ret;
ret = st_thermal_calibration(sensor);
if (ret)
return ret;
if (sensor->ops->enable_irq) {
ret = sensor->ops->enable_irq(sensor);
if (ret)
return ret;
}
return 0;
}
SIMPLE_DEV_PM_OPS(st_thermal_pm_ops, st_thermal_suspend, st_thermal_resume);
EXPORT_SYMBOL_GPL(st_thermal_pm_ops);
MODULE_AUTHOR("STMicroelectronics (R&D) Limited <ajitpal.singh@st.com>");
MODULE_DESCRIPTION("STMicroelectronics STi SoC Thermal Sensor Driver");
MODULE_LICENSE("GPL v2");
/*
* ST Thermal Sensor Driver for STi series of SoCs
* Author: Ajit Pal Singh <ajitpal.singh@st.com>
*
* Copyright (C) 2003-2014 STMicroelectronics (R&D) Limited
*
* 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.
*/
#ifndef __STI_THERMAL_SYSCFG_H
#define __STI_THERMAL_SYSCFG_H
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/thermal.h>
enum st_thermal_regfield_ids {
INT_THRESH_HI = 0, /* Top two regfield IDs are mutually exclusive */
TEMP_PWR = 0,
DCORRECT,
OVERFLOW,
DATA,
INT_ENABLE,
MAX_REGFIELDS
};
/* Thermal sensor power states */
enum st_thermal_power_state {
POWER_OFF = 0,
POWER_ON
};
struct st_thermal_sensor;
/**
* Description of private thermal sensor ops.
*
* @power_ctrl: Function for powering on/off a sensor. Clock to the
* sensor is also controlled from this function.
* @alloc_regfields: Allocate regmap register fields, specific to a sensor.
* @do_memmap_regmap: Memory map the thermal register space and init regmap
* instance or find regmap instance.
* @register_irq: Register an interrupt handler for a sensor.
*/
struct st_thermal_sensor_ops {
int (*power_ctrl)(struct st_thermal_sensor *, enum st_thermal_power_state);
int (*alloc_regfields)(struct st_thermal_sensor *);
int (*regmap_init)(struct st_thermal_sensor *);
int (*register_enable_irq)(struct st_thermal_sensor *);
int (*enable_irq)(struct st_thermal_sensor *);
};
/**
* Description of thermal driver compatible data.
*
* @reg_fields: Pointer to the regfields array for a sensor.
* @sys_compat: Pointer to the syscon node compatible string.
* @ops: Pointer to private thermal ops for a sensor.
* @calibration_val: Default calibration value to be written to the DCORRECT
* register field for a sensor.
* @temp_adjust_val: Value to be added/subtracted from the data read from
* the sensor. If value needs to be added please provide a
* positive value and if it is to be subtracted please
* provide a negative value.
* @crit_temp: The temperature beyond which the SoC should be shutdown
* to prevent damage.
*/
struct st_thermal_compat_data {
char *sys_compat;
const struct reg_field *reg_fields;
const struct st_thermal_sensor_ops *ops;
unsigned int calibration_val;
int temp_adjust_val;
int crit_temp;
};
struct st_thermal_sensor {
struct device *dev;
struct thermal_zone_device *thermal_dev;
const struct st_thermal_sensor_ops *ops;
const struct st_thermal_compat_data *cdata;
struct clk *clk;
struct regmap *regmap;
struct regmap_field *pwr;
struct regmap_field *dcorrect;
struct regmap_field *overflow;
struct regmap_field *temp_data;
struct regmap_field *int_thresh_hi;
struct regmap_field *int_enable;
int irq;
void __iomem *mmio_base;
};
extern int st_thermal_register(struct platform_device *pdev,
const struct of_device_id *st_thermal_of_match);
extern int st_thermal_unregister(struct platform_device *pdev);
extern const struct dev_pm_ops st_thermal_pm_ops;
#endif /* __STI_RESET_SYSCFG_H */
/*
* ST Thermal Sensor Driver for memory mapped sensors.
* Author: Ajit Pal Singh <ajitpal.singh@st.com>
*
* Copyright (C) 2003-2014 STMicroelectronics (R&D) Limited
*
* 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.
*/
#include <linux/of.h>
#include <linux/module.h>
#include "st_thermal.h"
#define STIH416_MPE_CONF 0x0
#define STIH416_MPE_STATUS 0x4
#define STIH416_MPE_INT_THRESH 0x8
#define STIH416_MPE_INT_EN 0xC
/* Power control bits for the memory mapped thermal sensor */
#define THERMAL_PDN BIT(4)
#define THERMAL_SRSTN BIT(10)
static const struct reg_field st_mmap_thermal_regfields[MAX_REGFIELDS] = {
/*
* According to the STIH416 MPE temp sensor data sheet -
* the PDN (Power Down Bit) and SRSTN (Soft Reset Bit) need to be
* written simultaneously for powering on and off the temperature
* sensor. regmap_update_bits() will be used to update the register.
*/
[INT_THRESH_HI] = REG_FIELD(STIH416_MPE_INT_THRESH, 0, 7),
[DCORRECT] = REG_FIELD(STIH416_MPE_CONF, 5, 9),
[OVERFLOW] = REG_FIELD(STIH416_MPE_STATUS, 9, 9),
[DATA] = REG_FIELD(STIH416_MPE_STATUS, 11, 18),
[INT_ENABLE] = REG_FIELD(STIH416_MPE_INT_EN, 0, 0),
};
static irqreturn_t st_mmap_thermal_trip_handler(int irq, void *sdata)
{
struct st_thermal_sensor *sensor = sdata;
thermal_zone_device_update(sensor->thermal_dev);
return IRQ_HANDLED;
}
/* Private ops for the Memory Mapped based thermal sensors */
static int st_mmap_power_ctrl(struct st_thermal_sensor *sensor,
enum st_thermal_power_state power_state)
{
const unsigned int mask = (THERMAL_PDN | THERMAL_SRSTN);
const unsigned int val = power_state ? mask : 0;
return regmap_update_bits(sensor->regmap, STIH416_MPE_CONF, mask, val);
}
static int st_mmap_alloc_regfields(struct st_thermal_sensor *sensor)
{
struct device *dev = sensor->dev;
struct regmap *regmap = sensor->regmap;
const struct reg_field *reg_fields = sensor->cdata->reg_fields;
sensor->int_thresh_hi = devm_regmap_field_alloc(dev, regmap,
reg_fields[INT_THRESH_HI]);
sensor->int_enable = devm_regmap_field_alloc(dev, regmap,
reg_fields[INT_ENABLE]);
if (IS_ERR(sensor->int_thresh_hi) || IS_ERR(sensor->int_enable)) {
dev_err(dev, "failed to alloc mmap regfields\n");
return -EINVAL;
}
return 0;
}
static int st_mmap_enable_irq(struct st_thermal_sensor *sensor)
{
int ret;
/* Set upper critical threshold */
ret = regmap_field_write(sensor->int_thresh_hi,
sensor->cdata->crit_temp -
sensor->cdata->temp_adjust_val);
if (ret)
return ret;
return regmap_field_write(sensor->int_enable, 1);
}
static int st_mmap_register_enable_irq(struct st_thermal_sensor *sensor)
{
struct device *dev = sensor->dev;
struct platform_device *pdev = to_platform_device(dev);
int ret;
sensor->irq = platform_get_irq(pdev, 0);
if (sensor->irq < 0) {
dev_err(dev, "failed to register IRQ\n");
return sensor->irq;
}
ret = devm_request_threaded_irq(dev, sensor->irq,
NULL, st_mmap_thermal_trip_handler,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
dev->driver->name, sensor);
if (ret) {
dev_err(dev, "failed to register IRQ %d\n", sensor->irq);
return ret;
}
return st_mmap_enable_irq(sensor);
}
static const struct regmap_config st_416mpe_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
static int st_mmap_regmap_init(struct st_thermal_sensor *sensor)
{
struct device *dev = sensor->dev;
struct platform_device *pdev = to_platform_device(dev);
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "no memory resources defined\n");
return -ENODEV;
}
sensor->mmio_base = devm_ioremap_resource(dev, res);
if (IS_ERR(sensor->mmio_base)) {
dev_err(dev, "failed to remap IO\n");
return PTR_ERR(sensor->mmio_base);
}
sensor->regmap = devm_regmap_init_mmio(dev, sensor->mmio_base,
&st_416mpe_regmap_config);
if (IS_ERR(sensor->regmap)) {
dev_err(dev, "failed to initialise regmap\n");
return PTR_ERR(sensor->regmap);
}
return 0;
}
static const struct st_thermal_sensor_ops st_mmap_sensor_ops = {
.power_ctrl = st_mmap_power_ctrl,
.alloc_regfields = st_mmap_alloc_regfields,
.regmap_init = st_mmap_regmap_init,
.register_enable_irq = st_mmap_register_enable_irq,
.enable_irq = st_mmap_enable_irq,
};
/* Compatible device data stih416 mpe thermal sensor */
const struct st_thermal_compat_data st_416mpe_cdata = {
.reg_fields = st_mmap_thermal_regfields,
.ops = &st_mmap_sensor_ops,
.calibration_val = 14,
.temp_adjust_val = -95,
.crit_temp = 120,
};
/* Compatible device data stih407 thermal sensor */
const struct st_thermal_compat_data st_407_cdata = {
.reg_fields = st_mmap_thermal_regfields,
.ops = &st_mmap_sensor_ops,
.calibration_val = 16,
.temp_adjust_val = -95,
.crit_temp = 120,
};
static struct of_device_id st_mmap_thermal_of_match[] = {
{ .compatible = "st,stih416-mpe-thermal", .data = &st_416mpe_cdata },
{ .compatible = "st,stih407-thermal", .data = &st_407_cdata },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, st_mmap_thermal_of_match);
int st_mmap_probe(struct platform_device *pdev)
{
return st_thermal_register(pdev, st_mmap_thermal_of_match);
}
int st_mmap_remove(struct platform_device *pdev)
{
return st_thermal_unregister(pdev);
}
static struct platform_driver st_mmap_thermal_driver = {
.driver = {
.name = "st_thermal_mmap",
.owner = THIS_MODULE,
.pm = &st_thermal_pm_ops,
.of_match_table = st_mmap_thermal_of_match,
},
.probe = st_mmap_probe,
.remove = st_mmap_remove,
};
module_platform_driver(st_mmap_thermal_driver);
MODULE_AUTHOR("STMicroelectronics (R&D) Limited <ajitpal.singh@st.com>");
MODULE_DESCRIPTION("STMicroelectronics STi SoC Thermal Sensor Driver");
MODULE_LICENSE("GPL v2");
/*
* ST Thermal Sensor Driver for syscfg based sensors.
* Author: Ajit Pal Singh <ajitpal.singh@st.com>
*
* Copyright (C) 2003-2014 STMicroelectronics (R&D) Limited
*
* 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.
*/
#include <linux/of.h>
#include <linux/module.h>
#include <linux/mfd/syscon.h>
#include "st_thermal.h"
/* STiH415 */
#define STIH415_SYSCFG_FRONT(num) ((num - 100) * 4)
#define STIH415_SAS_THSENS_CONF STIH415_SYSCFG_FRONT(178)
#define STIH415_SAS_THSENS_STATUS STIH415_SYSCFG_FRONT(198)
#define STIH415_SYSCFG_MPE(num) ((num - 600) * 4)
#define STIH415_MPE_THSENS_CONF STIH415_SYSCFG_MPE(607)
#define STIH415_MPE_THSENS_STATUS STIH415_SYSCFG_MPE(667)
/* STiH416 */
#define STIH416_SYSCFG_FRONT(num) ((num - 1000) * 4)
#define STIH416_SAS_THSENS_CONF STIH416_SYSCFG_FRONT(1552)
#define STIH416_SAS_THSENS_STATUS1 STIH416_SYSCFG_FRONT(1554)
#define STIH416_SAS_THSENS_STATUS2 STIH416_SYSCFG_FRONT(1594)
/* STiD127 */
#define STID127_SYSCFG_CPU(num) ((num - 700) * 4)
#define STID127_THSENS_CONF STID127_SYSCFG_CPU(743)
#define STID127_THSENS_STATUS STID127_SYSCFG_CPU(767)
static const struct reg_field st_415sas_regfields[MAX_REGFIELDS] = {
[TEMP_PWR] = REG_FIELD(STIH415_SAS_THSENS_CONF, 9, 9),
[DCORRECT] = REG_FIELD(STIH415_SAS_THSENS_CONF, 4, 8),
[OVERFLOW] = REG_FIELD(STIH415_SAS_THSENS_STATUS, 8, 8),
[DATA] = REG_FIELD(STIH415_SAS_THSENS_STATUS, 10, 16),
};
static const struct reg_field st_415mpe_regfields[MAX_REGFIELDS] = {
[TEMP_PWR] = REG_FIELD(STIH415_MPE_THSENS_CONF, 8, 8),
[DCORRECT] = REG_FIELD(STIH415_MPE_THSENS_CONF, 3, 7),
[OVERFLOW] = REG_FIELD(STIH415_MPE_THSENS_STATUS, 9, 9),
[DATA] = REG_FIELD(STIH415_MPE_THSENS_STATUS, 11, 18),
};
static const struct reg_field st_416sas_regfields[MAX_REGFIELDS] = {
[TEMP_PWR] = REG_FIELD(STIH416_SAS_THSENS_CONF, 9, 9),
[DCORRECT] = REG_FIELD(STIH416_SAS_THSENS_CONF, 4, 8),
[OVERFLOW] = REG_FIELD(STIH416_SAS_THSENS_STATUS1, 8, 8),
[DATA] = REG_FIELD(STIH416_SAS_THSENS_STATUS2, 10, 16),
};
static const struct reg_field st_127_regfields[MAX_REGFIELDS] = {
[TEMP_PWR] = REG_FIELD(STID127_THSENS_CONF, 7, 7),
[DCORRECT] = REG_FIELD(STID127_THSENS_CONF, 2, 6),
[OVERFLOW] = REG_FIELD(STID127_THSENS_STATUS, 9, 9),
[DATA] = REG_FIELD(STID127_THSENS_STATUS, 11, 18),
};
/* Private OPs for System Configuration Register based thermal sensors */
static int st_syscfg_power_ctrl(struct st_thermal_sensor *sensor,
enum st_thermal_power_state power_state)
{
return regmap_field_write(sensor->pwr, power_state);
}
static int st_syscfg_alloc_regfields(struct st_thermal_sensor *sensor)
{
struct device *dev = sensor->dev;
sensor->pwr = devm_regmap_field_alloc(dev, sensor->regmap,
sensor->cdata->reg_fields[TEMP_PWR]);
if (IS_ERR(sensor->pwr)) {
dev_err(dev, "failed to alloc syscfg regfields\n");
return PTR_ERR(sensor->pwr);
}
return 0;
}
static int st_syscfg_regmap_init(struct st_thermal_sensor *sensor)
{
sensor->regmap =
syscon_regmap_lookup_by_compatible(sensor->cdata->sys_compat);
if (IS_ERR(sensor->regmap)) {
dev_err(sensor->dev, "failed to find syscfg regmap\n");
return PTR_ERR(sensor->regmap);
}
return 0;
}
static const struct st_thermal_sensor_ops st_syscfg_sensor_ops = {
.power_ctrl = st_syscfg_power_ctrl,
.alloc_regfields = st_syscfg_alloc_regfields,
.regmap_init = st_syscfg_regmap_init,
};
/* Compatible device data for stih415 sas thermal sensor */
const struct st_thermal_compat_data st_415sas_cdata = {
.sys_compat = "st,stih415-front-syscfg",
.reg_fields = st_415sas_regfields,
.ops = &st_syscfg_sensor_ops,
.calibration_val = 16,
.temp_adjust_val = 20,
.crit_temp = 120,
};
/* Compatible device data for stih415 mpe thermal sensor */
const struct st_thermal_compat_data st_415mpe_cdata = {
.sys_compat = "st,stih415-system-syscfg",
.reg_fields = st_415mpe_regfields,
.ops = &st_syscfg_sensor_ops,
.calibration_val = 16,
.temp_adjust_val = -103,
.crit_temp = 120,
};
/* Compatible device data for stih416 sas thermal sensor */
const struct st_thermal_compat_data st_416sas_cdata = {
.sys_compat = "st,stih416-front-syscfg",
.reg_fields = st_416sas_regfields,
.ops = &st_syscfg_sensor_ops,
.calibration_val = 16,
.temp_adjust_val = 20,
.crit_temp = 120,
};
/* Compatible device data for stid127 thermal sensor */
const struct st_thermal_compat_data st_127_cdata = {
.sys_compat = "st,stid127-cpu-syscfg",
.reg_fields = st_127_regfields,
.ops = &st_syscfg_sensor_ops,
.calibration_val = 8,
.temp_adjust_val = -103,
.crit_temp = 120,
};
static struct of_device_id st_syscfg_thermal_of_match[] = {
{ .compatible = "st,stih415-sas-thermal", .data = &st_415sas_cdata },
{ .compatible = "st,stih415-mpe-thermal", .data = &st_415mpe_cdata },
{ .compatible = "st,stih416-sas-thermal", .data = &st_416sas_cdata },
{ .compatible = "st,stid127-thermal", .data = &st_127_cdata },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, st_syscfg_thermal_of_match);
int st_syscfg_probe(struct platform_device *pdev)
{
return st_thermal_register(pdev, st_syscfg_thermal_of_match);
}
int st_syscfg_remove(struct platform_device *pdev)
{
return st_thermal_unregister(pdev);
}
static struct platform_driver st_syscfg_thermal_driver = {
.driver = {
.name = "st_syscfg_thermal",
.owner = THIS_MODULE,
.pm = &st_thermal_pm_ops,
.of_match_table = st_syscfg_thermal_of_match,
},
.probe = st_syscfg_probe,
.remove = st_syscfg_remove,
};
module_platform_driver(st_syscfg_thermal_driver);
MODULE_AUTHOR("STMicroelectronics (R&D) Limited <ajitpal.singh@st.com>");
MODULE_DESCRIPTION("STMicroelectronics STi SoC Thermal Sensor Driver");
MODULE_LICENSE("GPL v2");
...@@ -158,6 +158,42 @@ struct thermal_attr { ...@@ -158,6 +158,42 @@ struct thermal_attr {
char name[THERMAL_NAME_LENGTH]; char name[THERMAL_NAME_LENGTH];
}; };
/**
* struct thermal_zone_device - structure for a thermal zone
* @id: unique id number for each thermal zone
* @type: the thermal zone device type
* @device: &struct device for this thermal zone
* @trip_temp_attrs: attributes for trip points for sysfs: trip temperature
* @trip_type_attrs: attributes for trip points for sysfs: trip type
* @trip_hyst_attrs: attributes for trip points for sysfs: trip hysteresis
* @devdata: private pointer for device private data
* @trips: number of trip points the thermal zone supports
* @passive_delay: number of milliseconds to wait between polls when
* performing passive cooling. Currenty only used by the
* step-wise governor
* @polling_delay: number of milliseconds to wait between polls when
* checking whether trip points have been crossed (0 for
* interrupt driven systems)
* @temperature: current temperature. This is only for core code,
* drivers should use thermal_zone_get_temp() to get the
* current temperature
* @last_temperature: previous temperature read
* @emul_temperature: emulated temperature when using CONFIG_THERMAL_EMULATION
* @passive: 1 if you've crossed a passive trip point, 0 otherwise.
* Currenty only used by the step-wise governor.
* @forced_passive: If > 0, temperature at which to switch on all ACPI
* processor cooling devices. Currently only used by the
* step-wise governor.
* @ops: operations this &thermal_zone_device supports
* @tzp: thermal zone parameters
* @governor: pointer to the governor for this thermal zone
* @thermal_instances: list of &struct thermal_instance of this thermal zone
* @idr: &struct idr to generate unique id for this zone's cooling
* devices
* @lock: lock to protect thermal_instances list
* @node: node in thermal_tz_list (in thermal_core.c)
* @poll_queue: delayed work for polling
*/
struct thermal_zone_device { struct thermal_zone_device {
int id; int id;
char type[THERMAL_NAME_LENGTH]; char type[THERMAL_NAME_LENGTH];
...@@ -179,12 +215,18 @@ struct thermal_zone_device { ...@@ -179,12 +215,18 @@ struct thermal_zone_device {
struct thermal_governor *governor; struct thermal_governor *governor;
struct list_head thermal_instances; struct list_head thermal_instances;
struct idr idr; struct idr idr;
struct mutex lock; /* protect thermal_instances list */ struct mutex lock;
struct list_head node; struct list_head node;
struct delayed_work poll_queue; struct delayed_work poll_queue;
}; };
/* Structure that holds thermal governor information */ /**
* struct thermal_governor - structure that holds thermal governor information
* @name: name of the governor
* @throttle: callback called for every trip point even if temperature is
* below the trip point temperature
* @governor_list: node in thermal_governor_list (in thermal_core.c)
*/
struct thermal_governor { struct thermal_governor {
char name[THERMAL_NAME_LENGTH]; char name[THERMAL_NAME_LENGTH];
int (*throttle)(struct thermal_zone_device *tz, int trip); int (*throttle)(struct thermal_zone_device *tz, int trip);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册