Commit 19785cf9 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/evalenti/linux-soc-thermal

Pull thermal SoC updates from Zhang Rui:
 "Thermal SoC management updates:

   - imx thermal driver now supports i.MX7 thermal sensor (Anson Huang)

   - exynos thermal driver dropped support for exynos 5440 (Krzysztof
     Kozlowski)

   - rcar_thermal now supports r8a77995 (Yoshihiro Kaneko)

   - rcar_gen3_thermal now supports r8a77965 (Niklas Söderlund)

   - qcom-spmi-temp-alarm now supports GEN2 PMIC peripherals (David
     Collins)

   - uniphier thermal now supports UniPhier PXs3 (Kunihiko Hayashi)

   - mediatek thermal now supports MT7622 SoC (Sean Wang)

   - considerable refactoring of exynos driver (Bartlomiej
     Zolnierkiewicz)

   - small fixes all over the place on different drivers"

* 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/evalenti/linux-soc-thermal: (50 commits)
  thermal: qcom: tsens: Allow number of sensors to come from DT
  thermal: tegra: soctherm: add const to struct thermal_cooling_device_ops
  thermal: exynos: Reduce severity of too early temperature read
  thermal: imx: Switch to SPDX identifier
  thermal: qcom-spmi-temp-alarm: add support for GEN2 PMIC peripherals
  thermal: ti-soc-thermal: fix incorrect entry in omap5430_adc_to_temp[]
  thermal: rcar_thermal: add r8a77995 support
  dt-bindings: thermal: rcar-thermal: add R8A77995 support
  thermal: mediatek: use of_device_get_match_data()
  thermal: exynos: remove trip reporting to user-space
  thermal: exynos: remove unused defines for Exynos5433
  thermal: exynos: cleanup code for enabling threshold interrupts
  thermal: exynos: check return values of ->get_trip_[temp, hyst] methods
  thermal: exynos: move trips setting to exynos_tmu_initialize()
  thermal: exynos: set trips in ascending order in exynos7_tmu_initialize()
  thermal: exynos: do not use trips structure directly in ->tmu_initialize
  thermal: exynos: add exynos*_tmu_set_[trip,hyst]() helpers
  thermal: exynos: move IRQs clearing to exynos_tmu_initialize()
  thermal: exynos: clear IRQs later in exynos4412_tmu_initialize()
  thermal: exynos: make ->tmu_initialize method void
  ...
parents 98db5e55 6d7c70d1
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
"samsung,exynos5420-tmu-ext-triminfo" for TMU channels 2, 3 and 4 "samsung,exynos5420-tmu-ext-triminfo" for TMU channels 2, 3 and 4
Exynos5420 (Must pass triminfo base and triminfo clock) Exynos5420 (Must pass triminfo base and triminfo clock)
"samsung,exynos5433-tmu" "samsung,exynos5433-tmu"
"samsung,exynos5440-tmu"
"samsung,exynos7-tmu" "samsung,exynos7-tmu"
- interrupt-parent : The phandle for the interrupt controller - interrupt-parent : The phandle for the interrupt controller
- reg : Address range of the thermal registers. For soc's which has multiple - reg : Address range of the thermal registers. For soc's which has multiple
...@@ -68,18 +67,7 @@ Example 1): ...@@ -68,18 +67,7 @@ Example 1):
#thermal-sensor-cells = <0>; #thermal-sensor-cells = <0>;
}; };
Example 2): Example 2): (In case of Exynos5420 "with misplaced TRIMINFO register")
tmuctrl_0: tmuctrl@160118 {
compatible = "samsung,exynos5440-tmu";
reg = <0x160118 0x230>, <0x160368 0x10>;
interrupts = <0 58 0>;
clocks = <&clock 21>;
clock-names = "tmu_apbif";
#thermal-sensor-cells = <0>;
};
Example 3): (In case of Exynos5420 "with misplaced TRIMINFO register")
tmu_cpu2: tmu@10068000 { tmu_cpu2: tmu@10068000 {
compatible = "samsung,exynos5420-tmu-ext-triminfo"; compatible = "samsung,exynos5420-tmu-ext-triminfo";
reg = <0x10068000 0x100>, <0x1006c000 0x4>; reg = <0x10068000 0x100>, <0x1006c000 0x4>;
......
* Temperature Monitor (TEMPMON) on Freescale i.MX SoCs * Temperature Monitor (TEMPMON) on Freescale i.MX SoCs
Required properties: Required properties:
- compatible : "fsl,imx6q-tempmon" for i.MX6Q, "fsl,imx6sx-tempmon" for i.MX6SX. - compatible : must be one of following:
i.MX6SX has two more IRQs than i.MX6Q, one is IRQ_LOW and the other is IRQ_PANIC, - "fsl,imx6q-tempmon" for i.MX6Q,
- "fsl,imx6sx-tempmon" for i.MX6SX,
- "fsl,imx7d-tempmon" for i.MX7S/D.
- interrupts : the interrupt output of the controller:
i.MX6Q has one IRQ which will be triggered when temperature is higher than high threshold,
i.MX6SX and i.MX7S/D have two more IRQs than i.MX6Q, one is IRQ_LOW and the other is IRQ_PANIC,
when temperature is below than low threshold, IRQ_LOW will be triggered, when temperature when temperature is below than low threshold, IRQ_LOW will be triggered, when temperature
is higher than panic threshold, system will auto reboot by SRC module. is higher than panic threshold, system will auto reboot by SRC module.
- fsl,tempmon : phandle pointer to system controller that contains TEMPMON - fsl,tempmon : phandle pointer to system controller that contains TEMPMON
......
...@@ -12,6 +12,7 @@ Required properties: ...@@ -12,6 +12,7 @@ Required properties:
- "mediatek,mt8173-thermal" : For MT8173 family of SoCs - "mediatek,mt8173-thermal" : For MT8173 family of SoCs
- "mediatek,mt2701-thermal" : For MT2701 family of SoCs - "mediatek,mt2701-thermal" : For MT2701 family of SoCs
- "mediatek,mt2712-thermal" : For MT2712 family of SoCs - "mediatek,mt2712-thermal" : For MT2712 family of SoCs
- "mediatek,mt7622-thermal" : For MT7622 SoC
- reg: Address range of the thermal controller - reg: Address range of the thermal controller
- interrupts: IRQ for the thermal controller - interrupts: IRQ for the thermal controller
- clocks, clock-names: Clocks needed for the thermal controller. required - clocks, clock-names: Clocks needed for the thermal controller. required
......
...@@ -8,6 +8,7 @@ Required properties: ...@@ -8,6 +8,7 @@ Required properties:
- reg: Address range of the thermal registers - reg: Address range of the thermal registers
- #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description. - #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description.
- #qcom,sensors: Number of sensors in tsens block
- Refer to Documentation/devicetree/bindings/nvmem/nvmem.txt to know how to specify - Refer to Documentation/devicetree/bindings/nvmem/nvmem.txt to know how to specify
nvmem cells nvmem cells
......
...@@ -9,6 +9,7 @@ Required properties: ...@@ -9,6 +9,7 @@ Required properties:
Examples with soctypes are: Examples with soctypes are:
- "renesas,r8a7795-thermal" (R-Car H3) - "renesas,r8a7795-thermal" (R-Car H3)
- "renesas,r8a7796-thermal" (R-Car M3-W) - "renesas,r8a7796-thermal" (R-Car M3-W)
- "renesas,r8a77965-thermal" (R-Car M3-N)
- reg : Address ranges of the thermal registers. Each sensor - reg : Address ranges of the thermal registers. Each sensor
needs one address range. Sorting must be done in needs one address range. Sorting must be done in
increasing order according to datasheet, i.e. increasing order according to datasheet, i.e.
...@@ -18,7 +19,7 @@ Required properties: ...@@ -18,7 +19,7 @@ Required properties:
Optional properties: Optional properties:
- interrupts : interrupts routed to the TSC (3 for H3 and M3-W) - interrupts : interrupts routed to the TSC (3 for H3, M3-W and M3-N)
- power-domain : Must contain a reference to the power domain. This - power-domain : Must contain a reference to the power domain. This
property is mandatory if the thermal sensor instance property is mandatory if the thermal sensor instance
is part of a controllable power domain. is part of a controllable power domain.
......
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
Required properties: Required properties:
- compatible : "renesas,thermal-<soctype>", - compatible : "renesas,thermal-<soctype>",
"renesas,rcar-gen2-thermal" (with thermal-zone) or "renesas,rcar-gen2-thermal" (with thermal-zone) or
"renesas,rcar-thermal" (without thermal-zone) as fallback. "renesas,rcar-thermal" (without thermal-zone) as
fallback except R-Car D3.
Examples with soctypes are: Examples with soctypes are:
- "renesas,thermal-r8a73a4" (R-Mobile APE6) - "renesas,thermal-r8a73a4" (R-Mobile APE6)
- "renesas,thermal-r8a7743" (RZ/G1M) - "renesas,thermal-r8a7743" (RZ/G1M)
...@@ -12,13 +13,15 @@ Required properties: ...@@ -12,13 +13,15 @@ Required properties:
- "renesas,thermal-r8a7791" (R-Car M2-W) - "renesas,thermal-r8a7791" (R-Car M2-W)
- "renesas,thermal-r8a7792" (R-Car V2H) - "renesas,thermal-r8a7792" (R-Car V2H)
- "renesas,thermal-r8a7793" (R-Car M2-N) - "renesas,thermal-r8a7793" (R-Car M2-N)
- "renesas,thermal-r8a77995" (R-Car D3)
- 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".
Option properties: Option properties:
- interrupts : use interrupt - interrupts : If present should contain 3 interrupts for
R-Car D3 or 1 interrupt otherwise.
Example (non interrupt support): Example (non interrupt support):
......
...@@ -8,6 +8,7 @@ Required properties: ...@@ -8,6 +8,7 @@ Required properties:
- compatible : - compatible :
- "socionext,uniphier-pxs2-thermal" : For UniPhier PXs2 SoC - "socionext,uniphier-pxs2-thermal" : For UniPhier PXs2 SoC
- "socionext,uniphier-ld20-thermal" : For UniPhier LD20 SoC - "socionext,uniphier-ld20-thermal" : For UniPhier LD20 SoC
- "socionext,uniphier-pxs3-thermal" : For UniPhier PXs3 SoC
- interrupts : IRQ for the temperature alarm - interrupts : IRQ for the temperature alarm
- #thermal-sensor-cells : Should be 0. See ./thermal.txt for details. - #thermal-sensor-cells : Should be 0. See ./thermal.txt for details.
......
/* // SPDX-License-Identifier: GPL-2.0
* Copyright 2013 Freescale Semiconductor, Inc. //
* // Copyright 2013 Freescale Semiconductor, Inc.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
...@@ -31,35 +26,57 @@ ...@@ -31,35 +26,57 @@
#define REG_CLR 0x8 #define REG_CLR 0x8
#define REG_TOG 0xc #define REG_TOG 0xc
#define MISC0 0x0150 /* i.MX6 specific */
#define MISC0_REFTOP_SELBIASOFF (1 << 3) #define IMX6_MISC0 0x0150
#define MISC1 0x0160 #define IMX6_MISC0_REFTOP_SELBIASOFF (1 << 3)
#define MISC1_IRQ_TEMPHIGH (1 << 29) #define IMX6_MISC1 0x0160
#define IMX6_MISC1_IRQ_TEMPHIGH (1 << 29)
/* Below LOW and PANIC bits are only for TEMPMON_IMX6SX */ /* Below LOW and PANIC bits are only for TEMPMON_IMX6SX */
#define MISC1_IRQ_TEMPLOW (1 << 28) #define IMX6_MISC1_IRQ_TEMPLOW (1 << 28)
#define MISC1_IRQ_TEMPPANIC (1 << 27) #define IMX6_MISC1_IRQ_TEMPPANIC (1 << 27)
#define TEMPSENSE0 0x0180 #define IMX6_TEMPSENSE0 0x0180
#define TEMPSENSE0_ALARM_VALUE_SHIFT 20 #define IMX6_TEMPSENSE0_ALARM_VALUE_SHIFT 20
#define TEMPSENSE0_ALARM_VALUE_MASK (0xfff << TEMPSENSE0_ALARM_VALUE_SHIFT) #define IMX6_TEMPSENSE0_ALARM_VALUE_MASK (0xfff << 20)
#define TEMPSENSE0_TEMP_CNT_SHIFT 8 #define IMX6_TEMPSENSE0_TEMP_CNT_SHIFT 8
#define TEMPSENSE0_TEMP_CNT_MASK (0xfff << TEMPSENSE0_TEMP_CNT_SHIFT) #define IMX6_TEMPSENSE0_TEMP_CNT_MASK (0xfff << 8)
#define TEMPSENSE0_FINISHED (1 << 2) #define IMX6_TEMPSENSE0_FINISHED (1 << 2)
#define TEMPSENSE0_MEASURE_TEMP (1 << 1) #define IMX6_TEMPSENSE0_MEASURE_TEMP (1 << 1)
#define TEMPSENSE0_POWER_DOWN (1 << 0) #define IMX6_TEMPSENSE0_POWER_DOWN (1 << 0)
#define TEMPSENSE1 0x0190 #define IMX6_TEMPSENSE1 0x0190
#define TEMPSENSE1_MEASURE_FREQ 0xffff #define IMX6_TEMPSENSE1_MEASURE_FREQ 0xffff
/* Below TEMPSENSE2 is only for TEMPMON_IMX6SX */ #define IMX6_TEMPSENSE1_MEASURE_FREQ_SHIFT 0
#define TEMPSENSE2 0x0290
#define TEMPSENSE2_LOW_VALUE_SHIFT 0
#define TEMPSENSE2_LOW_VALUE_MASK 0xfff
#define TEMPSENSE2_PANIC_VALUE_SHIFT 16
#define TEMPSENSE2_PANIC_VALUE_MASK 0xfff0000
#define OCOTP_MEM0 0x0480 #define OCOTP_MEM0 0x0480
#define OCOTP_ANA1 0x04e0 #define OCOTP_ANA1 0x04e0
/* Below TEMPSENSE2 is only for TEMPMON_IMX6SX */
#define IMX6_TEMPSENSE2 0x0290
#define IMX6_TEMPSENSE2_LOW_VALUE_SHIFT 0
#define IMX6_TEMPSENSE2_LOW_VALUE_MASK 0xfff
#define IMX6_TEMPSENSE2_PANIC_VALUE_SHIFT 16
#define IMX6_TEMPSENSE2_PANIC_VALUE_MASK 0xfff0000
/* i.MX7 specific */
#define IMX7_ANADIG_DIGPROG 0x800
#define IMX7_TEMPSENSE0 0x300
#define IMX7_TEMPSENSE0_PANIC_ALARM_SHIFT 18
#define IMX7_TEMPSENSE0_PANIC_ALARM_MASK (0x1ff << 18)
#define IMX7_TEMPSENSE0_HIGH_ALARM_SHIFT 9
#define IMX7_TEMPSENSE0_HIGH_ALARM_MASK (0x1ff << 9)
#define IMX7_TEMPSENSE0_LOW_ALARM_SHIFT 0
#define IMX7_TEMPSENSE0_LOW_ALARM_MASK 0x1ff
#define IMX7_TEMPSENSE1 0x310
#define IMX7_TEMPSENSE1_MEASURE_FREQ_SHIFT 16
#define IMX7_TEMPSENSE1_MEASURE_FREQ_MASK (0xffff << 16)
#define IMX7_TEMPSENSE1_FINISHED (1 << 11)
#define IMX7_TEMPSENSE1_MEASURE_TEMP (1 << 10)
#define IMX7_TEMPSENSE1_POWER_DOWN (1 << 9)
#define IMX7_TEMPSENSE1_TEMP_VALUE_SHIFT 0
#define IMX7_TEMPSENSE1_TEMP_VALUE_MASK 0x1ff
/* The driver supports 1 passive trip point and 1 critical trip point */ /* The driver supports 1 passive trip point and 1 critical trip point */
enum imx_thermal_trip { enum imx_thermal_trip {
IMX_TRIP_PASSIVE, IMX_TRIP_PASSIVE,
...@@ -72,17 +89,114 @@ enum imx_thermal_trip { ...@@ -72,17 +89,114 @@ enum imx_thermal_trip {
#define TEMPMON_IMX6Q 1 #define TEMPMON_IMX6Q 1
#define TEMPMON_IMX6SX 2 #define TEMPMON_IMX6SX 2
#define TEMPMON_IMX7D 3
struct thermal_soc_data { struct thermal_soc_data {
u32 version; u32 version;
u32 sensor_ctrl;
u32 power_down_mask;
u32 measure_temp_mask;
u32 measure_freq_ctrl;
u32 measure_freq_mask;
u32 measure_freq_shift;
u32 temp_data;
u32 temp_value_mask;
u32 temp_value_shift;
u32 temp_valid_mask;
u32 panic_alarm_ctrl;
u32 panic_alarm_mask;
u32 panic_alarm_shift;
u32 high_alarm_ctrl;
u32 high_alarm_mask;
u32 high_alarm_shift;
u32 low_alarm_ctrl;
u32 low_alarm_mask;
u32 low_alarm_shift;
}; };
static struct thermal_soc_data thermal_imx6q_data = { static struct thermal_soc_data thermal_imx6q_data = {
.version = TEMPMON_IMX6Q, .version = TEMPMON_IMX6Q,
.sensor_ctrl = IMX6_TEMPSENSE0,
.power_down_mask = IMX6_TEMPSENSE0_POWER_DOWN,
.measure_temp_mask = IMX6_TEMPSENSE0_MEASURE_TEMP,
.measure_freq_ctrl = IMX6_TEMPSENSE1,
.measure_freq_shift = IMX6_TEMPSENSE1_MEASURE_FREQ_SHIFT,
.measure_freq_mask = IMX6_TEMPSENSE1_MEASURE_FREQ,
.temp_data = IMX6_TEMPSENSE0,
.temp_value_mask = IMX6_TEMPSENSE0_TEMP_CNT_MASK,
.temp_value_shift = IMX6_TEMPSENSE0_TEMP_CNT_SHIFT,
.temp_valid_mask = IMX6_TEMPSENSE0_FINISHED,
.high_alarm_ctrl = IMX6_TEMPSENSE0,
.high_alarm_mask = IMX6_TEMPSENSE0_ALARM_VALUE_MASK,
.high_alarm_shift = IMX6_TEMPSENSE0_ALARM_VALUE_SHIFT,
}; };
static struct thermal_soc_data thermal_imx6sx_data = { static struct thermal_soc_data thermal_imx6sx_data = {
.version = TEMPMON_IMX6SX, .version = TEMPMON_IMX6SX,
.sensor_ctrl = IMX6_TEMPSENSE0,
.power_down_mask = IMX6_TEMPSENSE0_POWER_DOWN,
.measure_temp_mask = IMX6_TEMPSENSE0_MEASURE_TEMP,
.measure_freq_ctrl = IMX6_TEMPSENSE1,
.measure_freq_shift = IMX6_TEMPSENSE1_MEASURE_FREQ_SHIFT,
.measure_freq_mask = IMX6_TEMPSENSE1_MEASURE_FREQ,
.temp_data = IMX6_TEMPSENSE0,
.temp_value_mask = IMX6_TEMPSENSE0_TEMP_CNT_MASK,
.temp_value_shift = IMX6_TEMPSENSE0_TEMP_CNT_SHIFT,
.temp_valid_mask = IMX6_TEMPSENSE0_FINISHED,
.high_alarm_ctrl = IMX6_TEMPSENSE0,
.high_alarm_mask = IMX6_TEMPSENSE0_ALARM_VALUE_MASK,
.high_alarm_shift = IMX6_TEMPSENSE0_ALARM_VALUE_SHIFT,
.panic_alarm_ctrl = IMX6_TEMPSENSE2,
.panic_alarm_mask = IMX6_TEMPSENSE2_PANIC_VALUE_MASK,
.panic_alarm_shift = IMX6_TEMPSENSE2_PANIC_VALUE_SHIFT,
.low_alarm_ctrl = IMX6_TEMPSENSE2,
.low_alarm_mask = IMX6_TEMPSENSE2_LOW_VALUE_MASK,
.low_alarm_shift = IMX6_TEMPSENSE2_LOW_VALUE_SHIFT,
};
static struct thermal_soc_data thermal_imx7d_data = {
.version = TEMPMON_IMX7D,
.sensor_ctrl = IMX7_TEMPSENSE1,
.power_down_mask = IMX7_TEMPSENSE1_POWER_DOWN,
.measure_temp_mask = IMX7_TEMPSENSE1_MEASURE_TEMP,
.measure_freq_ctrl = IMX7_TEMPSENSE1,
.measure_freq_shift = IMX7_TEMPSENSE1_MEASURE_FREQ_SHIFT,
.measure_freq_mask = IMX7_TEMPSENSE1_MEASURE_FREQ_MASK,
.temp_data = IMX7_TEMPSENSE1,
.temp_value_mask = IMX7_TEMPSENSE1_TEMP_VALUE_MASK,
.temp_value_shift = IMX7_TEMPSENSE1_TEMP_VALUE_SHIFT,
.temp_valid_mask = IMX7_TEMPSENSE1_FINISHED,
.panic_alarm_ctrl = IMX7_TEMPSENSE1,
.panic_alarm_mask = IMX7_TEMPSENSE0_PANIC_ALARM_MASK,
.panic_alarm_shift = IMX7_TEMPSENSE0_PANIC_ALARM_SHIFT,
.high_alarm_ctrl = IMX7_TEMPSENSE0,
.high_alarm_mask = IMX7_TEMPSENSE0_HIGH_ALARM_MASK,
.high_alarm_shift = IMX7_TEMPSENSE0_HIGH_ALARM_SHIFT,
.low_alarm_ctrl = IMX7_TEMPSENSE0,
.low_alarm_mask = IMX7_TEMPSENSE0_LOW_ALARM_MASK,
.low_alarm_shift = IMX7_TEMPSENSE0_LOW_ALARM_SHIFT,
}; };
struct imx_thermal_data { struct imx_thermal_data {
...@@ -107,31 +221,42 @@ struct imx_thermal_data { ...@@ -107,31 +221,42 @@ struct imx_thermal_data {
static void imx_set_panic_temp(struct imx_thermal_data *data, static void imx_set_panic_temp(struct imx_thermal_data *data,
int panic_temp) int panic_temp)
{ {
const struct thermal_soc_data *soc_data = data->socdata;
struct regmap *map = data->tempmon; struct regmap *map = data->tempmon;
int critical_value; int critical_value;
critical_value = (data->c2 - panic_temp) / data->c1; critical_value = (data->c2 - panic_temp) / data->c1;
regmap_write(map, TEMPSENSE2 + REG_CLR, TEMPSENSE2_PANIC_VALUE_MASK);
regmap_write(map, TEMPSENSE2 + REG_SET, critical_value << regmap_write(map, soc_data->panic_alarm_ctrl + REG_CLR,
TEMPSENSE2_PANIC_VALUE_SHIFT); soc_data->panic_alarm_mask);
regmap_write(map, soc_data->panic_alarm_ctrl + REG_SET,
critical_value << soc_data->panic_alarm_shift);
} }
static void imx_set_alarm_temp(struct imx_thermal_data *data, static void imx_set_alarm_temp(struct imx_thermal_data *data,
int alarm_temp) int alarm_temp)
{ {
struct regmap *map = data->tempmon; struct regmap *map = data->tempmon;
const struct thermal_soc_data *soc_data = data->socdata;
int alarm_value; int alarm_value;
data->alarm_temp = alarm_temp; data->alarm_temp = alarm_temp;
alarm_value = (data->c2 - alarm_temp) / data->c1;
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_ALARM_VALUE_MASK); if (data->socdata->version == TEMPMON_IMX7D)
regmap_write(map, TEMPSENSE0 + REG_SET, alarm_value << alarm_value = alarm_temp / 1000 + data->c1 - 25;
TEMPSENSE0_ALARM_VALUE_SHIFT); else
alarm_value = (data->c2 - alarm_temp) / data->c1;
regmap_write(map, soc_data->high_alarm_ctrl + REG_CLR,
soc_data->high_alarm_mask);
regmap_write(map, soc_data->high_alarm_ctrl + REG_SET,
alarm_value << soc_data->high_alarm_shift);
} }
static int imx_get_temp(struct thermal_zone_device *tz, int *temp) static int imx_get_temp(struct thermal_zone_device *tz, int *temp)
{ {
struct imx_thermal_data *data = tz->devdata; struct imx_thermal_data *data = tz->devdata;
const struct thermal_soc_data *soc_data = data->socdata;
struct regmap *map = data->tempmon; struct regmap *map = data->tempmon;
unsigned int n_meas; unsigned int n_meas;
bool wait; bool wait;
...@@ -139,16 +264,18 @@ static int imx_get_temp(struct thermal_zone_device *tz, int *temp) ...@@ -139,16 +264,18 @@ static int imx_get_temp(struct thermal_zone_device *tz, int *temp)
if (data->mode == THERMAL_DEVICE_ENABLED) { if (data->mode == THERMAL_DEVICE_ENABLED) {
/* Check if a measurement is currently in progress */ /* Check if a measurement is currently in progress */
regmap_read(map, TEMPSENSE0, &val); regmap_read(map, soc_data->temp_data, &val);
wait = !(val & TEMPSENSE0_FINISHED); wait = !(val & soc_data->temp_valid_mask);
} else { } else {
/* /*
* Every time we measure the temperature, we will power on the * Every time we measure the temperature, we will power on the
* temperature sensor, enable measurements, take a reading, * temperature sensor, enable measurements, take a reading,
* disable measurements, power off the temperature sensor. * disable measurements, power off the temperature sensor.
*/ */
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN); regmap_write(map, soc_data->sensor_ctrl + REG_CLR,
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP); soc_data->power_down_mask);
regmap_write(map, soc_data->sensor_ctrl + REG_SET,
soc_data->measure_temp_mask);
wait = true; wait = true;
} }
...@@ -160,22 +287,28 @@ static int imx_get_temp(struct thermal_zone_device *tz, int *temp) ...@@ -160,22 +287,28 @@ static int imx_get_temp(struct thermal_zone_device *tz, int *temp)
if (wait) if (wait)
usleep_range(20, 50); usleep_range(20, 50);
regmap_read(map, TEMPSENSE0, &val); regmap_read(map, soc_data->temp_data, &val);
if (data->mode != THERMAL_DEVICE_ENABLED) { if (data->mode != THERMAL_DEVICE_ENABLED) {
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP); regmap_write(map, soc_data->sensor_ctrl + REG_CLR,
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN); soc_data->measure_temp_mask);
regmap_write(map, soc_data->sensor_ctrl + REG_SET,
soc_data->power_down_mask);
} }
if ((val & TEMPSENSE0_FINISHED) == 0) { if ((val & soc_data->temp_valid_mask) == 0) {
dev_dbg(&tz->device, "temp measurement never finished\n"); dev_dbg(&tz->device, "temp measurement never finished\n");
return -EAGAIN; return -EAGAIN;
} }
n_meas = (val & TEMPSENSE0_TEMP_CNT_MASK) >> TEMPSENSE0_TEMP_CNT_SHIFT; n_meas = (val & soc_data->temp_value_mask)
>> soc_data->temp_value_shift;
/* See imx_init_calib() for formula derivation */ /* See imx_init_calib() for formula derivation */
*temp = data->c2 - n_meas * data->c1; if (data->socdata->version == TEMPMON_IMX7D)
*temp = (n_meas - data->c1 + 25) * 1000;
else
*temp = data->c2 - n_meas * data->c1;
/* Update alarm value to next higher trip point for TEMPMON_IMX6Q */ /* Update alarm value to next higher trip point for TEMPMON_IMX6Q */
if (data->socdata->version == TEMPMON_IMX6Q) { if (data->socdata->version == TEMPMON_IMX6Q) {
...@@ -219,21 +352,26 @@ static int imx_set_mode(struct thermal_zone_device *tz, ...@@ -219,21 +352,26 @@ static int imx_set_mode(struct thermal_zone_device *tz,
{ {
struct imx_thermal_data *data = tz->devdata; struct imx_thermal_data *data = tz->devdata;
struct regmap *map = data->tempmon; struct regmap *map = data->tempmon;
const struct thermal_soc_data *soc_data = data->socdata;
if (mode == THERMAL_DEVICE_ENABLED) { if (mode == THERMAL_DEVICE_ENABLED) {
tz->polling_delay = IMX_POLLING_DELAY; tz->polling_delay = IMX_POLLING_DELAY;
tz->passive_delay = IMX_PASSIVE_DELAY; tz->passive_delay = IMX_PASSIVE_DELAY;
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN); regmap_write(map, soc_data->sensor_ctrl + REG_CLR,
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP); soc_data->power_down_mask);
regmap_write(map, soc_data->sensor_ctrl + REG_SET,
soc_data->measure_temp_mask);
if (!data->irq_enabled) { if (!data->irq_enabled) {
data->irq_enabled = true; data->irq_enabled = true;
enable_irq(data->irq); enable_irq(data->irq);
} }
} else { } else {
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP); regmap_write(map, soc_data->sensor_ctrl + REG_CLR,
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN); soc_data->measure_temp_mask);
regmap_write(map, soc_data->sensor_ctrl + REG_SET,
soc_data->power_down_mask);
tz->polling_delay = 0; tz->polling_delay = 0;
tz->passive_delay = 0; tz->passive_delay = 0;
...@@ -354,6 +492,15 @@ static int imx_init_calib(struct platform_device *pdev, u32 ocotp_ana1) ...@@ -354,6 +492,15 @@ static int imx_init_calib(struct platform_device *pdev, u32 ocotp_ana1)
return -EINVAL; return -EINVAL;
} }
/*
* On i.MX7D, we only use the calibration data at 25C to get the temp,
* Tmeas = ( Nmeas - n1) + 25; n1 is the fuse value for 25C.
*/
if (data->socdata->version == TEMPMON_IMX7D) {
data->c1 = (ocotp_ana1 >> 9) & 0x1ff;
return 0;
}
/* /*
* The sensor is calibrated at 25 °C (aka T1) and the value measured * The sensor is calibrated at 25 °C (aka T1) and the value measured
* (aka N1) at this temperature is provided in bits [31:20] in the * (aka N1) at this temperature is provided in bits [31:20] in the
...@@ -492,6 +639,7 @@ static irqreturn_t imx_thermal_alarm_irq_thread(int irq, void *dev) ...@@ -492,6 +639,7 @@ static irqreturn_t imx_thermal_alarm_irq_thread(int irq, void *dev)
static const struct of_device_id of_imx_thermal_match[] = { static const struct of_device_id of_imx_thermal_match[] = {
{ .compatible = "fsl,imx6q-tempmon", .data = &thermal_imx6q_data, }, { .compatible = "fsl,imx6q-tempmon", .data = &thermal_imx6q_data, },
{ .compatible = "fsl,imx6sx-tempmon", .data = &thermal_imx6sx_data, }, { .compatible = "fsl,imx6sx-tempmon", .data = &thermal_imx6sx_data, },
{ .compatible = "fsl,imx7d-tempmon", .data = &thermal_imx7d_data, },
{ /* end */ } { /* end */ }
}; };
MODULE_DEVICE_TABLE(of, of_imx_thermal_match); MODULE_DEVICE_TABLE(of, of_imx_thermal_match);
...@@ -523,14 +671,15 @@ static int imx_thermal_probe(struct platform_device *pdev) ...@@ -523,14 +671,15 @@ static int imx_thermal_probe(struct platform_device *pdev)
/* make sure the IRQ flag is clear before enabling irq on i.MX6SX */ /* make sure the IRQ flag is clear before enabling irq on i.MX6SX */
if (data->socdata->version == TEMPMON_IMX6SX) { if (data->socdata->version == TEMPMON_IMX6SX) {
regmap_write(map, MISC1 + REG_CLR, MISC1_IRQ_TEMPHIGH | regmap_write(map, IMX6_MISC1 + REG_CLR,
MISC1_IRQ_TEMPLOW | MISC1_IRQ_TEMPPANIC); IMX6_MISC1_IRQ_TEMPHIGH | IMX6_MISC1_IRQ_TEMPLOW
| IMX6_MISC1_IRQ_TEMPPANIC);
/* /*
* reset value of LOW ALARM is incorrect, set it to lowest * reset value of LOW ALARM is incorrect, set it to lowest
* value to avoid false trigger of low alarm. * value to avoid false trigger of low alarm.
*/ */
regmap_write(map, TEMPSENSE2 + REG_SET, regmap_write(map, data->socdata->low_alarm_ctrl + REG_SET,
TEMPSENSE2_LOW_VALUE_MASK); data->socdata->low_alarm_mask);
} }
data->irq = platform_get_irq(pdev, 0); data->irq = platform_get_irq(pdev, 0);
...@@ -557,11 +706,17 @@ static int imx_thermal_probe(struct platform_device *pdev) ...@@ -557,11 +706,17 @@ static int imx_thermal_probe(struct platform_device *pdev)
} }
/* Make sure sensor is in known good state for measurements */ /* Make sure sensor is in known good state for measurements */
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN); regmap_write(map, data->socdata->sensor_ctrl + REG_CLR,
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP); data->socdata->power_down_mask);
regmap_write(map, TEMPSENSE1 + REG_CLR, TEMPSENSE1_MEASURE_FREQ); regmap_write(map, data->socdata->sensor_ctrl + REG_CLR,
regmap_write(map, MISC0 + REG_SET, MISC0_REFTOP_SELBIASOFF); data->socdata->measure_temp_mask);
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN); regmap_write(map, data->socdata->measure_freq_ctrl + REG_CLR,
data->socdata->measure_freq_mask);
if (data->socdata->version != TEMPMON_IMX7D)
regmap_write(map, IMX6_MISC0 + REG_SET,
IMX6_MISC0_REFTOP_SELBIASOFF);
regmap_write(map, data->socdata->sensor_ctrl + REG_SET,
data->socdata->power_down_mask);
data->policy = cpufreq_cpu_get(0); data->policy = cpufreq_cpu_get(0);
if (!data->policy) { if (!data->policy) {
...@@ -626,16 +781,20 @@ static int imx_thermal_probe(struct platform_device *pdev) ...@@ -626,16 +781,20 @@ static int imx_thermal_probe(struct platform_device *pdev)
data->temp_passive / 1000); data->temp_passive / 1000);
/* Enable measurements at ~ 10 Hz */ /* Enable measurements at ~ 10 Hz */
regmap_write(map, TEMPSENSE1 + REG_CLR, TEMPSENSE1_MEASURE_FREQ); regmap_write(map, data->socdata->measure_freq_ctrl + REG_CLR,
data->socdata->measure_freq_mask);
measure_freq = DIV_ROUND_UP(32768, 10); /* 10 Hz */ measure_freq = DIV_ROUND_UP(32768, 10); /* 10 Hz */
regmap_write(map, TEMPSENSE1 + REG_SET, measure_freq); regmap_write(map, data->socdata->measure_freq_ctrl + REG_SET,
measure_freq << data->socdata->measure_freq_shift);
imx_set_alarm_temp(data, data->temp_passive); imx_set_alarm_temp(data, data->temp_passive);
if (data->socdata->version == TEMPMON_IMX6SX) if (data->socdata->version == TEMPMON_IMX6SX)
imx_set_panic_temp(data, data->temp_critical); imx_set_panic_temp(data, data->temp_critical);
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN); regmap_write(map, data->socdata->sensor_ctrl + REG_CLR,
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP); data->socdata->power_down_mask);
regmap_write(map, data->socdata->sensor_ctrl + REG_SET,
data->socdata->measure_temp_mask);
data->irq_enabled = true; data->irq_enabled = true;
data->mode = THERMAL_DEVICE_ENABLED; data->mode = THERMAL_DEVICE_ENABLED;
...@@ -661,7 +820,8 @@ static int imx_thermal_remove(struct platform_device *pdev) ...@@ -661,7 +820,8 @@ static int imx_thermal_remove(struct platform_device *pdev)
struct regmap *map = data->tempmon; struct regmap *map = data->tempmon;
/* Disable measurements */ /* Disable measurements */
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN); regmap_write(map, data->socdata->sensor_ctrl + REG_SET,
data->socdata->power_down_mask);
if (!IS_ERR(data->thermal_clk)) if (!IS_ERR(data->thermal_clk))
clk_disable_unprepare(data->thermal_clk); clk_disable_unprepare(data->thermal_clk);
...@@ -684,8 +844,10 @@ static int imx_thermal_suspend(struct device *dev) ...@@ -684,8 +844,10 @@ static int imx_thermal_suspend(struct device *dev)
* temperature will be read as the thermal sensor is powered * temperature will be read as the thermal sensor is powered
* down. * down.
*/ */
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP); regmap_write(map, data->socdata->sensor_ctrl + REG_CLR,
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN); data->socdata->measure_temp_mask);
regmap_write(map, data->socdata->sensor_ctrl + REG_SET,
data->socdata->power_down_mask);
data->mode = THERMAL_DEVICE_DISABLED; data->mode = THERMAL_DEVICE_DISABLED;
clk_disable_unprepare(data->thermal_clk); clk_disable_unprepare(data->thermal_clk);
...@@ -702,8 +864,10 @@ static int imx_thermal_resume(struct device *dev) ...@@ -702,8 +864,10 @@ static int imx_thermal_resume(struct device *dev)
if (ret) if (ret)
return ret; return ret;
/* Enabled thermal sensor after resume */ /* Enabled thermal sensor after resume */
regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN); regmap_write(map, data->socdata->sensor_ctrl + REG_CLR,
regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP); data->socdata->power_down_mask);
regmap_write(map, data->socdata->sensor_ctrl + REG_SET,
data->socdata->measure_temp_mask);
data->mode = THERMAL_DEVICE_ENABLED; data->mode = THERMAL_DEVICE_ENABLED;
return 0; return 0;
......
...@@ -153,6 +153,12 @@ ...@@ -153,6 +153,12 @@
/* The number of sensing points per bank */ /* The number of sensing points per bank */
#define MT2712_NUM_SENSORS_PER_ZONE 4 #define MT2712_NUM_SENSORS_PER_ZONE 4
#define MT7622_TEMP_AUXADC_CHANNEL 11
#define MT7622_NUM_SENSORS 1
#define MT7622_NUM_ZONES 1
#define MT7622_NUM_SENSORS_PER_ZONE 1
#define MT7622_TS1 0
struct mtk_thermal; struct mtk_thermal;
struct thermal_bank_cfg { struct thermal_bank_cfg {
...@@ -242,6 +248,12 @@ static const int mt2712_adcpnp[MT2712_NUM_SENSORS_PER_ZONE] = { ...@@ -242,6 +248,12 @@ static const int mt2712_adcpnp[MT2712_NUM_SENSORS_PER_ZONE] = {
static const int mt2712_mux_values[MT2712_NUM_SENSORS] = { 0, 1, 2, 3 }; static const int mt2712_mux_values[MT2712_NUM_SENSORS] = { 0, 1, 2, 3 };
/* MT7622 thermal sensor data */
static const int mt7622_bank_data[MT7622_NUM_SENSORS] = { MT7622_TS1, };
static const int mt7622_msr[MT7622_NUM_SENSORS_PER_ZONE] = { TEMP_MSR0, };
static const int mt7622_adcpnp[MT7622_NUM_SENSORS_PER_ZONE] = { TEMP_ADCPNP0, };
static const int mt7622_mux_values[MT7622_NUM_SENSORS] = { 0, };
/** /**
* The MT8173 thermal controller has four banks. Each bank can read up to * The MT8173 thermal controller has four banks. Each bank can read up to
* four temperature sensors simultaneously. The MT8173 has a total of 5 * four temperature sensors simultaneously. The MT8173 has a total of 5
...@@ -329,6 +341,25 @@ static const struct mtk_thermal_data mt2712_thermal_data = { ...@@ -329,6 +341,25 @@ static const struct mtk_thermal_data mt2712_thermal_data = {
.sensor_mux_values = mt2712_mux_values, .sensor_mux_values = mt2712_mux_values,
}; };
/*
* MT7622 have only one sensing point which uses AUXADC Channel 11 for raw data
* access.
*/
static const struct mtk_thermal_data mt7622_thermal_data = {
.auxadc_channel = MT7622_TEMP_AUXADC_CHANNEL,
.num_banks = MT7622_NUM_ZONES,
.num_sensors = MT7622_NUM_SENSORS,
.bank_data = {
{
.num_sensors = 1,
.sensors = mt7622_bank_data,
},
},
.msr = mt7622_msr,
.adcpnp = mt7622_adcpnp,
.sensor_mux_values = mt7622_mux_values,
};
/** /**
* raw_to_mcelsius - convert a raw ADC value to mcelsius * raw_to_mcelsius - convert a raw ADC value to mcelsius
* @mt: The thermal controller * @mt: The thermal controller
...@@ -631,6 +662,10 @@ static const struct of_device_id mtk_thermal_of_match[] = { ...@@ -631,6 +662,10 @@ static const struct of_device_id mtk_thermal_of_match[] = {
{ {
.compatible = "mediatek,mt2712-thermal", .compatible = "mediatek,mt2712-thermal",
.data = (void *)&mt2712_thermal_data, .data = (void *)&mt2712_thermal_data,
},
{
.compatible = "mediatek,mt7622-thermal",
.data = (void *)&mt7622_thermal_data,
}, { }, {
}, },
}; };
...@@ -642,7 +677,6 @@ static int mtk_thermal_probe(struct platform_device *pdev) ...@@ -642,7 +677,6 @@ static int mtk_thermal_probe(struct platform_device *pdev)
struct device_node *auxadc, *apmixedsys, *np = pdev->dev.of_node; struct device_node *auxadc, *apmixedsys, *np = pdev->dev.of_node;
struct mtk_thermal *mt; struct mtk_thermal *mt;
struct resource *res; struct resource *res;
const struct of_device_id *of_id;
u64 auxadc_phys_base, apmixed_phys_base; u64 auxadc_phys_base, apmixed_phys_base;
struct thermal_zone_device *tzdev; struct thermal_zone_device *tzdev;
...@@ -650,9 +684,7 @@ static int mtk_thermal_probe(struct platform_device *pdev) ...@@ -650,9 +684,7 @@ static int mtk_thermal_probe(struct platform_device *pdev)
if (!mt) if (!mt)
return -ENOMEM; return -ENOMEM;
of_id = of_match_device(mtk_thermal_of_match, &pdev->dev); mt->conf = of_device_get_match_data(&pdev->dev);
if (of_id)
mt->conf = (const struct mtk_thermal_data *)of_id->data;
mt->clk_peri_therm = devm_clk_get(&pdev->dev, "therm"); mt->clk_peri_therm = devm_clk_get(&pdev->dev, "therm");
if (IS_ERR(mt->clk_peri_therm)) if (IS_ERR(mt->clk_peri_therm))
......
/* /*
* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved. * Copyright (c) 2011-2015, 2017, The Linux Foundation. All rights reserved.
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and * it under the terms of the GNU General Public License version 2 and
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
* GNU General Public License for more details. * GNU General Public License for more details.
*/ */
#include <linux/bitops.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/iio/consumer.h> #include <linux/iio/consumer.h>
...@@ -29,13 +30,17 @@ ...@@ -29,13 +30,17 @@
#define QPNP_TM_REG_ALARM_CTRL 0x46 #define QPNP_TM_REG_ALARM_CTRL 0x46
#define QPNP_TM_TYPE 0x09 #define QPNP_TM_TYPE 0x09
#define QPNP_TM_SUBTYPE 0x08 #define QPNP_TM_SUBTYPE_GEN1 0x08
#define QPNP_TM_SUBTYPE_GEN2 0x09
#define STATUS_STAGE_MASK 0x03 #define STATUS_GEN1_STAGE_MASK GENMASK(1, 0)
#define STATUS_GEN2_STATE_MASK GENMASK(6, 4)
#define STATUS_GEN2_STATE_SHIFT 4
#define SHUTDOWN_CTRL1_THRESHOLD_MASK 0x03 #define SHUTDOWN_CTRL1_OVERRIDE_MASK GENMASK(7, 6)
#define SHUTDOWN_CTRL1_THRESHOLD_MASK GENMASK(1, 0)
#define ALARM_CTRL_FORCE_ENABLE 0x80 #define ALARM_CTRL_FORCE_ENABLE BIT(7)
/* /*
* Trip point values based on threshold control * Trip point values based on threshold control
...@@ -58,6 +63,7 @@ ...@@ -58,6 +63,7 @@
struct qpnp_tm_chip { struct qpnp_tm_chip {
struct regmap *map; struct regmap *map;
struct thermal_zone_device *tz_dev; struct thermal_zone_device *tz_dev;
unsigned int subtype;
long temp; long temp;
unsigned int thresh; unsigned int thresh;
unsigned int stage; unsigned int stage;
...@@ -66,6 +72,9 @@ struct qpnp_tm_chip { ...@@ -66,6 +72,9 @@ struct qpnp_tm_chip {
struct iio_channel *adc; struct iio_channel *adc;
}; };
/* This array maps from GEN2 alarm state to GEN1 alarm stage */
static const unsigned int alarm_state_map[8] = {0, 1, 1, 2, 2, 3, 3, 3};
static int qpnp_tm_read(struct qpnp_tm_chip *chip, u16 addr, u8 *data) static int qpnp_tm_read(struct qpnp_tm_chip *chip, u16 addr, u8 *data)
{ {
unsigned int val; unsigned int val;
...@@ -84,30 +93,59 @@ static int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 data) ...@@ -84,30 +93,59 @@ static int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 data)
return regmap_write(chip->map, chip->base + addr, data); return regmap_write(chip->map, chip->base + addr, data);
} }
/**
* qpnp_tm_get_temp_stage() - return over-temperature stage
* @chip: Pointer to the qpnp_tm chip
*
* Return: stage (GEN1) or state (GEN2) on success, or errno on failure.
*/
static int qpnp_tm_get_temp_stage(struct qpnp_tm_chip *chip)
{
int ret;
u8 reg = 0;
ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, &reg);
if (ret < 0)
return ret;
if (chip->subtype == QPNP_TM_SUBTYPE_GEN1)
ret = reg & STATUS_GEN1_STAGE_MASK;
else
ret = (reg & STATUS_GEN2_STATE_MASK) >> STATUS_GEN2_STATE_SHIFT;
return ret;
}
/* /*
* This function updates the internal temp value based on the * This function updates the internal temp value based on the
* current thermal stage and threshold as well as the previous stage * current thermal stage and threshold as well as the previous stage
*/ */
static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip) static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
{ {
unsigned int stage; unsigned int stage, stage_new, stage_old;
int ret; int ret;
u8 reg = 0;
ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, &reg); ret = qpnp_tm_get_temp_stage(chip);
if (ret < 0) if (ret < 0)
return ret; return ret;
stage = ret;
stage = reg & STATUS_STAGE_MASK; if (chip->subtype == QPNP_TM_SUBTYPE_GEN1) {
stage_new = stage;
stage_old = chip->stage;
} else {
stage_new = alarm_state_map[stage];
stage_old = alarm_state_map[chip->stage];
}
if (stage > chip->stage) { if (stage_new > stage_old) {
/* increasing stage, use lower bound */ /* increasing stage, use lower bound */
chip->temp = (stage - 1) * TEMP_STAGE_STEP + chip->temp = (stage_new - 1) * TEMP_STAGE_STEP +
chip->thresh * TEMP_THRESH_STEP + chip->thresh * TEMP_THRESH_STEP +
TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN; TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
} else if (stage < chip->stage) { } else if (stage_new < stage_old) {
/* decreasing stage, use upper bound */ /* decreasing stage, use upper bound */
chip->temp = stage * TEMP_STAGE_STEP + chip->temp = stage_new * TEMP_STAGE_STEP +
chip->thresh * TEMP_THRESH_STEP - chip->thresh * TEMP_THRESH_STEP -
TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN; TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
} }
...@@ -162,28 +200,37 @@ static irqreturn_t qpnp_tm_isr(int irq, void *data) ...@@ -162,28 +200,37 @@ static irqreturn_t qpnp_tm_isr(int irq, void *data)
*/ */
static int qpnp_tm_init(struct qpnp_tm_chip *chip) static int qpnp_tm_init(struct qpnp_tm_chip *chip)
{ {
unsigned int stage;
int ret; int ret;
u8 reg; u8 reg = 0;
chip->thresh = THRESH_MIN; ret = qpnp_tm_read(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, &reg);
if (ret < 0)
return ret;
chip->thresh = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK;
chip->temp = DEFAULT_TEMP; chip->temp = DEFAULT_TEMP;
ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, &reg); ret = qpnp_tm_get_temp_stage(chip);
if (ret < 0) if (ret < 0)
return ret; return ret;
chip->stage = ret;
chip->stage = reg & STATUS_STAGE_MASK; stage = chip->subtype == QPNP_TM_SUBTYPE_GEN1
? chip->stage : alarm_state_map[chip->stage];
if (chip->stage) if (stage)
chip->temp = chip->thresh * TEMP_THRESH_STEP + chip->temp = chip->thresh * TEMP_THRESH_STEP +
(chip->stage - 1) * TEMP_STAGE_STEP + (stage - 1) * TEMP_STAGE_STEP +
TEMP_THRESH_MIN; TEMP_THRESH_MIN;
/* /*
* Set threshold and disable software override of stage 2 and 3 * Set threshold and disable software override of stage 2 and 3
* shutdowns. * shutdowns.
*/ */
reg = chip->thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK; chip->thresh = THRESH_MIN;
reg &= ~(SHUTDOWN_CTRL1_OVERRIDE_MASK | SHUTDOWN_CTRL1_THRESHOLD_MASK);
reg |= chip->thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK;
ret = qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg); ret = qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -246,12 +293,15 @@ static int qpnp_tm_probe(struct platform_device *pdev) ...@@ -246,12 +293,15 @@ static int qpnp_tm_probe(struct platform_device *pdev)
return ret; return ret;
} }
if (type != QPNP_TM_TYPE || subtype != QPNP_TM_SUBTYPE) { if (type != QPNP_TM_TYPE || (subtype != QPNP_TM_SUBTYPE_GEN1
&& subtype != QPNP_TM_SUBTYPE_GEN2)) {
dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n", dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n",
type, subtype); type, subtype);
return -ENODEV; return -ENODEV;
} }
chip->subtype = subtype;
ret = qpnp_tm_init(chip); ret = qpnp_tm_init(chip);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "init failed\n"); dev_err(&pdev->dev, "init failed\n");
......
...@@ -115,6 +115,7 @@ static int tsens_probe(struct platform_device *pdev) ...@@ -115,6 +115,7 @@ static int tsens_probe(struct platform_device *pdev)
struct tsens_device *tmdev; struct tsens_device *tmdev;
const struct tsens_data *data; const struct tsens_data *data;
const struct of_device_id *id; const struct of_device_id *id;
u32 num_sensors;
if (pdev->dev.of_node) if (pdev->dev.of_node)
dev = &pdev->dev; dev = &pdev->dev;
...@@ -129,19 +130,24 @@ static int tsens_probe(struct platform_device *pdev) ...@@ -129,19 +130,24 @@ static int tsens_probe(struct platform_device *pdev)
else else
data = &data_8960; data = &data_8960;
if (data->num_sensors <= 0) { num_sensors = data->num_sensors;
if (np)
of_property_read_u32(np, "#qcom,sensors", &num_sensors);
if (num_sensors <= 0) {
dev_err(dev, "invalid number of sensors\n"); dev_err(dev, "invalid number of sensors\n");
return -EINVAL; return -EINVAL;
} }
tmdev = devm_kzalloc(dev, tmdev = devm_kzalloc(dev,
struct_size(tmdev, sensor, data->num_sensors), struct_size(tmdev, sensor, num_sensors),
GFP_KERNEL); GFP_KERNEL);
if (!tmdev) if (!tmdev)
return -ENOMEM; return -ENOMEM;
tmdev->dev = dev; tmdev->dev = dev;
tmdev->num_sensors = data->num_sensors; tmdev->num_sensors = num_sensors;
tmdev->ops = data->ops; tmdev->ops = data->ops;
for (i = 0; i < tmdev->num_sensors; i++) { for (i = 0; i < tmdev->num_sensors; i++) {
if (data->hw_ids) if (data->hw_ids)
......
...@@ -132,7 +132,7 @@ static inline void rcar_gen3_thermal_write(struct rcar_gen3_thermal_tsc *tsc, ...@@ -132,7 +132,7 @@ static inline void rcar_gen3_thermal_write(struct rcar_gen3_thermal_tsc *tsc,
#define RCAR3_THERMAL_GRAN 500 /* mili Celsius */ #define RCAR3_THERMAL_GRAN 500 /* mili Celsius */
/* no idea where these constants come from */ /* no idea where these constants come from */
#define TJ_1 96 #define TJ_1 116
#define TJ_3 -41 #define TJ_3 -41
static void rcar_gen3_thermal_calc_coefs(struct equation_coefs *coef, static void rcar_gen3_thermal_calc_coefs(struct equation_coefs *coef,
...@@ -146,7 +146,7 @@ static void rcar_gen3_thermal_calc_coefs(struct equation_coefs *coef, ...@@ -146,7 +146,7 @@ static void rcar_gen3_thermal_calc_coefs(struct equation_coefs *coef,
* Division is not scaled in BSP and if scaled it might overflow * Division is not scaled in BSP and if scaled it might overflow
* the dividend (4095 * 4095 << 14 > INT_MAX) so keep it unscaled * the dividend (4095 * 4095 << 14 > INT_MAX) so keep it unscaled
*/ */
tj_2 = (FIXPT_INT((ptat[1] - ptat[2]) * 137) tj_2 = (FIXPT_INT((ptat[1] - ptat[2]) * 157)
/ (ptat[0] - ptat[2])) - FIXPT_INT(41); / (ptat[0] - ptat[2])) - FIXPT_INT(41);
coef->a1 = FIXPT_DIV(FIXPT_INT(thcode[1] - thcode[2]), coef->a1 = FIXPT_DIV(FIXPT_INT(thcode[1] - thcode[2]),
...@@ -207,8 +207,8 @@ static int rcar_gen3_thermal_set_trips(void *devdata, int low, int high) ...@@ -207,8 +207,8 @@ static int rcar_gen3_thermal_set_trips(void *devdata, int low, int high)
{ {
struct rcar_gen3_thermal_tsc *tsc = devdata; struct rcar_gen3_thermal_tsc *tsc = devdata;
low = clamp_val(low, -40000, 125000); low = clamp_val(low, -40000, 120000);
high = clamp_val(high, -40000, 125000); high = clamp_val(high, -40000, 120000);
rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP1, rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP1,
rcar_gen3_thermal_mcelsius_to_temp(tsc, low)); rcar_gen3_thermal_mcelsius_to_temp(tsc, low));
...@@ -329,6 +329,7 @@ static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_tsc *tsc) ...@@ -329,6 +329,7 @@ static void rcar_gen3_thermal_init(struct rcar_gen3_thermal_tsc *tsc)
static const struct of_device_id rcar_gen3_thermal_dt_ids[] = { static const struct of_device_id rcar_gen3_thermal_dt_ids[] = {
{ .compatible = "renesas,r8a7795-thermal", }, { .compatible = "renesas,r8a7795-thermal", },
{ .compatible = "renesas,r8a7796-thermal", }, { .compatible = "renesas,r8a7796-thermal", },
{ .compatible = "renesas,r8a77965-thermal", },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids); MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids);
...@@ -354,11 +355,11 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) ...@@ -354,11 +355,11 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev)
/* default values if FUSEs are missing */ /* default values if FUSEs are missing */
/* TODO: Read values from hardware on supported platforms */ /* TODO: Read values from hardware on supported platforms */
int ptat[3] = { 2351, 1509, 435 }; int ptat[3] = { 2631, 1509, 435 };
int thcode[TSC_MAX_NUM][3] = { int thcode[TSC_MAX_NUM][3] = {
{ 3248, 2800, 2221 }, { 3397, 2800, 2221 },
{ 3245, 2795, 2216 }, { 3393, 2795, 2216 },
{ 3250, 2805, 2237 }, { 3389, 2805, 2237 },
}; };
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
......
...@@ -58,10 +58,47 @@ struct rcar_thermal_common { ...@@ -58,10 +58,47 @@ struct rcar_thermal_common {
spinlock_t lock; spinlock_t lock;
}; };
struct rcar_thermal_chip {
unsigned int use_of_thermal : 1;
unsigned int has_filonoff : 1;
unsigned int irq_per_ch : 1;
unsigned int needs_suspend_resume : 1;
unsigned int nirqs;
};
static const struct rcar_thermal_chip rcar_thermal = {
.use_of_thermal = 0,
.has_filonoff = 1,
.irq_per_ch = 0,
.needs_suspend_resume = 0,
.nirqs = 1,
};
static const struct rcar_thermal_chip rcar_gen2_thermal = {
.use_of_thermal = 1,
.has_filonoff = 1,
.irq_per_ch = 0,
.needs_suspend_resume = 0,
.nirqs = 1,
};
static const struct rcar_thermal_chip rcar_gen3_thermal = {
.use_of_thermal = 1,
.has_filonoff = 0,
.irq_per_ch = 1,
.needs_suspend_resume = 1,
/*
* The Gen3 chip has 3 interrupts, but this driver uses only 2
* interrupts to detect a temperature change, rise or fall.
*/
.nirqs = 2,
};
struct rcar_thermal_priv { struct rcar_thermal_priv {
void __iomem *base; void __iomem *base;
struct rcar_thermal_common *common; struct rcar_thermal_common *common;
struct thermal_zone_device *zone; struct thermal_zone_device *zone;
const struct rcar_thermal_chip *chip;
struct delayed_work work; struct delayed_work work;
struct mutex lock; struct mutex lock;
struct list_head list; struct list_head list;
...@@ -77,13 +114,20 @@ struct rcar_thermal_priv { ...@@ -77,13 +114,20 @@ struct rcar_thermal_priv {
#define rcar_priv_to_dev(priv) ((priv)->common->dev) #define rcar_priv_to_dev(priv) ((priv)->common->dev)
#define rcar_has_irq_support(priv) ((priv)->common->base) #define rcar_has_irq_support(priv) ((priv)->common->base)
#define rcar_id_to_shift(priv) ((priv)->id * 8) #define rcar_id_to_shift(priv) ((priv)->id * 8)
#define rcar_of_data(dev) ((unsigned long)of_device_get_match_data(dev))
#define rcar_use_of_thermal(dev) (rcar_of_data(dev) == USE_OF_THERMAL)
#define USE_OF_THERMAL 1
static const struct of_device_id rcar_thermal_dt_ids[] = { static const struct of_device_id rcar_thermal_dt_ids[] = {
{ .compatible = "renesas,rcar-thermal", }, {
{ .compatible = "renesas,rcar-gen2-thermal", .data = (void *)USE_OF_THERMAL }, .compatible = "renesas,rcar-thermal",
.data = &rcar_thermal,
},
{
.compatible = "renesas,rcar-gen2-thermal",
.data = &rcar_gen2_thermal,
},
{
.compatible = "renesas,thermal-r8a77995",
.data = &rcar_gen3_thermal,
},
{}, {},
}; };
MODULE_DEVICE_TABLE(of, rcar_thermal_dt_ids); MODULE_DEVICE_TABLE(of, rcar_thermal_dt_ids);
...@@ -190,7 +234,8 @@ static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv) ...@@ -190,7 +234,8 @@ static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv)
* enable IRQ * enable IRQ
*/ */
if (rcar_has_irq_support(priv)) { if (rcar_has_irq_support(priv)) {
rcar_thermal_write(priv, FILONOFF, 0); if (priv->chip->has_filonoff)
rcar_thermal_write(priv, FILONOFF, 0);
/* enable Rising/Falling edge interrupt */ /* enable Rising/Falling edge interrupt */
rcar_thermal_write(priv, POSNEG, 0x1); rcar_thermal_write(priv, POSNEG, 0x1);
...@@ -420,7 +465,7 @@ static int rcar_thermal_remove(struct platform_device *pdev) ...@@ -420,7 +465,7 @@ static int rcar_thermal_remove(struct platform_device *pdev)
rcar_thermal_for_each_priv(priv, common) { rcar_thermal_for_each_priv(priv, common) {
rcar_thermal_irq_disable(priv); rcar_thermal_irq_disable(priv);
if (rcar_use_of_thermal(dev)) if (priv->chip->use_of_thermal)
thermal_remove_hwmon_sysfs(priv->zone); thermal_remove_hwmon_sysfs(priv->zone);
else else
thermal_zone_device_unregister(priv->zone); thermal_zone_device_unregister(priv->zone);
...@@ -438,6 +483,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) ...@@ -438,6 +483,7 @@ static int rcar_thermal_probe(struct platform_device *pdev)
struct rcar_thermal_priv *priv; struct rcar_thermal_priv *priv;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct resource *res, *irq; struct resource *res, *irq;
const struct rcar_thermal_chip *chip = of_device_get_match_data(dev);
int mres = 0; int mres = 0;
int i; int i;
int ret = -ENODEV; int ret = -ENODEV;
...@@ -457,19 +503,35 @@ static int rcar_thermal_probe(struct platform_device *pdev) ...@@ -457,19 +503,35 @@ static int rcar_thermal_probe(struct platform_device *pdev)
pm_runtime_enable(dev); pm_runtime_enable(dev);
pm_runtime_get_sync(dev); pm_runtime_get_sync(dev);
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); for (i = 0; i < chip->nirqs; i++) {
if (irq) { irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
/* if (!irq)
* platform has IRQ support. continue;
* Then, driver uses common registers if (!common->base) {
* rcar_has_irq_support() will be enabled /*
*/ * platform has IRQ support.
res = platform_get_resource(pdev, IORESOURCE_MEM, mres++); * Then, driver uses common registers
common->base = devm_ioremap_resource(dev, res); * rcar_has_irq_support() will be enabled
if (IS_ERR(common->base)) */
return PTR_ERR(common->base); res = platform_get_resource(pdev, IORESOURCE_MEM,
mres++);
common->base = devm_ioremap_resource(dev, res);
if (IS_ERR(common->base))
return PTR_ERR(common->base);
idle = 0; /* polling delay is not needed */
}
idle = 0; /* polling delay is not needed */ ret = devm_request_irq(dev, irq->start, rcar_thermal_irq,
IRQF_SHARED, dev_name(dev), common);
if (ret) {
dev_err(dev, "irq request failed\n ");
goto error_unregister;
}
/* update ENR bits */
if (chip->irq_per_ch)
enr_bits |= 1 << i;
} }
for (i = 0;; i++) { for (i = 0;; i++) {
...@@ -491,6 +553,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) ...@@ -491,6 +553,7 @@ static int rcar_thermal_probe(struct platform_device *pdev)
priv->common = common; priv->common = common;
priv->id = i; priv->id = i;
priv->chip = chip;
mutex_init(&priv->lock); mutex_init(&priv->lock);
INIT_LIST_HEAD(&priv->list); INIT_LIST_HEAD(&priv->list);
INIT_DELAYED_WORK(&priv->work, rcar_thermal_work); INIT_DELAYED_WORK(&priv->work, rcar_thermal_work);
...@@ -498,7 +561,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) ...@@ -498,7 +561,7 @@ static int rcar_thermal_probe(struct platform_device *pdev)
if (ret < 0) if (ret < 0)
goto error_unregister; goto error_unregister;
if (rcar_use_of_thermal(dev)) if (chip->use_of_thermal)
priv->zone = devm_thermal_zone_of_sensor_register( priv->zone = devm_thermal_zone_of_sensor_register(
dev, i, priv, dev, i, priv,
&rcar_thermal_zone_of_ops); &rcar_thermal_zone_of_ops);
...@@ -515,7 +578,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) ...@@ -515,7 +578,7 @@ static int rcar_thermal_probe(struct platform_device *pdev)
goto error_unregister; goto error_unregister;
} }
if (rcar_use_of_thermal(dev)) { if (chip->use_of_thermal) {
/* /*
* thermal_zone doesn't enable hwmon as default, * thermal_zone doesn't enable hwmon as default,
* but, enable it here to keep compatible * but, enable it here to keep compatible
...@@ -531,20 +594,12 @@ static int rcar_thermal_probe(struct platform_device *pdev) ...@@ -531,20 +594,12 @@ static int rcar_thermal_probe(struct platform_device *pdev)
list_move_tail(&priv->list, &common->head); list_move_tail(&priv->list, &common->head);
/* update ENR bits */ /* update ENR bits */
enr_bits |= 3 << (i * 8); if (!chip->irq_per_ch)
enr_bits |= 3 << (i * 8);
} }
/* enable temperature comparation */ if (enr_bits)
if (irq) {
ret = devm_request_irq(dev, irq->start, rcar_thermal_irq, 0,
dev_name(dev), common);
if (ret) {
dev_err(dev, "irq request failed\n ");
goto error_unregister;
}
rcar_thermal_common_write(common, ENR, enr_bits); rcar_thermal_common_write(common, ENR, enr_bits);
}
dev_info(dev, "%d sensor probed\n", i); dev_info(dev, "%d sensor probed\n", i);
...@@ -556,9 +611,48 @@ static int rcar_thermal_probe(struct platform_device *pdev) ...@@ -556,9 +611,48 @@ static int rcar_thermal_probe(struct platform_device *pdev)
return ret; return ret;
} }
#ifdef CONFIG_PM_SLEEP
static int rcar_thermal_suspend(struct device *dev)
{
struct rcar_thermal_common *common = dev_get_drvdata(dev);
struct rcar_thermal_priv *priv = list_first_entry(&common->head,
typeof(*priv), list);
if (priv->chip->needs_suspend_resume) {
rcar_thermal_common_write(common, ENR, 0);
rcar_thermal_irq_disable(priv);
rcar_thermal_bset(priv, THSCR, CPCTL, 0);
}
return 0;
}
static int rcar_thermal_resume(struct device *dev)
{
struct rcar_thermal_common *common = dev_get_drvdata(dev);
struct rcar_thermal_priv *priv = list_first_entry(&common->head,
typeof(*priv), list);
int ret;
if (priv->chip->needs_suspend_resume) {
ret = rcar_thermal_update_temp(priv);
if (ret < 0)
return ret;
rcar_thermal_irq_enable(priv);
rcar_thermal_common_write(common, ENR, 0x03);
}
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(rcar_thermal_pm_ops, rcar_thermal_suspend,
rcar_thermal_resume);
static struct platform_driver rcar_thermal_driver = { static struct platform_driver rcar_thermal_driver = {
.driver = { .driver = {
.name = "rcar_thermal", .name = "rcar_thermal",
.pm = &rcar_thermal_pm_ops,
.of_match_table = rcar_thermal_dt_ids, .of_match_table = rcar_thermal_dt_ids,
}, },
.probe = rcar_thermal_probe, .probe = rcar_thermal_probe,
......
...@@ -29,13 +29,14 @@ ...@@ -29,13 +29,14 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of_device.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include "exynos_tmu.h" #include <dt-bindings/thermal/thermal_exynos.h>
#include "../thermal_core.h" #include "../thermal_core.h"
/* Exynos generic registers */ /* Exynos generic registers */
...@@ -75,9 +76,6 @@ ...@@ -75,9 +76,6 @@
#define EXYNOS_TMU_THERM_TRIP_EN_SHIFT 12 #define EXYNOS_TMU_THERM_TRIP_EN_SHIFT 12
#define EXYNOS_TMU_INTEN_RISE0_SHIFT 0 #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_FALL0_SHIFT 16
#define EXYNOS_EMUL_TIME 0x57F0 #define EXYNOS_EMUL_TIME 0x57F0
...@@ -98,11 +96,6 @@ ...@@ -98,11 +96,6 @@
#define EXYNOS4412_MUX_ADDR_SHIFT 20 #define EXYNOS4412_MUX_ADDR_SHIFT 20
/* Exynos5433 specific registers */ /* Exynos5433 specific registers */
#define EXYNOS5433_TMU_REG_CONTROL1 0x024
#define EXYNOS5433_TMU_SAMPLING_INTERVAL 0x02c
#define EXYNOS5433_TMU_COUNTER_VALUE0 0x030
#define EXYNOS5433_TMU_COUNTER_VALUE1 0x034
#define EXYNOS5433_TMU_REG_CURRENT_TEMP1 0x044
#define EXYNOS5433_THD_TEMP_RISE3_0 0x050 #define EXYNOS5433_THD_TEMP_RISE3_0 0x050
#define EXYNOS5433_THD_TEMP_RISE7_4 0x054 #define EXYNOS5433_THD_TEMP_RISE7_4 0x054
#define EXYNOS5433_THD_TEMP_FALL3_0 0x060 #define EXYNOS5433_THD_TEMP_FALL3_0 0x060
...@@ -123,27 +116,7 @@ ...@@ -123,27 +116,7 @@
#define EXYNOS5433_PD_DET_EN 1 #define EXYNOS5433_PD_DET_EN 1
/*exynos5440 specific registers*/ #define EXYNOS5433_G3D_BASE 0x10070000
#define EXYNOS5440_TMU_S0_7_TRIM 0x000
#define EXYNOS5440_TMU_S0_7_CTRL 0x020
#define EXYNOS5440_TMU_S0_7_DEBUG 0x040
#define EXYNOS5440_TMU_S0_7_TEMP 0x0f0
#define EXYNOS5440_TMU_S0_7_TH0 0x110
#define EXYNOS5440_TMU_S0_7_TH1 0x130
#define EXYNOS5440_TMU_S0_7_TH2 0x150
#define EXYNOS5440_TMU_S0_7_IRQEN 0x210
#define EXYNOS5440_TMU_S0_7_IRQ 0x230
/* exynos5440 common registers */
#define EXYNOS5440_TMU_IRQ_STATUS 0x000
#define EXYNOS5440_TMU_PMIN 0x004
#define EXYNOS5440_TMU_INTEN_RISE0_SHIFT 0
#define EXYNOS5440_TMU_INTEN_RISE1_SHIFT 1
#define EXYNOS5440_TMU_INTEN_RISE2_SHIFT 2
#define EXYNOS5440_TMU_INTEN_RISE3_SHIFT 3
#define EXYNOS5440_TMU_INTEN_FALL0_SHIFT 4
#define EXYNOS5440_TMU_TH_RISE4_SHIFT 24
#define EXYNOS5440_EFUSE_SWAP_OFFSET 8
/* Exynos7 specific registers */ /* Exynos7 specific registers */
#define EXYNOS7_THD_TEMP_RISE7_6 0x50 #define EXYNOS7_THD_TEMP_RISE7_6 0x50
...@@ -155,22 +128,32 @@ ...@@ -155,22 +128,32 @@
#define EXYNOS7_TMU_TEMP_MASK 0x1ff #define EXYNOS7_TMU_TEMP_MASK 0x1ff
#define EXYNOS7_PD_DET_EN_SHIFT 23 #define EXYNOS7_PD_DET_EN_SHIFT 23
#define EXYNOS7_TMU_INTEN_RISE0_SHIFT 0 #define EXYNOS7_TMU_INTEN_RISE0_SHIFT 0
#define EXYNOS7_TMU_INTEN_RISE1_SHIFT 1
#define EXYNOS7_TMU_INTEN_RISE2_SHIFT 2
#define EXYNOS7_TMU_INTEN_RISE3_SHIFT 3
#define EXYNOS7_TMU_INTEN_RISE4_SHIFT 4
#define EXYNOS7_TMU_INTEN_RISE5_SHIFT 5
#define EXYNOS7_TMU_INTEN_RISE6_SHIFT 6
#define EXYNOS7_TMU_INTEN_RISE7_SHIFT 7
#define EXYNOS7_EMUL_DATA_SHIFT 7 #define EXYNOS7_EMUL_DATA_SHIFT 7
#define EXYNOS7_EMUL_DATA_MASK 0x1ff #define EXYNOS7_EMUL_DATA_MASK 0x1ff
#define EXYNOS_FIRST_POINT_TRIM 25
#define EXYNOS_SECOND_POINT_TRIM 85
#define EXYNOS_NOISE_CANCEL_MODE 4
#define MCELSIUS 1000 #define MCELSIUS 1000
enum soc_type {
SOC_ARCH_EXYNOS3250 = 1,
SOC_ARCH_EXYNOS4210,
SOC_ARCH_EXYNOS4412,
SOC_ARCH_EXYNOS5250,
SOC_ARCH_EXYNOS5260,
SOC_ARCH_EXYNOS5420,
SOC_ARCH_EXYNOS5420_TRIMINFO,
SOC_ARCH_EXYNOS5433,
SOC_ARCH_EXYNOS7,
};
/** /**
* struct exynos_tmu_data : A structure to hold the private data of the TMU * struct exynos_tmu_data : A structure to hold the private data of the TMU
driver driver
* @id: identifier of the one instance of the TMU controller. * @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: base address of the single instance of the TMU controller.
* @base_second: base address of the common registers of the TMU controller. * @base_second: base address of the common registers of the TMU controller.
* @irq: irq number of the TMU controller. * @irq: irq number of the TMU controller.
...@@ -180,8 +163,17 @@ ...@@ -180,8 +163,17 @@
* @clk: pointer to the clock structure. * @clk: pointer to the clock structure.
* @clk_sec: pointer to the clock structure for accessing the base_second. * @clk_sec: pointer to the clock structure for accessing the base_second.
* @sclk: pointer to the clock structure for accessing the tmu special clk. * @sclk: pointer to the clock structure for accessing the tmu special clk.
* @cal_type: calibration type for temperature
* @efuse_value: SoC defined fuse value
* @min_efuse_value: minimum valid trimming data
* @max_efuse_value: maximum valid trimming data
* @temp_error1: fused value of the first point trim. * @temp_error1: fused value of the first point trim.
* @temp_error2: fused value of the second point trim. * @temp_error2: fused value of the second point trim.
* @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
* @regulator: pointer to the TMU regulator structure. * @regulator: pointer to the TMU regulator structure.
* @reg_conf: pointer to structure to register with core thermal. * @reg_conf: pointer to structure to register with core thermal.
* @ntrip: number of supported trip points. * @ntrip: number of supported trip points.
...@@ -194,7 +186,6 @@ ...@@ -194,7 +186,6 @@
*/ */
struct exynos_tmu_data { struct exynos_tmu_data {
int id; int id;
struct exynos_tmu_platform_data *pdata;
void __iomem *base; void __iomem *base;
void __iomem *base_second; void __iomem *base_second;
int irq; int irq;
...@@ -202,71 +193,42 @@ struct exynos_tmu_data { ...@@ -202,71 +193,42 @@ struct exynos_tmu_data {
struct work_struct irq_work; struct work_struct irq_work;
struct mutex lock; struct mutex lock;
struct clk *clk, *clk_sec, *sclk; struct clk *clk, *clk_sec, *sclk;
u32 cal_type;
u32 efuse_value;
u32 min_efuse_value;
u32 max_efuse_value;
u16 temp_error1, temp_error2; u16 temp_error1, temp_error2;
u8 gain;
u8 reference_voltage;
struct regulator *regulator; struct regulator *regulator;
struct thermal_zone_device *tzd; struct thermal_zone_device *tzd;
unsigned int ntrip; unsigned int ntrip;
bool enabled; bool enabled;
int (*tmu_initialize)(struct platform_device *pdev); void (*tmu_set_trip_temp)(struct exynos_tmu_data *data, int trip,
u8 temp);
void (*tmu_set_trip_hyst)(struct exynos_tmu_data *data, int trip,
u8 temp, u8 hyst);
void (*tmu_initialize)(struct platform_device *pdev);
void (*tmu_control)(struct platform_device *pdev, bool on); void (*tmu_control)(struct platform_device *pdev, bool on);
int (*tmu_read)(struct exynos_tmu_data *data); int (*tmu_read)(struct exynos_tmu_data *data);
void (*tmu_set_emulation)(struct exynos_tmu_data *data, int temp); void (*tmu_set_emulation)(struct exynos_tmu_data *data, int temp);
void (*tmu_clear_irqs)(struct exynos_tmu_data *data); void (*tmu_clear_irqs)(struct exynos_tmu_data *data);
}; };
static void exynos_report_trigger(struct exynos_tmu_data *p)
{
char data[10], *envp[] = { data, NULL };
struct thermal_zone_device *tz = p->tzd;
int temp;
unsigned int i;
if (!tz) {
pr_err("No thermal zone device defined\n");
return;
}
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
mutex_lock(&tz->lock);
/* Find the level for which trip happened */
for (i = 0; i < of_thermal_get_ntrips(tz); i++) {
tz->ops->get_trip_temp(tz, i, &temp);
if (tz->last_temperature < temp)
break;
}
snprintf(data, sizeof(data), "%u", i);
kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, envp);
mutex_unlock(&tz->lock);
}
/* /*
* TMU treats temperature as a mapped temperature code. * TMU treats temperature as a mapped temperature code.
* The temperature is converted differently depending on the calibration type. * The temperature is converted differently depending on the calibration type.
*/ */
static int temp_to_code(struct exynos_tmu_data *data, u8 temp) static int temp_to_code(struct exynos_tmu_data *data, u8 temp)
{ {
struct exynos_tmu_platform_data *pdata = data->pdata; if (data->cal_type == TYPE_ONE_POINT_TRIMMING)
int temp_code; return temp + data->temp_error1 - EXYNOS_FIRST_POINT_TRIM;
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;
}
return temp_code; return (temp - EXYNOS_FIRST_POINT_TRIM) *
(data->temp_error2 - data->temp_error1) /
(EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM) +
data->temp_error1;
} }
/* /*
...@@ -275,120 +237,123 @@ static int temp_to_code(struct exynos_tmu_data *data, u8 temp) ...@@ -275,120 +237,123 @@ static int temp_to_code(struct exynos_tmu_data *data, u8 temp)
*/ */
static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code) static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code)
{ {
struct exynos_tmu_platform_data *pdata = data->pdata; if (data->cal_type == TYPE_ONE_POINT_TRIMMING)
int temp; return temp_code - data->temp_error1 + EXYNOS_FIRST_POINT_TRIM;
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;
}
return temp; return (temp_code - data->temp_error1) *
(EXYNOS_SECOND_POINT_TRIM - EXYNOS_FIRST_POINT_TRIM) /
(data->temp_error2 - data->temp_error1) +
EXYNOS_FIRST_POINT_TRIM;
} }
static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info) static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info)
{ {
struct exynos_tmu_platform_data *pdata = data->pdata; u16 tmu_temp_mask =
(data->soc == SOC_ARCH_EXYNOS7) ? EXYNOS7_TMU_TEMP_MASK
: EXYNOS_TMU_TEMP_MASK;
data->temp_error1 = trim_info & EXYNOS_TMU_TEMP_MASK; data->temp_error1 = trim_info & tmu_temp_mask;
data->temp_error2 = ((trim_info >> EXYNOS_TRIMINFO_85_SHIFT) & data->temp_error2 = ((trim_info >> EXYNOS_TRIMINFO_85_SHIFT) &
EXYNOS_TMU_TEMP_MASK); EXYNOS_TMU_TEMP_MASK);
if (!data->temp_error1 || if (!data->temp_error1 ||
(pdata->min_efuse_value > data->temp_error1) || (data->min_efuse_value > data->temp_error1) ||
(data->temp_error1 > pdata->max_efuse_value)) (data->temp_error1 > data->max_efuse_value))
data->temp_error1 = pdata->efuse_value & EXYNOS_TMU_TEMP_MASK; data->temp_error1 = data->efuse_value & EXYNOS_TMU_TEMP_MASK;
if (!data->temp_error2) if (!data->temp_error2)
data->temp_error2 = data->temp_error2 =
(pdata->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) & (data->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) &
EXYNOS_TMU_TEMP_MASK; EXYNOS_TMU_TEMP_MASK;
} }
static u32 get_th_reg(struct exynos_tmu_data *data, u32 threshold, bool falling) static int exynos_tmu_initialize(struct platform_device *pdev)
{ {
struct thermal_zone_device *tz = data->tzd; struct exynos_tmu_data *data = platform_get_drvdata(pdev);
struct thermal_zone_device *tzd = data->tzd;
const struct thermal_trip * const trips = const struct thermal_trip * const trips =
of_thermal_get_trip_points(tz); of_thermal_get_trip_points(tzd);
unsigned long temp; unsigned int status;
int i; int ret = 0, temp, hyst;
if (!trips) { if (!trips) {
pr_err("%s: Cannot get trip points from of-thermal.c!\n", dev_err(&pdev->dev,
__func__); "Cannot get trip points from device tree!\n");
return 0; return -ENODEV;
} }
for (i = 0; i < of_thermal_get_ntrips(tz); i++) { if (data->soc != SOC_ARCH_EXYNOS5433) /* FIXME */
if (trips[i].type == THERMAL_TRIP_CRITICAL) ret = tzd->ops->get_crit_temp(tzd, &temp);
continue; if (ret) {
dev_err(&pdev->dev,
temp = trips[i].temperature / MCELSIUS; "No CRITICAL trip point defined in device tree!\n");
if (falling) goto out;
temp -= (trips[i].hysteresis / MCELSIUS);
else
threshold &= ~(0xff << 8 * i);
threshold |= temp_to_code(data, temp) << 8 * i;
} }
return threshold; if (of_thermal_get_ntrips(tzd) > data->ntrip) {
}
static int exynos_tmu_initialize(struct platform_device *pdev)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
int ret;
if (of_thermal_get_ntrips(data->tzd) > data->ntrip) {
dev_info(&pdev->dev, dev_info(&pdev->dev,
"More trip points than supported by this TMU.\n"); "More trip points than supported by this TMU.\n");
dev_info(&pdev->dev, dev_info(&pdev->dev,
"%d trip points should be configured in polling mode.\n", "%d trip points should be configured in polling mode.\n",
(of_thermal_get_ntrips(data->tzd) - data->ntrip)); (of_thermal_get_ntrips(tzd) - data->ntrip));
} }
mutex_lock(&data->lock); mutex_lock(&data->lock);
clk_enable(data->clk); clk_enable(data->clk);
if (!IS_ERR(data->clk_sec)) if (!IS_ERR(data->clk_sec))
clk_enable(data->clk_sec); clk_enable(data->clk_sec);
ret = data->tmu_initialize(pdev);
status = readb(data->base + EXYNOS_TMU_REG_STATUS);
if (!status) {
ret = -EBUSY;
} else {
int i, ntrips =
min_t(int, of_thermal_get_ntrips(tzd), data->ntrip);
data->tmu_initialize(pdev);
/* Write temperature code for rising and falling threshold */
for (i = 0; i < ntrips; i++) {
/* Write temperature code for rising threshold */
ret = tzd->ops->get_trip_temp(tzd, i, &temp);
if (ret)
goto err;
temp /= MCELSIUS;
data->tmu_set_trip_temp(data, i, temp);
/* Write temperature code for falling threshold */
ret = tzd->ops->get_trip_hyst(tzd, i, &hyst);
if (ret)
goto err;
hyst /= MCELSIUS;
data->tmu_set_trip_hyst(data, i, temp, hyst);
}
data->tmu_clear_irqs(data);
}
err:
clk_disable(data->clk); clk_disable(data->clk);
mutex_unlock(&data->lock); mutex_unlock(&data->lock);
if (!IS_ERR(data->clk_sec)) if (!IS_ERR(data->clk_sec))
clk_disable(data->clk_sec); clk_disable(data->clk_sec);
out:
return ret; return ret;
} }
static u32 get_con_reg(struct exynos_tmu_data *data, u32 con) static u32 get_con_reg(struct exynos_tmu_data *data, u32 con)
{ {
struct exynos_tmu_platform_data *pdata = data->pdata;
if (data->soc == SOC_ARCH_EXYNOS4412 || if (data->soc == SOC_ARCH_EXYNOS4412 ||
data->soc == SOC_ARCH_EXYNOS3250) data->soc == SOC_ARCH_EXYNOS3250)
con |= (EXYNOS4412_MUX_ADDR_VALUE << EXYNOS4412_MUX_ADDR_SHIFT); con |= (EXYNOS4412_MUX_ADDR_VALUE << EXYNOS4412_MUX_ADDR_SHIFT);
con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT); con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT);
con |= pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT; con |= data->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT;
con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
con |= (pdata->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT); con |= (data->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
if (pdata->noise_cancel_mode) { con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT);
con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT); con |= (EXYNOS_NOISE_CANCEL_MODE << EXYNOS_TMU_TRIP_MODE_SHIFT);
con |= (pdata->noise_cancel_mode << EXYNOS_TMU_TRIP_MODE_SHIFT);
}
return con; return con;
} }
...@@ -405,65 +370,70 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on) ...@@ -405,65 +370,70 @@ static void exynos_tmu_control(struct platform_device *pdev, bool on)
mutex_unlock(&data->lock); mutex_unlock(&data->lock);
} }
static int exynos4210_tmu_initialize(struct platform_device *pdev) static void exynos4210_tmu_set_trip_temp(struct exynos_tmu_data *data,
int trip, u8 temp)
{ {
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
struct thermal_zone_device *tz = data->tzd;
const struct thermal_trip * const trips = const struct thermal_trip * const trips =
of_thermal_get_trip_points(tz); of_thermal_get_trip_points(data->tzd);
int ret = 0, threshold_code, i; u8 ref, th_code;
unsigned long reference, temp;
unsigned int status;
if (!trips) { ref = trips[0].temperature / MCELSIUS;
pr_err("%s: Cannot get trip points from of-thermal.c!\n",
__func__);
ret = -ENODEV;
goto out;
}
status = readb(data->base + EXYNOS_TMU_REG_STATUS); if (trip == 0) {
if (!status) { th_code = temp_to_code(data, ref);
ret = -EBUSY; writeb(th_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP);
goto out;
} }
temp -= ref;
writeb(temp, data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0 + trip * 4);
}
/* failing thresholds are not supported on Exynos4210 */
static void exynos4210_tmu_set_trip_hyst(struct exynos_tmu_data *data,
int trip, u8 temp, u8 hyst)
{
}
static void exynos4210_tmu_initialize(struct platform_device *pdev)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO)); sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO));
}
/* Write temperature code for threshold */ static void exynos4412_tmu_set_trip_temp(struct exynos_tmu_data *data,
reference = trips[0].temperature / MCELSIUS; int trip, u8 temp)
threshold_code = temp_to_code(data, reference); {
if (threshold_code < 0) { u32 th, con;
ret = threshold_code;
goto out;
}
writeb(threshold_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP);
for (i = 0; i < of_thermal_get_ntrips(tz); i++) { th = readl(data->base + EXYNOS_THD_TEMP_RISE);
temp = trips[i].temperature / MCELSIUS; th &= ~(0xff << 8 * trip);
writeb(temp - reference, data->base + th |= temp_to_code(data, temp) << 8 * trip;
EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4); writel(th, data->base + EXYNOS_THD_TEMP_RISE);
if (trip == 3) {
con = readl(data->base + EXYNOS_TMU_REG_CONTROL);
con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT);
writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
} }
}
data->tmu_clear_irqs(data); static void exynos4412_tmu_set_trip_hyst(struct exynos_tmu_data *data,
out: int trip, u8 temp, u8 hyst)
return ret; {
u32 th;
th = readl(data->base + EXYNOS_THD_TEMP_FALL);
th &= ~(0xff << 8 * trip);
if (hyst)
th |= temp_to_code(data, temp - hyst) << 8 * trip;
writel(th, data->base + EXYNOS_THD_TEMP_FALL);
} }
static int exynos4412_tmu_initialize(struct platform_device *pdev) static void exynos4412_tmu_initialize(struct platform_device *pdev)
{ {
struct exynos_tmu_data *data = platform_get_drvdata(pdev); struct exynos_tmu_data *data = platform_get_drvdata(pdev);
const struct thermal_trip * const trips = unsigned int trim_info, ctrl;
of_thermal_get_trip_points(data->tzd);
unsigned int status, trim_info, con, ctrl, rising_threshold;
int ret = 0, threshold_code, i;
unsigned long crit_temp = 0;
status = readb(data->base + EXYNOS_TMU_REG_STATUS);
if (!status) {
ret = -EBUSY;
goto out;
}
if (data->soc == SOC_ARCH_EXYNOS3250 || if (data->soc == SOC_ARCH_EXYNOS3250 ||
data->soc == SOC_ARCH_EXYNOS4412 || data->soc == SOC_ARCH_EXYNOS4412 ||
...@@ -485,58 +455,53 @@ static int exynos4412_tmu_initialize(struct platform_device *pdev) ...@@ -485,58 +455,53 @@ static int exynos4412_tmu_initialize(struct platform_device *pdev)
trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO);
sanitize_temp_error(data, trim_info); sanitize_temp_error(data, trim_info);
}
/* Write temperature code for rising and falling threshold */ static void exynos5433_tmu_set_trip_temp(struct exynos_tmu_data *data,
rising_threshold = readl(data->base + EXYNOS_THD_TEMP_RISE); int trip, u8 temp)
rising_threshold = get_th_reg(data, rising_threshold, false); {
writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE); unsigned int reg_off, j;
writel(get_th_reg(data, 0, true), data->base + EXYNOS_THD_TEMP_FALL); u32 th;
data->tmu_clear_irqs(data);
/* if last threshold limit is also present */ if (trip > 3) {
for (i = 0; i < of_thermal_get_ntrips(data->tzd); i++) { reg_off = EXYNOS5433_THD_TEMP_RISE7_4;
if (trips[i].type == THERMAL_TRIP_CRITICAL) { j = trip - 4;
crit_temp = trips[i].temperature; } else {
break; reg_off = EXYNOS5433_THD_TEMP_RISE3_0;
} j = trip;
} }
if (i == of_thermal_get_ntrips(data->tzd)) { th = readl(data->base + reg_off);
pr_err("%s: No CRITICAL trip point defined at of-thermal.c!\n", th &= ~(0xff << j * 8);
__func__); th |= (temp_to_code(data, temp) << j * 8);
ret = -EINVAL; writel(th, data->base + reg_off);
goto out; }
}
threshold_code = temp_to_code(data, crit_temp / MCELSIUS); static void exynos5433_tmu_set_trip_hyst(struct exynos_tmu_data *data,
/* 1-4 level to be assigned in th0 reg */ int trip, u8 temp, u8 hyst)
rising_threshold &= ~(0xff << 8 * i); {
rising_threshold |= threshold_code << 8 * i; unsigned int reg_off, j;
writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE); u32 th;
con = readl(data->base + EXYNOS_TMU_REG_CONTROL);
con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT);
writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
out: if (trip > 3) {
return ret; reg_off = EXYNOS5433_THD_TEMP_FALL7_4;
j = trip - 4;
} else {
reg_off = EXYNOS5433_THD_TEMP_FALL3_0;
j = trip;
}
th = readl(data->base + reg_off);
th &= ~(0xff << j * 8);
th |= (temp_to_code(data, temp - hyst) << j * 8);
writel(th, data->base + reg_off);
} }
static int exynos5433_tmu_initialize(struct platform_device *pdev) static void exynos5433_tmu_initialize(struct platform_device *pdev)
{ {
struct exynos_tmu_data *data = platform_get_drvdata(pdev); struct exynos_tmu_data *data = platform_get_drvdata(pdev);
struct exynos_tmu_platform_data *pdata = data->pdata; unsigned int trim_info;
struct thermal_zone_device *tz = data->tzd; int sensor_id, cal_type;
unsigned int status, trim_info;
unsigned int rising_threshold = 0, falling_threshold = 0;
int temp, temp_hist;
int ret = 0, threshold_code, i, sensor_id, cal_type;
status = readb(data->base + EXYNOS_TMU_REG_STATUS);
if (!status) {
ret = -EBUSY;
goto out;
}
trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO);
sanitize_temp_error(data, trim_info); sanitize_temp_error(data, trim_info);
...@@ -552,227 +517,84 @@ static int exynos5433_tmu_initialize(struct platform_device *pdev) ...@@ -552,227 +517,84 @@ static int exynos5433_tmu_initialize(struct platform_device *pdev)
>> EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT; >> EXYNOS5433_TRIMINFO_CALIB_SEL_SHIFT;
switch (cal_type) { switch (cal_type) {
case EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING:
pdata->cal_type = TYPE_ONE_POINT_TRIMMING;
break;
case EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING: case EXYNOS5433_TRIMINFO_TWO_POINT_TRIMMING:
pdata->cal_type = TYPE_TWO_POINT_TRIMMING; data->cal_type = TYPE_TWO_POINT_TRIMMING;
break; break;
case EXYNOS5433_TRIMINFO_ONE_POINT_TRIMMING:
default: default:
pdata->cal_type = TYPE_ONE_POINT_TRIMMING; data->cal_type = TYPE_ONE_POINT_TRIMMING;
break; break;
} }
dev_info(&pdev->dev, "Calibration type is %d-point calibration\n", dev_info(&pdev->dev, "Calibration type is %d-point calibration\n",
cal_type ? 2 : 1); cal_type ? 2 : 1);
/* Write temperature code for rising and falling threshold */
for (i = 0; i < of_thermal_get_ntrips(tz); i++) {
int rising_reg_offset, falling_reg_offset;
int j = 0;
switch (i) {
case 0:
case 1:
case 2:
case 3:
rising_reg_offset = EXYNOS5433_THD_TEMP_RISE3_0;
falling_reg_offset = EXYNOS5433_THD_TEMP_FALL3_0;
j = i;
break;
case 4:
case 5:
case 6:
case 7:
rising_reg_offset = EXYNOS5433_THD_TEMP_RISE7_4;
falling_reg_offset = EXYNOS5433_THD_TEMP_FALL7_4;
j = i - 4;
break;
default:
continue;
}
/* Write temperature code for rising threshold */
tz->ops->get_trip_temp(tz, i, &temp);
temp /= MCELSIUS;
threshold_code = temp_to_code(data, temp);
rising_threshold = readl(data->base + rising_reg_offset);
rising_threshold |= (threshold_code << j * 8);
writel(rising_threshold, data->base + rising_reg_offset);
/* Write temperature code for falling threshold */
tz->ops->get_trip_hyst(tz, i, &temp_hist);
temp_hist = temp - (temp_hist / MCELSIUS);
threshold_code = temp_to_code(data, temp_hist);
falling_threshold = readl(data->base + falling_reg_offset);
falling_threshold &= ~(0xff << j * 8);
falling_threshold |= (threshold_code << j * 8);
writel(falling_threshold, data->base + falling_reg_offset);
}
data->tmu_clear_irqs(data);
out:
return ret;
} }
static int exynos5440_tmu_initialize(struct platform_device *pdev) static void exynos7_tmu_set_trip_temp(struct exynos_tmu_data *data,
int trip, u8 temp)
{ {
struct exynos_tmu_data *data = platform_get_drvdata(pdev); unsigned int reg_off, bit_off;
unsigned int trim_info = 0, con, rising_threshold; u32 th;
int threshold_code;
int crit_temp = 0;
/* reg_off = ((7 - trip) / 2) * 4;
* For exynos5440 soc triminfo value is swapped between TMU0 and bit_off = ((8 - trip) % 2);
* TMU2, so the below logic is needed.
*/
switch (data->id) {
case 0:
trim_info = readl(data->base + EXYNOS5440_EFUSE_SWAP_OFFSET +
EXYNOS5440_TMU_S0_7_TRIM);
break;
case 1:
trim_info = readl(data->base + EXYNOS5440_TMU_S0_7_TRIM);
break;
case 2:
trim_info = readl(data->base - EXYNOS5440_EFUSE_SWAP_OFFSET +
EXYNOS5440_TMU_S0_7_TRIM);
}
sanitize_temp_error(data, trim_info);
/* Write temperature code for rising and falling threshold */ th = readl(data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off);
rising_threshold = readl(data->base + EXYNOS5440_TMU_S0_7_TH0); th &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off));
rising_threshold = get_th_reg(data, rising_threshold, false); th |= temp_to_code(data, temp) << (16 * bit_off);
writel(rising_threshold, data->base + EXYNOS5440_TMU_S0_7_TH0); writel(th, data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off);
writel(0, data->base + EXYNOS5440_TMU_S0_7_TH1); }
data->tmu_clear_irqs(data); static void exynos7_tmu_set_trip_hyst(struct exynos_tmu_data *data,
int trip, u8 temp, u8 hyst)
{
unsigned int reg_off, bit_off;
u32 th;
/* if last threshold limit is also present */ reg_off = ((7 - trip) / 2) * 4;
if (!data->tzd->ops->get_crit_temp(data->tzd, &crit_temp)) { bit_off = ((8 - trip) % 2);
threshold_code = temp_to_code(data, crit_temp / MCELSIUS);
/* 5th level to be assigned in th2 reg */
rising_threshold =
threshold_code << EXYNOS5440_TMU_TH_RISE4_SHIFT;
writel(rising_threshold, data->base + EXYNOS5440_TMU_S0_7_TH2);
con = readl(data->base + EXYNOS5440_TMU_S0_7_CTRL);
con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT);
writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL);
}
/* Clear the PMIN in the common TMU register */
if (!data->id)
writel(0, data->base_second + EXYNOS5440_TMU_PMIN);
return 0; th = readl(data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off);
th &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off));
th |= temp_to_code(data, temp - hyst) << (16 * bit_off);
writel(th, data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off);
} }
static int exynos7_tmu_initialize(struct platform_device *pdev) static void exynos7_tmu_initialize(struct platform_device *pdev)
{ {
struct exynos_tmu_data *data = platform_get_drvdata(pdev); struct exynos_tmu_data *data = platform_get_drvdata(pdev);
struct thermal_zone_device *tz = data->tzd; unsigned int trim_info;
struct exynos_tmu_platform_data *pdata = data->pdata;
unsigned int status, trim_info;
unsigned int rising_threshold = 0, falling_threshold = 0;
int ret = 0, threshold_code, i;
int temp, temp_hist;
unsigned int reg_off, bit_off;
status = readb(data->base + EXYNOS_TMU_REG_STATUS);
if (!status) {
ret = -EBUSY;
goto out;
}
trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO);
sanitize_temp_error(data, trim_info);
data->temp_error1 = trim_info & EXYNOS7_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;
/* Write temperature code for rising and falling threshold */
for (i = (of_thermal_get_ntrips(tz) - 1); i >= 0; i--) {
/*
* On exynos7 there are 4 rising and 4 falling threshold
* registers (0x50-0x5c and 0x60-0x6c respectively). Each
* register holds the value of two threshold levels (at bit
* offsets 0 and 16). Based on the fact that there are atmost
* eight possible trigger levels, calculate the register and
* bit offsets where the threshold levels are to be written.
*
* e.g. EXYNOS7_THD_TEMP_RISE7_6 (0x50)
* [24:16] - Threshold level 7
* [8:0] - Threshold level 6
* e.g. EXYNOS7_THD_TEMP_RISE5_4 (0x54)
* [24:16] - Threshold level 5
* [8:0] - Threshold level 4
*
* and similarly for falling thresholds.
*
* Based on the above, calculate the register and bit offsets
* for rising/falling threshold levels and populate them.
*/
reg_off = ((7 - i) / 2) * 4;
bit_off = ((8 - i) % 2);
tz->ops->get_trip_temp(tz, i, &temp);
temp /= MCELSIUS;
tz->ops->get_trip_hyst(tz, i, &temp_hist);
temp_hist = temp - (temp_hist / MCELSIUS);
/* Set 9-bit temperature code for rising threshold levels */
threshold_code = temp_to_code(data, temp);
rising_threshold = readl(data->base +
EXYNOS7_THD_TEMP_RISE7_6 + reg_off);
rising_threshold &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off));
rising_threshold |= threshold_code << (16 * bit_off);
writel(rising_threshold,
data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off);
/* Set 9-bit temperature code for falling threshold levels */
threshold_code = temp_to_code(data, temp_hist);
falling_threshold &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off));
falling_threshold |= threshold_code << (16 * bit_off);
writel(falling_threshold,
data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off);
}
data->tmu_clear_irqs(data);
out:
return ret;
} }
static void exynos4210_tmu_control(struct platform_device *pdev, bool on) static void exynos4210_tmu_control(struct platform_device *pdev, bool on)
{ {
struct exynos_tmu_data *data = platform_get_drvdata(pdev); struct exynos_tmu_data *data = platform_get_drvdata(pdev);
struct thermal_zone_device *tz = data->tzd; struct thermal_zone_device *tz = data->tzd;
unsigned int con, interrupt_en; unsigned int con, interrupt_en = 0, i;
con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
if (on) { if (on) {
con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); for (i = 0; i < data->ntrip; i++) {
interrupt_en = if (!of_thermal_is_trip_valid(tz, i))
(of_thermal_is_trip_valid(tz, 3) continue;
<< EXYNOS_TMU_INTEN_RISE3_SHIFT) |
(of_thermal_is_trip_valid(tz, 2) interrupt_en |=
<< EXYNOS_TMU_INTEN_RISE2_SHIFT) | (1 << (EXYNOS_TMU_INTEN_RISE0_SHIFT + i * 4));
(of_thermal_is_trip_valid(tz, 1) }
<< EXYNOS_TMU_INTEN_RISE1_SHIFT) |
(of_thermal_is_trip_valid(tz, 0)
<< EXYNOS_TMU_INTEN_RISE0_SHIFT);
if (data->soc != SOC_ARCH_EXYNOS4210) if (data->soc != SOC_ARCH_EXYNOS4210)
interrupt_en |= interrupt_en |=
interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT;
con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
} else { } else {
con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
interrupt_en = 0; /* Disable all interrupts */
} }
writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN); writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN);
writel(con, data->base + EXYNOS_TMU_REG_CONTROL); writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
} }
...@@ -781,36 +603,25 @@ static void exynos5433_tmu_control(struct platform_device *pdev, bool on) ...@@ -781,36 +603,25 @@ static void exynos5433_tmu_control(struct platform_device *pdev, bool on)
{ {
struct exynos_tmu_data *data = platform_get_drvdata(pdev); struct exynos_tmu_data *data = platform_get_drvdata(pdev);
struct thermal_zone_device *tz = data->tzd; struct thermal_zone_device *tz = data->tzd;
unsigned int con, interrupt_en, pd_det_en; unsigned int con, interrupt_en = 0, pd_det_en, i;
con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
if (on) { if (on) {
con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); for (i = 0; i < data->ntrip; i++) {
interrupt_en = if (!of_thermal_is_trip_valid(tz, i))
(of_thermal_is_trip_valid(tz, 7) continue;
<< EXYNOS7_TMU_INTEN_RISE7_SHIFT) |
(of_thermal_is_trip_valid(tz, 6) interrupt_en |=
<< EXYNOS7_TMU_INTEN_RISE6_SHIFT) | (1 << (EXYNOS7_TMU_INTEN_RISE0_SHIFT + i));
(of_thermal_is_trip_valid(tz, 5) }
<< EXYNOS7_TMU_INTEN_RISE5_SHIFT) |
(of_thermal_is_trip_valid(tz, 4)
<< EXYNOS7_TMU_INTEN_RISE4_SHIFT) |
(of_thermal_is_trip_valid(tz, 3)
<< EXYNOS7_TMU_INTEN_RISE3_SHIFT) |
(of_thermal_is_trip_valid(tz, 2)
<< EXYNOS7_TMU_INTEN_RISE2_SHIFT) |
(of_thermal_is_trip_valid(tz, 1)
<< EXYNOS7_TMU_INTEN_RISE1_SHIFT) |
(of_thermal_is_trip_valid(tz, 0)
<< EXYNOS7_TMU_INTEN_RISE0_SHIFT);
interrupt_en |= interrupt_en |=
interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT;
} else {
con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
} else
con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
interrupt_en = 0; /* Disable all interrupts */
}
pd_det_en = on ? EXYNOS5433_PD_DET_EN : 0; pd_det_en = on ? EXYNOS5433_PD_DET_EN : 0;
...@@ -819,70 +630,31 @@ static void exynos5433_tmu_control(struct platform_device *pdev, bool on) ...@@ -819,70 +630,31 @@ static void exynos5433_tmu_control(struct platform_device *pdev, bool on)
writel(con, data->base + EXYNOS_TMU_REG_CONTROL); writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
} }
static void exynos5440_tmu_control(struct platform_device *pdev, bool on)
{
struct exynos_tmu_data *data = platform_get_drvdata(pdev);
struct thermal_zone_device *tz = data->tzd;
unsigned int con, interrupt_en;
con = get_con_reg(data, readl(data->base + EXYNOS5440_TMU_S0_7_CTRL));
if (on) {
con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
interrupt_en =
(of_thermal_is_trip_valid(tz, 3)
<< EXYNOS5440_TMU_INTEN_RISE3_SHIFT) |
(of_thermal_is_trip_valid(tz, 2)
<< EXYNOS5440_TMU_INTEN_RISE2_SHIFT) |
(of_thermal_is_trip_valid(tz, 1)
<< EXYNOS5440_TMU_INTEN_RISE1_SHIFT) |
(of_thermal_is_trip_valid(tz, 0)
<< EXYNOS5440_TMU_INTEN_RISE0_SHIFT);
interrupt_en |=
interrupt_en << EXYNOS5440_TMU_INTEN_FALL0_SHIFT;
} else {
con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
interrupt_en = 0; /* Disable all interrupts */
}
writel(interrupt_en, data->base + EXYNOS5440_TMU_S0_7_IRQEN);
writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL);
}
static void exynos7_tmu_control(struct platform_device *pdev, bool on) static void exynos7_tmu_control(struct platform_device *pdev, bool on)
{ {
struct exynos_tmu_data *data = platform_get_drvdata(pdev); struct exynos_tmu_data *data = platform_get_drvdata(pdev);
struct thermal_zone_device *tz = data->tzd; struct thermal_zone_device *tz = data->tzd;
unsigned int con, interrupt_en; unsigned int con, interrupt_en = 0, i;
con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL)); con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
if (on) { if (on) {
con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT); for (i = 0; i < data->ntrip; i++) {
con |= (1 << EXYNOS7_PD_DET_EN_SHIFT); if (!of_thermal_is_trip_valid(tz, i))
interrupt_en = continue;
(of_thermal_is_trip_valid(tz, 7)
<< EXYNOS7_TMU_INTEN_RISE7_SHIFT) | interrupt_en |=
(of_thermal_is_trip_valid(tz, 6) (1 << (EXYNOS7_TMU_INTEN_RISE0_SHIFT + i));
<< EXYNOS7_TMU_INTEN_RISE6_SHIFT) | }
(of_thermal_is_trip_valid(tz, 5)
<< EXYNOS7_TMU_INTEN_RISE5_SHIFT) |
(of_thermal_is_trip_valid(tz, 4)
<< EXYNOS7_TMU_INTEN_RISE4_SHIFT) |
(of_thermal_is_trip_valid(tz, 3)
<< EXYNOS7_TMU_INTEN_RISE3_SHIFT) |
(of_thermal_is_trip_valid(tz, 2)
<< EXYNOS7_TMU_INTEN_RISE2_SHIFT) |
(of_thermal_is_trip_valid(tz, 1)
<< EXYNOS7_TMU_INTEN_RISE1_SHIFT) |
(of_thermal_is_trip_valid(tz, 0)
<< EXYNOS7_TMU_INTEN_RISE0_SHIFT);
interrupt_en |= interrupt_en |=
interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT; interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT;
con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
con |= (1 << EXYNOS7_PD_DET_EN_SHIFT);
} else { } else {
con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT); con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
con &= ~(1 << EXYNOS7_PD_DET_EN_SHIFT); con &= ~(1 << EXYNOS7_PD_DET_EN_SHIFT);
interrupt_en = 0; /* Disable all interrupts */
} }
writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN); writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN);
...@@ -896,6 +668,12 @@ static int exynos_get_temp(void *p, int *temp) ...@@ -896,6 +668,12 @@ static int exynos_get_temp(void *p, int *temp)
if (!data || !data->tmu_read || !data->enabled) if (!data || !data->tmu_read || !data->enabled)
return -EINVAL; return -EINVAL;
else if (!data->enabled)
/*
* Called too early, probably
* from thermal_zone_of_sensor_register().
*/
return -EAGAIN;
mutex_lock(&data->lock); mutex_lock(&data->lock);
clk_enable(data->clk); clk_enable(data->clk);
...@@ -919,10 +697,8 @@ static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val, ...@@ -919,10 +697,8 @@ static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val,
if (temp) { if (temp) {
temp /= MCELSIUS; temp /= MCELSIUS;
if (data->soc != SOC_ARCH_EXYNOS5440) { val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT);
val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT); val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT);
val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT);
}
if (data->soc == SOC_ARCH_EXYNOS7) { if (data->soc == SOC_ARCH_EXYNOS7) {
val &= ~(EXYNOS7_EMUL_DATA_MASK << val &= ~(EXYNOS7_EMUL_DATA_MASK <<
EXYNOS7_EMUL_DATA_SHIFT); EXYNOS7_EMUL_DATA_SHIFT);
...@@ -963,16 +739,6 @@ static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data, ...@@ -963,16 +739,6 @@ static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data,
writel(val, data->base + emul_con); writel(val, data->base + emul_con);
} }
static void exynos5440_tmu_set_emulation(struct exynos_tmu_data *data,
int temp)
{
unsigned int val;
val = readl(data->base + EXYNOS5440_TMU_S0_7_DEBUG);
val = get_emul_con_reg(data, val, temp);
writel(val, data->base + EXYNOS5440_TMU_S0_7_DEBUG);
}
static int exynos_tmu_set_emulation(void *drv_data, int temp) static int exynos_tmu_set_emulation(void *drv_data, int temp)
{ {
struct exynos_tmu_data *data = drv_data; struct exynos_tmu_data *data = drv_data;
...@@ -995,7 +761,6 @@ static int exynos_tmu_set_emulation(void *drv_data, int temp) ...@@ -995,7 +761,6 @@ static int exynos_tmu_set_emulation(void *drv_data, int temp)
} }
#else #else
#define exynos4412_tmu_set_emulation NULL #define exynos4412_tmu_set_emulation NULL
#define exynos5440_tmu_set_emulation NULL
static int exynos_tmu_set_emulation(void *drv_data, int temp) static int exynos_tmu_set_emulation(void *drv_data, int temp)
{ return -EINVAL; } { return -EINVAL; }
#endif /* CONFIG_THERMAL_EMULATION */ #endif /* CONFIG_THERMAL_EMULATION */
...@@ -1013,11 +778,6 @@ static int exynos4412_tmu_read(struct exynos_tmu_data *data) ...@@ -1013,11 +778,6 @@ static int exynos4412_tmu_read(struct exynos_tmu_data *data)
return readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); return readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP);
} }
static int exynos5440_tmu_read(struct exynos_tmu_data *data)
{
return readb(data->base + EXYNOS5440_TMU_S0_7_TEMP);
}
static int exynos7_tmu_read(struct exynos_tmu_data *data) static int exynos7_tmu_read(struct exynos_tmu_data *data)
{ {
return readw(data->base + EXYNOS_TMU_REG_CURRENT_TEMP) & return readw(data->base + EXYNOS_TMU_REG_CURRENT_TEMP) &
...@@ -1028,20 +788,14 @@ static void exynos_tmu_work(struct work_struct *work) ...@@ -1028,20 +788,14 @@ static void exynos_tmu_work(struct work_struct *work)
{ {
struct exynos_tmu_data *data = container_of(work, struct exynos_tmu_data *data = container_of(work,
struct exynos_tmu_data, irq_work); struct exynos_tmu_data, irq_work);
unsigned int val_type;
if (!IS_ERR(data->clk_sec)) if (!IS_ERR(data->clk_sec))
clk_enable(data->clk_sec); clk_enable(data->clk_sec);
/* Find which sensor generated this interrupt */
if (data->soc == SOC_ARCH_EXYNOS5440) {
val_type = readl(data->base_second + EXYNOS5440_TMU_IRQ_STATUS);
if (!((val_type >> data->id) & 0x1))
goto out;
}
if (!IS_ERR(data->clk_sec)) if (!IS_ERR(data->clk_sec))
clk_disable(data->clk_sec); clk_disable(data->clk_sec);
exynos_report_trigger(data); thermal_zone_device_update(data->tzd, THERMAL_EVENT_UNSPECIFIED);
mutex_lock(&data->lock); mutex_lock(&data->lock);
clk_enable(data->clk); clk_enable(data->clk);
...@@ -1050,7 +804,6 @@ static void exynos_tmu_work(struct work_struct *work) ...@@ -1050,7 +804,6 @@ static void exynos_tmu_work(struct work_struct *work)
clk_disable(data->clk); clk_disable(data->clk);
mutex_unlock(&data->lock); mutex_unlock(&data->lock);
out:
enable_irq(data->irq); enable_irq(data->irq);
} }
...@@ -1085,15 +838,6 @@ static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data) ...@@ -1085,15 +838,6 @@ static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data)
writel(val_irq, data->base + tmu_intclear); writel(val_irq, data->base + tmu_intclear);
} }
static void exynos5440_tmu_clear_irqs(struct exynos_tmu_data *data)
{
unsigned int val_irq;
val_irq = readl(data->base + EXYNOS5440_TMU_S0_7_IRQ);
/* clear the interrupts */
writel(val_irq, data->base + EXYNOS5440_TMU_S0_7_IRQ);
}
static irqreturn_t exynos_tmu_irq(int irq, void *id) static irqreturn_t exynos_tmu_irq(int irq, void *id)
{ {
struct exynos_tmu_data *data = id; struct exynos_tmu_data *data = id;
...@@ -1105,86 +849,41 @@ static irqreturn_t exynos_tmu_irq(int irq, void *id) ...@@ -1105,86 +849,41 @@ 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", }, {
{ .compatible = "samsung,exynos4210-tmu", }, .compatible = "samsung,exynos3250-tmu",
{ .compatible = "samsung,exynos4412-tmu", }, .data = (const void *)SOC_ARCH_EXYNOS3250,
{ .compatible = "samsung,exynos5250-tmu", }, }, {
{ .compatible = "samsung,exynos5260-tmu", }, .compatible = "samsung,exynos4210-tmu",
{ .compatible = "samsung,exynos5420-tmu", }, .data = (const void *)SOC_ARCH_EXYNOS4210,
{ .compatible = "samsung,exynos5420-tmu-ext-triminfo", }, }, {
{ .compatible = "samsung,exynos5433-tmu", }, .compatible = "samsung,exynos4412-tmu",
{ .compatible = "samsung,exynos5440-tmu", }, .data = (const void *)SOC_ARCH_EXYNOS4412,
{ .compatible = "samsung,exynos7-tmu", }, }, {
{ /* sentinel */ }, .compatible = "samsung,exynos5250-tmu",
.data = (const void *)SOC_ARCH_EXYNOS5250,
}, {
.compatible = "samsung,exynos5260-tmu",
.data = (const void *)SOC_ARCH_EXYNOS5260,
}, {
.compatible = "samsung,exynos5420-tmu",
.data = (const void *)SOC_ARCH_EXYNOS5420,
}, {
.compatible = "samsung,exynos5420-tmu-ext-triminfo",
.data = (const void *)SOC_ARCH_EXYNOS5420_TRIMINFO,
}, {
.compatible = "samsung,exynos5433-tmu",
.data = (const void *)SOC_ARCH_EXYNOS5433,
}, {
.compatible = "samsung,exynos7-tmu",
.data = (const void *)SOC_ARCH_EXYNOS7,
},
{ },
}; };
MODULE_DEVICE_TABLE(of, exynos_tmu_match); MODULE_DEVICE_TABLE(of, exynos_tmu_match);
static int exynos_of_get_soc_type(struct device_node *np)
{
if (of_device_is_compatible(np, "samsung,exynos3250-tmu"))
return SOC_ARCH_EXYNOS3250;
else if (of_device_is_compatible(np, "samsung,exynos4210-tmu"))
return SOC_ARCH_EXYNOS4210;
else if (of_device_is_compatible(np, "samsung,exynos4412-tmu"))
return SOC_ARCH_EXYNOS4412;
else if (of_device_is_compatible(np, "samsung,exynos5250-tmu"))
return SOC_ARCH_EXYNOS5250;
else if (of_device_is_compatible(np, "samsung,exynos5260-tmu"))
return SOC_ARCH_EXYNOS5260;
else if (of_device_is_compatible(np, "samsung,exynos5420-tmu"))
return SOC_ARCH_EXYNOS5420;
else if (of_device_is_compatible(np,
"samsung,exynos5420-tmu-ext-triminfo"))
return SOC_ARCH_EXYNOS5420_TRIMINFO;
else if (of_device_is_compatible(np, "samsung,exynos5433-tmu"))
return SOC_ARCH_EXYNOS5433;
else if (of_device_is_compatible(np, "samsung,exynos5440-tmu"))
return SOC_ARCH_EXYNOS5440;
else if (of_device_is_compatible(np, "samsung,exynos7-tmu"))
return SOC_ARCH_EXYNOS7;
return -EINVAL;
}
static int exynos_of_sensor_conf(struct device_node *np,
struct exynos_tmu_platform_data *pdata)
{
u32 value;
int ret;
of_node_get(np);
ret = of_property_read_u32(np, "samsung,tmu_gain", &value);
pdata->gain = (u8)value;
of_property_read_u32(np, "samsung,tmu_reference_voltage", &value);
pdata->reference_voltage = (u8)value;
of_property_read_u32(np, "samsung,tmu_noise_cancel_mode", &value);
pdata->noise_cancel_mode = (u8)value;
of_property_read_u32(np, "samsung,tmu_efuse_value",
&pdata->efuse_value);
of_property_read_u32(np, "samsung,tmu_min_efuse_value",
&pdata->min_efuse_value);
of_property_read_u32(np, "samsung,tmu_max_efuse_value",
&pdata->max_efuse_value);
of_property_read_u32(np, "samsung,tmu_first_point_trim", &value);
pdata->first_point_trim = (u8)value;
of_property_read_u32(np, "samsung,tmu_second_point_trim", &value);
pdata->second_point_trim = (u8)value;
of_property_read_u32(np, "samsung,tmu_default_temp_offset", &value);
pdata->default_temp_offset = (u8)value;
of_property_read_u32(np, "samsung,tmu_cal_type", &pdata->cal_type);
of_node_put(np);
return 0;
}
static int exynos_map_dt_data(struct platform_device *pdev) static int exynos_map_dt_data(struct platform_device *pdev)
{ {
struct exynos_tmu_data *data = platform_get_drvdata(pdev); struct exynos_tmu_data *data = platform_get_drvdata(pdev);
struct exynos_tmu_platform_data *pdata;
struct resource res; struct resource res;
if (!data || !pdev->dev.of_node) if (!data || !pdev->dev.of_node)
...@@ -1211,23 +910,22 @@ static int exynos_map_dt_data(struct platform_device *pdev) ...@@ -1211,23 +910,22 @@ static int exynos_map_dt_data(struct platform_device *pdev)
return -EADDRNOTAVAIL; return -EADDRNOTAVAIL;
} }
pdata = devm_kzalloc(&pdev->dev, data->soc = (enum soc_type)of_device_get_match_data(&pdev->dev);
sizeof(struct exynos_tmu_platform_data),
GFP_KERNEL);
if (!pdata)
return -ENOMEM;
exynos_of_sensor_conf(pdev->dev.of_node, pdata);
data->pdata = pdata;
data->soc = exynos_of_get_soc_type(pdev->dev.of_node);
switch (data->soc) { switch (data->soc) {
case SOC_ARCH_EXYNOS4210: case SOC_ARCH_EXYNOS4210:
data->tmu_set_trip_temp = exynos4210_tmu_set_trip_temp;
data->tmu_set_trip_hyst = exynos4210_tmu_set_trip_hyst;
data->tmu_initialize = exynos4210_tmu_initialize; data->tmu_initialize = exynos4210_tmu_initialize;
data->tmu_control = exynos4210_tmu_control; data->tmu_control = exynos4210_tmu_control;
data->tmu_read = exynos4210_tmu_read; data->tmu_read = exynos4210_tmu_read;
data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
data->ntrip = 4; data->ntrip = 4;
data->gain = 15;
data->reference_voltage = 7;
data->efuse_value = 55;
data->min_efuse_value = 40;
data->max_efuse_value = 100;
break; break;
case SOC_ARCH_EXYNOS3250: case SOC_ARCH_EXYNOS3250:
case SOC_ARCH_EXYNOS4412: case SOC_ARCH_EXYNOS4412:
...@@ -1235,48 +933,69 @@ static int exynos_map_dt_data(struct platform_device *pdev) ...@@ -1235,48 +933,69 @@ static int exynos_map_dt_data(struct platform_device *pdev)
case SOC_ARCH_EXYNOS5260: case SOC_ARCH_EXYNOS5260:
case SOC_ARCH_EXYNOS5420: case SOC_ARCH_EXYNOS5420:
case SOC_ARCH_EXYNOS5420_TRIMINFO: case SOC_ARCH_EXYNOS5420_TRIMINFO:
data->tmu_set_trip_temp = exynos4412_tmu_set_trip_temp;
data->tmu_set_trip_hyst = exynos4412_tmu_set_trip_hyst;
data->tmu_initialize = exynos4412_tmu_initialize; data->tmu_initialize = exynos4412_tmu_initialize;
data->tmu_control = exynos4210_tmu_control; data->tmu_control = exynos4210_tmu_control;
data->tmu_read = exynos4412_tmu_read; data->tmu_read = exynos4412_tmu_read;
data->tmu_set_emulation = exynos4412_tmu_set_emulation; data->tmu_set_emulation = exynos4412_tmu_set_emulation;
data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
data->ntrip = 4; data->ntrip = 4;
data->gain = 8;
data->reference_voltage = 16;
data->efuse_value = 55;
if (data->soc != SOC_ARCH_EXYNOS5420 &&
data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO)
data->min_efuse_value = 40;
else
data->min_efuse_value = 0;
data->max_efuse_value = 100;
break; break;
case SOC_ARCH_EXYNOS5433: case SOC_ARCH_EXYNOS5433:
data->tmu_set_trip_temp = exynos5433_tmu_set_trip_temp;
data->tmu_set_trip_hyst = exynos5433_tmu_set_trip_hyst;
data->tmu_initialize = exynos5433_tmu_initialize; data->tmu_initialize = exynos5433_tmu_initialize;
data->tmu_control = exynos5433_tmu_control; data->tmu_control = exynos5433_tmu_control;
data->tmu_read = exynos4412_tmu_read; data->tmu_read = exynos4412_tmu_read;
data->tmu_set_emulation = exynos4412_tmu_set_emulation; data->tmu_set_emulation = exynos4412_tmu_set_emulation;
data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
data->ntrip = 8; data->ntrip = 8;
break; data->gain = 8;
case SOC_ARCH_EXYNOS5440: if (res.start == EXYNOS5433_G3D_BASE)
data->tmu_initialize = exynos5440_tmu_initialize; data->reference_voltage = 23;
data->tmu_control = exynos5440_tmu_control; else
data->tmu_read = exynos5440_tmu_read; data->reference_voltage = 16;
data->tmu_set_emulation = exynos5440_tmu_set_emulation; data->efuse_value = 75;
data->tmu_clear_irqs = exynos5440_tmu_clear_irqs; data->min_efuse_value = 40;
data->ntrip = 4; data->max_efuse_value = 150;
break; break;
case SOC_ARCH_EXYNOS7: case SOC_ARCH_EXYNOS7:
data->tmu_set_trip_temp = exynos7_tmu_set_trip_temp;
data->tmu_set_trip_hyst = exynos7_tmu_set_trip_hyst;
data->tmu_initialize = exynos7_tmu_initialize; data->tmu_initialize = exynos7_tmu_initialize;
data->tmu_control = exynos7_tmu_control; data->tmu_control = exynos7_tmu_control;
data->tmu_read = exynos7_tmu_read; data->tmu_read = exynos7_tmu_read;
data->tmu_set_emulation = exynos4412_tmu_set_emulation; data->tmu_set_emulation = exynos4412_tmu_set_emulation;
data->tmu_clear_irqs = exynos4210_tmu_clear_irqs; data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
data->ntrip = 8; data->ntrip = 8;
data->gain = 9;
data->reference_voltage = 17;
data->efuse_value = 75;
data->min_efuse_value = 15;
data->max_efuse_value = 100;
break; break;
default: default:
dev_err(&pdev->dev, "Platform not supported\n"); dev_err(&pdev->dev, "Platform not supported\n");
return -EINVAL; return -EINVAL;
} }
data->cal_type = TYPE_ONE_POINT_TRIMMING;
/* /*
* Check if the TMU shares some registers and then try to map the * Check if the TMU shares some registers and then try to map the
* memory of common registers. * memory of common registers.
*/ */
if (data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO && if (data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO)
data->soc != SOC_ARCH_EXYNOS5440)
return 0; return 0;
if (of_address_to_resource(pdev->dev.of_node, 1, &res)) { if (of_address_to_resource(pdev->dev.of_node, 1, &res)) {
......
/*
* 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 <dt-bindings/thermal/thermal_exynos.h>
enum soc_type {
SOC_ARCH_EXYNOS3250 = 1,
SOC_ARCH_EXYNOS4210,
SOC_ARCH_EXYNOS4412,
SOC_ARCH_EXYNOS5250,
SOC_ARCH_EXYNOS5260,
SOC_ARCH_EXYNOS5420,
SOC_ARCH_EXYNOS5420_TRIMINFO,
SOC_ARCH_EXYNOS5433,
SOC_ARCH_EXYNOS5440,
SOC_ARCH_EXYNOS7,
};
/**
* struct exynos_tmu_platform_data
* @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
* @default_temp_offset: default temperature offset in case of no trimming
* @cal_type: calibration type for temperature
*
* This structure is required for configuration of exynos_tmu driver.
*/
struct exynos_tmu_platform_data {
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 soc_type type;
u32 cal_type;
};
#endif /* _EXYNOS_TMU_H */
...@@ -240,31 +240,6 @@ struct tegra_soctherm { ...@@ -240,31 +240,6 @@ struct tegra_soctherm {
struct dentry *debugfs_dir; struct dentry *debugfs_dir;
}; };
/**
* clk_writel() - writes a value to a CAR register
* @ts: pointer to a struct tegra_soctherm
* @v: the value to write
* @reg: the register offset
*
* Writes @v to @reg. No return value.
*/
static inline void clk_writel(struct tegra_soctherm *ts, u32 value, u32 reg)
{
writel(value, (ts->clk_regs + reg));
}
/**
* clk_readl() - reads specified register from CAR IP block
* @ts: pointer to a struct tegra_soctherm
* @reg: register address to be read
*
* Return: the value of the register
*/
static inline u32 clk_readl(struct tegra_soctherm *ts, u32 reg)
{
return readl(ts->clk_regs + reg);
}
/** /**
* ccroc_writel() - writes a value to a CCROC register * ccroc_writel() - writes a value to a CCROC register
* @ts: pointer to a struct tegra_soctherm * @ts: pointer to a struct tegra_soctherm
...@@ -926,7 +901,7 @@ static int throt_set_cdev_state(struct thermal_cooling_device *cdev, ...@@ -926,7 +901,7 @@ static int throt_set_cdev_state(struct thermal_cooling_device *cdev,
return 0; return 0;
} }
static struct thermal_cooling_device_ops throt_cooling_ops = { static const struct thermal_cooling_device_ops throt_cooling_ops = {
.get_max_state = throt_get_cdev_max_state, .get_max_state = throt_get_cdev_max_state,
.get_cur_state = throt_get_cdev_cur_state, .get_cur_state = throt_get_cdev_cur_state,
.set_cur_state = throt_set_cdev_state, .set_cur_state = throt_set_cdev_state,
...@@ -1207,9 +1182,9 @@ static void tegra_soctherm_throttle(struct device *dev) ...@@ -1207,9 +1182,9 @@ static void tegra_soctherm_throttle(struct device *dev)
} else { } else {
writel(v, ts->regs + THROT_GLOBAL_CFG); writel(v, ts->regs + THROT_GLOBAL_CFG);
v = clk_readl(ts, CAR_SUPER_CCLKG_DIVIDER); v = readl(ts->clk_regs + CAR_SUPER_CCLKG_DIVIDER);
v = REG_SET_MASK(v, CDIVG_USE_THERM_CONTROLS_MASK, 1); v = REG_SET_MASK(v, CDIVG_USE_THERM_CONTROLS_MASK, 1);
clk_writel(ts, v, CAR_SUPER_CCLKG_DIVIDER); writel(v, ts->clk_regs + CAR_SUPER_CCLKG_DIVIDER);
} }
/* initialize stats collection */ /* initialize stats collection */
......
...@@ -310,7 +310,7 @@ omap5430_adc_to_temp[ ...@@ -310,7 +310,7 @@ omap5430_adc_to_temp[
119800, 120200, 120600, 121000, 121400, 121800, 122400, 122600, 123000, 119800, 120200, 120600, 121000, 121400, 121800, 122400, 122600, 123000,
123400, 123400,
/* Index 940 - 945 */ /* Index 940 - 945 */
123800, 1242000, 124600, 124900, 125000, 125000, 123800, 124200, 124600, 124900, 125000, 125000,
}; };
/* OMAP54xx ES2.0 data */ /* OMAP54xx ES2.0 data */
......
...@@ -365,6 +365,10 @@ static const struct of_device_id uniphier_tm_dt_ids[] = { ...@@ -365,6 +365,10 @@ static const struct of_device_id uniphier_tm_dt_ids[] = {
.compatible = "socionext,uniphier-ld20-thermal", .compatible = "socionext,uniphier-ld20-thermal",
.data = &uniphier_ld20_tm_data, .data = &uniphier_ld20_tm_data,
}, },
{
.compatible = "socionext,uniphier-pxs3-thermal",
.data = &uniphier_ld20_tm_data,
},
{ /* sentinel */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, uniphier_tm_dt_ids); MODULE_DEVICE_TABLE(of, uniphier_tm_dt_ids);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment