Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'thermal-v5.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux

Pull thermal updates from Daniel Lezcano:

- Add the tegra3 thermal sensor and fix the compilation testing on
tegra by adding a dependency on ARCH_TEGRA along with COMPILE_TEST
(Dmitry Osipenko)

- Fix the error code for the exynos when devm_get_clk() fails (Dan
Carpenter)

- Add the TCC cooling support for AlderLake platform (Sumeet Pawnikar)

- Add support for hardware trip points for the rcar gen3 thermal driver
and store TSC id as unsigned int (Niklas Söderlund)

- Replace the deprecated CPU-hotplug functions get_online_cpus() and
put_online_cpus (Sebastian Andrzej Siewior)

- Add the thermal tools directory in the MAINTAINERS file (Daniel
Lezcano)

- Fix the Makefile and the cross compilation flags for the userspace
'tmon' tool (Rolf Eike Beer)

- Allow to use the IMOK independently from the GDDV on Int340x (Sumeet
Pawnikar)

- Fix the stub thermal_cooling_device_register() function prototype
which does not match the real function (Arnd Bergmann)

- Make the thermal trip point optional in the DT bindings (Maxime
Ripard)

- Fix a typo in a comment in the core code (Geert Uytterhoeven)

- Reduce the verbosity of the trace in the SoC thermal tegra driver
(Dmitry Osipenko)

- Add the support for the LMh (Limit Management hardware) driver on the
QCom platforms (Thara Gopinath)

- Allow processing of HWP interrupt by adding a weak function in the
Intel driver (Srinivas Pandruvada)

- Prevent an abort of the sensor probe is a channel is not used
(Matthias Kaehlcke)

* tag 'thermal-v5.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thermal/linux:
thermal/drivers/qcom/spmi-adc-tm5: Don't abort probing if a sensor is not used
thermal/drivers/intel: Allow processing of HWP interrupt
dt-bindings: thermal: Add dt binding for QCOM LMh
thermal/drivers/qcom: Add support for LMh driver
firmware: qcom_scm: Introduce SCM calls to access LMh
thermal/drivers/tegra-soctherm: Silence message about clamped temperature
thermal: Spelling s/scallbacks/callbacks/
dt-bindings: thermal: Make trips node optional
thermal/core: Fix thermal_cooling_device_register() prototype
thermal/drivers/int340x: Use IMOK independently
tools/thermal/tmon: Add cross compiling support
thermal/tools/tmon: Improve the Makefile
MAINTAINERS: Add missing userspace thermal tools to the thermal section
thermal/drivers/intel_powerclamp: Replace deprecated CPU-hotplug functions.
thermal/drivers/rcar_gen3_thermal: Store TSC id as unsigned int
thermal/drivers/rcar_gen3_thermal: Add support for hardware trip points
drivers/thermal/intel: Add TCC cooling support for AlderLake platform
thermal/drivers/exynos: Fix an error code in exynos_tmu_probe()
thermal/drivers/tegra: Correct compile-testing of drivers
thermal/drivers/tegra: Add driver for Tegra30 thermal sensor

+1221 -24
+82
Documentation/devicetree/bindings/thermal/qcom-lmh.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + # Copyright 2021 Linaro Ltd. 3 + %YAML 1.2 4 + --- 5 + $id: http://devicetree.org/schemas/thermal/qcom-lmh.yaml# 6 + $schema: http://devicetree.org/meta-schemas/core.yaml# 7 + 8 + title: Qualcomm Limits Management Hardware(LMh) 9 + 10 + maintainers: 11 + - Thara Gopinath <thara.gopinath@linaro.org> 12 + 13 + description: 14 + Limits Management Hardware(LMh) is a hardware infrastructure on some 15 + Qualcomm SoCs that can enforce temperature and current limits as 16 + programmed by software for certain IPs like CPU. 17 + 18 + properties: 19 + compatible: 20 + enum: 21 + - qcom,sdm845-lmh 22 + 23 + reg: 24 + items: 25 + - description: core registers 26 + 27 + interrupts: 28 + maxItems: 1 29 + 30 + '#interrupt-cells': 31 + const: 1 32 + 33 + interrupt-controller: true 34 + 35 + cpus: 36 + description: 37 + phandle of the first cpu in the LMh cluster 38 + $ref: /schemas/types.yaml#/definitions/phandle 39 + 40 + qcom,lmh-temp-arm-millicelsius: 41 + description: 42 + An integer expressing temperature threshold at which the LMh thermal 43 + FSM is engaged. 44 + 45 + qcom,lmh-temp-low-millicelsius: 46 + description: 47 + An integer expressing temperature threshold at which the state machine 48 + will attempt to remove frequency throttling. 49 + 50 + qcom,lmh-temp-high-millicelsius: 51 + description: 52 + An integer expressing temperature threshold at which the state machine 53 + will attempt to throttle the frequency. 54 + 55 + required: 56 + - compatible 57 + - reg 58 + - interrupts 59 + - '#interrupt-cells' 60 + - interrupt-controller 61 + - cpus 62 + - qcom,lmh-temp-arm-millicelsius 63 + - qcom,lmh-temp-low-millicelsius 64 + - qcom,lmh-temp-high-millicelsius 65 + 66 + additionalProperties: false 67 + 68 + examples: 69 + - | 70 + #include <dt-bindings/interrupt-controller/arm-gic.h> 71 + 72 + lmh@17d70800 { 73 + compatible = "qcom,sdm845-lmh"; 74 + reg = <0x17d70800 0x400>; 75 + interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; 76 + cpus = <&CPU4>; 77 + qcom,lmh-temp-arm-millicelsius = <65000>; 78 + qcom,lmh-temp-low-millicelsius = <94500>; 79 + qcom,lmh-temp-high-millicelsius = <95000>; 80 + interrupt-controller; 81 + #interrupt-cells = <1>; 82 + };
+1 -1
Documentation/devicetree/bindings/thermal/thermal-zones.yaml
··· 215 215 - polling-delay 216 216 - polling-delay-passive 217 217 - thermal-sensors 218 - - trips 218 + 219 219 additionalProperties: false 220 220 221 221 additionalProperties: false
+1
MAINTAINERS
··· 18566 18566 F: include/linux/cpu_cooling.h 18567 18567 F: include/linux/thermal.h 18568 18568 F: include/uapi/linux/thermal.h 18569 + F: tools/thermal/ 18569 18570 18570 18571 THERMAL DRIVER FOR AMLOGIC SOCS 18571 18572 M: Guillaume La Roque <glaroque@baylibre.com>
+58
drivers/firmware/qcom_scm.c
··· 1147 1147 } 1148 1148 EXPORT_SYMBOL(qcom_scm_qsmmu500_wait_safe_toggle); 1149 1149 1150 + bool qcom_scm_lmh_dcvsh_available(void) 1151 + { 1152 + return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_LMH, QCOM_SCM_LMH_LIMIT_DCVSH); 1153 + } 1154 + EXPORT_SYMBOL(qcom_scm_lmh_dcvsh_available); 1155 + 1156 + int qcom_scm_lmh_profile_change(u32 profile_id) 1157 + { 1158 + struct qcom_scm_desc desc = { 1159 + .svc = QCOM_SCM_SVC_LMH, 1160 + .cmd = QCOM_SCM_LMH_LIMIT_PROFILE_CHANGE, 1161 + .arginfo = QCOM_SCM_ARGS(1, QCOM_SCM_VAL), 1162 + .args[0] = profile_id, 1163 + .owner = ARM_SMCCC_OWNER_SIP, 1164 + }; 1165 + 1166 + return qcom_scm_call(__scm->dev, &desc, NULL); 1167 + } 1168 + EXPORT_SYMBOL(qcom_scm_lmh_profile_change); 1169 + 1170 + int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val, 1171 + u64 limit_node, u32 node_id, u64 version) 1172 + { 1173 + dma_addr_t payload_phys; 1174 + u32 *payload_buf; 1175 + int ret, payload_size = 5 * sizeof(u32); 1176 + 1177 + struct qcom_scm_desc desc = { 1178 + .svc = QCOM_SCM_SVC_LMH, 1179 + .cmd = QCOM_SCM_LMH_LIMIT_DCVSH, 1180 + .arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_VAL, 1181 + QCOM_SCM_VAL, QCOM_SCM_VAL), 1182 + .args[1] = payload_size, 1183 + .args[2] = limit_node, 1184 + .args[3] = node_id, 1185 + .args[4] = version, 1186 + .owner = ARM_SMCCC_OWNER_SIP, 1187 + }; 1188 + 1189 + payload_buf = dma_alloc_coherent(__scm->dev, payload_size, &payload_phys, GFP_KERNEL); 1190 + if (!payload_buf) 1191 + return -ENOMEM; 1192 + 1193 + payload_buf[0] = payload_fn; 1194 + payload_buf[1] = 0; 1195 + payload_buf[2] = payload_reg; 1196 + payload_buf[3] = 1; 1197 + payload_buf[4] = payload_val; 1198 + 1199 + desc.args[0] = payload_phys; 1200 + 1201 + ret = qcom_scm_call(__scm->dev, &desc, NULL); 1202 + 1203 + dma_free_coherent(__scm->dev, payload_size, payload_buf, payload_phys); 1204 + return ret; 1205 + } 1206 + EXPORT_SYMBOL(qcom_scm_lmh_dcvsh); 1207 + 1150 1208 static int qcom_scm_find_dload_address(struct device *dev, u64 *addr) 1151 1209 { 1152 1210 struct device_node *tcsr;
+4
drivers/firmware/qcom_scm.h
··· 114 114 #define QCOM_SCM_SVC_HDCP 0x11 115 115 #define QCOM_SCM_HDCP_INVOKE 0x01 116 116 117 + #define QCOM_SCM_SVC_LMH 0x13 118 + #define QCOM_SCM_LMH_LIMIT_PROFILE_CHANGE 0x01 119 + #define QCOM_SCM_LMH_LIMIT_DCVSH 0x10 120 + 117 121 #define QCOM_SCM_SVC_SMMU_PROGRAM 0x15 118 122 #define QCOM_SCM_SMMU_CONFIG_ERRATA1 0x03 119 123 #define QCOM_SCM_SMMU_CONFIG_ERRATA1_CLIENT_ALL 0x02
+13 -1
drivers/thermal/intel/int340x_thermal/int3400_thermal.c
··· 108 108 NULL 109 109 }; 110 110 111 + static const struct attribute_group imok_attribute_group = { 112 + .attrs = imok_attr, 113 + }; 114 + 111 115 static const struct attribute_group data_attribute_group = { 112 116 .bin_attrs = data_attributes, 113 - .attrs = imok_attr, 114 117 }; 115 118 116 119 static ssize_t available_uuids_show(struct device *dev, ··· 525 522 if (result) 526 523 goto free_rel_misc; 527 524 525 + if (acpi_has_method(priv->adev->handle, "IMOK")) { 526 + result = sysfs_create_group(&pdev->dev.kobj, &imok_attribute_group); 527 + if (result) 528 + goto free_imok; 529 + } 530 + 528 531 if (priv->data_vault) { 529 532 result = sysfs_create_group(&pdev->dev.kobj, 530 533 &data_attribute_group); ··· 554 545 } 555 546 free_uuid: 556 547 sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group); 548 + free_imok: 549 + sysfs_remove_group(&pdev->dev.kobj, &imok_attribute_group); 557 550 free_rel_misc: 558 551 if (!priv->rel_misc_dev_res) 559 552 acpi_thermal_rel_misc_device_remove(priv->adev->handle); ··· 584 573 if (priv->data_vault) 585 574 sysfs_remove_group(&pdev->dev.kobj, &data_attribute_group); 586 575 sysfs_remove_group(&pdev->dev.kobj, &uuid_attribute_group); 576 + sysfs_remove_group(&pdev->dev.kobj, &imok_attribute_group); 587 577 thermal_zone_device_unregister(priv->thermal); 588 578 kfree(priv->data_vault); 589 579 kfree(priv->trts);
+2 -2
drivers/thermal/intel/intel_powerclamp.c
··· 528 528 529 529 set_target_ratio = clamp(set_target_ratio, 0U, MAX_TARGET_RATIO - 1); 530 530 /* prevent cpu hotplug */ 531 - get_online_cpus(); 531 + cpus_read_lock(); 532 532 533 533 /* prefer BSP */ 534 534 control_cpu = 0; ··· 542 542 for_each_online_cpu(cpu) { 543 543 start_power_clamp_worker(cpu); 544 544 } 545 - put_online_cpus(); 545 + cpus_read_unlock(); 546 546 547 547 return 0; 548 548 }
+2
drivers/thermal/intel/intel_tcc_cooling.c
··· 79 79 X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, NULL), 80 80 X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L, NULL), 81 81 X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE, NULL), 82 + X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, NULL), 83 + X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, NULL), 82 84 {} 83 85 }; 84 86
+10
drivers/thermal/qcom/Kconfig
··· 31 31 trip points. The temperature reported by the thermal sensor reflects the 32 32 real time die temperature if an ADC is present or an estimate of the 33 33 temperature based upon the over temperature stage value. 34 + 35 + config QCOM_LMH 36 + tristate "Qualcomm Limits Management Hardware" 37 + depends on ARCH_QCOM 38 + help 39 + This enables initialization of Qualcomm limits management 40 + hardware(LMh). LMh allows for hardware-enforced mitigation for cpus based on 41 + input from temperature and current sensors. On many newer Qualcomm SoCs 42 + LMh is configured in the firmware and this feature need not be enabled. 43 + However, on certain SoCs like sdm845 LMh has to be configured from kernel.
+1
drivers/thermal/qcom/Makefile
··· 5 5 tsens-8960.o 6 6 obj-$(CONFIG_QCOM_SPMI_ADC_TM5) += qcom-spmi-adc-tm5.o 7 7 obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o 8 + obj-$(CONFIG_QCOM_LMH) += lmh.o
+232
drivers/thermal/qcom/lmh.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + 3 + /* 4 + * Copyright (C) 2021, Linaro Limited. All rights reserved. 5 + */ 6 + #include <linux/module.h> 7 + #include <linux/interrupt.h> 8 + #include <linux/irqdomain.h> 9 + #include <linux/err.h> 10 + #include <linux/platform_device.h> 11 + #include <linux/of_platform.h> 12 + #include <linux/slab.h> 13 + #include <linux/qcom_scm.h> 14 + 15 + #define LMH_NODE_DCVS 0x44435653 16 + #define LMH_CLUSTER0_NODE_ID 0x6370302D 17 + #define LMH_CLUSTER1_NODE_ID 0x6370312D 18 + 19 + #define LMH_SUB_FN_THERMAL 0x54484D4C 20 + #define LMH_SUB_FN_CRNT 0x43524E54 21 + #define LMH_SUB_FN_REL 0x52454C00 22 + #define LMH_SUB_FN_BCL 0x42434C00 23 + 24 + #define LMH_ALGO_MODE_ENABLE 0x454E424C 25 + #define LMH_TH_HI_THRESHOLD 0x48494748 26 + #define LMH_TH_LOW_THRESHOLD 0x4C4F5700 27 + #define LMH_TH_ARM_THRESHOLD 0x41524D00 28 + 29 + #define LMH_REG_DCVS_INTR_CLR 0x8 30 + 31 + struct lmh_hw_data { 32 + void __iomem *base; 33 + struct irq_domain *domain; 34 + int irq; 35 + }; 36 + 37 + static irqreturn_t lmh_handle_irq(int hw_irq, void *data) 38 + { 39 + struct lmh_hw_data *lmh_data = data; 40 + int irq = irq_find_mapping(lmh_data->domain, 0); 41 + 42 + /* Call the cpufreq driver to handle the interrupt */ 43 + if (irq) 44 + generic_handle_irq(irq); 45 + 46 + return 0; 47 + } 48 + 49 + static void lmh_enable_interrupt(struct irq_data *d) 50 + { 51 + struct lmh_hw_data *lmh_data = irq_data_get_irq_chip_data(d); 52 + 53 + /* Clear the existing interrupt */ 54 + writel(0xff, lmh_data->base + LMH_REG_DCVS_INTR_CLR); 55 + enable_irq(lmh_data->irq); 56 + } 57 + 58 + static void lmh_disable_interrupt(struct irq_data *d) 59 + { 60 + struct lmh_hw_data *lmh_data = irq_data_get_irq_chip_data(d); 61 + 62 + disable_irq_nosync(lmh_data->irq); 63 + } 64 + 65 + static struct irq_chip lmh_irq_chip = { 66 + .name = "lmh", 67 + .irq_enable = lmh_enable_interrupt, 68 + .irq_disable = lmh_disable_interrupt 69 + }; 70 + 71 + static int lmh_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) 72 + { 73 + struct lmh_hw_data *lmh_data = d->host_data; 74 + 75 + irq_set_chip_and_handler(irq, &lmh_irq_chip, handle_simple_irq); 76 + irq_set_chip_data(irq, lmh_data); 77 + 78 + return 0; 79 + } 80 + 81 + static const struct irq_domain_ops lmh_irq_ops = { 82 + .map = lmh_irq_map, 83 + .xlate = irq_domain_xlate_onecell, 84 + }; 85 + 86 + static int lmh_probe(struct platform_device *pdev) 87 + { 88 + struct device *dev = &pdev->dev; 89 + struct device_node *np = dev->of_node; 90 + struct device_node *cpu_node; 91 + struct lmh_hw_data *lmh_data; 92 + int temp_low, temp_high, temp_arm, cpu_id, ret; 93 + u32 node_id; 94 + 95 + lmh_data = devm_kzalloc(dev, sizeof(*lmh_data), GFP_KERNEL); 96 + if (!lmh_data) 97 + return -ENOMEM; 98 + 99 + lmh_data->base = devm_platform_ioremap_resource(pdev, 0); 100 + if (IS_ERR(lmh_data->base)) 101 + return PTR_ERR(lmh_data->base); 102 + 103 + cpu_node = of_parse_phandle(np, "cpus", 0); 104 + if (!cpu_node) 105 + return -EINVAL; 106 + cpu_id = of_cpu_node_to_id(cpu_node); 107 + of_node_put(cpu_node); 108 + 109 + ret = of_property_read_u32(np, "qcom,lmh-temp-high-millicelsius", &temp_high); 110 + if (ret) { 111 + dev_err(dev, "missing qcom,lmh-temp-high-millicelsius property\n"); 112 + return ret; 113 + } 114 + 115 + ret = of_property_read_u32(np, "qcom,lmh-temp-low-millicelsius", &temp_low); 116 + if (ret) { 117 + dev_err(dev, "missing qcom,lmh-temp-low-millicelsius property\n"); 118 + return ret; 119 + } 120 + 121 + ret = of_property_read_u32(np, "qcom,lmh-temp-arm-millicelsius", &temp_arm); 122 + if (ret) { 123 + dev_err(dev, "missing qcom,lmh-temp-arm-millicelsius property\n"); 124 + return ret; 125 + } 126 + 127 + /* 128 + * Only sdm845 has lmh hardware currently enabled from hlos. If this is needed 129 + * for other platforms, revisit this to check if the <cpu-id, node-id> should be part 130 + * of a dt match table. 131 + */ 132 + if (cpu_id == 0) { 133 + node_id = LMH_CLUSTER0_NODE_ID; 134 + } else if (cpu_id == 4) { 135 + node_id = LMH_CLUSTER1_NODE_ID; 136 + } else { 137 + dev_err(dev, "Wrong CPU id associated with LMh node\n"); 138 + return -EINVAL; 139 + } 140 + 141 + if (!qcom_scm_lmh_dcvsh_available()) 142 + return -EINVAL; 143 + 144 + ret = qcom_scm_lmh_dcvsh(LMH_SUB_FN_CRNT, LMH_ALGO_MODE_ENABLE, 1, 145 + LMH_NODE_DCVS, node_id, 0); 146 + if (ret) 147 + dev_err(dev, "Error %d enabling current subfunction\n", ret); 148 + 149 + ret = qcom_scm_lmh_dcvsh(LMH_SUB_FN_REL, LMH_ALGO_MODE_ENABLE, 1, 150 + LMH_NODE_DCVS, node_id, 0); 151 + if (ret) 152 + dev_err(dev, "Error %d enabling reliability subfunction\n", ret); 153 + 154 + ret = qcom_scm_lmh_dcvsh(LMH_SUB_FN_BCL, LMH_ALGO_MODE_ENABLE, 1, 155 + LMH_NODE_DCVS, node_id, 0); 156 + if (ret) 157 + dev_err(dev, "Error %d enabling BCL subfunction\n", ret); 158 + 159 + ret = qcom_scm_lmh_dcvsh(LMH_SUB_FN_THERMAL, LMH_ALGO_MODE_ENABLE, 1, 160 + LMH_NODE_DCVS, node_id, 0); 161 + if (ret) { 162 + dev_err(dev, "Error %d enabling thermal subfunction\n", ret); 163 + return ret; 164 + } 165 + 166 + ret = qcom_scm_lmh_profile_change(0x1); 167 + if (ret) { 168 + dev_err(dev, "Error %d changing profile\n", ret); 169 + return ret; 170 + } 171 + 172 + /* Set default thermal trips */ 173 + ret = qcom_scm_lmh_dcvsh(LMH_SUB_FN_THERMAL, LMH_TH_ARM_THRESHOLD, temp_arm, 174 + LMH_NODE_DCVS, node_id, 0); 175 + if (ret) { 176 + dev_err(dev, "Error setting thermal ARM threshold%d\n", ret); 177 + return ret; 178 + } 179 + 180 + ret = qcom_scm_lmh_dcvsh(LMH_SUB_FN_THERMAL, LMH_TH_HI_THRESHOLD, temp_high, 181 + LMH_NODE_DCVS, node_id, 0); 182 + if (ret) { 183 + dev_err(dev, "Error setting thermal HI threshold%d\n", ret); 184 + return ret; 185 + } 186 + 187 + ret = qcom_scm_lmh_dcvsh(LMH_SUB_FN_THERMAL, LMH_TH_LOW_THRESHOLD, temp_low, 188 + LMH_NODE_DCVS, node_id, 0); 189 + if (ret) { 190 + dev_err(dev, "Error setting thermal ARM threshold%d\n", ret); 191 + return ret; 192 + } 193 + 194 + lmh_data->irq = platform_get_irq(pdev, 0); 195 + lmh_data->domain = irq_domain_add_linear(np, 1, &lmh_irq_ops, lmh_data); 196 + if (!lmh_data->domain) { 197 + dev_err(dev, "Error adding irq_domain\n"); 198 + return -EINVAL; 199 + } 200 + 201 + /* Disable the irq and let cpufreq enable it when ready to handle the interrupt */ 202 + irq_set_status_flags(lmh_data->irq, IRQ_NOAUTOEN); 203 + ret = devm_request_irq(dev, lmh_data->irq, lmh_handle_irq, 204 + IRQF_ONESHOT | IRQF_NO_SUSPEND, 205 + "lmh-irq", lmh_data); 206 + if (ret) { 207 + dev_err(dev, "Error %d registering irq %x\n", ret, lmh_data->irq); 208 + irq_domain_remove(lmh_data->domain); 209 + return ret; 210 + } 211 + 212 + return 0; 213 + } 214 + 215 + static const struct of_device_id lmh_table[] = { 216 + { .compatible = "qcom,sdm845-lmh", }, 217 + {} 218 + }; 219 + MODULE_DEVICE_TABLE(of, lmh_table); 220 + 221 + static struct platform_driver lmh_driver = { 222 + .probe = lmh_probe, 223 + .driver = { 224 + .name = "qcom-lmh", 225 + .of_match_table = lmh_table, 226 + .suppress_bind_attrs = true, 227 + }, 228 + }; 229 + module_platform_driver(lmh_driver); 230 + 231 + MODULE_LICENSE("GPL v2"); 232 + MODULE_DESCRIPTION("QCOM LMh driver");
+6
drivers/thermal/qcom/qcom-spmi-adc-tm5.c
··· 359 359 &adc_tm->channels[i], 360 360 &adc_tm5_ops); 361 361 if (IS_ERR(tzd)) { 362 + if (PTR_ERR(tzd) == -ENODEV) { 363 + dev_warn(adc_tm->dev, "thermal sensor on channel %d is not used\n", 364 + adc_tm->channels[i].channel); 365 + continue; 366 + } 367 + 362 368 dev_err(adc_tm->dev, "Error registering TZ zone for channel %d: %ld\n", 363 369 adc_tm->channels[i].channel, PTR_ERR(tzd)); 364 370 return PTR_ERR(tzd);
+103 -7
drivers/thermal/rcar_gen3_thermal.c
··· 84 84 struct thermal_zone_device *zone; 85 85 struct equation_coefs coef; 86 86 int tj_t; 87 - int id; /* thermal channel id */ 87 + unsigned int id; /* thermal channel id */ 88 88 }; 89 89 90 90 struct rcar_gen3_thermal_priv { ··· 190 190 return 0; 191 191 } 192 192 193 - static const struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = { 193 + static int rcar_gen3_thermal_mcelsius_to_temp(struct rcar_gen3_thermal_tsc *tsc, 194 + int mcelsius) 195 + { 196 + int celsius, val; 197 + 198 + celsius = DIV_ROUND_CLOSEST(mcelsius, 1000); 199 + if (celsius <= INT_FIXPT(tsc->tj_t)) 200 + val = celsius * tsc->coef.a1 + tsc->coef.b1; 201 + else 202 + val = celsius * tsc->coef.a2 + tsc->coef.b2; 203 + 204 + return INT_FIXPT(val); 205 + } 206 + 207 + static int rcar_gen3_thermal_set_trips(void *devdata, int low, int high) 208 + { 209 + struct rcar_gen3_thermal_tsc *tsc = devdata; 210 + u32 irqmsk = 0; 211 + 212 + if (low != -INT_MAX) { 213 + irqmsk |= IRQ_TEMPD1; 214 + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP1, 215 + rcar_gen3_thermal_mcelsius_to_temp(tsc, low)); 216 + } 217 + 218 + if (high != INT_MAX) { 219 + irqmsk |= IRQ_TEMP2; 220 + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP2, 221 + rcar_gen3_thermal_mcelsius_to_temp(tsc, high)); 222 + } 223 + 224 + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, irqmsk); 225 + 226 + return 0; 227 + } 228 + 229 + static struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = { 194 230 .get_temp = rcar_gen3_thermal_get_temp, 231 + .set_trips = rcar_gen3_thermal_set_trips, 195 232 }; 233 + 234 + static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data) 235 + { 236 + struct rcar_gen3_thermal_priv *priv = data; 237 + unsigned int i; 238 + u32 status; 239 + 240 + for (i = 0; i < priv->num_tscs; i++) { 241 + status = rcar_gen3_thermal_read(priv->tscs[i], REG_GEN3_IRQSTR); 242 + rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQSTR, 0); 243 + if (status) 244 + thermal_zone_device_update(priv->tscs[i]->zone, 245 + THERMAL_EVENT_UNSPECIFIED); 246 + } 247 + 248 + return IRQ_HANDLED; 249 + } 196 250 197 251 static const struct soc_device_attribute r8a7795es1[] = { 198 252 { .soc_id = "r8a7795", .revision = "ES1.*" }, ··· 264 210 265 211 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F); 266 212 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0); 213 + if (tsc->zone->ops->set_trips) 214 + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN, 215 + IRQ_TEMPD1 | IRQ_TEMP2); 267 216 268 217 rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, 269 218 CTSR_PONM | CTSR_AOUT | CTSR_THBGR | CTSR_VMEN); ··· 292 235 293 236 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0); 294 237 rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0); 238 + if (tsc->zone->ops->set_trips) 239 + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN, 240 + IRQ_TEMPD1 | IRQ_TEMP2); 295 241 296 242 reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR); 297 243 reg_val |= THCTR_THSST; ··· 363 303 thermal_remove_hwmon_sysfs(zone); 364 304 } 365 305 306 + static int rcar_gen3_thermal_request_irqs(struct rcar_gen3_thermal_priv *priv, 307 + struct platform_device *pdev) 308 + { 309 + struct device *dev = &pdev->dev; 310 + unsigned int i; 311 + char *irqname; 312 + int ret, irq; 313 + 314 + for (i = 0; i < 2; i++) { 315 + irq = platform_get_irq_optional(pdev, i); 316 + if (irq < 0) 317 + return irq; 318 + 319 + irqname = devm_kasprintf(dev, GFP_KERNEL, "%s:ch%d", 320 + dev_name(dev), i); 321 + if (!irqname) 322 + return -ENOMEM; 323 + 324 + ret = devm_request_threaded_irq(dev, irq, NULL, 325 + rcar_gen3_thermal_irq, 326 + IRQF_ONESHOT, irqname, priv); 327 + if (ret) 328 + return ret; 329 + } 330 + 331 + return 0; 332 + } 333 + 366 334 static int rcar_gen3_thermal_probe(struct platform_device *pdev) 367 335 { 368 336 struct rcar_gen3_thermal_priv *priv; ··· 398 310 const int *ths_tj_1 = of_device_get_match_data(dev); 399 311 struct resource *res; 400 312 struct thermal_zone_device *zone; 401 - int ret, i; 313 + unsigned int i; 314 + int ret; 402 315 403 316 /* default values if FUSEs are missing */ 404 317 /* TODO: Read values from hardware on supported platforms */ ··· 414 325 priv->thermal_init = rcar_gen3_thermal_init_r8a7795es1; 415 326 416 327 platform_set_drvdata(pdev, priv); 328 + 329 + if (rcar_gen3_thermal_request_irqs(priv, pdev)) 330 + rcar_gen3_tz_of_ops.set_trips = NULL; 417 331 418 332 pm_runtime_enable(dev); 419 333 pm_runtime_get_sync(dev); ··· 443 351 444 352 priv->tscs[i] = tsc; 445 353 446 - priv->thermal_init(tsc); 447 - rcar_gen3_thermal_calc_coefs(tsc, ptat, thcodes[i], *ths_tj_1); 448 - 449 354 zone = devm_thermal_zone_of_sensor_register(dev, i, tsc, 450 355 &rcar_gen3_tz_of_ops); 451 356 if (IS_ERR(zone)) { ··· 451 362 goto error_unregister; 452 363 } 453 364 tsc->zone = zone; 365 + 366 + priv->thermal_init(tsc); 367 + rcar_gen3_thermal_calc_coefs(tsc, ptat, thcodes[i], *ths_tj_1); 454 368 455 369 tsc->zone->tzp->no_hwmon = false; 456 370 ret = thermal_add_hwmon_sysfs(tsc->zone); ··· 468 376 if (ret < 0) 469 377 goto error_unregister; 470 378 471 - dev_info(dev, "TSC%d: Loaded %d trip points\n", i, ret); 379 + dev_info(dev, "TSC%u: Loaded %d trip points\n", i, ret); 472 380 } 473 381 474 382 priv->num_tscs = i; ··· 493 401 494 402 for (i = 0; i < priv->num_tscs; i++) { 495 403 struct rcar_gen3_thermal_tsc *tsc = priv->tscs[i]; 404 + struct thermal_zone_device *zone = tsc->zone; 496 405 497 406 priv->thermal_init(tsc); 407 + if (zone->ops->set_trips) 408 + rcar_gen3_thermal_set_trips(tsc, zone->prev_low_trip, 409 + zone->prev_high_trip); 498 410 } 499 411 500 412 return 0;
+1
drivers/thermal/samsung/exynos_tmu.c
··· 1073 1073 data->sclk = devm_clk_get(&pdev->dev, "tmu_sclk"); 1074 1074 if (IS_ERR(data->sclk)) { 1075 1075 dev_err(&pdev->dev, "Failed to get sclk\n"); 1076 + ret = PTR_ERR(data->sclk); 1076 1077 goto err_clk; 1077 1078 } else { 1078 1079 ret = clk_prepare_enable(data->sclk);
+8 -1
drivers/thermal/tegra/Kconfig
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 menu "NVIDIA Tegra thermal drivers" 3 - depends on ARCH_TEGRA 3 + depends on ARCH_TEGRA || COMPILE_TEST 4 4 5 5 config TEGRA_SOCTHERM 6 6 tristate "Tegra SOCTHERM thermal management" ··· 17 17 help 18 18 Enable this option for support for sensing system temperature of NVIDIA 19 19 Tegra systems-on-chip with the BPMP coprocessor (Tegra186). 20 + 21 + config TEGRA30_TSENSOR 22 + tristate "Tegra30 Thermal Sensor" 23 + depends on ARCH_TEGRA_3x_SOC || COMPILE_TEST 24 + help 25 + Enable this option to support thermal management of NVIDIA Tegra30 26 + system-on-chip. 20 27 21 28 endmenu
+1
drivers/thermal/tegra/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 obj-$(CONFIG_TEGRA_SOCTHERM) += tegra-soctherm.o 3 3 obj-$(CONFIG_TEGRA_BPMP_THERMAL) += tegra-bpmp-thermal.o 4 + obj-$(CONFIG_TEGRA30_TSENSOR) += tegra30-tsensor.o 4 5 5 6 tegra-soctherm-y := soctherm.o soctherm-fuse.o 6 7 tegra-soctherm-$(CONFIG_ARCH_TEGRA_124_SOC) += tegra124-soctherm.o
+2 -2
drivers/thermal/tegra/soctherm.c
··· 450 450 451 451 temp = clamp_val(trip_temp, min_low_temp, max_high_temp); 452 452 if (temp != trip_temp) 453 - dev_info(dev, "soctherm: trip temperature %d forced to %d\n", 454 - trip_temp, temp); 453 + dev_dbg(dev, "soctherm: trip temperature %d forced to %d\n", 454 + trip_temp, temp); 455 455 return temp; 456 456 } 457 457
+673
drivers/thermal/tegra/tegra30-tsensor.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Tegra30 SoC Thermal Sensor driver 4 + * 5 + * Based on downstream HWMON driver from NVIDIA. 6 + * Copyright (C) 2011 NVIDIA Corporation 7 + * 8 + * Author: Dmitry Osipenko <digetx@gmail.com> 9 + * Copyright (C) 2021 GRATE-DRIVER project 10 + */ 11 + 12 + #include <linux/bitfield.h> 13 + #include <linux/clk.h> 14 + #include <linux/delay.h> 15 + #include <linux/errno.h> 16 + #include <linux/interrupt.h> 17 + #include <linux/io.h> 18 + #include <linux/iopoll.h> 19 + #include <linux/math.h> 20 + #include <linux/module.h> 21 + #include <linux/of_device.h> 22 + #include <linux/platform_device.h> 23 + #include <linux/pm.h> 24 + #include <linux/reset.h> 25 + #include <linux/slab.h> 26 + #include <linux/thermal.h> 27 + #include <linux/types.h> 28 + 29 + #include <soc/tegra/fuse.h> 30 + 31 + #include "../thermal_core.h" 32 + #include "../thermal_hwmon.h" 33 + 34 + #define TSENSOR_SENSOR0_CONFIG0 0x0 35 + #define TSENSOR_SENSOR0_CONFIG0_SENSOR_STOP BIT(0) 36 + #define TSENSOR_SENSOR0_CONFIG0_HW_FREQ_DIV_EN BIT(1) 37 + #define TSENSOR_SENSOR0_CONFIG0_THERMAL_RST_EN BIT(2) 38 + #define TSENSOR_SENSOR0_CONFIG0_DVFS_EN BIT(3) 39 + #define TSENSOR_SENSOR0_CONFIG0_INTR_OVERFLOW_EN BIT(4) 40 + #define TSENSOR_SENSOR0_CONFIG0_INTR_HW_FREQ_DIV_EN BIT(5) 41 + #define TSENSOR_SENSOR0_CONFIG0_INTR_THERMAL_RST_EN BIT(6) 42 + #define TSENSOR_SENSOR0_CONFIG0_M GENMASK(23, 8) 43 + #define TSENSOR_SENSOR0_CONFIG0_N GENMASK(31, 24) 44 + 45 + #define TSENSOR_SENSOR0_CONFIG1 0x8 46 + #define TSENSOR_SENSOR0_CONFIG1_TH1 GENMASK(15, 0) 47 + #define TSENSOR_SENSOR0_CONFIG1_TH2 GENMASK(31, 16) 48 + 49 + #define TSENSOR_SENSOR0_CONFIG2 0xc 50 + #define TSENSOR_SENSOR0_CONFIG2_TH3 GENMASK(15, 0) 51 + 52 + #define TSENSOR_SENSOR0_STATUS0 0x18 53 + #define TSENSOR_SENSOR0_STATUS0_STATE GENMASK(2, 0) 54 + #define TSENSOR_SENSOR0_STATUS0_INTR BIT(8) 55 + #define TSENSOR_SENSOR0_STATUS0_CURRENT_VALID BIT(9) 56 + 57 + #define TSENSOR_SENSOR0_TS_STATUS1 0x1c 58 + #define TSENSOR_SENSOR0_TS_STATUS1_CURRENT_COUNT GENMASK(31, 16) 59 + 60 + #define TEGRA30_FUSE_TEST_PROG_VER 0x28 61 + 62 + #define TEGRA30_FUSE_TSENSOR_CALIB 0x98 63 + #define TEGRA30_FUSE_TSENSOR_CALIB_LOW GENMASK(15, 0) 64 + #define TEGRA30_FUSE_TSENSOR_CALIB_HIGH GENMASK(31, 16) 65 + 66 + #define TEGRA30_FUSE_SPARE_BIT 0x144 67 + 68 + struct tegra_tsensor; 69 + 70 + struct tegra_tsensor_calibration_data { 71 + int a, b, m, n, p, r; 72 + }; 73 + 74 + struct tegra_tsensor_channel { 75 + void __iomem *regs; 76 + unsigned int id; 77 + struct tegra_tsensor *ts; 78 + struct thermal_zone_device *tzd; 79 + }; 80 + 81 + struct tegra_tsensor { 82 + void __iomem *regs; 83 + bool swap_channels; 84 + struct clk *clk; 85 + struct device *dev; 86 + struct reset_control *rst; 87 + struct tegra_tsensor_channel ch[2]; 88 + struct tegra_tsensor_calibration_data calib; 89 + }; 90 + 91 + static int tegra_tsensor_hw_enable(const struct tegra_tsensor *ts) 92 + { 93 + u32 val; 94 + int err; 95 + 96 + err = reset_control_assert(ts->rst); 97 + if (err) { 98 + dev_err(ts->dev, "failed to assert hardware reset: %d\n", err); 99 + return err; 100 + } 101 + 102 + err = clk_prepare_enable(ts->clk); 103 + if (err) { 104 + dev_err(ts->dev, "failed to enable clock: %d\n", err); 105 + return err; 106 + } 107 + 108 + fsleep(1000); 109 + 110 + err = reset_control_deassert(ts->rst); 111 + if (err) { 112 + dev_err(ts->dev, "failed to deassert hardware reset: %d\n", err); 113 + goto disable_clk; 114 + } 115 + 116 + /* 117 + * Sensors are enabled after reset by default, but not gauging 118 + * until clock counter is programmed. 119 + * 120 + * M: number of reference clock pulses after which every 121 + * temperature / voltage measurement is made 122 + * 123 + * N: number of reference clock counts for which the counter runs 124 + */ 125 + val = FIELD_PREP(TSENSOR_SENSOR0_CONFIG0_M, 12500); 126 + val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG0_N, 255); 127 + 128 + /* apply the same configuration to both channels */ 129 + writel_relaxed(val, ts->regs + 0x40 + TSENSOR_SENSOR0_CONFIG0); 130 + writel_relaxed(val, ts->regs + 0x80 + TSENSOR_SENSOR0_CONFIG0); 131 + 132 + return 0; 133 + 134 + disable_clk: 135 + clk_disable_unprepare(ts->clk); 136 + 137 + return err; 138 + } 139 + 140 + static int tegra_tsensor_hw_disable(const struct tegra_tsensor *ts) 141 + { 142 + int err; 143 + 144 + err = reset_control_assert(ts->rst); 145 + if (err) { 146 + dev_err(ts->dev, "failed to assert hardware reset: %d\n", err); 147 + return err; 148 + } 149 + 150 + clk_disable_unprepare(ts->clk); 151 + 152 + return 0; 153 + } 154 + 155 + static void devm_tegra_tsensor_hw_disable(void *data) 156 + { 157 + const struct tegra_tsensor *ts = data; 158 + 159 + tegra_tsensor_hw_disable(ts); 160 + } 161 + 162 + static int tegra_tsensor_get_temp(void *data, int *temp) 163 + { 164 + const struct tegra_tsensor_channel *tsc = data; 165 + const struct tegra_tsensor *ts = tsc->ts; 166 + int err, c1, c2, c3, c4, counter; 167 + u32 val; 168 + 169 + /* 170 + * Counter will be invalid if hardware is misprogrammed or not enough 171 + * time passed since the time when sensor was enabled. 172 + */ 173 + err = readl_relaxed_poll_timeout(tsc->regs + TSENSOR_SENSOR0_STATUS0, val, 174 + val & TSENSOR_SENSOR0_STATUS0_CURRENT_VALID, 175 + 21 * USEC_PER_MSEC, 176 + 21 * USEC_PER_MSEC * 50); 177 + if (err) { 178 + dev_err_once(ts->dev, "ch%u: counter invalid\n", tsc->id); 179 + return err; 180 + } 181 + 182 + val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_TS_STATUS1); 183 + counter = FIELD_GET(TSENSOR_SENSOR0_TS_STATUS1_CURRENT_COUNT, val); 184 + 185 + /* 186 + * This shouldn't happen with a valid counter status, nevertheless 187 + * lets verify the value since it's in a separate (from status) 188 + * register. 189 + */ 190 + if (counter == 0xffff) { 191 + dev_err_once(ts->dev, "ch%u: counter overflow\n", tsc->id); 192 + return -EINVAL; 193 + } 194 + 195 + /* 196 + * temperature = a * counter + b 197 + * temperature = m * (temperature ^ 2) + n * temperature + p 198 + */ 199 + c1 = DIV_ROUND_CLOSEST(ts->calib.a * counter + ts->calib.b, 1000000); 200 + c1 = c1 ?: 1; 201 + c2 = DIV_ROUND_CLOSEST(ts->calib.p, c1); 202 + c3 = c1 * ts->calib.m; 203 + c4 = ts->calib.n; 204 + 205 + *temp = DIV_ROUND_CLOSEST(c1 * (c2 + c3 + c4), 1000); 206 + 207 + return 0; 208 + } 209 + 210 + static int tegra_tsensor_temp_to_counter(const struct tegra_tsensor *ts, int temp) 211 + { 212 + int c1, c2; 213 + 214 + c1 = DIV_ROUND_CLOSEST(ts->calib.p - temp * 1000, ts->calib.m); 215 + c2 = -ts->calib.r - int_sqrt(ts->calib.r * ts->calib.r - c1); 216 + 217 + return DIV_ROUND_CLOSEST(c2 * 1000000 - ts->calib.b, ts->calib.a); 218 + } 219 + 220 + static int tegra_tsensor_set_trips(void *data, int low, int high) 221 + { 222 + const struct tegra_tsensor_channel *tsc = data; 223 + const struct tegra_tsensor *ts = tsc->ts; 224 + u32 val; 225 + 226 + /* 227 + * TSENSOR doesn't trigger interrupt on the "low" temperature breach, 228 + * hence bail out if high temperature is unspecified. 229 + */ 230 + if (high == INT_MAX) 231 + return 0; 232 + 233 + val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_CONFIG1); 234 + val &= ~TSENSOR_SENSOR0_CONFIG1_TH1; 235 + 236 + high = tegra_tsensor_temp_to_counter(ts, high); 237 + val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG1_TH1, high); 238 + writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_CONFIG1); 239 + 240 + return 0; 241 + } 242 + 243 + static const struct thermal_zone_of_device_ops ops = { 244 + .get_temp = tegra_tsensor_get_temp, 245 + .set_trips = tegra_tsensor_set_trips, 246 + }; 247 + 248 + static bool 249 + tegra_tsensor_handle_channel_interrupt(const struct tegra_tsensor *ts, 250 + unsigned int id) 251 + { 252 + const struct tegra_tsensor_channel *tsc = &ts->ch[id]; 253 + u32 val; 254 + 255 + val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_STATUS0); 256 + writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_STATUS0); 257 + 258 + if (FIELD_GET(TSENSOR_SENSOR0_STATUS0_STATE, val) == 5) 259 + dev_err_ratelimited(ts->dev, "ch%u: counter overflowed\n", id); 260 + 261 + if (!FIELD_GET(TSENSOR_SENSOR0_STATUS0_INTR, val)) 262 + return false; 263 + 264 + thermal_zone_device_update(tsc->tzd, THERMAL_EVENT_UNSPECIFIED); 265 + 266 + return true; 267 + } 268 + 269 + static irqreturn_t tegra_tsensor_isr(int irq, void *data) 270 + { 271 + const struct tegra_tsensor *ts = data; 272 + bool handled = false; 273 + unsigned int i; 274 + 275 + for (i = 0; i < ARRAY_SIZE(ts->ch); i++) 276 + handled |= tegra_tsensor_handle_channel_interrupt(ts, i); 277 + 278 + return handled ? IRQ_HANDLED : IRQ_NONE; 279 + } 280 + 281 + static int tegra_tsensor_disable_hw_channel(const struct tegra_tsensor *ts, 282 + unsigned int id) 283 + { 284 + const struct tegra_tsensor_channel *tsc = &ts->ch[id]; 285 + struct thermal_zone_device *tzd = tsc->tzd; 286 + u32 val; 287 + int err; 288 + 289 + if (!tzd) 290 + goto stop_channel; 291 + 292 + err = thermal_zone_device_disable(tzd); 293 + if (err) { 294 + dev_err(ts->dev, "ch%u: failed to disable zone: %d\n", id, err); 295 + return err; 296 + } 297 + 298 + stop_channel: 299 + /* stop channel gracefully */ 300 + val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_CONFIG0); 301 + val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG0_SENSOR_STOP, 1); 302 + writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_CONFIG0); 303 + 304 + return 0; 305 + } 306 + 307 + static void tegra_tsensor_get_hw_channel_trips(struct thermal_zone_device *tzd, 308 + int *hot_trip, int *crit_trip) 309 + { 310 + unsigned int i; 311 + 312 + /* 313 + * 90C is the maximal critical temperature of all Tegra30 SoC variants, 314 + * use it for the default trip if unspecified in a device-tree. 315 + */ 316 + *hot_trip = 85000; 317 + *crit_trip = 90000; 318 + 319 + for (i = 0; i < tzd->trips; i++) { 320 + enum thermal_trip_type type; 321 + int trip_temp; 322 + 323 + tzd->ops->get_trip_temp(tzd, i, &trip_temp); 324 + tzd->ops->get_trip_type(tzd, i, &type); 325 + 326 + if (type == THERMAL_TRIP_HOT) 327 + *hot_trip = trip_temp; 328 + 329 + if (type == THERMAL_TRIP_CRITICAL) 330 + *crit_trip = trip_temp; 331 + } 332 + 333 + /* clamp hardware trips to the calibration limits */ 334 + *hot_trip = clamp(*hot_trip, 25000, 90000); 335 + 336 + /* 337 + * Kernel will perform a normal system shut down if it will 338 + * see that critical temperature is breached, hence set the 339 + * hardware limit by 5C higher in order to allow system to 340 + * shut down gracefully before sending signal to the Power 341 + * Management controller. 342 + */ 343 + *crit_trip = clamp(*crit_trip + 5000, 25000, 90000); 344 + } 345 + 346 + static int tegra_tsensor_enable_hw_channel(const struct tegra_tsensor *ts, 347 + unsigned int id) 348 + { 349 + const struct tegra_tsensor_channel *tsc = &ts->ch[id]; 350 + struct thermal_zone_device *tzd = tsc->tzd; 351 + int err, hot_trip = 0, crit_trip = 0; 352 + u32 val; 353 + 354 + if (!tzd) { 355 + val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_CONFIG0); 356 + val &= ~TSENSOR_SENSOR0_CONFIG0_SENSOR_STOP; 357 + writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_CONFIG0); 358 + 359 + return 0; 360 + } 361 + 362 + tegra_tsensor_get_hw_channel_trips(tzd, &hot_trip, &crit_trip); 363 + 364 + /* prevent potential racing with tegra_tsensor_set_trips() */ 365 + mutex_lock(&tzd->lock); 366 + 367 + dev_info_once(ts->dev, "ch%u: PMC emergency shutdown trip set to %dC\n", 368 + id, DIV_ROUND_CLOSEST(crit_trip, 1000)); 369 + 370 + hot_trip = tegra_tsensor_temp_to_counter(ts, hot_trip); 371 + crit_trip = tegra_tsensor_temp_to_counter(ts, crit_trip); 372 + 373 + /* program LEVEL2 counter threshold */ 374 + val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_CONFIG1); 375 + val &= ~TSENSOR_SENSOR0_CONFIG1_TH2; 376 + val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG1_TH2, hot_trip); 377 + writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_CONFIG1); 378 + 379 + /* program LEVEL3 counter threshold */ 380 + val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_CONFIG2); 381 + val &= ~TSENSOR_SENSOR0_CONFIG2_TH3; 382 + val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG2_TH3, crit_trip); 383 + writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_CONFIG2); 384 + 385 + /* 386 + * Enable sensor, emergency shutdown, interrupts for level 1/2/3 387 + * breaches and counter overflow condition. 388 + * 389 + * Disable DIV2 throttle for now since we need to figure out how 390 + * to integrate it properly with the thermal framework. 391 + * 392 + * Thermal levels supported by hardware: 393 + * 394 + * Level 0 = cold 395 + * Level 1 = passive cooling (cpufreq DVFS) 396 + * Level 2 = passive cooling assisted by hardware (DIV2) 397 + * Level 3 = emergency shutdown assisted by hardware (PMC) 398 + */ 399 + val = readl_relaxed(tsc->regs + TSENSOR_SENSOR0_CONFIG0); 400 + val &= ~TSENSOR_SENSOR0_CONFIG0_SENSOR_STOP; 401 + val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG0_DVFS_EN, 1); 402 + val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG0_HW_FREQ_DIV_EN, 0); 403 + val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG0_THERMAL_RST_EN, 1); 404 + val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG0_INTR_OVERFLOW_EN, 1); 405 + val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG0_INTR_HW_FREQ_DIV_EN, 1); 406 + val |= FIELD_PREP(TSENSOR_SENSOR0_CONFIG0_INTR_THERMAL_RST_EN, 1); 407 + writel_relaxed(val, tsc->regs + TSENSOR_SENSOR0_CONFIG0); 408 + 409 + mutex_unlock(&tzd->lock); 410 + 411 + err = thermal_zone_device_enable(tzd); 412 + if (err) { 413 + dev_err(ts->dev, "ch%u: failed to enable zone: %d\n", id, err); 414 + return err; 415 + } 416 + 417 + return 0; 418 + } 419 + 420 + static bool tegra_tsensor_fuse_read_spare(unsigned int spare) 421 + { 422 + u32 val = 0; 423 + 424 + tegra_fuse_readl(TEGRA30_FUSE_SPARE_BIT + spare * 4, &val); 425 + 426 + return !!val; 427 + } 428 + 429 + static int tegra_tsensor_nvmem_setup(struct tegra_tsensor *ts) 430 + { 431 + u32 i, ate_ver = 0, cal = 0, t1_25C = 0, t2_90C = 0; 432 + int err, c1_25C, c2_90C; 433 + 434 + err = tegra_fuse_readl(TEGRA30_FUSE_TEST_PROG_VER, &ate_ver); 435 + if (err) { 436 + dev_err_probe(ts->dev, err, "failed to get ATE version\n"); 437 + return err; 438 + } 439 + 440 + if (ate_ver < 8) { 441 + dev_info(ts->dev, "unsupported ATE version: %u\n", ate_ver); 442 + return -ENODEV; 443 + } 444 + 445 + /* 446 + * We have two TSENSOR channels in a two different spots on SoC. 447 + * Second channel provides more accurate data on older SoC versions, 448 + * use it as a primary channel. 449 + */ 450 + if (ate_ver <= 21) { 451 + dev_info_once(ts->dev, 452 + "older ATE version detected, channels remapped\n"); 453 + ts->swap_channels = true; 454 + } 455 + 456 + err = tegra_fuse_readl(TEGRA30_FUSE_TSENSOR_CALIB, &cal); 457 + if (err) { 458 + dev_err(ts->dev, "failed to get calibration data: %d\n", err); 459 + return err; 460 + } 461 + 462 + /* get calibrated counter values for 25C/90C thresholds */ 463 + c1_25C = FIELD_GET(TEGRA30_FUSE_TSENSOR_CALIB_LOW, cal); 464 + c2_90C = FIELD_GET(TEGRA30_FUSE_TSENSOR_CALIB_HIGH, cal); 465 + 466 + /* and calibrated temperatures corresponding to the counter values */ 467 + for (i = 0; i < 7; i++) { 468 + t1_25C |= tegra_tsensor_fuse_read_spare(14 + i) << i; 469 + t1_25C |= tegra_tsensor_fuse_read_spare(21 + i) << i; 470 + 471 + t2_90C |= tegra_tsensor_fuse_read_spare(0 + i) << i; 472 + t2_90C |= tegra_tsensor_fuse_read_spare(7 + i) << i; 473 + } 474 + 475 + if (c2_90C - c1_25C <= t2_90C - t1_25C) { 476 + dev_err(ts->dev, "invalid calibration data: %d %d %u %u\n", 477 + c2_90C, c1_25C, t2_90C, t1_25C); 478 + return -EINVAL; 479 + } 480 + 481 + /* all calibration coefficients are premultiplied by 1000000 */ 482 + 483 + ts->calib.a = DIV_ROUND_CLOSEST((t2_90C - t1_25C) * 1000000, 484 + (c2_90C - c1_25C)); 485 + 486 + ts->calib.b = t1_25C * 1000000 - ts->calib.a * c1_25C; 487 + 488 + if (tegra_sku_info.revision == TEGRA_REVISION_A01) { 489 + ts->calib.m = -2775; 490 + ts->calib.n = 1338811; 491 + ts->calib.p = -7300000; 492 + } else { 493 + ts->calib.m = -3512; 494 + ts->calib.n = 1528943; 495 + ts->calib.p = -11100000; 496 + } 497 + 498 + /* except the coefficient of a reduced quadratic equation */ 499 + ts->calib.r = DIV_ROUND_CLOSEST(ts->calib.n, ts->calib.m * 2); 500 + 501 + dev_info_once(ts->dev, 502 + "calibration: %d %d %u %u ATE ver: %u SoC rev: %u\n", 503 + c2_90C, c1_25C, t2_90C, t1_25C, ate_ver, 504 + tegra_sku_info.revision); 505 + 506 + return 0; 507 + } 508 + 509 + static int tegra_tsensor_register_channel(struct tegra_tsensor *ts, 510 + unsigned int id) 511 + { 512 + struct tegra_tsensor_channel *tsc = &ts->ch[id]; 513 + unsigned int hw_id = ts->swap_channels ? !id : id; 514 + 515 + tsc->ts = ts; 516 + tsc->id = id; 517 + tsc->regs = ts->regs + 0x40 * (hw_id + 1); 518 + 519 + tsc->tzd = devm_thermal_zone_of_sensor_register(ts->dev, id, tsc, &ops); 520 + if (IS_ERR(tsc->tzd)) { 521 + if (PTR_ERR(tsc->tzd) != -ENODEV) 522 + return dev_err_probe(ts->dev, PTR_ERR(tsc->tzd), 523 + "failed to register thermal zone\n"); 524 + 525 + /* 526 + * It's okay if sensor isn't assigned to any thermal zone 527 + * in a device-tree. 528 + */ 529 + tsc->tzd = NULL; 530 + return 0; 531 + } 532 + 533 + if (devm_thermal_add_hwmon_sysfs(tsc->tzd)) 534 + dev_warn(ts->dev, "failed to add hwmon sysfs attributes\n"); 535 + 536 + return 0; 537 + } 538 + 539 + static int tegra_tsensor_probe(struct platform_device *pdev) 540 + { 541 + struct tegra_tsensor *ts; 542 + unsigned int i; 543 + int err, irq; 544 + 545 + ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL); 546 + if (!ts) 547 + return -ENOMEM; 548 + 549 + irq = platform_get_irq(pdev, 0); 550 + if (irq < 0) 551 + return irq; 552 + 553 + ts->dev = &pdev->dev; 554 + platform_set_drvdata(pdev, ts); 555 + 556 + ts->regs = devm_platform_ioremap_resource(pdev, 0); 557 + if (IS_ERR(ts->regs)) 558 + return PTR_ERR(ts->regs); 559 + 560 + ts->clk = devm_clk_get(&pdev->dev, NULL); 561 + if (IS_ERR(ts->clk)) 562 + return dev_err_probe(&pdev->dev, PTR_ERR(ts->clk), 563 + "failed to get clock\n"); 564 + 565 + ts->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); 566 + if (IS_ERR(ts->rst)) 567 + return dev_err_probe(&pdev->dev, PTR_ERR(ts->rst), 568 + "failed to get reset control\n"); 569 + 570 + err = tegra_tsensor_nvmem_setup(ts); 571 + if (err) 572 + return err; 573 + 574 + err = tegra_tsensor_hw_enable(ts); 575 + if (err) 576 + return err; 577 + 578 + err = devm_add_action_or_reset(&pdev->dev, 579 + devm_tegra_tsensor_hw_disable, 580 + ts); 581 + if (err) 582 + return err; 583 + 584 + for (i = 0; i < ARRAY_SIZE(ts->ch); i++) { 585 + err = tegra_tsensor_register_channel(ts, i); 586 + if (err) 587 + return err; 588 + } 589 + 590 + err = devm_request_threaded_irq(&pdev->dev, irq, NULL, 591 + tegra_tsensor_isr, IRQF_ONESHOT, 592 + "tegra_tsensor", ts); 593 + if (err) 594 + return dev_err_probe(&pdev->dev, err, 595 + "failed to request interrupt\n"); 596 + 597 + for (i = 0; i < ARRAY_SIZE(ts->ch); i++) { 598 + err = tegra_tsensor_enable_hw_channel(ts, i); 599 + if (err) 600 + return err; 601 + } 602 + 603 + return 0; 604 + } 605 + 606 + static int __maybe_unused tegra_tsensor_suspend(struct device *dev) 607 + { 608 + struct tegra_tsensor *ts = dev_get_drvdata(dev); 609 + unsigned int i; 610 + int err; 611 + 612 + for (i = 0; i < ARRAY_SIZE(ts->ch); i++) { 613 + err = tegra_tsensor_disable_hw_channel(ts, i); 614 + if (err) 615 + goto enable_channel; 616 + } 617 + 618 + err = tegra_tsensor_hw_disable(ts); 619 + if (err) 620 + goto enable_channel; 621 + 622 + return 0; 623 + 624 + enable_channel: 625 + while (i--) 626 + tegra_tsensor_enable_hw_channel(ts, i); 627 + 628 + return err; 629 + } 630 + 631 + static int __maybe_unused tegra_tsensor_resume(struct device *dev) 632 + { 633 + struct tegra_tsensor *ts = dev_get_drvdata(dev); 634 + unsigned int i; 635 + int err; 636 + 637 + err = tegra_tsensor_hw_enable(ts); 638 + if (err) 639 + return err; 640 + 641 + for (i = 0; i < ARRAY_SIZE(ts->ch); i++) { 642 + err = tegra_tsensor_enable_hw_channel(ts, i); 643 + if (err) 644 + return err; 645 + } 646 + 647 + return 0; 648 + } 649 + 650 + static const struct dev_pm_ops tegra_tsensor_pm_ops = { 651 + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_tsensor_suspend, 652 + tegra_tsensor_resume) 653 + }; 654 + 655 + static const struct of_device_id tegra_tsensor_of_match[] = { 656 + { .compatible = "nvidia,tegra30-tsensor", }, 657 + {}, 658 + }; 659 + MODULE_DEVICE_TABLE(of, tegra_tsensor_of_match); 660 + 661 + static struct platform_driver tegra_tsensor_driver = { 662 + .probe = tegra_tsensor_probe, 663 + .driver = { 664 + .name = "tegra30-tsensor", 665 + .of_match_table = tegra_tsensor_of_match, 666 + .pm = &tegra_tsensor_pm_ops, 667 + }, 668 + }; 669 + module_platform_driver(tegra_tsensor_driver); 670 + 671 + MODULE_DESCRIPTION("NVIDIA Tegra30 Thermal Sensor driver"); 672 + MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>"); 673 + MODULE_LICENSE("GPL");
+14
include/linux/qcom_scm.h
··· 109 109 u32 *resp); 110 110 111 111 extern int qcom_scm_qsmmu500_wait_safe_toggle(bool en); 112 + 113 + extern int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val, 114 + u64 limit_node, u32 node_id, u64 version); 115 + extern int qcom_scm_lmh_profile_change(u32 profile_id); 116 + extern bool qcom_scm_lmh_dcvsh_available(void); 117 + 112 118 #else 113 119 114 120 #include <linux/errno.h> ··· 176 170 177 171 static inline int qcom_scm_qsmmu500_wait_safe_toggle(bool en) 178 172 { return -ENODEV; } 173 + 174 + static inline int qcom_scm_lmh_dcvsh(u32 payload_fn, u32 payload_reg, u32 payload_val, 175 + u64 limit_node, u32 node_id, u64 version) 176 + { return -ENODEV; } 177 + 178 + static inline int qcom_scm_lmh_profile_change(u32 profile_id) { return -ENODEV; } 179 + 180 + static inline bool qcom_scm_lmh_dcvsh_available(void) { return -ENODEV; } 179 181 #endif 180 182 #endif
+4 -3
include/linux/thermal.h
··· 285 285 }; 286 286 287 287 /** 288 - * struct thermal_zone_of_device_ops - scallbacks for handling DT based zones 288 + * struct thermal_zone_of_device_ops - callbacks for handling DT based zones 289 289 * 290 290 * Mandatory: 291 291 * @get_temp: a pointer to a function that reads the sensor temperature. ··· 404 404 struct thermal_zone_device *tz) 405 405 { } 406 406 static inline struct thermal_cooling_device * 407 - thermal_cooling_device_register(char *type, void *devdata, 407 + thermal_cooling_device_register(const char *type, void *devdata, 408 408 const struct thermal_cooling_device_ops *ops) 409 409 { return ERR_PTR(-ENODEV); } 410 410 static inline struct thermal_cooling_device * 411 411 thermal_of_cooling_device_register(struct device_node *np, 412 - char *type, void *devdata, const struct thermal_cooling_device_ops *ops) 412 + const char *type, void *devdata, 413 + const struct thermal_cooling_device_ops *ops) 413 414 { return ERR_PTR(-ENODEV); } 414 415 static inline struct thermal_cooling_device * 415 416 devm_thermal_of_cooling_device_register(struct device *dev,
+3 -7
tools/thermal/tmon/Makefile
··· 10 10 # Add "-fstack-protector" only if toolchain supports it. 11 11 override CFLAGS+= $(call cc-option,-fstack-protector-strong) 12 12 CC?= $(CROSS_COMPILE)gcc 13 - PKG_CONFIG?= pkg-config 13 + PKG_CONFIG?= $(CROSS_COMPILE)pkg-config 14 14 15 15 override CFLAGS+=-D VERSION=\"$(VERSION)\" 16 - LDFLAGS+= 17 16 TARGET=tmon 18 17 19 18 INSTALL_PROGRAM=install -m 755 -p ··· 32 33 $(PKG_CONFIG) --cflags $(STATIC) panel ncurses 2> /dev/null) 33 34 34 35 OBJS = tmon.o tui.o sysfs.o pid.o 35 - OBJS += 36 36 37 37 tmon: $(OBJS) Makefile tmon.h 38 38 $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) -o $(TARGET) $(TMON_LIBS) ··· 40 42 sudo valgrind -v --track-origins=yes --tool=memcheck --leak-check=yes --show-reachable=yes --num-callers=20 --track-fds=yes ./$(TARGET) 1> /dev/null 41 43 42 44 install: 43 - - mkdir -p $(INSTALL_ROOT)/$(BINDIR) 44 - - $(INSTALL_PROGRAM) "$(TARGET)" "$(INSTALL_ROOT)/$(BINDIR)/$(TARGET)" 45 + - $(INSTALL_PROGRAM) -D "$(TARGET)" "$(INSTALL_ROOT)/$(BINDIR)/$(TARGET)" 45 46 46 47 uninstall: 47 48 $(DEL_FILE) "$(INSTALL_ROOT)/$(BINDIR)/$(TARGET)" 48 49 49 50 clean: 50 - find . -name "*.o" | xargs $(DEL_FILE) 51 - rm -f $(TARGET) 51 + rm -f $(TARGET) $(OBJS) 52 52 53 53 dist: 54 54 git tag v$(VERSION)