Commit c2802302 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 Eduardo Valentin:

 - Tegra DT binding documentation for Tegra194

 - Armada now supports ap806 and cp110

 - RCAR thermal now supports R8A774C0 and R8A77990

 - Fixes on thermal_hwmon, IMX, generic-ADC, ST, RCAR, Broadcom,
   Uniphier, QCOM, Tegra, PowerClamp, and Armada thermal drivers.

* 'linus' of git://git.kernel.org/pub/scm/linux/kernel/git/evalenti/linux-soc-thermal: (22 commits)
  thermal: generic-adc: Fix adc to temp interpolation
  thermal: rcar_thermal: add R8A77990 support
  dt-bindings: thermal: rcar-thermal: add R8A77990 support
  thermal: rcar_thermal: add R8A774C0 support
  dt-bindings: thermal: rcar-thermal: add R8A774C0 support
  dt-bindings: cp110: document the thermal interrupt capabilities
  dt-bindings: ap806: document the thermal interrupt capabilities
  MAINTAINERS: thermal: add entry for Marvell MVEBU thermal driver
  thermal: armada: add overheat interrupt support
  thermal: st: fix Makefile typo
  thermal: uniphier: Convert to SPDX identifier
  thermal/intel_powerclamp: Change to use DEFINE_SHOW_ATTRIBUTE macro
  thermal: tegra: soctherm: Change to use DEFINE_SHOW_ATTRIBUTE macro
  dt-bindings: thermal: tegra-bpmp: Add Tegra194 support
  thermal: imx: save one condition block for normal case of nvmem initialization
  thermal: imx: fix for dependency on cpu-freq
  thermal: tsens: qcom: do not create duplicate regmap debugfs entries
  thermal: armada: Use PTR_ERR_OR_ZERO in armada_thermal_probe_legacy()
  dt-bindings: thermal: rcar-gen3-thermal: All variants use 3 interrupts
  thermal: broadcom: use devm_thermal_zone_of_sensor_register
  ...
parents a6701241 9d216211
...@@ -114,12 +114,17 @@ Documentation/devicetree/bindings/thermal/thermal.txt ...@@ -114,12 +114,17 @@ Documentation/devicetree/bindings/thermal/thermal.txt
The thermal IP can probe the temperature all around the processor. It The thermal IP can probe the temperature all around the processor. It
may feature several channels, each of them wired to one sensor. may feature several channels, each of them wired to one sensor.
It is possible to setup an overheat interrupt by giving at least one
critical point to any subnode of the thermal-zone node.
Required properties: Required properties:
- compatible: must be one of: - compatible: must be one of:
* marvell,armada-ap806-thermal * marvell,armada-ap806-thermal
- reg: register range associated with the thermal functions. - reg: register range associated with the thermal functions.
Optional properties: Optional properties:
- interrupts: overheat interrupt handle. Should point to line 18 of the
SEI irqchip. See interrupt-controller/interrupts.txt
- #thermal-sensor-cells: shall be <1> when thermal-zones subnodes refer - #thermal-sensor-cells: shall be <1> when thermal-zones subnodes refer
to this IP and represents the channel ID. There is one sensor per to this IP and represents the channel ID. There is one sensor per
channel. O refers to the thermal IP internal channel, while positive channel. O refers to the thermal IP internal channel, while positive
...@@ -133,6 +138,8 @@ ap_syscon1: system-controller@6f8000 { ...@@ -133,6 +138,8 @@ ap_syscon1: system-controller@6f8000 {
ap_thermal: thermal-sensor@80 { ap_thermal: thermal-sensor@80 {
compatible = "marvell,armada-ap806-thermal"; compatible = "marvell,armada-ap806-thermal";
reg = <0x80 0x10>; reg = <0x80 0x10>;
interrupt-parent = <&sei>;
interrupts = <18>;
#thermal-sensor-cells = <1>; #thermal-sensor-cells = <1>;
}; };
}; };
...@@ -199,6 +199,9 @@ Thermal: ...@@ -199,6 +199,9 @@ Thermal:
The thermal IP can probe the temperature all around the processor. It The thermal IP can probe the temperature all around the processor. It
may feature several channels, each of them wired to one sensor. may feature several channels, each of them wired to one sensor.
It is possible to setup an overheat interrupt by giving at least one
critical point to any subnode of the thermal-zone node.
For common binding part and usage, refer to For common binding part and usage, refer to
Documentation/devicetree/bindings/thermal/thermal.txt Documentation/devicetree/bindings/thermal/thermal.txt
...@@ -208,6 +211,11 @@ Required properties: ...@@ -208,6 +211,11 @@ Required properties:
- reg: register range associated with the thermal functions. - reg: register range associated with the thermal functions.
Optional properties: Optional properties:
- interrupts-extended: overheat interrupt handle. Should point to
a line of the ICU-SEI irqchip (116 is what is usually used by the
firmware). The ICU-SEI will redirect towards interrupt line #37 of the
AP SEI which is shared across all CPs.
See interrupt-controller/interrupts.txt
- #thermal-sensor-cells: shall be <1> when thermal-zones subnodes refer - #thermal-sensor-cells: shall be <1> when thermal-zones subnodes refer
to this IP and represents the channel ID. There is one sensor per to this IP and represents the channel ID. There is one sensor per
channel. O refers to the thermal IP internal channel. channel. O refers to the thermal IP internal channel.
...@@ -220,6 +228,7 @@ CP110_LABEL(syscon1): system-controller@6f8000 { ...@@ -220,6 +228,7 @@ CP110_LABEL(syscon1): system-controller@6f8000 {
CP110_LABEL(thermal): thermal-sensor@70 { CP110_LABEL(thermal): thermal-sensor@70 {
compatible = "marvell,armada-cp110-thermal"; compatible = "marvell,armada-cp110-thermal";
reg = <0x70 0x10>; reg = <0x70 0x10>;
interrupts-extended = <&CP110_LABEL(icu_sei) 116 IRQ_TYPE_LEVEL_HIGH>;
#thermal-sensor-cells = <1>; #thermal-sensor-cells = <1>;
}; };
}; };
...@@ -21,8 +21,7 @@ Required properties: ...@@ -21,8 +21,7 @@ Required properties:
Optional properties: Optional properties:
- interrupts : interrupts routed to the TSC (3 for H3, M3-W, M3-N, - interrupts : interrupts routed to the TSC (must be 3).
and V3H)
- 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.
......
...@@ -4,17 +4,19 @@ Required properties: ...@@ -4,17 +4,19 @@ 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 "renesas,rcar-thermal" (without thermal-zone) as
fallback except R-Car V3M/D3. fallback except R-Car V3M/E3/D3 and RZ/G2E.
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)
- "renesas,thermal-r8a7744" (RZ/G1N) - "renesas,thermal-r8a7744" (RZ/G1N)
- "renesas,thermal-r8a774c0" (RZ/G2E)
- "renesas,thermal-r8a7779" (R-Car H1) - "renesas,thermal-r8a7779" (R-Car H1)
- "renesas,thermal-r8a7790" (R-Car H2) - "renesas,thermal-r8a7790" (R-Car H2)
- "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-r8a77970" (R-Car V3M) - "renesas,thermal-r8a77970" (R-Car V3M)
- "renesas,thermal-r8a77990" (R-Car E3)
- "renesas,thermal-r8a77995" (R-Car D3) - "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
...@@ -23,7 +25,7 @@ Required properties: ...@@ -23,7 +25,7 @@ Required properties:
Option properties: Option properties:
- interrupts : If present should contain 3 interrupts for - interrupts : If present should contain 3 interrupts for
R-Car V3M/D3 or 1 interrupt otherwise. R-Car V3M/E3/D3 and RZ/G2E or 1 interrupt otherwise.
Example (non interrupt support): Example (non interrupt support):
......
...@@ -9109,6 +9109,11 @@ L: netdev@vger.kernel.org ...@@ -9109,6 +9109,11 @@ L: netdev@vger.kernel.org
S: Maintained S: Maintained
F: drivers/net/phy/marvell10g.c F: drivers/net/phy/marvell10g.c
MARVELL MVEBU THERMAL DRIVER
M: Miquel Raynal <miquel.raynal@bootlin.com>
S: Maintained
F: drivers/thermal/armada_thermal.c
MARVELL MVNETA ETHERNET DRIVER MARVELL MVNETA ETHERNET DRIVER
M: Thomas Petazzoni <thomas.petazzoni@bootlin.com> M: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
L: netdev@vger.kernel.org L: netdev@vger.kernel.org
......
...@@ -212,7 +212,7 @@ config HISI_THERMAL ...@@ -212,7 +212,7 @@ config HISI_THERMAL
config IMX_THERMAL config IMX_THERMAL
tristate "Temperature sensor driver for Freescale i.MX SoCs" tristate "Temperature sensor driver for Freescale i.MX SoCs"
depends on (ARCH_MXC && CPU_THERMAL) || COMPILE_TEST depends on ARCH_MXC || COMPILE_TEST
depends on NVMEM || !NVMEM depends on NVMEM || !NVMEM
depends on MFD_SYSCON depends on MFD_SYSCON
depends on OF depends on OF
......
...@@ -26,6 +26,11 @@ ...@@ -26,6 +26,11 @@
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/mfd/syscon.h> #include <linux/mfd/syscon.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/interrupt.h>
#include "thermal_core.h"
#define TO_MCELSIUS(c) ((c) * 1000)
/* Thermal Manager Control and Status Register */ /* Thermal Manager Control and Status Register */
#define PMU_TDC0_SW_RST_MASK (0x1 << 1) #define PMU_TDC0_SW_RST_MASK (0x1 << 1)
...@@ -61,9 +66,13 @@ ...@@ -61,9 +66,13 @@
#define CONTROL1_TSEN_AVG_MASK 0x7 #define CONTROL1_TSEN_AVG_MASK 0x7
#define CONTROL1_EXT_TSEN_SW_RESET BIT(7) #define CONTROL1_EXT_TSEN_SW_RESET BIT(7)
#define CONTROL1_EXT_TSEN_HW_RESETn BIT(8) #define CONTROL1_EXT_TSEN_HW_RESETn BIT(8)
#define CONTROL1_TSEN_INT_EN BIT(25)
#define CONTROL1_TSEN_SELECT_OFF 21
#define CONTROL1_TSEN_SELECT_MASK 0x3
#define STATUS_POLL_PERIOD_US 1000 #define STATUS_POLL_PERIOD_US 1000
#define STATUS_POLL_TIMEOUT_US 100000 #define STATUS_POLL_TIMEOUT_US 100000
#define OVERHEAT_INT_POLL_DELAY_MS 1000
struct armada_thermal_data; struct armada_thermal_data;
...@@ -75,7 +84,11 @@ struct armada_thermal_priv { ...@@ -75,7 +84,11 @@ struct armada_thermal_priv {
/* serialize temperature reads/updates */ /* serialize temperature reads/updates */
struct mutex update_lock; struct mutex update_lock;
struct armada_thermal_data *data; struct armada_thermal_data *data;
struct thermal_zone_device *overheat_sensor;
int interrupt_source;
int current_channel; int current_channel;
long current_threshold;
long current_hysteresis;
}; };
struct armada_thermal_data { struct armada_thermal_data {
...@@ -93,12 +106,20 @@ struct armada_thermal_data { ...@@ -93,12 +106,20 @@ struct armada_thermal_data {
/* Register shift and mask to access the sensor temperature */ /* Register shift and mask to access the sensor temperature */
unsigned int temp_shift; unsigned int temp_shift;
unsigned int temp_mask; unsigned int temp_mask;
unsigned int thresh_shift;
unsigned int hyst_shift;
unsigned int hyst_mask;
u32 is_valid_bit; u32 is_valid_bit;
/* Syscon access */ /* Syscon access */
unsigned int syscon_control0_off; unsigned int syscon_control0_off;
unsigned int syscon_control1_off; unsigned int syscon_control1_off;
unsigned int syscon_status_off; unsigned int syscon_status_off;
unsigned int dfx_irq_cause_off;
unsigned int dfx_irq_mask_off;
unsigned int dfx_overheat_irq;
unsigned int dfx_server_irq_mask_off;
unsigned int dfx_server_irq_en;
/* One sensor is in the thermal IC, the others are in the CPUs if any */ /* One sensor is in the thermal IC, the others are in the CPUs if any */
unsigned int cpu_nr; unsigned int cpu_nr;
...@@ -272,6 +293,41 @@ static bool armada_is_valid(struct armada_thermal_priv *priv) ...@@ -272,6 +293,41 @@ static bool armada_is_valid(struct armada_thermal_priv *priv)
return reg & priv->data->is_valid_bit; return reg & priv->data->is_valid_bit;
} }
static void armada_enable_overheat_interrupt(struct armada_thermal_priv *priv)
{
struct armada_thermal_data *data = priv->data;
u32 reg;
/* Clear DFX temperature IRQ cause */
regmap_read(priv->syscon, data->dfx_irq_cause_off, &reg);
/* Enable DFX Temperature IRQ */
regmap_read(priv->syscon, data->dfx_irq_mask_off, &reg);
reg |= data->dfx_overheat_irq;
regmap_write(priv->syscon, data->dfx_irq_mask_off, reg);
/* Enable DFX server IRQ */
regmap_read(priv->syscon, data->dfx_server_irq_mask_off, &reg);
reg |= data->dfx_server_irq_en;
regmap_write(priv->syscon, data->dfx_server_irq_mask_off, reg);
/* Enable overheat interrupt */
regmap_read(priv->syscon, data->syscon_control1_off, &reg);
reg |= CONTROL1_TSEN_INT_EN;
regmap_write(priv->syscon, data->syscon_control1_off, reg);
}
static void __maybe_unused
armada_disable_overheat_interrupt(struct armada_thermal_priv *priv)
{
struct armada_thermal_data *data = priv->data;
u32 reg;
regmap_read(priv->syscon, data->syscon_control1_off, &reg);
reg &= ~CONTROL1_TSEN_INT_EN;
regmap_write(priv->syscon, data->syscon_control1_off, reg);
}
/* There is currently no board with more than one sensor per channel */ /* There is currently no board with more than one sensor per channel */
static int armada_select_channel(struct armada_thermal_priv *priv, int channel) static int armada_select_channel(struct armada_thermal_priv *priv, int channel)
{ {
...@@ -388,6 +444,14 @@ static int armada_get_temp(void *_sensor, int *temp) ...@@ -388,6 +444,14 @@ static int armada_get_temp(void *_sensor, int *temp)
/* Do the actual reading */ /* Do the actual reading */
ret = armada_read_sensor(priv, temp); ret = armada_read_sensor(priv, temp);
if (ret)
goto unlock_mutex;
/*
* Select back the interrupt source channel from which a potential
* critical trip point has been set.
*/
ret = armada_select_channel(priv, priv->interrupt_source);
unlock_mutex: unlock_mutex:
mutex_unlock(&priv->update_lock); mutex_unlock(&priv->update_lock);
...@@ -399,6 +463,123 @@ static const struct thermal_zone_of_device_ops of_ops = { ...@@ -399,6 +463,123 @@ static const struct thermal_zone_of_device_ops of_ops = {
.get_temp = armada_get_temp, .get_temp = armada_get_temp,
}; };
static unsigned int armada_mc_to_reg_temp(struct armada_thermal_data *data,
unsigned int temp_mc)
{
s64 b = data->coef_b;
s64 m = data->coef_m;
s64 div = data->coef_div;
unsigned int sample;
if (data->inverted)
sample = div_s64(((temp_mc * div) + b), m);
else
sample = div_s64((b - (temp_mc * div)), m);
return sample & data->temp_mask;
}
/*
* The documentation states:
* high/low watermark = threshold +/- 0.4761 * 2^(hysteresis + 2)
* which is the mathematical derivation for:
* 0x0 <=> 1.9°C, 0x1 <=> 3.8°C, 0x2 <=> 7.6°C, 0x3 <=> 15.2°C
*/
static unsigned int hyst_levels_mc[] = {1900, 3800, 7600, 15200};
static unsigned int armada_mc_to_reg_hyst(struct armada_thermal_data *data,
unsigned int hyst_mc)
{
int i;
/*
* We will always take the smallest possible hysteresis to avoid risking
* the hardware integrity by enlarging the threshold by +8°C in the
* worst case.
*/
for (i = ARRAY_SIZE(hyst_levels_mc) - 1; i > 0; i--)
if (hyst_mc >= hyst_levels_mc[i])
break;
return i & data->hyst_mask;
}
static void armada_set_overheat_thresholds(struct armada_thermal_priv *priv,
int thresh_mc, int hyst_mc)
{
struct armada_thermal_data *data = priv->data;
unsigned int threshold = armada_mc_to_reg_temp(data, thresh_mc);
unsigned int hysteresis = armada_mc_to_reg_hyst(data, hyst_mc);
u32 ctrl1;
regmap_read(priv->syscon, data->syscon_control1_off, &ctrl1);
/* Set Threshold */
if (thresh_mc >= 0) {
ctrl1 &= ~(data->temp_mask << data->thresh_shift);
ctrl1 |= threshold << data->thresh_shift;
priv->current_threshold = thresh_mc;
}
/* Set Hysteresis */
if (hyst_mc >= 0) {
ctrl1 &= ~(data->hyst_mask << data->hyst_shift);
ctrl1 |= hysteresis << data->hyst_shift;
priv->current_hysteresis = hyst_mc;
}
regmap_write(priv->syscon, data->syscon_control1_off, ctrl1);
}
static irqreturn_t armada_overheat_isr(int irq, void *blob)
{
/*
* Disable the IRQ and continue in thread context (thermal core
* notification and temperature monitoring).
*/
disable_irq_nosync(irq);
return IRQ_WAKE_THREAD;
}
static irqreturn_t armada_overheat_isr_thread(int irq, void *blob)
{
struct armada_thermal_priv *priv = blob;
int low_threshold = priv->current_threshold - priv->current_hysteresis;
int temperature;
u32 dummy;
int ret;
/* Notify the core in thread context */
thermal_zone_device_update(priv->overheat_sensor,
THERMAL_EVENT_UNSPECIFIED);
/*
* The overheat interrupt must be cleared by reading the DFX interrupt
* cause _after_ the temperature has fallen down to the low threshold.
* Otherwise future interrupts might not be served.
*/
do {
msleep(OVERHEAT_INT_POLL_DELAY_MS);
mutex_lock(&priv->update_lock);
ret = armada_read_sensor(priv, &temperature);
mutex_unlock(&priv->update_lock);
if (ret)
goto enable_irq;
} while (temperature >= low_threshold);
regmap_read(priv->syscon, priv->data->dfx_irq_cause_off, &dummy);
/* Notify the thermal core that the temperature is acceptable again */
thermal_zone_device_update(priv->overheat_sensor,
THERMAL_EVENT_UNSPECIFIED);
enable_irq:
enable_irq(irq);
return IRQ_HANDLED;
}
static const struct armada_thermal_data armadaxp_data = { static const struct armada_thermal_data armadaxp_data = {
.init = armadaxp_init, .init = armadaxp_init,
.temp_shift = 10, .temp_shift = 10,
...@@ -454,6 +635,9 @@ static const struct armada_thermal_data armada_ap806_data = { ...@@ -454,6 +635,9 @@ static const struct armada_thermal_data armada_ap806_data = {
.is_valid_bit = BIT(16), .is_valid_bit = BIT(16),
.temp_shift = 0, .temp_shift = 0,
.temp_mask = 0x3ff, .temp_mask = 0x3ff,
.thresh_shift = 3,
.hyst_shift = 19,
.hyst_mask = 0x3,
.coef_b = -150000LL, .coef_b = -150000LL,
.coef_m = 423ULL, .coef_m = 423ULL,
.coef_div = 1, .coef_div = 1,
...@@ -462,6 +646,11 @@ static const struct armada_thermal_data armada_ap806_data = { ...@@ -462,6 +646,11 @@ static const struct armada_thermal_data armada_ap806_data = {
.syscon_control0_off = 0x84, .syscon_control0_off = 0x84,
.syscon_control1_off = 0x88, .syscon_control1_off = 0x88,
.syscon_status_off = 0x8C, .syscon_status_off = 0x8C,
.dfx_irq_cause_off = 0x108,
.dfx_irq_mask_off = 0x10C,
.dfx_overheat_irq = BIT(22),
.dfx_server_irq_mask_off = 0x104,
.dfx_server_irq_en = BIT(1),
.cpu_nr = 4, .cpu_nr = 4,
}; };
...@@ -470,6 +659,9 @@ static const struct armada_thermal_data armada_cp110_data = { ...@@ -470,6 +659,9 @@ static const struct armada_thermal_data armada_cp110_data = {
.is_valid_bit = BIT(10), .is_valid_bit = BIT(10),
.temp_shift = 0, .temp_shift = 0,
.temp_mask = 0x3ff, .temp_mask = 0x3ff,
.thresh_shift = 16,
.hyst_shift = 26,
.hyst_mask = 0x3,
.coef_b = 1172499100ULL, .coef_b = 1172499100ULL,
.coef_m = 2000096ULL, .coef_m = 2000096ULL,
.coef_div = 4201, .coef_div = 4201,
...@@ -477,6 +669,11 @@ static const struct armada_thermal_data armada_cp110_data = { ...@@ -477,6 +669,11 @@ static const struct armada_thermal_data armada_cp110_data = {
.syscon_control0_off = 0x70, .syscon_control0_off = 0x70,
.syscon_control1_off = 0x74, .syscon_control1_off = 0x74,
.syscon_status_off = 0x78, .syscon_status_off = 0x78,
.dfx_irq_cause_off = 0x108,
.dfx_irq_mask_off = 0x10C,
.dfx_overheat_irq = BIT(20),
.dfx_server_irq_mask_off = 0x104,
.dfx_server_irq_en = BIT(1),
}; };
static const struct of_device_id armada_thermal_id_table[] = { static const struct of_device_id armada_thermal_id_table[] = {
...@@ -543,20 +740,14 @@ static int armada_thermal_probe_legacy(struct platform_device *pdev, ...@@ -543,20 +740,14 @@ static int armada_thermal_probe_legacy(struct platform_device *pdev,
priv->syscon = devm_regmap_init_mmio(&pdev->dev, base, priv->syscon = devm_regmap_init_mmio(&pdev->dev, base,
&armada_thermal_regmap_config); &armada_thermal_regmap_config);
if (IS_ERR(priv->syscon)) return PTR_ERR_OR_ZERO(priv->syscon);
return PTR_ERR(priv->syscon);
return 0;
} }
static int armada_thermal_probe_syscon(struct platform_device *pdev, static int armada_thermal_probe_syscon(struct platform_device *pdev,
struct armada_thermal_priv *priv) struct armada_thermal_priv *priv)
{ {
priv->syscon = syscon_node_to_regmap(pdev->dev.parent->of_node); priv->syscon = syscon_node_to_regmap(pdev->dev.parent->of_node);
if (IS_ERR(priv->syscon)) return PTR_ERR_OR_ZERO(priv->syscon);
return PTR_ERR(priv->syscon);
return 0;
} }
static void armada_set_sane_name(struct platform_device *pdev, static void armada_set_sane_name(struct platform_device *pdev,
...@@ -590,6 +781,48 @@ static void armada_set_sane_name(struct platform_device *pdev, ...@@ -590,6 +781,48 @@ static void armada_set_sane_name(struct platform_device *pdev,
} while (insane_char); } while (insane_char);
} }
/*
* The IP can manage to trigger interrupts on overheat situation from all the
* sensors. However, the interrupt source changes along with the last selected
* source (ie. the last read sensor), which is an inconsistent behavior. Avoid
* possible glitches by always selecting back only one channel (arbitrarily: the
* first in the DT which has a critical trip point). We also disable sensor
* switch during overheat situations.
*/
static int armada_configure_overheat_int(struct armada_thermal_priv *priv,
struct thermal_zone_device *tz,
int sensor_id)
{
/* Retrieve the critical trip point to enable the overheat interrupt */
const struct thermal_trip *trips = of_thermal_get_trip_points(tz);
int ret;
int i;
if (!trips)
return -EINVAL;
for (i = 0; i < of_thermal_get_ntrips(tz); i++)
if (trips[i].type == THERMAL_TRIP_CRITICAL)
break;
if (i == of_thermal_get_ntrips(tz))
return -EINVAL;
ret = armada_select_channel(priv, sensor_id);
if (ret)
return ret;
armada_set_overheat_thresholds(priv,
trips[i].temperature,
trips[i].hysteresis);
priv->overheat_sensor = tz;
priv->interrupt_source = sensor_id;
armada_enable_overheat_interrupt(priv);
return 0;
}
static int armada_thermal_probe(struct platform_device *pdev) static int armada_thermal_probe(struct platform_device *pdev)
{ {
struct thermal_zone_device *tz; struct thermal_zone_device *tz;
...@@ -597,7 +830,7 @@ static int armada_thermal_probe(struct platform_device *pdev) ...@@ -597,7 +830,7 @@ static int armada_thermal_probe(struct platform_device *pdev)
struct armada_drvdata *drvdata; struct armada_drvdata *drvdata;
const struct of_device_id *match; const struct of_device_id *match;
struct armada_thermal_priv *priv; struct armada_thermal_priv *priv;
int sensor_id; int sensor_id, irq;
int ret; int ret;
match = of_match_device(armada_thermal_id_table, &pdev->dev); match = of_match_device(armada_thermal_id_table, &pdev->dev);
...@@ -667,6 +900,23 @@ static int armada_thermal_probe(struct platform_device *pdev) ...@@ -667,6 +900,23 @@ static int armada_thermal_probe(struct platform_device *pdev)
drvdata->data.priv = priv; drvdata->data.priv = priv;
platform_set_drvdata(pdev, drvdata); platform_set_drvdata(pdev, drvdata);
irq = platform_get_irq(pdev, 0);
if (irq == -EPROBE_DEFER)
return irq;
/* The overheat interrupt feature is not mandatory */
if (irq > 0) {
ret = devm_request_threaded_irq(&pdev->dev, irq,
armada_overheat_isr,
armada_overheat_isr_thread,
0, NULL, priv);
if (ret) {
dev_err(&pdev->dev, "Cannot request threaded IRQ %d\n",
irq);
return ret;
}
}
/* /*
* There is one channel for the IC and one per CPU (if any), each * There is one channel for the IC and one per CPU (if any), each
* channel has one sensor. * channel has one sensor.
...@@ -690,8 +940,20 @@ static int armada_thermal_probe(struct platform_device *pdev) ...@@ -690,8 +940,20 @@ static int armada_thermal_probe(struct platform_device *pdev)
devm_kfree(&pdev->dev, sensor); devm_kfree(&pdev->dev, sensor);
continue; continue;
} }
/*
* The first channel that has a critical trip point registered
* in the DT will serve as interrupt source. Others possible
* critical trip points will simply be ignored by the driver.
*/
if (irq > 0 && !priv->overheat_sensor)
armada_configure_overheat_int(priv, tz, sensor->id);
} }
/* Just complain if no overheat interrupt was set up */
if (!priv->overheat_sensor)
dev_warn(&pdev->dev, "Overheat interrupt not available\n");
return 0; return 0;
} }
......
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/thermal.h> #include <linux/thermal.h>
#include "../thermal_hwmon.h"
#define BCM2835_TS_TSENSCTL 0x00 #define BCM2835_TS_TSENSCTL 0x00
#define BCM2835_TS_TSENSSTAT 0x04 #define BCM2835_TS_TSENSSTAT 0x04
...@@ -266,6 +268,15 @@ static int bcm2835_thermal_probe(struct platform_device *pdev) ...@@ -266,6 +268,15 @@ static int bcm2835_thermal_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, tz); platform_set_drvdata(pdev, tz);
/*
* Thermal_zone doesn't enable hwmon as default,
* enable it here
*/
tz->tzp->no_hwmon = false;
err = thermal_add_hwmon_sysfs(tz);
if (err)
goto err_tz;
bcm2835_thermal_debugfs(pdev); bcm2835_thermal_debugfs(pdev);
return 0; return 0;
......
...@@ -329,7 +329,8 @@ static int brcmstb_thermal_probe(struct platform_device *pdev) ...@@ -329,7 +329,8 @@ static int brcmstb_thermal_probe(struct platform_device *pdev)
priv->dev = &pdev->dev; priv->dev = &pdev->dev;
platform_set_drvdata(pdev, priv); platform_set_drvdata(pdev, priv);
thermal = thermal_zone_of_sensor_register(&pdev->dev, 0, priv, &of_ops); thermal = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, priv,
&of_ops);
if (IS_ERR(thermal)) { if (IS_ERR(thermal)) {
ret = PTR_ERR(thermal); ret = PTR_ERR(thermal);
dev_err(&pdev->dev, "could not register sensor: %d\n", ret); dev_err(&pdev->dev, "could not register sensor: %d\n", ret);
...@@ -341,40 +342,23 @@ static int brcmstb_thermal_probe(struct platform_device *pdev) ...@@ -341,40 +342,23 @@ static int brcmstb_thermal_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq < 0) { if (irq < 0) {
dev_err(&pdev->dev, "could not get IRQ\n"); dev_err(&pdev->dev, "could not get IRQ\n");
ret = irq; return irq;
goto err;
} }
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
brcmstb_tmon_irq_thread, IRQF_ONESHOT, brcmstb_tmon_irq_thread, IRQF_ONESHOT,
DRV_NAME, priv); DRV_NAME, priv);
if (ret < 0) { if (ret < 0) {
dev_err(&pdev->dev, "could not request IRQ: %d\n", ret); dev_err(&pdev->dev, "could not request IRQ: %d\n", ret);
goto err; return ret;
} }
dev_info(&pdev->dev, "registered AVS TMON of-sensor driver\n"); dev_info(&pdev->dev, "registered AVS TMON of-sensor driver\n");
return 0; return 0;
err:
thermal_zone_of_sensor_unregister(&pdev->dev, thermal);
return ret;
}
static int brcmstb_thermal_exit(struct platform_device *pdev)
{
struct brcmstb_thermal_priv *priv = platform_get_drvdata(pdev);
struct thermal_zone_device *thermal = priv->thermal;
if (thermal)
thermal_zone_of_sensor_unregister(&pdev->dev, priv->thermal);
return 0;
} }
static struct platform_driver brcmstb_thermal_driver = { static struct platform_driver brcmstb_thermal_driver = {
.probe = brcmstb_thermal_probe, .probe = brcmstb_thermal_probe,
.remove = brcmstb_thermal_exit,
.driver = { .driver = {
.name = DRV_NAME, .name = DRV_NAME,
.of_match_table = brcmstb_thermal_id_table, .of_match_table = brcmstb_thermal_id_table,
......
...@@ -648,15 +648,24 @@ static const struct of_device_id of_imx_thermal_match[] = { ...@@ -648,15 +648,24 @@ static const struct of_device_id of_imx_thermal_match[] = {
}; };
MODULE_DEVICE_TABLE(of, of_imx_thermal_match); MODULE_DEVICE_TABLE(of, of_imx_thermal_match);
#ifdef CONFIG_CPU_FREQ
/* /*
* Create cooling device in case no #cooling-cells property is available in * Create cooling device in case no #cooling-cells property is available in
* CPU node * CPU node
*/ */
static int imx_thermal_register_legacy_cooling(struct imx_thermal_data *data) static int imx_thermal_register_legacy_cooling(struct imx_thermal_data *data)
{ {
struct device_node *np = of_get_cpu_node(data->policy->cpu, NULL); struct device_node *np;
int ret; int ret;
data->policy = cpufreq_cpu_get(0);
if (!data->policy) {
pr_debug("%s: CPUFreq policy not found\n", __func__);
return -EPROBE_DEFER;
}
np = of_get_cpu_node(data->policy->cpu, NULL);
if (!np || !of_find_property(np, "#cooling-cells", NULL)) { if (!np || !of_find_property(np, "#cooling-cells", NULL)) {
data->cdev = cpufreq_cooling_register(data->policy); data->cdev = cpufreq_cooling_register(data->policy);
if (IS_ERR(data->cdev)) { if (IS_ERR(data->cdev)) {
...@@ -669,6 +678,24 @@ static int imx_thermal_register_legacy_cooling(struct imx_thermal_data *data) ...@@ -669,6 +678,24 @@ static int imx_thermal_register_legacy_cooling(struct imx_thermal_data *data)
return 0; return 0;
} }
static void imx_thermal_unregister_legacy_cooling(struct imx_thermal_data *data)
{
cpufreq_cooling_unregister(data->cdev);
cpufreq_cpu_put(data->policy);
}
#else
static inline int imx_thermal_register_legacy_cooling(struct imx_thermal_data *data)
{
return 0;
}
static inline void imx_thermal_unregister_legacy_cooling(struct imx_thermal_data *data)
{
}
#endif
static int imx_thermal_probe(struct platform_device *pdev) static int imx_thermal_probe(struct platform_device *pdev)
{ {
struct imx_thermal_data *data; struct imx_thermal_data *data;
...@@ -715,9 +742,10 @@ static int imx_thermal_probe(struct platform_device *pdev) ...@@ -715,9 +742,10 @@ static int imx_thermal_probe(struct platform_device *pdev)
if (of_find_property(pdev->dev.of_node, "nvmem-cells", NULL)) { if (of_find_property(pdev->dev.of_node, "nvmem-cells", NULL)) {
ret = imx_init_from_nvmem_cells(pdev); ret = imx_init_from_nvmem_cells(pdev);
if (ret) {
if (ret == -EPROBE_DEFER) if (ret == -EPROBE_DEFER)
return ret; return ret;
if (ret) {
dev_err(&pdev->dev, "failed to init from nvmem: %d\n", dev_err(&pdev->dev, "failed to init from nvmem: %d\n",
ret); ret);
return ret; return ret;
...@@ -743,14 +771,11 @@ static int imx_thermal_probe(struct platform_device *pdev) ...@@ -743,14 +771,11 @@ static int imx_thermal_probe(struct platform_device *pdev)
regmap_write(map, data->socdata->sensor_ctrl + REG_SET, regmap_write(map, data->socdata->sensor_ctrl + REG_SET,
data->socdata->power_down_mask); data->socdata->power_down_mask);
data->policy = cpufreq_cpu_get(0);
if (!data->policy) {
pr_debug("%s: CPUFreq policy not found\n", __func__);
return -EPROBE_DEFER;
}
ret = imx_thermal_register_legacy_cooling(data); ret = imx_thermal_register_legacy_cooling(data);
if (ret) { if (ret) {
if (ret == -EPROBE_DEFER)
return ret;
dev_err(&pdev->dev, dev_err(&pdev->dev,
"failed to register cpufreq cooling device: %d\n", ret); "failed to register cpufreq cooling device: %d\n", ret);
return ret; return ret;
...@@ -762,7 +787,7 @@ static int imx_thermal_probe(struct platform_device *pdev) ...@@ -762,7 +787,7 @@ static int imx_thermal_probe(struct platform_device *pdev)
if (ret != -EPROBE_DEFER) if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, dev_err(&pdev->dev,
"failed to get thermal clk: %d\n", ret); "failed to get thermal clk: %d\n", ret);
goto cpufreq_put; goto legacy_cleanup;
} }
/* /*
...@@ -775,7 +800,7 @@ static int imx_thermal_probe(struct platform_device *pdev) ...@@ -775,7 +800,7 @@ static int imx_thermal_probe(struct platform_device *pdev)
ret = clk_prepare_enable(data->thermal_clk); ret = clk_prepare_enable(data->thermal_clk);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to enable thermal clk: %d\n", ret); dev_err(&pdev->dev, "failed to enable thermal clk: %d\n", ret);
goto cpufreq_put; goto legacy_cleanup;
} }
data->tz = thermal_zone_device_register("imx_thermal_zone", data->tz = thermal_zone_device_register("imx_thermal_zone",
...@@ -829,9 +854,8 @@ static int imx_thermal_probe(struct platform_device *pdev) ...@@ -829,9 +854,8 @@ static int imx_thermal_probe(struct platform_device *pdev)
thermal_zone_device_unregister(data->tz); thermal_zone_device_unregister(data->tz);
clk_disable: clk_disable:
clk_disable_unprepare(data->thermal_clk); clk_disable_unprepare(data->thermal_clk);
cpufreq_put: legacy_cleanup:
cpufreq_cooling_unregister(data->cdev); imx_thermal_unregister_legacy_cooling(data);
cpufreq_cpu_put(data->policy);
return ret; return ret;
} }
......
...@@ -708,19 +708,7 @@ static int powerclamp_debug_show(struct seq_file *m, void *unused) ...@@ -708,19 +708,7 @@ static int powerclamp_debug_show(struct seq_file *m, void *unused)
return 0; return 0;
} }
static int powerclamp_debug_open(struct inode *inode, DEFINE_SHOW_ATTRIBUTE(powerclamp_debug);
struct file *file)
{
return single_open(file, powerclamp_debug_show, inode->i_private);
}
static const struct file_operations powerclamp_debug_fops = {
.open = powerclamp_debug_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static inline void powerclamp_create_debug_files(void) static inline void powerclamp_create_debug_files(void)
{ {
......
...@@ -114,6 +114,14 @@ int get_temp_common(struct tsens_device *tmdev, int id, int *temp) ...@@ -114,6 +114,14 @@ int get_temp_common(struct tsens_device *tmdev, int id, int *temp)
} }
static const struct regmap_config tsens_config = { static const struct regmap_config tsens_config = {
.name = "tm",
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
static const struct regmap_config tsens_srot_config = {
.name = "srot",
.reg_bits = 32, .reg_bits = 32,
.val_bits = 32, .val_bits = 32,
.reg_stride = 4, .reg_stride = 4,
...@@ -139,8 +147,8 @@ int __init init_common(struct tsens_device *tmdev) ...@@ -139,8 +147,8 @@ int __init init_common(struct tsens_device *tmdev)
if (IS_ERR(srot_base)) if (IS_ERR(srot_base))
return PTR_ERR(srot_base); return PTR_ERR(srot_base);
tmdev->srot_map = devm_regmap_init_mmio(tmdev->dev, tmdev->srot_map = devm_regmap_init_mmio(tmdev->dev, srot_base,
srot_base, &tsens_config); &tsens_srot_config);
if (IS_ERR(tmdev->srot_map)) if (IS_ERR(tmdev->srot_map))
return PTR_ERR(tmdev->srot_map); return PTR_ERR(tmdev->srot_map);
......
...@@ -112,10 +112,18 @@ static const struct of_device_id rcar_thermal_dt_ids[] = { ...@@ -112,10 +112,18 @@ static const struct of_device_id rcar_thermal_dt_ids[] = {
.compatible = "renesas,rcar-gen2-thermal", .compatible = "renesas,rcar-gen2-thermal",
.data = &rcar_gen2_thermal, .data = &rcar_gen2_thermal,
}, },
{
.compatible = "renesas,thermal-r8a774c0",
.data = &rcar_gen3_thermal,
},
{ {
.compatible = "renesas,thermal-r8a77970", .compatible = "renesas,thermal-r8a77970",
.data = &rcar_gen3_thermal, .data = &rcar_gen3_thermal,
}, },
{
.compatible = "renesas,thermal-r8a77990",
.data = &rcar_gen3_thermal,
},
{ {
.compatible = "renesas,thermal-r8a77995", .compatible = "renesas,thermal-r8a77995",
.data = &rcar_gen3_thermal, .data = &rcar_gen3_thermal,
......
obj-$(CONFIG_ST_THERMAL) := st_thermal.o obj-$(CONFIG_ST_THERMAL) := st_thermal.o
obj-$(CONFIG_ST_THERMAL_SYSCFG) += st_thermal_syscfg.o obj-$(CONFIG_ST_THERMAL_SYSCFG) += st_thermal_syscfg.o
obj-$(CONFIG_ST_THERMAL_MEMMAP) += st_thermal_memmap.o obj-$(CONFIG_ST_THERMAL_MEMMAP) += st_thermal_memmap.o
obj-$(CONFIG_STM32_THERMAL) := stm_thermal.o obj-$(CONFIG_STM32_THERMAL) += stm_thermal.o
\ No newline at end of file
...@@ -803,17 +803,7 @@ static int regs_show(struct seq_file *s, void *data) ...@@ -803,17 +803,7 @@ static int regs_show(struct seq_file *s, void *data)
return 0; return 0;
} }
static int regs_open(struct inode *inode, struct file *file) DEFINE_SHOW_ATTRIBUTE(regs);
{
return single_open(file, regs_show, inode->i_private);
}
static const struct file_operations regs_fops = {
.open = regs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void soctherm_debug_init(struct platform_device *pdev) static void soctherm_debug_init(struct platform_device *pdev)
{ {
......
...@@ -26,7 +26,7 @@ struct gadc_thermal_info { ...@@ -26,7 +26,7 @@ struct gadc_thermal_info {
static int gadc_thermal_adc_to_temp(struct gadc_thermal_info *gti, int val) static int gadc_thermal_adc_to_temp(struct gadc_thermal_info *gti, int val)
{ {
int temp, adc_hi, adc_lo; int temp, temp_hi, temp_lo, adc_hi, adc_lo;
int i; int i;
for (i = 0; i < gti->nlookup_table; i++) { for (i = 0; i < gti->nlookup_table; i++) {
...@@ -36,13 +36,17 @@ static int gadc_thermal_adc_to_temp(struct gadc_thermal_info *gti, int val) ...@@ -36,13 +36,17 @@ static int gadc_thermal_adc_to_temp(struct gadc_thermal_info *gti, int val)
if (i == 0) { if (i == 0) {
temp = gti->lookup_table[0]; temp = gti->lookup_table[0];
} else if (i >= (gti->nlookup_table - 1)) { } else if (i >= gti->nlookup_table) {
temp = gti->lookup_table[2 * (gti->nlookup_table - 1)]; temp = gti->lookup_table[2 * (gti->nlookup_table - 1)];
} else { } else {
adc_hi = gti->lookup_table[2 * i - 1]; adc_hi = gti->lookup_table[2 * i - 1];
adc_lo = gti->lookup_table[2 * i + 1]; adc_lo = gti->lookup_table[2 * i + 1];
temp = gti->lookup_table[2 * i];
temp -= ((val - adc_lo) * 1000) / (adc_hi - adc_lo); temp_hi = gti->lookup_table[2 * i - 2];
temp_lo = gti->lookup_table[2 * i];
temp = temp_hi + mult_frac(temp_lo - temp_hi, val - adc_hi,
adc_lo - adc_hi);
} }
return temp; return temp;
......
...@@ -19,13 +19,13 @@ ...@@ -19,13 +19,13 @@
int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz); int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz);
void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz); void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz);
#else #else
static int static inline int
thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) thermal_add_hwmon_sysfs(struct thermal_zone_device *tz)
{ {
return 0; return 0;
} }
static void static inline void
thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz)
{ {
} }
......
// SPDX-License-Identifier: GPL-2.0
/** /**
* uniphier_thermal.c - Socionext UniPhier thermal driver * uniphier_thermal.c - Socionext UniPhier thermal driver
*
* Copyright 2014 Panasonic Corporation * Copyright 2014 Panasonic Corporation
* Copyright 2016-2017 Socionext Inc. * Copyright 2016-2017 Socionext Inc.
* All rights reserved.
*
* Author: * Author:
* Kunihiko Hayashi <hayashi.kunihiko@socionext.com> * Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 of
* the License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/ */
#include <linux/bitops.h> #include <linux/bitops.h>
......
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