diff --git a/Documentation/devicetree/bindings/arm/marvell/ap806-system-controller.txt b/Documentation/devicetree/bindings/arm/marvell/ap806-system-controller.txt index 3fd21bb7cb37..7b8b8eb0191f 100644 --- a/Documentation/devicetree/bindings/arm/marvell/ap806-system-controller.txt +++ b/Documentation/devicetree/bindings/arm/marvell/ap806-system-controller.txt @@ -114,12 +114,17 @@ Documentation/devicetree/bindings/thermal/thermal.txt The thermal IP can probe the temperature all around the processor. It 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: - compatible: must be one of: * marvell,armada-ap806-thermal - reg: register range associated with the thermal functions. 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 to this IP and represents the channel ID. There is one sensor per channel. O refers to the thermal IP internal channel, while positive @@ -133,6 +138,8 @@ ap_syscon1: system-controller@6f8000 { ap_thermal: thermal-sensor@80 { compatible = "marvell,armada-ap806-thermal"; reg = <0x80 0x10>; + interrupt-parent = <&sei>; + interrupts = <18>; #thermal-sensor-cells = <1>; }; }; diff --git a/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller.txt b/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller.txt index 81ce742d2760..4db4119a6d19 100644 --- a/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller.txt +++ b/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller.txt @@ -199,6 +199,9 @@ Thermal: The thermal IP can probe the temperature all around the processor. It 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 Documentation/devicetree/bindings/thermal/thermal.txt @@ -208,6 +211,11 @@ Required properties: - reg: register range associated with the thermal functions. 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 to this IP and represents the channel ID. There is one sensor per channel. O refers to the thermal IP internal channel. @@ -220,6 +228,7 @@ CP110_LABEL(syscon1): system-controller@6f8000 { CP110_LABEL(thermal): thermal-sensor@70 { compatible = "marvell,armada-cp110-thermal"; reg = <0x70 0x10>; + interrupts-extended = <&CP110_LABEL(icu_sei) 116 IRQ_TYPE_LEVEL_HIGH>; #thermal-sensor-cells = <1>; }; }; diff --git a/Documentation/devicetree/bindings/thermal/rcar-gen3-thermal.txt b/Documentation/devicetree/bindings/thermal/rcar-gen3-thermal.txt index ad9a435afef4..b6ab60f6abbf 100644 --- a/Documentation/devicetree/bindings/thermal/rcar-gen3-thermal.txt +++ b/Documentation/devicetree/bindings/thermal/rcar-gen3-thermal.txt @@ -21,8 +21,7 @@ Required properties: Optional properties: -- interrupts : interrupts routed to the TSC (3 for H3, M3-W, M3-N, - and V3H) +- interrupts : interrupts routed to the TSC (must be 3). - power-domain : Must contain a reference to the power domain. This property is mandatory if the thermal sensor instance is part of a controllable power domain. diff --git a/Documentation/devicetree/bindings/thermal/rcar-thermal.txt b/Documentation/devicetree/bindings/thermal/rcar-thermal.txt index 73e1613d2cb0..196112d23b1e 100644 --- a/Documentation/devicetree/bindings/thermal/rcar-thermal.txt +++ b/Documentation/devicetree/bindings/thermal/rcar-thermal.txt @@ -4,17 +4,19 @@ Required properties: - compatible : "renesas,thermal-", "renesas,rcar-gen2-thermal" (with thermal-zone) or "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: - "renesas,thermal-r8a73a4" (R-Mobile APE6) - "renesas,thermal-r8a7743" (RZ/G1M) - "renesas,thermal-r8a7744" (RZ/G1N) + - "renesas,thermal-r8a774c0" (RZ/G2E) - "renesas,thermal-r8a7779" (R-Car H1) - "renesas,thermal-r8a7790" (R-Car H2) - "renesas,thermal-r8a7791" (R-Car M2-W) - "renesas,thermal-r8a7792" (R-Car V2H) - "renesas,thermal-r8a7793" (R-Car M2-N) - "renesas,thermal-r8a77970" (R-Car V3M) + - "renesas,thermal-r8a77990" (R-Car E3) - "renesas,thermal-r8a77995" (R-Car D3) - reg : Address range of the thermal registers. The 1st reg will be recognized as common register @@ -23,7 +25,7 @@ Required properties: Option properties: - 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): diff --git a/MAINTAINERS b/MAINTAINERS index bc00793a03c8..39e75bbefc3d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9109,6 +9109,11 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/phy/marvell10g.c +MARVELL MVEBU THERMAL DRIVER +M: Miquel Raynal +S: Maintained +F: drivers/thermal/armada_thermal.c + MARVELL MVNETA ETHERNET DRIVER M: Thomas Petazzoni L: netdev@vger.kernel.org diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 5fbfabbf627b..fdc48a1655e7 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -212,7 +212,7 @@ config HISI_THERMAL config IMX_THERMAL 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 MFD_SYSCON depends on OF diff --git a/drivers/thermal/armada_thermal.c b/drivers/thermal/armada_thermal.c index d7105d01859a..53129de59dd9 100644 --- a/drivers/thermal/armada_thermal.c +++ b/drivers/thermal/armada_thermal.c @@ -26,6 +26,11 @@ #include #include #include +#include + +#include "thermal_core.h" + +#define TO_MCELSIUS(c) ((c) * 1000) /* Thermal Manager Control and Status Register */ #define PMU_TDC0_SW_RST_MASK (0x1 << 1) @@ -61,9 +66,13 @@ #define CONTROL1_TSEN_AVG_MASK 0x7 #define CONTROL1_EXT_TSEN_SW_RESET BIT(7) #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_TIMEOUT_US 100000 +#define OVERHEAT_INT_POLL_DELAY_MS 1000 struct armada_thermal_data; @@ -75,7 +84,11 @@ struct armada_thermal_priv { /* serialize temperature reads/updates */ struct mutex update_lock; struct armada_thermal_data *data; + struct thermal_zone_device *overheat_sensor; + int interrupt_source; int current_channel; + long current_threshold; + long current_hysteresis; }; struct armada_thermal_data { @@ -93,12 +106,20 @@ struct armada_thermal_data { /* Register shift and mask to access the sensor temperature */ unsigned int temp_shift; unsigned int temp_mask; + unsigned int thresh_shift; + unsigned int hyst_shift; + unsigned int hyst_mask; u32 is_valid_bit; /* Syscon access */ unsigned int syscon_control0_off; unsigned int syscon_control1_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 */ unsigned int cpu_nr; @@ -272,6 +293,41 @@ static bool armada_is_valid(struct armada_thermal_priv *priv) 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, ®); + + /* Enable DFX Temperature IRQ */ + regmap_read(priv->syscon, data->dfx_irq_mask_off, ®); + 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 |= 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 |= 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 &= ~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 */ 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) /* Do the actual reading */ 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: mutex_unlock(&priv->update_lock); @@ -399,6 +463,123 @@ static const struct thermal_zone_of_device_ops of_ops = { .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 = { .init = armadaxp_init, .temp_shift = 10, @@ -454,6 +635,9 @@ static const struct armada_thermal_data armada_ap806_data = { .is_valid_bit = BIT(16), .temp_shift = 0, .temp_mask = 0x3ff, + .thresh_shift = 3, + .hyst_shift = 19, + .hyst_mask = 0x3, .coef_b = -150000LL, .coef_m = 423ULL, .coef_div = 1, @@ -462,6 +646,11 @@ static const struct armada_thermal_data armada_ap806_data = { .syscon_control0_off = 0x84, .syscon_control1_off = 0x88, .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, }; @@ -470,6 +659,9 @@ static const struct armada_thermal_data armada_cp110_data = { .is_valid_bit = BIT(10), .temp_shift = 0, .temp_mask = 0x3ff, + .thresh_shift = 16, + .hyst_shift = 26, + .hyst_mask = 0x3, .coef_b = 1172499100ULL, .coef_m = 2000096ULL, .coef_div = 4201, @@ -477,6 +669,11 @@ static const struct armada_thermal_data armada_cp110_data = { .syscon_control0_off = 0x70, .syscon_control1_off = 0x74, .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[] = { @@ -543,20 +740,14 @@ static int armada_thermal_probe_legacy(struct platform_device *pdev, priv->syscon = devm_regmap_init_mmio(&pdev->dev, base, &armada_thermal_regmap_config); - if (IS_ERR(priv->syscon)) - return PTR_ERR(priv->syscon); - - return 0; + return PTR_ERR_OR_ZERO(priv->syscon); } static int armada_thermal_probe_syscon(struct platform_device *pdev, struct armada_thermal_priv *priv) { priv->syscon = syscon_node_to_regmap(pdev->dev.parent->of_node); - if (IS_ERR(priv->syscon)) - return PTR_ERR(priv->syscon); - - return 0; + return PTR_ERR_OR_ZERO(priv->syscon); } 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); } +/* + * 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) { struct thermal_zone_device *tz; @@ -597,7 +830,7 @@ static int armada_thermal_probe(struct platform_device *pdev) struct armada_drvdata *drvdata; const struct of_device_id *match; struct armada_thermal_priv *priv; - int sensor_id; + int sensor_id, irq; int ret; match = of_match_device(armada_thermal_id_table, &pdev->dev); @@ -667,6 +900,23 @@ static int armada_thermal_probe(struct platform_device *pdev) drvdata->data.priv = priv; 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 * channel has one sensor. @@ -690,8 +940,20 @@ static int armada_thermal_probe(struct platform_device *pdev) devm_kfree(&pdev->dev, sensor); 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; } diff --git a/drivers/thermal/broadcom/bcm2835_thermal.c b/drivers/thermal/broadcom/bcm2835_thermal.c index b9d90f0ed504..720760cd493f 100644 --- a/drivers/thermal/broadcom/bcm2835_thermal.c +++ b/drivers/thermal/broadcom/bcm2835_thermal.c @@ -18,6 +18,8 @@ #include #include +#include "../thermal_hwmon.h" + #define BCM2835_TS_TSENSCTL 0x00 #define BCM2835_TS_TSENSSTAT 0x04 @@ -266,6 +268,15 @@ static int bcm2835_thermal_probe(struct platform_device *pdev) 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); return 0; diff --git a/drivers/thermal/broadcom/brcmstb_thermal.c b/drivers/thermal/broadcom/brcmstb_thermal.c index e8b1570cc388..65704bdd18e4 100644 --- a/drivers/thermal/broadcom/brcmstb_thermal.c +++ b/drivers/thermal/broadcom/brcmstb_thermal.c @@ -329,7 +329,8 @@ static int brcmstb_thermal_probe(struct platform_device *pdev) priv->dev = &pdev->dev; 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)) { ret = PTR_ERR(thermal); dev_err(&pdev->dev, "could not register sensor: %d\n", ret); @@ -341,40 +342,23 @@ static int brcmstb_thermal_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "could not get IRQ\n"); - ret = irq; - goto err; + return irq; } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, brcmstb_tmon_irq_thread, IRQF_ONESHOT, DRV_NAME, priv); if (ret < 0) { 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"); 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 = { .probe = brcmstb_thermal_probe, - .remove = brcmstb_thermal_exit, .driver = { .name = DRV_NAME, .of_match_table = brcmstb_thermal_id_table, diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index 15661549eb67..bb6754a5342c 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -648,15 +648,24 @@ static const struct of_device_id 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 * CPU node */ 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; + 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)) { data->cdev = cpufreq_cooling_register(data->policy); if (IS_ERR(data->cdev)) { @@ -669,6 +678,24 @@ static int imx_thermal_register_legacy_cooling(struct imx_thermal_data *data) 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) { struct imx_thermal_data *data; @@ -715,9 +742,10 @@ static int imx_thermal_probe(struct platform_device *pdev) if (of_find_property(pdev->dev.of_node, "nvmem-cells", NULL)) { ret = imx_init_from_nvmem_cells(pdev); - if (ret == -EPROBE_DEFER) - return ret; if (ret) { + if (ret == -EPROBE_DEFER) + return ret; + dev_err(&pdev->dev, "failed to init from nvmem: %d\n", ret); return ret; @@ -743,14 +771,11 @@ static int imx_thermal_probe(struct platform_device *pdev) regmap_write(map, data->socdata->sensor_ctrl + REG_SET, 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); if (ret) { + if (ret == -EPROBE_DEFER) + return ret; + dev_err(&pdev->dev, "failed to register cpufreq cooling device: %d\n", ret); return ret; @@ -762,7 +787,7 @@ static int imx_thermal_probe(struct platform_device *pdev) if (ret != -EPROBE_DEFER) dev_err(&pdev->dev, "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) ret = clk_prepare_enable(data->thermal_clk); if (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", @@ -829,9 +854,8 @@ thermal_zone_unregister: thermal_zone_device_unregister(data->tz); clk_disable: clk_disable_unprepare(data->thermal_clk); -cpufreq_put: - cpufreq_cooling_unregister(data->cdev); - cpufreq_cpu_put(data->policy); +legacy_cleanup: + imx_thermal_unregister_legacy_cooling(data); return ret; } diff --git a/drivers/thermal/intel_powerclamp.c b/drivers/thermal/intel_powerclamp.c index cde891c54cde..7571f7c2e7c9 100644 --- a/drivers/thermal/intel_powerclamp.c +++ b/drivers/thermal/intel_powerclamp.c @@ -708,19 +708,7 @@ static int powerclamp_debug_show(struct seq_file *m, void *unused) return 0; } -static int powerclamp_debug_open(struct inode *inode, - 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, -}; +DEFINE_SHOW_ATTRIBUTE(powerclamp_debug); static inline void powerclamp_create_debug_files(void) { diff --git a/drivers/thermal/qcom/tsens-common.c b/drivers/thermal/qcom/tsens-common.c index 3be4be2e0465..78652cac7f3d 100644 --- a/drivers/thermal/qcom/tsens-common.c +++ b/drivers/thermal/qcom/tsens-common.c @@ -114,6 +114,14 @@ int get_temp_common(struct tsens_device *tmdev, int id, int *temp) } 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, .val_bits = 32, .reg_stride = 4, @@ -139,8 +147,8 @@ int __init init_common(struct tsens_device *tmdev) if (IS_ERR(srot_base)) return PTR_ERR(srot_base); - tmdev->srot_map = devm_regmap_init_mmio(tmdev->dev, - srot_base, &tsens_config); + tmdev->srot_map = devm_regmap_init_mmio(tmdev->dev, srot_base, + &tsens_srot_config); if (IS_ERR(tmdev->srot_map)) return PTR_ERR(tmdev->srot_map); diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 8014a207d8d9..97462e9b40d8 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -112,10 +112,18 @@ static const struct of_device_id rcar_thermal_dt_ids[] = { .compatible = "renesas,rcar-gen2-thermal", .data = &rcar_gen2_thermal, }, + { + .compatible = "renesas,thermal-r8a774c0", + .data = &rcar_gen3_thermal, + }, { .compatible = "renesas,thermal-r8a77970", .data = &rcar_gen3_thermal, }, + { + .compatible = "renesas,thermal-r8a77990", + .data = &rcar_gen3_thermal, + }, { .compatible = "renesas,thermal-r8a77995", .data = &rcar_gen3_thermal, diff --git a/drivers/thermal/st/Makefile b/drivers/thermal/st/Makefile index b2b9e9b96296..243ca7881b12 100644 --- a/drivers/thermal/st/Makefile +++ b/drivers/thermal/st/Makefile @@ -1,4 +1,4 @@ obj-$(CONFIG_ST_THERMAL) := st_thermal.o obj-$(CONFIG_ST_THERMAL_SYSCFG) += st_thermal_syscfg.o obj-$(CONFIG_ST_THERMAL_MEMMAP) += st_thermal_memmap.o -obj-$(CONFIG_STM32_THERMAL) := stm_thermal.o \ No newline at end of file +obj-$(CONFIG_STM32_THERMAL) += stm_thermal.o diff --git a/drivers/thermal/tegra/soctherm.c b/drivers/thermal/tegra/soctherm.c index ed28110a3535..45b41b885f49 100644 --- a/drivers/thermal/tegra/soctherm.c +++ b/drivers/thermal/tegra/soctherm.c @@ -803,17 +803,7 @@ static int regs_show(struct seq_file *s, void *data) return 0; } -static int regs_open(struct inode *inode, struct file *file) -{ - 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, -}; +DEFINE_SHOW_ATTRIBUTE(regs); static void soctherm_debug_init(struct platform_device *pdev) { diff --git a/drivers/thermal/thermal-generic-adc.c b/drivers/thermal/thermal-generic-adc.c index bf1c628d4a7a..e22fc60ad36d 100644 --- a/drivers/thermal/thermal-generic-adc.c +++ b/drivers/thermal/thermal-generic-adc.c @@ -26,7 +26,7 @@ struct gadc_thermal_info { 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; 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) if (i == 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)]; } else { adc_hi = 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; diff --git a/drivers/thermal/thermal_hwmon.h b/drivers/thermal/thermal_hwmon.h index 019f6f88224e..a160b9d62dd0 100644 --- a/drivers/thermal/thermal_hwmon.h +++ b/drivers/thermal/thermal_hwmon.h @@ -19,13 +19,13 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz); void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz); #else -static int +static inline int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) { return 0; } -static void +static inline void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) { } diff --git a/drivers/thermal/uniphier_thermal.c b/drivers/thermal/uniphier_thermal.c index 55477d74d591..bba2284412d3 100644 --- a/drivers/thermal/uniphier_thermal.c +++ b/drivers/thermal/uniphier_thermal.c @@ -1,21 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /** * uniphier_thermal.c - Socionext UniPhier thermal driver - * * Copyright 2014 Panasonic Corporation * Copyright 2016-2017 Socionext Inc. - * All rights reserved. - * * Author: * Kunihiko Hayashi - * - * 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