18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2014 - 2018, NVIDIA CORPORATION. All rights reserved. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: 68c2ecf20Sopenharmony_ci * Mikko Perttunen <mperttunen@nvidia.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This software is licensed under the terms of the GNU General Public 98c2ecf20Sopenharmony_ci * License version 2, as published by the Free Software Foundation, and 108c2ecf20Sopenharmony_ci * may be copied, distributed, and modified under those terms. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, 138c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 148c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 158c2ecf20Sopenharmony_ci * GNU General Public License for more details. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 208c2ecf20Sopenharmony_ci#include <linux/bitops.h> 218c2ecf20Sopenharmony_ci#include <linux/clk.h> 228c2ecf20Sopenharmony_ci#include <linux/delay.h> 238c2ecf20Sopenharmony_ci#include <linux/err.h> 248c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 258c2ecf20Sopenharmony_ci#include <linux/io.h> 268c2ecf20Sopenharmony_ci#include <linux/irq.h> 278c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 288c2ecf20Sopenharmony_ci#include <linux/module.h> 298c2ecf20Sopenharmony_ci#include <linux/of.h> 308c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 318c2ecf20Sopenharmony_ci#include <linux/reset.h> 328c2ecf20Sopenharmony_ci#include <linux/thermal.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#include <dt-bindings/thermal/tegra124-soctherm.h> 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include "../thermal_core.h" 378c2ecf20Sopenharmony_ci#include "soctherm.h" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define SENSOR_CONFIG0 0 408c2ecf20Sopenharmony_ci#define SENSOR_CONFIG0_STOP BIT(0) 418c2ecf20Sopenharmony_ci#define SENSOR_CONFIG0_CPTR_OVER BIT(2) 428c2ecf20Sopenharmony_ci#define SENSOR_CONFIG0_OVER BIT(3) 438c2ecf20Sopenharmony_ci#define SENSOR_CONFIG0_TCALC_OVER BIT(4) 448c2ecf20Sopenharmony_ci#define SENSOR_CONFIG0_TALL_MASK (0xfffff << 8) 458c2ecf20Sopenharmony_ci#define SENSOR_CONFIG0_TALL_SHIFT 8 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define SENSOR_CONFIG1 4 488c2ecf20Sopenharmony_ci#define SENSOR_CONFIG1_TSAMPLE_MASK 0x3ff 498c2ecf20Sopenharmony_ci#define SENSOR_CONFIG1_TSAMPLE_SHIFT 0 508c2ecf20Sopenharmony_ci#define SENSOR_CONFIG1_TIDDQ_EN_MASK (0x3f << 15) 518c2ecf20Sopenharmony_ci#define SENSOR_CONFIG1_TIDDQ_EN_SHIFT 15 528c2ecf20Sopenharmony_ci#define SENSOR_CONFIG1_TEN_COUNT_MASK (0x3f << 24) 538c2ecf20Sopenharmony_ci#define SENSOR_CONFIG1_TEN_COUNT_SHIFT 24 548c2ecf20Sopenharmony_ci#define SENSOR_CONFIG1_TEMP_ENABLE BIT(31) 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* 578c2ecf20Sopenharmony_ci * SENSOR_CONFIG2 is defined in soctherm.h 588c2ecf20Sopenharmony_ci * because, it will be used by tegra_soctherm_fuse.c 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define SENSOR_STATUS0 0xc 628c2ecf20Sopenharmony_ci#define SENSOR_STATUS0_VALID_MASK BIT(31) 638c2ecf20Sopenharmony_ci#define SENSOR_STATUS0_CAPTURE_MASK 0xffff 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define SENSOR_STATUS1 0x10 668c2ecf20Sopenharmony_ci#define SENSOR_STATUS1_TEMP_VALID_MASK BIT(31) 678c2ecf20Sopenharmony_ci#define SENSOR_STATUS1_TEMP_MASK 0xffff 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define READBACK_VALUE_MASK 0xff00 708c2ecf20Sopenharmony_ci#define READBACK_VALUE_SHIFT 8 718c2ecf20Sopenharmony_ci#define READBACK_ADD_HALF BIT(7) 728c2ecf20Sopenharmony_ci#define READBACK_NEGATE BIT(0) 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* 758c2ecf20Sopenharmony_ci * THERMCTL_LEVEL0_GROUP_CPU is defined in soctherm.h 768c2ecf20Sopenharmony_ci * because it will be used by tegraxxx_soctherm.c 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_ci#define THERMCTL_LVL0_CPU0_EN_MASK BIT(8) 798c2ecf20Sopenharmony_ci#define THERMCTL_LVL0_CPU0_CPU_THROT_MASK (0x3 << 5) 808c2ecf20Sopenharmony_ci#define THERMCTL_LVL0_CPU0_CPU_THROT_LIGHT 0x1 818c2ecf20Sopenharmony_ci#define THERMCTL_LVL0_CPU0_CPU_THROT_HEAVY 0x2 828c2ecf20Sopenharmony_ci#define THERMCTL_LVL0_CPU0_GPU_THROT_MASK (0x3 << 3) 838c2ecf20Sopenharmony_ci#define THERMCTL_LVL0_CPU0_GPU_THROT_LIGHT 0x1 848c2ecf20Sopenharmony_ci#define THERMCTL_LVL0_CPU0_GPU_THROT_HEAVY 0x2 858c2ecf20Sopenharmony_ci#define THERMCTL_LVL0_CPU0_MEM_THROT_MASK BIT(2) 868c2ecf20Sopenharmony_ci#define THERMCTL_LVL0_CPU0_STATUS_MASK 0x3 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci#define THERMCTL_LVL0_UP_STATS 0x10 898c2ecf20Sopenharmony_ci#define THERMCTL_LVL0_DN_STATS 0x14 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#define THERMCTL_INTR_STATUS 0x84 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#define TH_INTR_MD0_MASK BIT(25) 948c2ecf20Sopenharmony_ci#define TH_INTR_MU0_MASK BIT(24) 958c2ecf20Sopenharmony_ci#define TH_INTR_GD0_MASK BIT(17) 968c2ecf20Sopenharmony_ci#define TH_INTR_GU0_MASK BIT(16) 978c2ecf20Sopenharmony_ci#define TH_INTR_CD0_MASK BIT(9) 988c2ecf20Sopenharmony_ci#define TH_INTR_CU0_MASK BIT(8) 998c2ecf20Sopenharmony_ci#define TH_INTR_PD0_MASK BIT(1) 1008c2ecf20Sopenharmony_ci#define TH_INTR_PU0_MASK BIT(0) 1018c2ecf20Sopenharmony_ci#define TH_INTR_IGNORE_MASK 0xFCFCFCFC 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#define THERMCTL_STATS_CTL 0x94 1048c2ecf20Sopenharmony_ci#define STATS_CTL_CLR_DN 0x8 1058c2ecf20Sopenharmony_ci#define STATS_CTL_EN_DN 0x4 1068c2ecf20Sopenharmony_ci#define STATS_CTL_CLR_UP 0x2 1078c2ecf20Sopenharmony_ci#define STATS_CTL_EN_UP 0x1 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci#define OC1_CFG 0x310 1108c2ecf20Sopenharmony_ci#define OC1_CFG_LONG_LATENCY_MASK BIT(6) 1118c2ecf20Sopenharmony_ci#define OC1_CFG_HW_RESTORE_MASK BIT(5) 1128c2ecf20Sopenharmony_ci#define OC1_CFG_PWR_GOOD_MASK_MASK BIT(4) 1138c2ecf20Sopenharmony_ci#define OC1_CFG_THROTTLE_MODE_MASK (0x3 << 2) 1148c2ecf20Sopenharmony_ci#define OC1_CFG_ALARM_POLARITY_MASK BIT(1) 1158c2ecf20Sopenharmony_ci#define OC1_CFG_EN_THROTTLE_MASK BIT(0) 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci#define OC1_CNT_THRESHOLD 0x314 1188c2ecf20Sopenharmony_ci#define OC1_THROTTLE_PERIOD 0x318 1198c2ecf20Sopenharmony_ci#define OC1_ALARM_COUNT 0x31c 1208c2ecf20Sopenharmony_ci#define OC1_FILTER 0x320 1218c2ecf20Sopenharmony_ci#define OC1_STATS 0x3a8 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci#define OC_INTR_STATUS 0x39c 1248c2ecf20Sopenharmony_ci#define OC_INTR_ENABLE 0x3a0 1258c2ecf20Sopenharmony_ci#define OC_INTR_DISABLE 0x3a4 1268c2ecf20Sopenharmony_ci#define OC_STATS_CTL 0x3c4 1278c2ecf20Sopenharmony_ci#define OC_STATS_CTL_CLR_ALL 0x2 1288c2ecf20Sopenharmony_ci#define OC_STATS_CTL_EN_ALL 0x1 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci#define OC_INTR_OC1_MASK BIT(0) 1318c2ecf20Sopenharmony_ci#define OC_INTR_OC2_MASK BIT(1) 1328c2ecf20Sopenharmony_ci#define OC_INTR_OC3_MASK BIT(2) 1338c2ecf20Sopenharmony_ci#define OC_INTR_OC4_MASK BIT(3) 1348c2ecf20Sopenharmony_ci#define OC_INTR_OC5_MASK BIT(4) 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci#define THROT_GLOBAL_CFG 0x400 1378c2ecf20Sopenharmony_ci#define THROT_GLOBAL_ENB_MASK BIT(0) 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci#define CPU_PSKIP_STATUS 0x418 1408c2ecf20Sopenharmony_ci#define XPU_PSKIP_STATUS_M_MASK (0xff << 12) 1418c2ecf20Sopenharmony_ci#define XPU_PSKIP_STATUS_N_MASK (0xff << 4) 1428c2ecf20Sopenharmony_ci#define XPU_PSKIP_STATUS_SW_OVERRIDE_MASK BIT(1) 1438c2ecf20Sopenharmony_ci#define XPU_PSKIP_STATUS_ENABLED_MASK BIT(0) 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci#define THROT_PRIORITY_LOCK 0x424 1468c2ecf20Sopenharmony_ci#define THROT_PRIORITY_LOCK_PRIORITY_MASK 0xff 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci#define THROT_STATUS 0x428 1498c2ecf20Sopenharmony_ci#define THROT_STATUS_BREACH_MASK BIT(12) 1508c2ecf20Sopenharmony_ci#define THROT_STATUS_STATE_MASK (0xff << 4) 1518c2ecf20Sopenharmony_ci#define THROT_STATUS_ENABLED_MASK BIT(0) 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci#define THROT_PSKIP_CTRL_LITE_CPU 0x430 1548c2ecf20Sopenharmony_ci#define THROT_PSKIP_CTRL_ENABLE_MASK BIT(31) 1558c2ecf20Sopenharmony_ci#define THROT_PSKIP_CTRL_DIVIDEND_MASK (0xff << 8) 1568c2ecf20Sopenharmony_ci#define THROT_PSKIP_CTRL_DIVISOR_MASK 0xff 1578c2ecf20Sopenharmony_ci#define THROT_PSKIP_CTRL_VECT_GPU_MASK (0x7 << 16) 1588c2ecf20Sopenharmony_ci#define THROT_PSKIP_CTRL_VECT_CPU_MASK (0x7 << 8) 1598c2ecf20Sopenharmony_ci#define THROT_PSKIP_CTRL_VECT2_CPU_MASK 0x7 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci#define THROT_VECT_NONE 0x0 /* 3'b000 */ 1628c2ecf20Sopenharmony_ci#define THROT_VECT_LOW 0x1 /* 3'b001 */ 1638c2ecf20Sopenharmony_ci#define THROT_VECT_MED 0x3 /* 3'b011 */ 1648c2ecf20Sopenharmony_ci#define THROT_VECT_HIGH 0x7 /* 3'b111 */ 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci#define THROT_PSKIP_RAMP_LITE_CPU 0x434 1678c2ecf20Sopenharmony_ci#define THROT_PSKIP_RAMP_SEQ_BYPASS_MODE_MASK BIT(31) 1688c2ecf20Sopenharmony_ci#define THROT_PSKIP_RAMP_DURATION_MASK (0xffff << 8) 1698c2ecf20Sopenharmony_ci#define THROT_PSKIP_RAMP_STEP_MASK 0xff 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci#define THROT_PRIORITY_LITE 0x444 1728c2ecf20Sopenharmony_ci#define THROT_PRIORITY_LITE_PRIO_MASK 0xff 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci#define THROT_DELAY_LITE 0x448 1758c2ecf20Sopenharmony_ci#define THROT_DELAY_LITE_DELAY_MASK 0xff 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci/* car register offsets needed for enabling HW throttling */ 1788c2ecf20Sopenharmony_ci#define CAR_SUPER_CCLKG_DIVIDER 0x36c 1798c2ecf20Sopenharmony_ci#define CDIVG_USE_THERM_CONTROLS_MASK BIT(30) 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* ccroc register offsets needed for enabling HW throttling for Tegra132 */ 1828c2ecf20Sopenharmony_ci#define CCROC_SUPER_CCLKG_DIVIDER 0x024 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci#define CCROC_GLOBAL_CFG 0x148 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci#define CCROC_THROT_PSKIP_RAMP_CPU 0x150 1878c2ecf20Sopenharmony_ci#define CCROC_THROT_PSKIP_RAMP_SEQ_BYPASS_MODE_MASK BIT(31) 1888c2ecf20Sopenharmony_ci#define CCROC_THROT_PSKIP_RAMP_DURATION_MASK (0xffff << 8) 1898c2ecf20Sopenharmony_ci#define CCROC_THROT_PSKIP_RAMP_STEP_MASK 0xff 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci#define CCROC_THROT_PSKIP_CTRL_CPU 0x154 1928c2ecf20Sopenharmony_ci#define CCROC_THROT_PSKIP_CTRL_ENB_MASK BIT(31) 1938c2ecf20Sopenharmony_ci#define CCROC_THROT_PSKIP_CTRL_DIVIDEND_MASK (0xff << 8) 1948c2ecf20Sopenharmony_ci#define CCROC_THROT_PSKIP_CTRL_DIVISOR_MASK 0xff 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* get val from register(r) mask bits(m) */ 1978c2ecf20Sopenharmony_ci#define REG_GET_MASK(r, m) (((r) & (m)) >> (ffs(m) - 1)) 1988c2ecf20Sopenharmony_ci/* set val(v) to mask bits(m) of register(r) */ 1998c2ecf20Sopenharmony_ci#define REG_SET_MASK(r, m, v) (((r) & ~(m)) | \ 2008c2ecf20Sopenharmony_ci (((v) & (m >> (ffs(m) - 1))) << (ffs(m) - 1))) 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/* get dividend from the depth */ 2038c2ecf20Sopenharmony_ci#define THROT_DEPTH_DIVIDEND(depth) ((256 * (100 - (depth)) / 100) - 1) 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/* gk20a nv_therm interface N:3 Mapping. Levels defined in tegra124-soctherm.h 2068c2ecf20Sopenharmony_ci * level vector 2078c2ecf20Sopenharmony_ci * NONE 3'b000 2088c2ecf20Sopenharmony_ci * LOW 3'b001 2098c2ecf20Sopenharmony_ci * MED 3'b011 2108c2ecf20Sopenharmony_ci * HIGH 3'b111 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_ci#define THROT_LEVEL_TO_DEPTH(level) ((0x1 << (level)) - 1) 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci/* get THROT_PSKIP_xxx offset per LIGHT/HEAVY throt and CPU/GPU dev */ 2158c2ecf20Sopenharmony_ci#define THROT_OFFSET 0x30 2168c2ecf20Sopenharmony_ci#define THROT_PSKIP_CTRL(throt, dev) (THROT_PSKIP_CTRL_LITE_CPU + \ 2178c2ecf20Sopenharmony_ci (THROT_OFFSET * throt) + (8 * dev)) 2188c2ecf20Sopenharmony_ci#define THROT_PSKIP_RAMP(throt, dev) (THROT_PSKIP_RAMP_LITE_CPU + \ 2198c2ecf20Sopenharmony_ci (THROT_OFFSET * throt) + (8 * dev)) 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/* get THROT_xxx_CTRL offset per LIGHT/HEAVY throt */ 2228c2ecf20Sopenharmony_ci#define THROT_PRIORITY_CTRL(throt) (THROT_PRIORITY_LITE + \ 2238c2ecf20Sopenharmony_ci (THROT_OFFSET * throt)) 2248c2ecf20Sopenharmony_ci#define THROT_DELAY_CTRL(throt) (THROT_DELAY_LITE + \ 2258c2ecf20Sopenharmony_ci (THROT_OFFSET * throt)) 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci#define ALARM_OFFSET 0x14 2288c2ecf20Sopenharmony_ci#define ALARM_CFG(throt) (OC1_CFG + \ 2298c2ecf20Sopenharmony_ci (ALARM_OFFSET * (throt - THROTTLE_OC1))) 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci#define ALARM_CNT_THRESHOLD(throt) (OC1_CNT_THRESHOLD + \ 2328c2ecf20Sopenharmony_ci (ALARM_OFFSET * (throt - THROTTLE_OC1))) 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci#define ALARM_THROTTLE_PERIOD(throt) (OC1_THROTTLE_PERIOD + \ 2358c2ecf20Sopenharmony_ci (ALARM_OFFSET * (throt - THROTTLE_OC1))) 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci#define ALARM_ALARM_COUNT(throt) (OC1_ALARM_COUNT + \ 2388c2ecf20Sopenharmony_ci (ALARM_OFFSET * (throt - THROTTLE_OC1))) 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci#define ALARM_FILTER(throt) (OC1_FILTER + \ 2418c2ecf20Sopenharmony_ci (ALARM_OFFSET * (throt - THROTTLE_OC1))) 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci#define ALARM_STATS(throt) (OC1_STATS + \ 2448c2ecf20Sopenharmony_ci (4 * (throt - THROTTLE_OC1))) 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci/* get CCROC_THROT_PSKIP_xxx offset per HIGH/MED/LOW vect*/ 2478c2ecf20Sopenharmony_ci#define CCROC_THROT_OFFSET 0x0c 2488c2ecf20Sopenharmony_ci#define CCROC_THROT_PSKIP_CTRL_CPU_REG(vect) (CCROC_THROT_PSKIP_CTRL_CPU + \ 2498c2ecf20Sopenharmony_ci (CCROC_THROT_OFFSET * vect)) 2508c2ecf20Sopenharmony_ci#define CCROC_THROT_PSKIP_RAMP_CPU_REG(vect) (CCROC_THROT_PSKIP_RAMP_CPU + \ 2518c2ecf20Sopenharmony_ci (CCROC_THROT_OFFSET * vect)) 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci/* get THERMCTL_LEVELx offset per CPU/GPU/MEM/TSENSE rg and LEVEL0~3 lv */ 2548c2ecf20Sopenharmony_ci#define THERMCTL_LVL_REGS_SIZE 0x20 2558c2ecf20Sopenharmony_ci#define THERMCTL_LVL_REG(rg, lv) ((rg) + ((lv) * THERMCTL_LVL_REGS_SIZE)) 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci#define OC_THROTTLE_MODE_DISABLED 0 2588c2ecf20Sopenharmony_ci#define OC_THROTTLE_MODE_BRIEF 2 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic const int min_low_temp = -127000; 2618c2ecf20Sopenharmony_cistatic const int max_high_temp = 127000; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cienum soctherm_throttle_id { 2648c2ecf20Sopenharmony_ci THROTTLE_LIGHT = 0, 2658c2ecf20Sopenharmony_ci THROTTLE_HEAVY, 2668c2ecf20Sopenharmony_ci THROTTLE_OC1, 2678c2ecf20Sopenharmony_ci THROTTLE_OC2, 2688c2ecf20Sopenharmony_ci THROTTLE_OC3, 2698c2ecf20Sopenharmony_ci THROTTLE_OC4, 2708c2ecf20Sopenharmony_ci THROTTLE_OC5, /* OC5 is reserved */ 2718c2ecf20Sopenharmony_ci THROTTLE_SIZE, 2728c2ecf20Sopenharmony_ci}; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cienum soctherm_oc_irq_id { 2758c2ecf20Sopenharmony_ci TEGRA_SOC_OC_IRQ_1, 2768c2ecf20Sopenharmony_ci TEGRA_SOC_OC_IRQ_2, 2778c2ecf20Sopenharmony_ci TEGRA_SOC_OC_IRQ_3, 2788c2ecf20Sopenharmony_ci TEGRA_SOC_OC_IRQ_4, 2798c2ecf20Sopenharmony_ci TEGRA_SOC_OC_IRQ_5, 2808c2ecf20Sopenharmony_ci TEGRA_SOC_OC_IRQ_MAX, 2818c2ecf20Sopenharmony_ci}; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cienum soctherm_throttle_dev_id { 2848c2ecf20Sopenharmony_ci THROTTLE_DEV_CPU = 0, 2858c2ecf20Sopenharmony_ci THROTTLE_DEV_GPU, 2868c2ecf20Sopenharmony_ci THROTTLE_DEV_SIZE, 2878c2ecf20Sopenharmony_ci}; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic const char *const throt_names[] = { 2908c2ecf20Sopenharmony_ci [THROTTLE_LIGHT] = "light", 2918c2ecf20Sopenharmony_ci [THROTTLE_HEAVY] = "heavy", 2928c2ecf20Sopenharmony_ci [THROTTLE_OC1] = "oc1", 2938c2ecf20Sopenharmony_ci [THROTTLE_OC2] = "oc2", 2948c2ecf20Sopenharmony_ci [THROTTLE_OC3] = "oc3", 2958c2ecf20Sopenharmony_ci [THROTTLE_OC4] = "oc4", 2968c2ecf20Sopenharmony_ci [THROTTLE_OC5] = "oc5", 2978c2ecf20Sopenharmony_ci}; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistruct tegra_soctherm; 3008c2ecf20Sopenharmony_cistruct tegra_thermctl_zone { 3018c2ecf20Sopenharmony_ci void __iomem *reg; 3028c2ecf20Sopenharmony_ci struct device *dev; 3038c2ecf20Sopenharmony_ci struct tegra_soctherm *ts; 3048c2ecf20Sopenharmony_ci struct thermal_zone_device *tz; 3058c2ecf20Sopenharmony_ci const struct tegra_tsensor_group *sg; 3068c2ecf20Sopenharmony_ci}; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistruct soctherm_oc_cfg { 3098c2ecf20Sopenharmony_ci u32 active_low; 3108c2ecf20Sopenharmony_ci u32 throt_period; 3118c2ecf20Sopenharmony_ci u32 alarm_cnt_thresh; 3128c2ecf20Sopenharmony_ci u32 alarm_filter; 3138c2ecf20Sopenharmony_ci u32 mode; 3148c2ecf20Sopenharmony_ci bool intr_en; 3158c2ecf20Sopenharmony_ci}; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistruct soctherm_throt_cfg { 3188c2ecf20Sopenharmony_ci const char *name; 3198c2ecf20Sopenharmony_ci unsigned int id; 3208c2ecf20Sopenharmony_ci u8 priority; 3218c2ecf20Sopenharmony_ci u8 cpu_throt_level; 3228c2ecf20Sopenharmony_ci u32 cpu_throt_depth; 3238c2ecf20Sopenharmony_ci u32 gpu_throt_level; 3248c2ecf20Sopenharmony_ci struct soctherm_oc_cfg oc_cfg; 3258c2ecf20Sopenharmony_ci struct thermal_cooling_device *cdev; 3268c2ecf20Sopenharmony_ci bool init; 3278c2ecf20Sopenharmony_ci}; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistruct tegra_soctherm { 3308c2ecf20Sopenharmony_ci struct reset_control *reset; 3318c2ecf20Sopenharmony_ci struct clk *clock_tsensor; 3328c2ecf20Sopenharmony_ci struct clk *clock_soctherm; 3338c2ecf20Sopenharmony_ci void __iomem *regs; 3348c2ecf20Sopenharmony_ci void __iomem *clk_regs; 3358c2ecf20Sopenharmony_ci void __iomem *ccroc_regs; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci int thermal_irq; 3388c2ecf20Sopenharmony_ci int edp_irq; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci u32 *calib; 3418c2ecf20Sopenharmony_ci struct thermal_zone_device **thermctl_tzs; 3428c2ecf20Sopenharmony_ci struct tegra_soctherm_soc *soc; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci struct soctherm_throt_cfg throt_cfgs[THROTTLE_SIZE]; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci struct dentry *debugfs_dir; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci struct mutex thermctl_lock; 3498c2ecf20Sopenharmony_ci}; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistruct soctherm_oc_irq_chip_data { 3528c2ecf20Sopenharmony_ci struct mutex irq_lock; /* serialize OC IRQs */ 3538c2ecf20Sopenharmony_ci struct irq_chip irq_chip; 3548c2ecf20Sopenharmony_ci struct irq_domain *domain; 3558c2ecf20Sopenharmony_ci int irq_enable; 3568c2ecf20Sopenharmony_ci}; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic struct soctherm_oc_irq_chip_data soc_irq_cdata; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci/** 3618c2ecf20Sopenharmony_ci * ccroc_writel() - writes a value to a CCROC register 3628c2ecf20Sopenharmony_ci * @ts: pointer to a struct tegra_soctherm 3638c2ecf20Sopenharmony_ci * @value: the value to write 3648c2ecf20Sopenharmony_ci * @reg: the register offset 3658c2ecf20Sopenharmony_ci * 3668c2ecf20Sopenharmony_ci * Writes @v to @reg. No return value. 3678c2ecf20Sopenharmony_ci */ 3688c2ecf20Sopenharmony_cistatic inline void ccroc_writel(struct tegra_soctherm *ts, u32 value, u32 reg) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci writel(value, (ts->ccroc_regs + reg)); 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci/** 3748c2ecf20Sopenharmony_ci * ccroc_readl() - reads specified register from CCROC IP block 3758c2ecf20Sopenharmony_ci * @ts: pointer to a struct tegra_soctherm 3768c2ecf20Sopenharmony_ci * @reg: register address to be read 3778c2ecf20Sopenharmony_ci * 3788c2ecf20Sopenharmony_ci * Return: the value of the register 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_cistatic inline u32 ccroc_readl(struct tegra_soctherm *ts, u32 reg) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci return readl(ts->ccroc_regs + reg); 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic void enable_tsensor(struct tegra_soctherm *tegra, unsigned int i) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci const struct tegra_tsensor *sensor = &tegra->soc->tsensors[i]; 3888c2ecf20Sopenharmony_ci void __iomem *base = tegra->regs + sensor->base; 3898c2ecf20Sopenharmony_ci unsigned int val; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci val = sensor->config->tall << SENSOR_CONFIG0_TALL_SHIFT; 3928c2ecf20Sopenharmony_ci writel(val, base + SENSOR_CONFIG0); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci val = (sensor->config->tsample - 1) << SENSOR_CONFIG1_TSAMPLE_SHIFT; 3958c2ecf20Sopenharmony_ci val |= sensor->config->tiddq_en << SENSOR_CONFIG1_TIDDQ_EN_SHIFT; 3968c2ecf20Sopenharmony_ci val |= sensor->config->ten_count << SENSOR_CONFIG1_TEN_COUNT_SHIFT; 3978c2ecf20Sopenharmony_ci val |= SENSOR_CONFIG1_TEMP_ENABLE; 3988c2ecf20Sopenharmony_ci writel(val, base + SENSOR_CONFIG1); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci writel(tegra->calib[i], base + SENSOR_CONFIG2); 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci/* 4048c2ecf20Sopenharmony_ci * Translate from soctherm readback format to millicelsius. 4058c2ecf20Sopenharmony_ci * The soctherm readback format in bits is as follows: 4068c2ecf20Sopenharmony_ci * TTTTTTTT H______N 4078c2ecf20Sopenharmony_ci * where T's contain the temperature in Celsius, 4088c2ecf20Sopenharmony_ci * H denotes an addition of 0.5 Celsius and N denotes negation 4098c2ecf20Sopenharmony_ci * of the final value. 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_cistatic int translate_temp(u16 val) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci int t; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci t = ((val & READBACK_VALUE_MASK) >> READBACK_VALUE_SHIFT) * 1000; 4168c2ecf20Sopenharmony_ci if (val & READBACK_ADD_HALF) 4178c2ecf20Sopenharmony_ci t += 500; 4188c2ecf20Sopenharmony_ci if (val & READBACK_NEGATE) 4198c2ecf20Sopenharmony_ci t *= -1; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci return t; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic int tegra_thermctl_get_temp(void *data, int *out_temp) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct tegra_thermctl_zone *zone = data; 4278c2ecf20Sopenharmony_ci u32 val; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci val = readl(zone->reg); 4308c2ecf20Sopenharmony_ci val = REG_GET_MASK(val, zone->sg->sensor_temp_mask); 4318c2ecf20Sopenharmony_ci *out_temp = translate_temp(val); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci return 0; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci/** 4378c2ecf20Sopenharmony_ci * enforce_temp_range() - check and enforce temperature range [min, max] 4388c2ecf20Sopenharmony_ci * @dev: struct device * of the SOC_THERM instance 4398c2ecf20Sopenharmony_ci * @trip_temp: the trip temperature to check 4408c2ecf20Sopenharmony_ci * 4418c2ecf20Sopenharmony_ci * Checks and enforces the permitted temperature range that SOC_THERM 4428c2ecf20Sopenharmony_ci * HW can support This is 4438c2ecf20Sopenharmony_ci * done while taking care of precision. 4448c2ecf20Sopenharmony_ci * 4458c2ecf20Sopenharmony_ci * Return: The precision adjusted capped temperature in millicelsius. 4468c2ecf20Sopenharmony_ci */ 4478c2ecf20Sopenharmony_cistatic int enforce_temp_range(struct device *dev, int trip_temp) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci int temp; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci temp = clamp_val(trip_temp, min_low_temp, max_high_temp); 4528c2ecf20Sopenharmony_ci if (temp != trip_temp) 4538c2ecf20Sopenharmony_ci dev_info(dev, "soctherm: trip temperature %d forced to %d\n", 4548c2ecf20Sopenharmony_ci trip_temp, temp); 4558c2ecf20Sopenharmony_ci return temp; 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci/** 4598c2ecf20Sopenharmony_ci * thermtrip_program() - Configures the hardware to shut down the 4608c2ecf20Sopenharmony_ci * system if a given sensor group reaches a given temperature 4618c2ecf20Sopenharmony_ci * @dev: ptr to the struct device for the SOC_THERM IP block 4628c2ecf20Sopenharmony_ci * @sg: pointer to the sensor group to set the thermtrip temperature for 4638c2ecf20Sopenharmony_ci * @trip_temp: the temperature in millicelsius to trigger the thermal trip at 4648c2ecf20Sopenharmony_ci * 4658c2ecf20Sopenharmony_ci * Sets the thermal trip threshold of the given sensor group to be the 4668c2ecf20Sopenharmony_ci * @trip_temp. If this threshold is crossed, the hardware will shut 4678c2ecf20Sopenharmony_ci * down. 4688c2ecf20Sopenharmony_ci * 4698c2ecf20Sopenharmony_ci * Note that, although @trip_temp is specified in millicelsius, the 4708c2ecf20Sopenharmony_ci * hardware is programmed in degrees Celsius. 4718c2ecf20Sopenharmony_ci * 4728c2ecf20Sopenharmony_ci * Return: 0 upon success, or %-EINVAL upon failure. 4738c2ecf20Sopenharmony_ci */ 4748c2ecf20Sopenharmony_cistatic int thermtrip_program(struct device *dev, 4758c2ecf20Sopenharmony_ci const struct tegra_tsensor_group *sg, 4768c2ecf20Sopenharmony_ci int trip_temp) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci struct tegra_soctherm *ts = dev_get_drvdata(dev); 4798c2ecf20Sopenharmony_ci int temp; 4808c2ecf20Sopenharmony_ci u32 r; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (!sg || !sg->thermtrip_threshold_mask) 4838c2ecf20Sopenharmony_ci return -EINVAL; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci temp = enforce_temp_range(dev, trip_temp) / ts->soc->thresh_grain; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci r = readl(ts->regs + THERMCTL_THERMTRIP_CTL); 4888c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, sg->thermtrip_threshold_mask, temp); 4898c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, sg->thermtrip_enable_mask, 1); 4908c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, sg->thermtrip_any_en_mask, 0); 4918c2ecf20Sopenharmony_ci writel(r, ts->regs + THERMCTL_THERMTRIP_CTL); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci return 0; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci/** 4978c2ecf20Sopenharmony_ci * throttrip_program() - Configures the hardware to throttle the 4988c2ecf20Sopenharmony_ci * pulse if a given sensor group reaches a given temperature 4998c2ecf20Sopenharmony_ci * @dev: ptr to the struct device for the SOC_THERM IP block 5008c2ecf20Sopenharmony_ci * @sg: pointer to the sensor group to set the thermtrip temperature for 5018c2ecf20Sopenharmony_ci * @stc: pointer to the throttle need to be triggered 5028c2ecf20Sopenharmony_ci * @trip_temp: the temperature in millicelsius to trigger the thermal trip at 5038c2ecf20Sopenharmony_ci * 5048c2ecf20Sopenharmony_ci * Sets the thermal trip threshold and throttle event of the given sensor 5058c2ecf20Sopenharmony_ci * group. If this threshold is crossed, the hardware will trigger the 5068c2ecf20Sopenharmony_ci * throttle. 5078c2ecf20Sopenharmony_ci * 5088c2ecf20Sopenharmony_ci * Note that, although @trip_temp is specified in millicelsius, the 5098c2ecf20Sopenharmony_ci * hardware is programmed in degrees Celsius. 5108c2ecf20Sopenharmony_ci * 5118c2ecf20Sopenharmony_ci * Return: 0 upon success, or %-EINVAL upon failure. 5128c2ecf20Sopenharmony_ci */ 5138c2ecf20Sopenharmony_cistatic int throttrip_program(struct device *dev, 5148c2ecf20Sopenharmony_ci const struct tegra_tsensor_group *sg, 5158c2ecf20Sopenharmony_ci struct soctherm_throt_cfg *stc, 5168c2ecf20Sopenharmony_ci int trip_temp) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci struct tegra_soctherm *ts = dev_get_drvdata(dev); 5198c2ecf20Sopenharmony_ci int temp, cpu_throt, gpu_throt; 5208c2ecf20Sopenharmony_ci unsigned int throt; 5218c2ecf20Sopenharmony_ci u32 r, reg_off; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (!sg || !stc || !stc->init) 5248c2ecf20Sopenharmony_ci return -EINVAL; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci temp = enforce_temp_range(dev, trip_temp) / ts->soc->thresh_grain; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci /* Hardcode LIGHT on LEVEL1 and HEAVY on LEVEL2 */ 5298c2ecf20Sopenharmony_ci throt = stc->id; 5308c2ecf20Sopenharmony_ci reg_off = THERMCTL_LVL_REG(sg->thermctl_lvl0_offset, throt + 1); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (throt == THROTTLE_LIGHT) { 5338c2ecf20Sopenharmony_ci cpu_throt = THERMCTL_LVL0_CPU0_CPU_THROT_LIGHT; 5348c2ecf20Sopenharmony_ci gpu_throt = THERMCTL_LVL0_CPU0_GPU_THROT_LIGHT; 5358c2ecf20Sopenharmony_ci } else { 5368c2ecf20Sopenharmony_ci cpu_throt = THERMCTL_LVL0_CPU0_CPU_THROT_HEAVY; 5378c2ecf20Sopenharmony_ci gpu_throt = THERMCTL_LVL0_CPU0_GPU_THROT_HEAVY; 5388c2ecf20Sopenharmony_ci if (throt != THROTTLE_HEAVY) 5398c2ecf20Sopenharmony_ci dev_warn(dev, 5408c2ecf20Sopenharmony_ci "invalid throt id %d - assuming HEAVY", 5418c2ecf20Sopenharmony_ci throt); 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci r = readl(ts->regs + reg_off); 5458c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, sg->thermctl_lvl0_up_thresh_mask, temp); 5468c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, sg->thermctl_lvl0_dn_thresh_mask, temp); 5478c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_CPU_THROT_MASK, cpu_throt); 5488c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_GPU_THROT_MASK, gpu_throt); 5498c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_EN_MASK, 1); 5508c2ecf20Sopenharmony_ci writel(r, ts->regs + reg_off); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci return 0; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic struct soctherm_throt_cfg * 5568c2ecf20Sopenharmony_cifind_throttle_cfg_by_name(struct tegra_soctherm *ts, const char *name) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci unsigned int i; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci for (i = 0; ts->throt_cfgs[i].name; i++) 5618c2ecf20Sopenharmony_ci if (!strcmp(ts->throt_cfgs[i].name, name)) 5628c2ecf20Sopenharmony_ci return &ts->throt_cfgs[i]; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci return NULL; 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic int tsensor_group_thermtrip_get(struct tegra_soctherm *ts, int id) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci int i, temp = min_low_temp; 5708c2ecf20Sopenharmony_ci struct tsensor_group_thermtrips *tt = ts->soc->thermtrips; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if (id >= TEGRA124_SOCTHERM_SENSOR_NUM) 5738c2ecf20Sopenharmony_ci return temp; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (tt) { 5768c2ecf20Sopenharmony_ci for (i = 0; i < ts->soc->num_ttgs; i++) { 5778c2ecf20Sopenharmony_ci if (tt[i].id == id) 5788c2ecf20Sopenharmony_ci return tt[i].temp; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci return temp; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic int tegra_thermctl_set_trip_temp(void *data, int trip, int temp) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci struct tegra_thermctl_zone *zone = data; 5888c2ecf20Sopenharmony_ci struct thermal_zone_device *tz = zone->tz; 5898c2ecf20Sopenharmony_ci struct tegra_soctherm *ts = zone->ts; 5908c2ecf20Sopenharmony_ci const struct tegra_tsensor_group *sg = zone->sg; 5918c2ecf20Sopenharmony_ci struct device *dev = zone->dev; 5928c2ecf20Sopenharmony_ci enum thermal_trip_type type; 5938c2ecf20Sopenharmony_ci int ret; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (!tz) 5968c2ecf20Sopenharmony_ci return -EINVAL; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci ret = tz->ops->get_trip_type(tz, trip, &type); 5998c2ecf20Sopenharmony_ci if (ret) 6008c2ecf20Sopenharmony_ci return ret; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci if (type == THERMAL_TRIP_CRITICAL) { 6038c2ecf20Sopenharmony_ci /* 6048c2ecf20Sopenharmony_ci * If thermtrips property is set in DT, 6058c2ecf20Sopenharmony_ci * doesn't need to program critical type trip to HW, 6068c2ecf20Sopenharmony_ci * if not, program critical trip to HW. 6078c2ecf20Sopenharmony_ci */ 6088c2ecf20Sopenharmony_ci if (min_low_temp == tsensor_group_thermtrip_get(ts, sg->id)) 6098c2ecf20Sopenharmony_ci return thermtrip_program(dev, sg, temp); 6108c2ecf20Sopenharmony_ci else 6118c2ecf20Sopenharmony_ci return 0; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci } else if (type == THERMAL_TRIP_HOT) { 6148c2ecf20Sopenharmony_ci int i; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci for (i = 0; i < THROTTLE_SIZE; i++) { 6178c2ecf20Sopenharmony_ci struct thermal_cooling_device *cdev; 6188c2ecf20Sopenharmony_ci struct soctherm_throt_cfg *stc; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if (!ts->throt_cfgs[i].init) 6218c2ecf20Sopenharmony_ci continue; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci cdev = ts->throt_cfgs[i].cdev; 6248c2ecf20Sopenharmony_ci if (get_thermal_instance(tz, cdev, trip)) 6258c2ecf20Sopenharmony_ci stc = find_throttle_cfg_by_name(ts, cdev->type); 6268c2ecf20Sopenharmony_ci else 6278c2ecf20Sopenharmony_ci continue; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return throttrip_program(dev, sg, stc, temp); 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci return 0; 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cistatic int tegra_thermctl_get_trend(void *data, int trip, 6378c2ecf20Sopenharmony_ci enum thermal_trend *trend) 6388c2ecf20Sopenharmony_ci{ 6398c2ecf20Sopenharmony_ci struct tegra_thermctl_zone *zone = data; 6408c2ecf20Sopenharmony_ci struct thermal_zone_device *tz = zone->tz; 6418c2ecf20Sopenharmony_ci int trip_temp, temp, last_temp, ret; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (!tz) 6448c2ecf20Sopenharmony_ci return -EINVAL; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci ret = tz->ops->get_trip_temp(zone->tz, trip, &trip_temp); 6478c2ecf20Sopenharmony_ci if (ret) 6488c2ecf20Sopenharmony_ci return ret; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci temp = READ_ONCE(tz->temperature); 6518c2ecf20Sopenharmony_ci last_temp = READ_ONCE(tz->last_temperature); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci if (temp > trip_temp) { 6548c2ecf20Sopenharmony_ci if (temp >= last_temp) 6558c2ecf20Sopenharmony_ci *trend = THERMAL_TREND_RAISING; 6568c2ecf20Sopenharmony_ci else 6578c2ecf20Sopenharmony_ci *trend = THERMAL_TREND_STABLE; 6588c2ecf20Sopenharmony_ci } else if (temp < trip_temp) { 6598c2ecf20Sopenharmony_ci *trend = THERMAL_TREND_DROPPING; 6608c2ecf20Sopenharmony_ci } else { 6618c2ecf20Sopenharmony_ci *trend = THERMAL_TREND_STABLE; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci return 0; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic void thermal_irq_enable(struct tegra_thermctl_zone *zn) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci u32 r; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci /* multiple zones could be handling and setting trips at once */ 6728c2ecf20Sopenharmony_ci mutex_lock(&zn->ts->thermctl_lock); 6738c2ecf20Sopenharmony_ci r = readl(zn->ts->regs + THERMCTL_INTR_ENABLE); 6748c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, zn->sg->thermctl_isr_mask, TH_INTR_UP_DN_EN); 6758c2ecf20Sopenharmony_ci writel(r, zn->ts->regs + THERMCTL_INTR_ENABLE); 6768c2ecf20Sopenharmony_ci mutex_unlock(&zn->ts->thermctl_lock); 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic void thermal_irq_disable(struct tegra_thermctl_zone *zn) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci u32 r; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci /* multiple zones could be handling and setting trips at once */ 6848c2ecf20Sopenharmony_ci mutex_lock(&zn->ts->thermctl_lock); 6858c2ecf20Sopenharmony_ci r = readl(zn->ts->regs + THERMCTL_INTR_DISABLE); 6868c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, zn->sg->thermctl_isr_mask, 0); 6878c2ecf20Sopenharmony_ci writel(r, zn->ts->regs + THERMCTL_INTR_DISABLE); 6888c2ecf20Sopenharmony_ci mutex_unlock(&zn->ts->thermctl_lock); 6898c2ecf20Sopenharmony_ci} 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_cistatic int tegra_thermctl_set_trips(void *data, int lo, int hi) 6928c2ecf20Sopenharmony_ci{ 6938c2ecf20Sopenharmony_ci struct tegra_thermctl_zone *zone = data; 6948c2ecf20Sopenharmony_ci u32 r; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci thermal_irq_disable(zone); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci r = readl(zone->ts->regs + zone->sg->thermctl_lvl0_offset); 6998c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_EN_MASK, 0); 7008c2ecf20Sopenharmony_ci writel(r, zone->ts->regs + zone->sg->thermctl_lvl0_offset); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci lo = enforce_temp_range(zone->dev, lo) / zone->ts->soc->thresh_grain; 7038c2ecf20Sopenharmony_ci hi = enforce_temp_range(zone->dev, hi) / zone->ts->soc->thresh_grain; 7048c2ecf20Sopenharmony_ci dev_dbg(zone->dev, "%s hi:%d, lo:%d\n", __func__, hi, lo); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, zone->sg->thermctl_lvl0_up_thresh_mask, hi); 7078c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, zone->sg->thermctl_lvl0_dn_thresh_mask, lo); 7088c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_EN_MASK, 1); 7098c2ecf20Sopenharmony_ci writel(r, zone->ts->regs + zone->sg->thermctl_lvl0_offset); 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci thermal_irq_enable(zone); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci return 0; 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic const struct thermal_zone_of_device_ops tegra_of_thermal_ops = { 7178c2ecf20Sopenharmony_ci .get_temp = tegra_thermctl_get_temp, 7188c2ecf20Sopenharmony_ci .set_trip_temp = tegra_thermctl_set_trip_temp, 7198c2ecf20Sopenharmony_ci .get_trend = tegra_thermctl_get_trend, 7208c2ecf20Sopenharmony_ci .set_trips = tegra_thermctl_set_trips, 7218c2ecf20Sopenharmony_ci}; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_cistatic int get_hot_temp(struct thermal_zone_device *tz, int *trip, int *temp) 7248c2ecf20Sopenharmony_ci{ 7258c2ecf20Sopenharmony_ci int ntrips, i, ret; 7268c2ecf20Sopenharmony_ci enum thermal_trip_type type; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci ntrips = of_thermal_get_ntrips(tz); 7298c2ecf20Sopenharmony_ci if (ntrips <= 0) 7308c2ecf20Sopenharmony_ci return -EINVAL; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci for (i = 0; i < ntrips; i++) { 7338c2ecf20Sopenharmony_ci ret = tz->ops->get_trip_type(tz, i, &type); 7348c2ecf20Sopenharmony_ci if (ret) 7358c2ecf20Sopenharmony_ci return -EINVAL; 7368c2ecf20Sopenharmony_ci if (type == THERMAL_TRIP_HOT) { 7378c2ecf20Sopenharmony_ci ret = tz->ops->get_trip_temp(tz, i, temp); 7388c2ecf20Sopenharmony_ci if (!ret) 7398c2ecf20Sopenharmony_ci *trip = i; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci return ret; 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci return -EINVAL; 7468c2ecf20Sopenharmony_ci} 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci/** 7498c2ecf20Sopenharmony_ci * tegra_soctherm_set_hwtrips() - set HW trip point from DT data 7508c2ecf20Sopenharmony_ci * @dev: struct device * of the SOC_THERM instance 7518c2ecf20Sopenharmony_ci * @sg: pointer to the sensor group to set the thermtrip temperature for 7528c2ecf20Sopenharmony_ci * @tz: struct thermal_zone_device * 7538c2ecf20Sopenharmony_ci * 7548c2ecf20Sopenharmony_ci * Configure the SOC_THERM HW trip points, setting "THERMTRIP" 7558c2ecf20Sopenharmony_ci * "THROTTLE" trip points , using "thermtrips", "critical" or "hot" 7568c2ecf20Sopenharmony_ci * type trip_temp 7578c2ecf20Sopenharmony_ci * from thermal zone. 7588c2ecf20Sopenharmony_ci * After they have been configured, THERMTRIP or THROTTLE will take 7598c2ecf20Sopenharmony_ci * action when the configured SoC thermal sensor group reaches a 7608c2ecf20Sopenharmony_ci * certain temperature. 7618c2ecf20Sopenharmony_ci * 7628c2ecf20Sopenharmony_ci * Return: 0 upon success, or a negative error code on failure. 7638c2ecf20Sopenharmony_ci * "Success" does not mean that trips was enabled; it could also 7648c2ecf20Sopenharmony_ci * mean that no node was found in DT. 7658c2ecf20Sopenharmony_ci * THERMTRIP has been enabled successfully when a message similar to 7668c2ecf20Sopenharmony_ci * this one appears on the serial console: 7678c2ecf20Sopenharmony_ci * "thermtrip: will shut down when sensor group XXX reaches YYYYYY mC" 7688c2ecf20Sopenharmony_ci * THROTTLE has been enabled successfully when a message similar to 7698c2ecf20Sopenharmony_ci * this one appears on the serial console: 7708c2ecf20Sopenharmony_ci * ""throttrip: will throttle when sensor group XXX reaches YYYYYY mC" 7718c2ecf20Sopenharmony_ci */ 7728c2ecf20Sopenharmony_cistatic int tegra_soctherm_set_hwtrips(struct device *dev, 7738c2ecf20Sopenharmony_ci const struct tegra_tsensor_group *sg, 7748c2ecf20Sopenharmony_ci struct thermal_zone_device *tz) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci struct tegra_soctherm *ts = dev_get_drvdata(dev); 7778c2ecf20Sopenharmony_ci struct soctherm_throt_cfg *stc; 7788c2ecf20Sopenharmony_ci int i, trip, temperature, ret; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci /* Get thermtrips. If missing, try to get critical trips. */ 7818c2ecf20Sopenharmony_ci temperature = tsensor_group_thermtrip_get(ts, sg->id); 7828c2ecf20Sopenharmony_ci if (min_low_temp == temperature) 7838c2ecf20Sopenharmony_ci if (tz->ops->get_crit_temp(tz, &temperature)) 7848c2ecf20Sopenharmony_ci temperature = max_high_temp; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci ret = thermtrip_program(dev, sg, temperature); 7878c2ecf20Sopenharmony_ci if (ret) { 7888c2ecf20Sopenharmony_ci dev_err(dev, "thermtrip: %s: error during enable\n", sg->name); 7898c2ecf20Sopenharmony_ci return ret; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci dev_info(dev, "thermtrip: will shut down when %s reaches %d mC\n", 7938c2ecf20Sopenharmony_ci sg->name, temperature); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci ret = get_hot_temp(tz, &trip, &temperature); 7968c2ecf20Sopenharmony_ci if (ret) { 7978c2ecf20Sopenharmony_ci dev_info(dev, "throttrip: %s: missing hot temperature\n", 7988c2ecf20Sopenharmony_ci sg->name); 7998c2ecf20Sopenharmony_ci return 0; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci for (i = 0; i < THROTTLE_OC1; i++) { 8038c2ecf20Sopenharmony_ci struct thermal_cooling_device *cdev; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci if (!ts->throt_cfgs[i].init) 8068c2ecf20Sopenharmony_ci continue; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci cdev = ts->throt_cfgs[i].cdev; 8098c2ecf20Sopenharmony_ci if (get_thermal_instance(tz, cdev, trip)) 8108c2ecf20Sopenharmony_ci stc = find_throttle_cfg_by_name(ts, cdev->type); 8118c2ecf20Sopenharmony_ci else 8128c2ecf20Sopenharmony_ci continue; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci ret = throttrip_program(dev, sg, stc, temperature); 8158c2ecf20Sopenharmony_ci if (ret) { 8168c2ecf20Sopenharmony_ci dev_err(dev, "throttrip: %s: error during enable\n", 8178c2ecf20Sopenharmony_ci sg->name); 8188c2ecf20Sopenharmony_ci return ret; 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci dev_info(dev, 8228c2ecf20Sopenharmony_ci "throttrip: will throttle when %s reaches %d mC\n", 8238c2ecf20Sopenharmony_ci sg->name, temperature); 8248c2ecf20Sopenharmony_ci break; 8258c2ecf20Sopenharmony_ci } 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci if (i == THROTTLE_SIZE) 8288c2ecf20Sopenharmony_ci dev_info(dev, "throttrip: %s: missing throttle cdev\n", 8298c2ecf20Sopenharmony_ci sg->name); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci return 0; 8328c2ecf20Sopenharmony_ci} 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_cistatic irqreturn_t soctherm_thermal_isr(int irq, void *dev_id) 8358c2ecf20Sopenharmony_ci{ 8368c2ecf20Sopenharmony_ci struct tegra_soctherm *ts = dev_id; 8378c2ecf20Sopenharmony_ci u32 r; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci /* Case for no lock: 8408c2ecf20Sopenharmony_ci * Although interrupts are enabled in set_trips, there is still no need 8418c2ecf20Sopenharmony_ci * to lock here because the interrupts are disabled before programming 8428c2ecf20Sopenharmony_ci * new trip points. Hence there cant be a interrupt on the same sensor. 8438c2ecf20Sopenharmony_ci * An interrupt can however occur on a sensor while trips are being 8448c2ecf20Sopenharmony_ci * programmed on a different one. This beign a LEVEL interrupt won't 8458c2ecf20Sopenharmony_ci * cause a new interrupt but this is taken care of by the re-reading of 8468c2ecf20Sopenharmony_ci * the STATUS register in the thread function. 8478c2ecf20Sopenharmony_ci */ 8488c2ecf20Sopenharmony_ci r = readl(ts->regs + THERMCTL_INTR_STATUS); 8498c2ecf20Sopenharmony_ci writel(r, ts->regs + THERMCTL_INTR_DISABLE); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci/** 8558c2ecf20Sopenharmony_ci * soctherm_thermal_isr_thread() - Handles a thermal interrupt request 8568c2ecf20Sopenharmony_ci * @irq: The interrupt number being requested; not used 8578c2ecf20Sopenharmony_ci * @dev_id: Opaque pointer to tegra_soctherm; 8588c2ecf20Sopenharmony_ci * 8598c2ecf20Sopenharmony_ci * Clears the interrupt status register if there are expected 8608c2ecf20Sopenharmony_ci * interrupt bits set. 8618c2ecf20Sopenharmony_ci * The interrupt(s) are then handled by updating the corresponding 8628c2ecf20Sopenharmony_ci * thermal zones. 8638c2ecf20Sopenharmony_ci * 8648c2ecf20Sopenharmony_ci * An error is logged if any unexpected interrupt bits are set. 8658c2ecf20Sopenharmony_ci * 8668c2ecf20Sopenharmony_ci * Disabled interrupts are re-enabled. 8678c2ecf20Sopenharmony_ci * 8688c2ecf20Sopenharmony_ci * Return: %IRQ_HANDLED. Interrupt was handled and no further processing 8698c2ecf20Sopenharmony_ci * is needed. 8708c2ecf20Sopenharmony_ci */ 8718c2ecf20Sopenharmony_cistatic irqreturn_t soctherm_thermal_isr_thread(int irq, void *dev_id) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci struct tegra_soctherm *ts = dev_id; 8748c2ecf20Sopenharmony_ci struct thermal_zone_device *tz; 8758c2ecf20Sopenharmony_ci u32 st, ex = 0, cp = 0, gp = 0, pl = 0, me = 0; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci st = readl(ts->regs + THERMCTL_INTR_STATUS); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci /* deliberately clear expected interrupts handled in SW */ 8808c2ecf20Sopenharmony_ci cp |= st & TH_INTR_CD0_MASK; 8818c2ecf20Sopenharmony_ci cp |= st & TH_INTR_CU0_MASK; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci gp |= st & TH_INTR_GD0_MASK; 8848c2ecf20Sopenharmony_ci gp |= st & TH_INTR_GU0_MASK; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci pl |= st & TH_INTR_PD0_MASK; 8878c2ecf20Sopenharmony_ci pl |= st & TH_INTR_PU0_MASK; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci me |= st & TH_INTR_MD0_MASK; 8908c2ecf20Sopenharmony_ci me |= st & TH_INTR_MU0_MASK; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci ex |= cp | gp | pl | me; 8938c2ecf20Sopenharmony_ci if (ex) { 8948c2ecf20Sopenharmony_ci writel(ex, ts->regs + THERMCTL_INTR_STATUS); 8958c2ecf20Sopenharmony_ci st &= ~ex; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci if (cp) { 8988c2ecf20Sopenharmony_ci tz = ts->thermctl_tzs[TEGRA124_SOCTHERM_SENSOR_CPU]; 8998c2ecf20Sopenharmony_ci thermal_zone_device_update(tz, 9008c2ecf20Sopenharmony_ci THERMAL_EVENT_UNSPECIFIED); 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci if (gp) { 9048c2ecf20Sopenharmony_ci tz = ts->thermctl_tzs[TEGRA124_SOCTHERM_SENSOR_GPU]; 9058c2ecf20Sopenharmony_ci thermal_zone_device_update(tz, 9068c2ecf20Sopenharmony_ci THERMAL_EVENT_UNSPECIFIED); 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci if (pl) { 9108c2ecf20Sopenharmony_ci tz = ts->thermctl_tzs[TEGRA124_SOCTHERM_SENSOR_PLLX]; 9118c2ecf20Sopenharmony_ci thermal_zone_device_update(tz, 9128c2ecf20Sopenharmony_ci THERMAL_EVENT_UNSPECIFIED); 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci if (me) { 9168c2ecf20Sopenharmony_ci tz = ts->thermctl_tzs[TEGRA124_SOCTHERM_SENSOR_MEM]; 9178c2ecf20Sopenharmony_ci thermal_zone_device_update(tz, 9188c2ecf20Sopenharmony_ci THERMAL_EVENT_UNSPECIFIED); 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci /* deliberately ignore expected interrupts NOT handled in SW */ 9238c2ecf20Sopenharmony_ci ex |= TH_INTR_IGNORE_MASK; 9248c2ecf20Sopenharmony_ci st &= ~ex; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci if (st) { 9278c2ecf20Sopenharmony_ci /* Whine about any other unexpected INTR bits still set */ 9288c2ecf20Sopenharmony_ci pr_err("soctherm: Ignored unexpected INTRs 0x%08x\n", st); 9298c2ecf20Sopenharmony_ci writel(st, ts->regs + THERMCTL_INTR_STATUS); 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci return IRQ_HANDLED; 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci/** 9368c2ecf20Sopenharmony_ci * soctherm_oc_intr_enable() - Enables the soctherm over-current interrupt 9378c2ecf20Sopenharmony_ci * @ts: pointer to a struct tegra_soctherm 9388c2ecf20Sopenharmony_ci * @alarm: The soctherm throttle id 9398c2ecf20Sopenharmony_ci * @enable: Flag indicating enable the soctherm over-current 9408c2ecf20Sopenharmony_ci * interrupt or disable it 9418c2ecf20Sopenharmony_ci * 9428c2ecf20Sopenharmony_ci * Enables a specific over-current pins @alarm to raise an interrupt if the flag 9438c2ecf20Sopenharmony_ci * is set and the alarm corresponds to OC1, OC2, OC3, or OC4. 9448c2ecf20Sopenharmony_ci */ 9458c2ecf20Sopenharmony_cistatic void soctherm_oc_intr_enable(struct tegra_soctherm *ts, 9468c2ecf20Sopenharmony_ci enum soctherm_throttle_id alarm, 9478c2ecf20Sopenharmony_ci bool enable) 9488c2ecf20Sopenharmony_ci{ 9498c2ecf20Sopenharmony_ci u32 r; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci if (!enable) 9528c2ecf20Sopenharmony_ci return; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci r = readl(ts->regs + OC_INTR_ENABLE); 9558c2ecf20Sopenharmony_ci switch (alarm) { 9568c2ecf20Sopenharmony_ci case THROTTLE_OC1: 9578c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, OC_INTR_OC1_MASK, 1); 9588c2ecf20Sopenharmony_ci break; 9598c2ecf20Sopenharmony_ci case THROTTLE_OC2: 9608c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, OC_INTR_OC2_MASK, 1); 9618c2ecf20Sopenharmony_ci break; 9628c2ecf20Sopenharmony_ci case THROTTLE_OC3: 9638c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, OC_INTR_OC3_MASK, 1); 9648c2ecf20Sopenharmony_ci break; 9658c2ecf20Sopenharmony_ci case THROTTLE_OC4: 9668c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, OC_INTR_OC4_MASK, 1); 9678c2ecf20Sopenharmony_ci break; 9688c2ecf20Sopenharmony_ci default: 9698c2ecf20Sopenharmony_ci r = 0; 9708c2ecf20Sopenharmony_ci break; 9718c2ecf20Sopenharmony_ci } 9728c2ecf20Sopenharmony_ci writel(r, ts->regs + OC_INTR_ENABLE); 9738c2ecf20Sopenharmony_ci} 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci/** 9768c2ecf20Sopenharmony_ci * soctherm_handle_alarm() - Handles soctherm alarms 9778c2ecf20Sopenharmony_ci * @alarm: The soctherm throttle id 9788c2ecf20Sopenharmony_ci * 9798c2ecf20Sopenharmony_ci * "Handles" over-current alarms (OC1, OC2, OC3, and OC4) by printing 9808c2ecf20Sopenharmony_ci * a warning or informative message. 9818c2ecf20Sopenharmony_ci * 9828c2ecf20Sopenharmony_ci * Return: -EINVAL for @alarm = THROTTLE_OC3, otherwise 0 (success). 9838c2ecf20Sopenharmony_ci */ 9848c2ecf20Sopenharmony_cistatic int soctherm_handle_alarm(enum soctherm_throttle_id alarm) 9858c2ecf20Sopenharmony_ci{ 9868c2ecf20Sopenharmony_ci int rv = -EINVAL; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci switch (alarm) { 9898c2ecf20Sopenharmony_ci case THROTTLE_OC1: 9908c2ecf20Sopenharmony_ci pr_debug("soctherm: Successfully handled OC1 alarm\n"); 9918c2ecf20Sopenharmony_ci rv = 0; 9928c2ecf20Sopenharmony_ci break; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci case THROTTLE_OC2: 9958c2ecf20Sopenharmony_ci pr_debug("soctherm: Successfully handled OC2 alarm\n"); 9968c2ecf20Sopenharmony_ci rv = 0; 9978c2ecf20Sopenharmony_ci break; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci case THROTTLE_OC3: 10008c2ecf20Sopenharmony_ci pr_debug("soctherm: Successfully handled OC3 alarm\n"); 10018c2ecf20Sopenharmony_ci rv = 0; 10028c2ecf20Sopenharmony_ci break; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci case THROTTLE_OC4: 10058c2ecf20Sopenharmony_ci pr_debug("soctherm: Successfully handled OC4 alarm\n"); 10068c2ecf20Sopenharmony_ci rv = 0; 10078c2ecf20Sopenharmony_ci break; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci default: 10108c2ecf20Sopenharmony_ci break; 10118c2ecf20Sopenharmony_ci } 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci if (rv) 10148c2ecf20Sopenharmony_ci pr_err("soctherm: ERROR in handling %s alarm\n", 10158c2ecf20Sopenharmony_ci throt_names[alarm]); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci return rv; 10188c2ecf20Sopenharmony_ci} 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci/** 10218c2ecf20Sopenharmony_ci * soctherm_edp_isr_thread() - log an over-current interrupt request 10228c2ecf20Sopenharmony_ci * @irq: OC irq number. Currently not being used. See description 10238c2ecf20Sopenharmony_ci * @arg: a void pointer for callback, currently not being used 10248c2ecf20Sopenharmony_ci * 10258c2ecf20Sopenharmony_ci * Over-current events are handled in hardware. This function is called to log 10268c2ecf20Sopenharmony_ci * and handle any OC events that happened. Additionally, it checks every 10278c2ecf20Sopenharmony_ci * over-current interrupt registers for registers are set but 10288c2ecf20Sopenharmony_ci * was not expected (i.e. any discrepancy in interrupt status) by the function, 10298c2ecf20Sopenharmony_ci * the discrepancy will logged. 10308c2ecf20Sopenharmony_ci * 10318c2ecf20Sopenharmony_ci * Return: %IRQ_HANDLED 10328c2ecf20Sopenharmony_ci */ 10338c2ecf20Sopenharmony_cistatic irqreturn_t soctherm_edp_isr_thread(int irq, void *arg) 10348c2ecf20Sopenharmony_ci{ 10358c2ecf20Sopenharmony_ci struct tegra_soctherm *ts = arg; 10368c2ecf20Sopenharmony_ci u32 st, ex, oc1, oc2, oc3, oc4; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci st = readl(ts->regs + OC_INTR_STATUS); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci /* deliberately clear expected interrupts handled in SW */ 10418c2ecf20Sopenharmony_ci oc1 = st & OC_INTR_OC1_MASK; 10428c2ecf20Sopenharmony_ci oc2 = st & OC_INTR_OC2_MASK; 10438c2ecf20Sopenharmony_ci oc3 = st & OC_INTR_OC3_MASK; 10448c2ecf20Sopenharmony_ci oc4 = st & OC_INTR_OC4_MASK; 10458c2ecf20Sopenharmony_ci ex = oc1 | oc2 | oc3 | oc4; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci pr_err("soctherm: OC ALARM 0x%08x\n", ex); 10488c2ecf20Sopenharmony_ci if (ex) { 10498c2ecf20Sopenharmony_ci writel(st, ts->regs + OC_INTR_STATUS); 10508c2ecf20Sopenharmony_ci st &= ~ex; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci if (oc1 && !soctherm_handle_alarm(THROTTLE_OC1)) 10538c2ecf20Sopenharmony_ci soctherm_oc_intr_enable(ts, THROTTLE_OC1, true); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci if (oc2 && !soctherm_handle_alarm(THROTTLE_OC2)) 10568c2ecf20Sopenharmony_ci soctherm_oc_intr_enable(ts, THROTTLE_OC2, true); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci if (oc3 && !soctherm_handle_alarm(THROTTLE_OC3)) 10598c2ecf20Sopenharmony_ci soctherm_oc_intr_enable(ts, THROTTLE_OC3, true); 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci if (oc4 && !soctherm_handle_alarm(THROTTLE_OC4)) 10628c2ecf20Sopenharmony_ci soctherm_oc_intr_enable(ts, THROTTLE_OC4, true); 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci if (oc1 && soc_irq_cdata.irq_enable & BIT(0)) 10658c2ecf20Sopenharmony_ci handle_nested_irq( 10668c2ecf20Sopenharmony_ci irq_find_mapping(soc_irq_cdata.domain, 0)); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci if (oc2 && soc_irq_cdata.irq_enable & BIT(1)) 10698c2ecf20Sopenharmony_ci handle_nested_irq( 10708c2ecf20Sopenharmony_ci irq_find_mapping(soc_irq_cdata.domain, 1)); 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci if (oc3 && soc_irq_cdata.irq_enable & BIT(2)) 10738c2ecf20Sopenharmony_ci handle_nested_irq( 10748c2ecf20Sopenharmony_ci irq_find_mapping(soc_irq_cdata.domain, 2)); 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci if (oc4 && soc_irq_cdata.irq_enable & BIT(3)) 10778c2ecf20Sopenharmony_ci handle_nested_irq( 10788c2ecf20Sopenharmony_ci irq_find_mapping(soc_irq_cdata.domain, 3)); 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci if (st) { 10828c2ecf20Sopenharmony_ci pr_err("soctherm: Ignored unexpected OC ALARM 0x%08x\n", st); 10838c2ecf20Sopenharmony_ci writel(st, ts->regs + OC_INTR_STATUS); 10848c2ecf20Sopenharmony_ci } 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci return IRQ_HANDLED; 10878c2ecf20Sopenharmony_ci} 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci/** 10908c2ecf20Sopenharmony_ci * soctherm_edp_isr() - Disables any active interrupts 10918c2ecf20Sopenharmony_ci * @irq: The interrupt request number 10928c2ecf20Sopenharmony_ci * @arg: Opaque pointer to an argument 10938c2ecf20Sopenharmony_ci * 10948c2ecf20Sopenharmony_ci * Writes to the OC_INTR_DISABLE register the over current interrupt status, 10958c2ecf20Sopenharmony_ci * masking any asserted interrupts. Doing this prevents the same interrupts 10968c2ecf20Sopenharmony_ci * from triggering this isr repeatedly. The thread woken by this isr will 10978c2ecf20Sopenharmony_ci * handle asserted interrupts and subsequently unmask/re-enable them. 10988c2ecf20Sopenharmony_ci * 10998c2ecf20Sopenharmony_ci * The OC_INTR_DISABLE register indicates which OC interrupts 11008c2ecf20Sopenharmony_ci * have been disabled. 11018c2ecf20Sopenharmony_ci * 11028c2ecf20Sopenharmony_ci * Return: %IRQ_WAKE_THREAD, handler requests to wake the handler thread 11038c2ecf20Sopenharmony_ci */ 11048c2ecf20Sopenharmony_cistatic irqreturn_t soctherm_edp_isr(int irq, void *arg) 11058c2ecf20Sopenharmony_ci{ 11068c2ecf20Sopenharmony_ci struct tegra_soctherm *ts = arg; 11078c2ecf20Sopenharmony_ci u32 r; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci if (!ts) 11108c2ecf20Sopenharmony_ci return IRQ_NONE; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci r = readl(ts->regs + OC_INTR_STATUS); 11138c2ecf20Sopenharmony_ci writel(r, ts->regs + OC_INTR_DISABLE); 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 11168c2ecf20Sopenharmony_ci} 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci/** 11198c2ecf20Sopenharmony_ci * soctherm_oc_irq_lock() - locks the over-current interrupt request 11208c2ecf20Sopenharmony_ci * @data: Interrupt request data 11218c2ecf20Sopenharmony_ci * 11228c2ecf20Sopenharmony_ci * Looks up the chip data from @data and locks the mutex associated with 11238c2ecf20Sopenharmony_ci * a particular over-current interrupt request. 11248c2ecf20Sopenharmony_ci */ 11258c2ecf20Sopenharmony_cistatic void soctherm_oc_irq_lock(struct irq_data *data) 11268c2ecf20Sopenharmony_ci{ 11278c2ecf20Sopenharmony_ci struct soctherm_oc_irq_chip_data *d = irq_data_get_irq_chip_data(data); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci mutex_lock(&d->irq_lock); 11308c2ecf20Sopenharmony_ci} 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci/** 11338c2ecf20Sopenharmony_ci * soctherm_oc_irq_sync_unlock() - Unlocks the OC interrupt request 11348c2ecf20Sopenharmony_ci * @data: Interrupt request data 11358c2ecf20Sopenharmony_ci * 11368c2ecf20Sopenharmony_ci * Looks up the interrupt request data @data and unlocks the mutex associated 11378c2ecf20Sopenharmony_ci * with a particular over-current interrupt request. 11388c2ecf20Sopenharmony_ci */ 11398c2ecf20Sopenharmony_cistatic void soctherm_oc_irq_sync_unlock(struct irq_data *data) 11408c2ecf20Sopenharmony_ci{ 11418c2ecf20Sopenharmony_ci struct soctherm_oc_irq_chip_data *d = irq_data_get_irq_chip_data(data); 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci mutex_unlock(&d->irq_lock); 11448c2ecf20Sopenharmony_ci} 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci/** 11478c2ecf20Sopenharmony_ci * soctherm_oc_irq_enable() - Enables the SOC_THERM over-current interrupt queue 11488c2ecf20Sopenharmony_ci * @data: irq_data structure of the chip 11498c2ecf20Sopenharmony_ci * 11508c2ecf20Sopenharmony_ci * Sets the irq_enable bit of SOC_THERM allowing SOC_THERM 11518c2ecf20Sopenharmony_ci * to respond to over-current interrupts. 11528c2ecf20Sopenharmony_ci * 11538c2ecf20Sopenharmony_ci */ 11548c2ecf20Sopenharmony_cistatic void soctherm_oc_irq_enable(struct irq_data *data) 11558c2ecf20Sopenharmony_ci{ 11568c2ecf20Sopenharmony_ci struct soctherm_oc_irq_chip_data *d = irq_data_get_irq_chip_data(data); 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci d->irq_enable |= BIT(data->hwirq); 11598c2ecf20Sopenharmony_ci} 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci/** 11628c2ecf20Sopenharmony_ci * soctherm_oc_irq_disable() - Disables overcurrent interrupt requests 11638c2ecf20Sopenharmony_ci * @data: The interrupt request information 11648c2ecf20Sopenharmony_ci * 11658c2ecf20Sopenharmony_ci * Clears the interrupt request enable bit of the overcurrent 11668c2ecf20Sopenharmony_ci * interrupt request chip data. 11678c2ecf20Sopenharmony_ci * 11688c2ecf20Sopenharmony_ci * Return: Nothing is returned (void) 11698c2ecf20Sopenharmony_ci */ 11708c2ecf20Sopenharmony_cistatic void soctherm_oc_irq_disable(struct irq_data *data) 11718c2ecf20Sopenharmony_ci{ 11728c2ecf20Sopenharmony_ci struct soctherm_oc_irq_chip_data *d = irq_data_get_irq_chip_data(data); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci d->irq_enable &= ~BIT(data->hwirq); 11758c2ecf20Sopenharmony_ci} 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_cistatic int soctherm_oc_irq_set_type(struct irq_data *data, unsigned int type) 11788c2ecf20Sopenharmony_ci{ 11798c2ecf20Sopenharmony_ci return 0; 11808c2ecf20Sopenharmony_ci} 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci/** 11838c2ecf20Sopenharmony_ci * soctherm_oc_irq_map() - SOC_THERM interrupt request domain mapper 11848c2ecf20Sopenharmony_ci * @h: Interrupt request domain 11858c2ecf20Sopenharmony_ci * @virq: Virtual interrupt request number 11868c2ecf20Sopenharmony_ci * @hw: Hardware interrupt request number 11878c2ecf20Sopenharmony_ci * 11888c2ecf20Sopenharmony_ci * Mapping callback function for SOC_THERM's irq_domain. When a SOC_THERM 11898c2ecf20Sopenharmony_ci * interrupt request is called, the irq_domain takes the request's virtual 11908c2ecf20Sopenharmony_ci * request number (much like a virtual memory address) and maps it to a 11918c2ecf20Sopenharmony_ci * physical hardware request number. 11928c2ecf20Sopenharmony_ci * 11938c2ecf20Sopenharmony_ci * When a mapping doesn't already exist for a virtual request number, the 11948c2ecf20Sopenharmony_ci * irq_domain calls this function to associate the virtual request number with 11958c2ecf20Sopenharmony_ci * a hardware request number. 11968c2ecf20Sopenharmony_ci * 11978c2ecf20Sopenharmony_ci * Return: 0 11988c2ecf20Sopenharmony_ci */ 11998c2ecf20Sopenharmony_cistatic int soctherm_oc_irq_map(struct irq_domain *h, unsigned int virq, 12008c2ecf20Sopenharmony_ci irq_hw_number_t hw) 12018c2ecf20Sopenharmony_ci{ 12028c2ecf20Sopenharmony_ci struct soctherm_oc_irq_chip_data *data = h->host_data; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci irq_set_chip_data(virq, data); 12058c2ecf20Sopenharmony_ci irq_set_chip(virq, &data->irq_chip); 12068c2ecf20Sopenharmony_ci irq_set_nested_thread(virq, 1); 12078c2ecf20Sopenharmony_ci return 0; 12088c2ecf20Sopenharmony_ci} 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci/** 12118c2ecf20Sopenharmony_ci * soctherm_irq_domain_xlate_twocell() - xlate for soctherm interrupts 12128c2ecf20Sopenharmony_ci * @d: Interrupt request domain 12138c2ecf20Sopenharmony_ci * @ctrlr: Controller device tree node 12148c2ecf20Sopenharmony_ci * @intspec: Array of u32s from DTs "interrupt" property 12158c2ecf20Sopenharmony_ci * @intsize: Number of values inside the intspec array 12168c2ecf20Sopenharmony_ci * @out_hwirq: HW IRQ value associated with this interrupt 12178c2ecf20Sopenharmony_ci * @out_type: The IRQ SENSE type for this interrupt. 12188c2ecf20Sopenharmony_ci * 12198c2ecf20Sopenharmony_ci * This Device Tree IRQ specifier translation function will translate a 12208c2ecf20Sopenharmony_ci * specific "interrupt" as defined by 2 DT values where the cell values map 12218c2ecf20Sopenharmony_ci * the hwirq number + 1 and linux irq flags. Since the output is the hwirq 12228c2ecf20Sopenharmony_ci * number, this function will subtract 1 from the value listed in DT. 12238c2ecf20Sopenharmony_ci * 12248c2ecf20Sopenharmony_ci * Return: 0 12258c2ecf20Sopenharmony_ci */ 12268c2ecf20Sopenharmony_cistatic int soctherm_irq_domain_xlate_twocell(struct irq_domain *d, 12278c2ecf20Sopenharmony_ci struct device_node *ctrlr, const u32 *intspec, unsigned int intsize, 12288c2ecf20Sopenharmony_ci irq_hw_number_t *out_hwirq, unsigned int *out_type) 12298c2ecf20Sopenharmony_ci{ 12308c2ecf20Sopenharmony_ci if (WARN_ON(intsize < 2)) 12318c2ecf20Sopenharmony_ci return -EINVAL; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci /* 12348c2ecf20Sopenharmony_ci * The HW value is 1 index less than the DT IRQ values. 12358c2ecf20Sopenharmony_ci * i.e. OC4 goes to HW index 3. 12368c2ecf20Sopenharmony_ci */ 12378c2ecf20Sopenharmony_ci *out_hwirq = intspec[0] - 1; 12388c2ecf20Sopenharmony_ci *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK; 12398c2ecf20Sopenharmony_ci return 0; 12408c2ecf20Sopenharmony_ci} 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_cistatic const struct irq_domain_ops soctherm_oc_domain_ops = { 12438c2ecf20Sopenharmony_ci .map = soctherm_oc_irq_map, 12448c2ecf20Sopenharmony_ci .xlate = soctherm_irq_domain_xlate_twocell, 12458c2ecf20Sopenharmony_ci}; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci/** 12488c2ecf20Sopenharmony_ci * soctherm_oc_int_init() - Initial enabling of the over 12498c2ecf20Sopenharmony_ci * current interrupts 12508c2ecf20Sopenharmony_ci * @np: The devicetree node for soctherm 12518c2ecf20Sopenharmony_ci * @num_irqs: The number of new interrupt requests 12528c2ecf20Sopenharmony_ci * 12538c2ecf20Sopenharmony_ci * Sets the over current interrupt request chip data 12548c2ecf20Sopenharmony_ci * 12558c2ecf20Sopenharmony_ci * Return: 0 on success or if overcurrent interrupts are not enabled, 12568c2ecf20Sopenharmony_ci * -ENOMEM (out of memory), or irq_base if the function failed to 12578c2ecf20Sopenharmony_ci * allocate the irqs 12588c2ecf20Sopenharmony_ci */ 12598c2ecf20Sopenharmony_cistatic int soctherm_oc_int_init(struct device_node *np, int num_irqs) 12608c2ecf20Sopenharmony_ci{ 12618c2ecf20Sopenharmony_ci if (!num_irqs) { 12628c2ecf20Sopenharmony_ci pr_info("%s(): OC interrupts are not enabled\n", __func__); 12638c2ecf20Sopenharmony_ci return 0; 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci mutex_init(&soc_irq_cdata.irq_lock); 12678c2ecf20Sopenharmony_ci soc_irq_cdata.irq_enable = 0; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci soc_irq_cdata.irq_chip.name = "soc_therm_oc"; 12708c2ecf20Sopenharmony_ci soc_irq_cdata.irq_chip.irq_bus_lock = soctherm_oc_irq_lock; 12718c2ecf20Sopenharmony_ci soc_irq_cdata.irq_chip.irq_bus_sync_unlock = 12728c2ecf20Sopenharmony_ci soctherm_oc_irq_sync_unlock; 12738c2ecf20Sopenharmony_ci soc_irq_cdata.irq_chip.irq_disable = soctherm_oc_irq_disable; 12748c2ecf20Sopenharmony_ci soc_irq_cdata.irq_chip.irq_enable = soctherm_oc_irq_enable; 12758c2ecf20Sopenharmony_ci soc_irq_cdata.irq_chip.irq_set_type = soctherm_oc_irq_set_type; 12768c2ecf20Sopenharmony_ci soc_irq_cdata.irq_chip.irq_set_wake = NULL; 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci soc_irq_cdata.domain = irq_domain_add_linear(np, num_irqs, 12798c2ecf20Sopenharmony_ci &soctherm_oc_domain_ops, 12808c2ecf20Sopenharmony_ci &soc_irq_cdata); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci if (!soc_irq_cdata.domain) { 12838c2ecf20Sopenharmony_ci pr_err("%s: Failed to create IRQ domain\n", __func__); 12848c2ecf20Sopenharmony_ci return -ENOMEM; 12858c2ecf20Sopenharmony_ci } 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci pr_debug("%s(): OC interrupts enabled successful\n", __func__); 12888c2ecf20Sopenharmony_ci return 0; 12898c2ecf20Sopenharmony_ci} 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 12928c2ecf20Sopenharmony_cistatic int regs_show(struct seq_file *s, void *data) 12938c2ecf20Sopenharmony_ci{ 12948c2ecf20Sopenharmony_ci struct platform_device *pdev = s->private; 12958c2ecf20Sopenharmony_ci struct tegra_soctherm *ts = platform_get_drvdata(pdev); 12968c2ecf20Sopenharmony_ci const struct tegra_tsensor *tsensors = ts->soc->tsensors; 12978c2ecf20Sopenharmony_ci const struct tegra_tsensor_group **ttgs = ts->soc->ttgs; 12988c2ecf20Sopenharmony_ci u32 r, state; 12998c2ecf20Sopenharmony_ci int i, level; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci seq_puts(s, "-----TSENSE (convert HW)-----\n"); 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci for (i = 0; i < ts->soc->num_tsensors; i++) { 13048c2ecf20Sopenharmony_ci r = readl(ts->regs + tsensors[i].base + SENSOR_CONFIG1); 13058c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG1_TEMP_ENABLE); 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci seq_printf(s, "%s: ", tsensors[i].name); 13088c2ecf20Sopenharmony_ci seq_printf(s, "En(%d) ", state); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci if (!state) { 13118c2ecf20Sopenharmony_ci seq_puts(s, "\n"); 13128c2ecf20Sopenharmony_ci continue; 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG1_TIDDQ_EN_MASK); 13168c2ecf20Sopenharmony_ci seq_printf(s, "tiddq(%d) ", state); 13178c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG1_TEN_COUNT_MASK); 13188c2ecf20Sopenharmony_ci seq_printf(s, "ten_count(%d) ", state); 13198c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG1_TSAMPLE_MASK); 13208c2ecf20Sopenharmony_ci seq_printf(s, "tsample(%d) ", state + 1); 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci r = readl(ts->regs + tsensors[i].base + SENSOR_STATUS1); 13238c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_STATUS1_TEMP_VALID_MASK); 13248c2ecf20Sopenharmony_ci seq_printf(s, "Temp(%d/", state); 13258c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_STATUS1_TEMP_MASK); 13268c2ecf20Sopenharmony_ci seq_printf(s, "%d) ", translate_temp(state)); 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci r = readl(ts->regs + tsensors[i].base + SENSOR_STATUS0); 13298c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_STATUS0_VALID_MASK); 13308c2ecf20Sopenharmony_ci seq_printf(s, "Capture(%d/", state); 13318c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_STATUS0_CAPTURE_MASK); 13328c2ecf20Sopenharmony_ci seq_printf(s, "%d) ", state); 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci r = readl(ts->regs + tsensors[i].base + SENSOR_CONFIG0); 13358c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG0_STOP); 13368c2ecf20Sopenharmony_ci seq_printf(s, "Stop(%d) ", state); 13378c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG0_TALL_MASK); 13388c2ecf20Sopenharmony_ci seq_printf(s, "Tall(%d) ", state); 13398c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG0_TCALC_OVER); 13408c2ecf20Sopenharmony_ci seq_printf(s, "Over(%d/", state); 13418c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG0_OVER); 13428c2ecf20Sopenharmony_ci seq_printf(s, "%d/", state); 13438c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG0_CPTR_OVER); 13448c2ecf20Sopenharmony_ci seq_printf(s, "%d) ", state); 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ci r = readl(ts->regs + tsensors[i].base + SENSOR_CONFIG2); 13478c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG2_THERMA_MASK); 13488c2ecf20Sopenharmony_ci seq_printf(s, "Therm_A/B(%d/", state); 13498c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG2_THERMB_MASK); 13508c2ecf20Sopenharmony_ci seq_printf(s, "%d)\n", (s16)state); 13518c2ecf20Sopenharmony_ci } 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci r = readl(ts->regs + SENSOR_PDIV); 13548c2ecf20Sopenharmony_ci seq_printf(s, "PDIV: 0x%x\n", r); 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci r = readl(ts->regs + SENSOR_HOTSPOT_OFF); 13578c2ecf20Sopenharmony_ci seq_printf(s, "HOTSPOT: 0x%x\n", r); 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci seq_puts(s, "\n"); 13608c2ecf20Sopenharmony_ci seq_puts(s, "-----SOC_THERM-----\n"); 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci r = readl(ts->regs + SENSOR_TEMP1); 13638c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_TEMP1_CPU_TEMP_MASK); 13648c2ecf20Sopenharmony_ci seq_printf(s, "Temperatures: CPU(%d) ", translate_temp(state)); 13658c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_TEMP1_GPU_TEMP_MASK); 13668c2ecf20Sopenharmony_ci seq_printf(s, " GPU(%d) ", translate_temp(state)); 13678c2ecf20Sopenharmony_ci r = readl(ts->regs + SENSOR_TEMP2); 13688c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_TEMP2_PLLX_TEMP_MASK); 13698c2ecf20Sopenharmony_ci seq_printf(s, " PLLX(%d) ", translate_temp(state)); 13708c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_TEMP2_MEM_TEMP_MASK); 13718c2ecf20Sopenharmony_ci seq_printf(s, " MEM(%d)\n", translate_temp(state)); 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci for (i = 0; i < ts->soc->num_ttgs; i++) { 13748c2ecf20Sopenharmony_ci seq_printf(s, "%s:\n", ttgs[i]->name); 13758c2ecf20Sopenharmony_ci for (level = 0; level < 4; level++) { 13768c2ecf20Sopenharmony_ci s32 v; 13778c2ecf20Sopenharmony_ci u32 mask; 13788c2ecf20Sopenharmony_ci u16 off = ttgs[i]->thermctl_lvl0_offset; 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci r = readl(ts->regs + THERMCTL_LVL_REG(off, level)); 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci mask = ttgs[i]->thermctl_lvl0_up_thresh_mask; 13838c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, mask); 13848c2ecf20Sopenharmony_ci v = sign_extend32(state, ts->soc->bptt - 1); 13858c2ecf20Sopenharmony_ci v *= ts->soc->thresh_grain; 13868c2ecf20Sopenharmony_ci seq_printf(s, " %d: Up/Dn(%d /", level, v); 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci mask = ttgs[i]->thermctl_lvl0_dn_thresh_mask; 13898c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, mask); 13908c2ecf20Sopenharmony_ci v = sign_extend32(state, ts->soc->bptt - 1); 13918c2ecf20Sopenharmony_ci v *= ts->soc->thresh_grain; 13928c2ecf20Sopenharmony_ci seq_printf(s, "%d ) ", v); 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci mask = THERMCTL_LVL0_CPU0_EN_MASK; 13958c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, mask); 13968c2ecf20Sopenharmony_ci seq_printf(s, "En(%d) ", state); 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci mask = THERMCTL_LVL0_CPU0_CPU_THROT_MASK; 13998c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, mask); 14008c2ecf20Sopenharmony_ci seq_puts(s, "CPU Throt"); 14018c2ecf20Sopenharmony_ci if (!state) 14028c2ecf20Sopenharmony_ci seq_printf(s, "(%s) ", "none"); 14038c2ecf20Sopenharmony_ci else if (state == THERMCTL_LVL0_CPU0_CPU_THROT_LIGHT) 14048c2ecf20Sopenharmony_ci seq_printf(s, "(%s) ", "L"); 14058c2ecf20Sopenharmony_ci else if (state == THERMCTL_LVL0_CPU0_CPU_THROT_HEAVY) 14068c2ecf20Sopenharmony_ci seq_printf(s, "(%s) ", "H"); 14078c2ecf20Sopenharmony_ci else 14088c2ecf20Sopenharmony_ci seq_printf(s, "(%s) ", "H+L"); 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci mask = THERMCTL_LVL0_CPU0_GPU_THROT_MASK; 14118c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, mask); 14128c2ecf20Sopenharmony_ci seq_puts(s, "GPU Throt"); 14138c2ecf20Sopenharmony_ci if (!state) 14148c2ecf20Sopenharmony_ci seq_printf(s, "(%s) ", "none"); 14158c2ecf20Sopenharmony_ci else if (state == THERMCTL_LVL0_CPU0_GPU_THROT_LIGHT) 14168c2ecf20Sopenharmony_ci seq_printf(s, "(%s) ", "L"); 14178c2ecf20Sopenharmony_ci else if (state == THERMCTL_LVL0_CPU0_GPU_THROT_HEAVY) 14188c2ecf20Sopenharmony_ci seq_printf(s, "(%s) ", "H"); 14198c2ecf20Sopenharmony_ci else 14208c2ecf20Sopenharmony_ci seq_printf(s, "(%s) ", "H+L"); 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci mask = THERMCTL_LVL0_CPU0_STATUS_MASK; 14238c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, mask); 14248c2ecf20Sopenharmony_ci seq_printf(s, "Status(%s)\n", 14258c2ecf20Sopenharmony_ci state == 0 ? "LO" : 14268c2ecf20Sopenharmony_ci state == 1 ? "In" : 14278c2ecf20Sopenharmony_ci state == 2 ? "Res" : "HI"); 14288c2ecf20Sopenharmony_ci } 14298c2ecf20Sopenharmony_ci } 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci r = readl(ts->regs + THERMCTL_STATS_CTL); 14328c2ecf20Sopenharmony_ci seq_printf(s, "STATS: Up(%s) Dn(%s)\n", 14338c2ecf20Sopenharmony_ci r & STATS_CTL_EN_UP ? "En" : "--", 14348c2ecf20Sopenharmony_ci r & STATS_CTL_EN_DN ? "En" : "--"); 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci for (level = 0; level < 4; level++) { 14378c2ecf20Sopenharmony_ci u16 off; 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci off = THERMCTL_LVL0_UP_STATS; 14408c2ecf20Sopenharmony_ci r = readl(ts->regs + THERMCTL_LVL_REG(off, level)); 14418c2ecf20Sopenharmony_ci seq_printf(s, " Level_%d Up(%d) ", level, r); 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci off = THERMCTL_LVL0_DN_STATS; 14448c2ecf20Sopenharmony_ci r = readl(ts->regs + THERMCTL_LVL_REG(off, level)); 14458c2ecf20Sopenharmony_ci seq_printf(s, "Dn(%d)\n", r); 14468c2ecf20Sopenharmony_ci } 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci r = readl(ts->regs + THERMCTL_THERMTRIP_CTL); 14498c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, ttgs[0]->thermtrip_any_en_mask); 14508c2ecf20Sopenharmony_ci seq_printf(s, "Thermtrip Any En(%d)\n", state); 14518c2ecf20Sopenharmony_ci for (i = 0; i < ts->soc->num_ttgs; i++) { 14528c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, ttgs[i]->thermtrip_enable_mask); 14538c2ecf20Sopenharmony_ci seq_printf(s, " %s En(%d) ", ttgs[i]->name, state); 14548c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, ttgs[i]->thermtrip_threshold_mask); 14558c2ecf20Sopenharmony_ci state *= ts->soc->thresh_grain; 14568c2ecf20Sopenharmony_ci seq_printf(s, "Thresh(%d)\n", state); 14578c2ecf20Sopenharmony_ci } 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci r = readl(ts->regs + THROT_GLOBAL_CFG); 14608c2ecf20Sopenharmony_ci seq_puts(s, "\n"); 14618c2ecf20Sopenharmony_ci seq_printf(s, "GLOBAL THROTTLE CONFIG: 0x%08x\n", r); 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci seq_puts(s, "---------------------------------------------------\n"); 14648c2ecf20Sopenharmony_ci r = readl(ts->regs + THROT_STATUS); 14658c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, THROT_STATUS_BREACH_MASK); 14668c2ecf20Sopenharmony_ci seq_printf(s, "THROT STATUS: breach(%d) ", state); 14678c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, THROT_STATUS_STATE_MASK); 14688c2ecf20Sopenharmony_ci seq_printf(s, "state(%d) ", state); 14698c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, THROT_STATUS_ENABLED_MASK); 14708c2ecf20Sopenharmony_ci seq_printf(s, "enabled(%d)\n", state); 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci r = readl(ts->regs + CPU_PSKIP_STATUS); 14738c2ecf20Sopenharmony_ci if (ts->soc->use_ccroc) { 14748c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, XPU_PSKIP_STATUS_ENABLED_MASK); 14758c2ecf20Sopenharmony_ci seq_printf(s, "CPU PSKIP STATUS: enabled(%d)\n", state); 14768c2ecf20Sopenharmony_ci } else { 14778c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, XPU_PSKIP_STATUS_M_MASK); 14788c2ecf20Sopenharmony_ci seq_printf(s, "CPU PSKIP STATUS: M(%d) ", state); 14798c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, XPU_PSKIP_STATUS_N_MASK); 14808c2ecf20Sopenharmony_ci seq_printf(s, "N(%d) ", state); 14818c2ecf20Sopenharmony_ci state = REG_GET_MASK(r, XPU_PSKIP_STATUS_ENABLED_MASK); 14828c2ecf20Sopenharmony_ci seq_printf(s, "enabled(%d)\n", state); 14838c2ecf20Sopenharmony_ci } 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci return 0; 14868c2ecf20Sopenharmony_ci} 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(regs); 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_cistatic void soctherm_debug_init(struct platform_device *pdev) 14918c2ecf20Sopenharmony_ci{ 14928c2ecf20Sopenharmony_ci struct tegra_soctherm *tegra = platform_get_drvdata(pdev); 14938c2ecf20Sopenharmony_ci struct dentry *root; 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci root = debugfs_create_dir("soctherm", NULL); 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci tegra->debugfs_dir = root; 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci debugfs_create_file("reg_contents", 0644, root, pdev, ®s_fops); 15008c2ecf20Sopenharmony_ci} 15018c2ecf20Sopenharmony_ci#else 15028c2ecf20Sopenharmony_cistatic inline void soctherm_debug_init(struct platform_device *pdev) {} 15038c2ecf20Sopenharmony_ci#endif 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_cistatic int soctherm_clk_enable(struct platform_device *pdev, bool enable) 15068c2ecf20Sopenharmony_ci{ 15078c2ecf20Sopenharmony_ci struct tegra_soctherm *tegra = platform_get_drvdata(pdev); 15088c2ecf20Sopenharmony_ci int err; 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci if (!tegra->clock_soctherm || !tegra->clock_tsensor) 15118c2ecf20Sopenharmony_ci return -EINVAL; 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_ci reset_control_assert(tegra->reset); 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci if (enable) { 15168c2ecf20Sopenharmony_ci err = clk_prepare_enable(tegra->clock_soctherm); 15178c2ecf20Sopenharmony_ci if (err) { 15188c2ecf20Sopenharmony_ci reset_control_deassert(tegra->reset); 15198c2ecf20Sopenharmony_ci return err; 15208c2ecf20Sopenharmony_ci } 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci err = clk_prepare_enable(tegra->clock_tsensor); 15238c2ecf20Sopenharmony_ci if (err) { 15248c2ecf20Sopenharmony_ci clk_disable_unprepare(tegra->clock_soctherm); 15258c2ecf20Sopenharmony_ci reset_control_deassert(tegra->reset); 15268c2ecf20Sopenharmony_ci return err; 15278c2ecf20Sopenharmony_ci } 15288c2ecf20Sopenharmony_ci } else { 15298c2ecf20Sopenharmony_ci clk_disable_unprepare(tegra->clock_tsensor); 15308c2ecf20Sopenharmony_ci clk_disable_unprepare(tegra->clock_soctherm); 15318c2ecf20Sopenharmony_ci } 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci reset_control_deassert(tegra->reset); 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci return 0; 15368c2ecf20Sopenharmony_ci} 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_cistatic int throt_get_cdev_max_state(struct thermal_cooling_device *cdev, 15398c2ecf20Sopenharmony_ci unsigned long *max_state) 15408c2ecf20Sopenharmony_ci{ 15418c2ecf20Sopenharmony_ci *max_state = 1; 15428c2ecf20Sopenharmony_ci return 0; 15438c2ecf20Sopenharmony_ci} 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_cistatic int throt_get_cdev_cur_state(struct thermal_cooling_device *cdev, 15468c2ecf20Sopenharmony_ci unsigned long *cur_state) 15478c2ecf20Sopenharmony_ci{ 15488c2ecf20Sopenharmony_ci struct tegra_soctherm *ts = cdev->devdata; 15498c2ecf20Sopenharmony_ci u32 r; 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci r = readl(ts->regs + THROT_STATUS); 15528c2ecf20Sopenharmony_ci if (REG_GET_MASK(r, THROT_STATUS_STATE_MASK)) 15538c2ecf20Sopenharmony_ci *cur_state = 1; 15548c2ecf20Sopenharmony_ci else 15558c2ecf20Sopenharmony_ci *cur_state = 0; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci return 0; 15588c2ecf20Sopenharmony_ci} 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_cistatic int throt_set_cdev_state(struct thermal_cooling_device *cdev, 15618c2ecf20Sopenharmony_ci unsigned long cur_state) 15628c2ecf20Sopenharmony_ci{ 15638c2ecf20Sopenharmony_ci return 0; 15648c2ecf20Sopenharmony_ci} 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_cistatic const struct thermal_cooling_device_ops throt_cooling_ops = { 15678c2ecf20Sopenharmony_ci .get_max_state = throt_get_cdev_max_state, 15688c2ecf20Sopenharmony_ci .get_cur_state = throt_get_cdev_cur_state, 15698c2ecf20Sopenharmony_ci .set_cur_state = throt_set_cdev_state, 15708c2ecf20Sopenharmony_ci}; 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_cistatic int soctherm_thermtrips_parse(struct platform_device *pdev) 15738c2ecf20Sopenharmony_ci{ 15748c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 15758c2ecf20Sopenharmony_ci struct tegra_soctherm *ts = dev_get_drvdata(dev); 15768c2ecf20Sopenharmony_ci struct tsensor_group_thermtrips *tt = ts->soc->thermtrips; 15778c2ecf20Sopenharmony_ci const int max_num_prop = ts->soc->num_ttgs * 2; 15788c2ecf20Sopenharmony_ci u32 *tlb; 15798c2ecf20Sopenharmony_ci int i, j, n, ret; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci if (!tt) 15828c2ecf20Sopenharmony_ci return -ENOMEM; 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci n = of_property_count_u32_elems(dev->of_node, "nvidia,thermtrips"); 15858c2ecf20Sopenharmony_ci if (n <= 0) { 15868c2ecf20Sopenharmony_ci dev_info(dev, 15878c2ecf20Sopenharmony_ci "missing thermtrips, will use critical trips as shut down temp\n"); 15888c2ecf20Sopenharmony_ci return n; 15898c2ecf20Sopenharmony_ci } 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci n = min(max_num_prop, n); 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci tlb = devm_kcalloc(&pdev->dev, max_num_prop, sizeof(u32), GFP_KERNEL); 15948c2ecf20Sopenharmony_ci if (!tlb) 15958c2ecf20Sopenharmony_ci return -ENOMEM; 15968c2ecf20Sopenharmony_ci ret = of_property_read_u32_array(dev->of_node, "nvidia,thermtrips", 15978c2ecf20Sopenharmony_ci tlb, n); 15988c2ecf20Sopenharmony_ci if (ret) { 15998c2ecf20Sopenharmony_ci dev_err(dev, "invalid num ele: thermtrips:%d\n", ret); 16008c2ecf20Sopenharmony_ci return ret; 16018c2ecf20Sopenharmony_ci } 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci i = 0; 16048c2ecf20Sopenharmony_ci for (j = 0; j < n; j = j + 2) { 16058c2ecf20Sopenharmony_ci if (tlb[j] >= TEGRA124_SOCTHERM_SENSOR_NUM) 16068c2ecf20Sopenharmony_ci continue; 16078c2ecf20Sopenharmony_ci 16088c2ecf20Sopenharmony_ci tt[i].id = tlb[j]; 16098c2ecf20Sopenharmony_ci tt[i].temp = tlb[j + 1]; 16108c2ecf20Sopenharmony_ci i++; 16118c2ecf20Sopenharmony_ci } 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci return 0; 16148c2ecf20Sopenharmony_ci} 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_cistatic void soctherm_oc_cfg_parse(struct device *dev, 16178c2ecf20Sopenharmony_ci struct device_node *np_oc, 16188c2ecf20Sopenharmony_ci struct soctherm_throt_cfg *stc) 16198c2ecf20Sopenharmony_ci{ 16208c2ecf20Sopenharmony_ci u32 val; 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci if (of_property_read_bool(np_oc, "nvidia,polarity-active-low")) 16238c2ecf20Sopenharmony_ci stc->oc_cfg.active_low = 1; 16248c2ecf20Sopenharmony_ci else 16258c2ecf20Sopenharmony_ci stc->oc_cfg.active_low = 0; 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci if (!of_property_read_u32(np_oc, "nvidia,count-threshold", &val)) { 16288c2ecf20Sopenharmony_ci stc->oc_cfg.intr_en = 1; 16298c2ecf20Sopenharmony_ci stc->oc_cfg.alarm_cnt_thresh = val; 16308c2ecf20Sopenharmony_ci } 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci if (!of_property_read_u32(np_oc, "nvidia,throttle-period-us", &val)) 16338c2ecf20Sopenharmony_ci stc->oc_cfg.throt_period = val; 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci if (!of_property_read_u32(np_oc, "nvidia,alarm-filter", &val)) 16368c2ecf20Sopenharmony_ci stc->oc_cfg.alarm_filter = val; 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci /* BRIEF throttling by default, do not support STICKY */ 16398c2ecf20Sopenharmony_ci stc->oc_cfg.mode = OC_THROTTLE_MODE_BRIEF; 16408c2ecf20Sopenharmony_ci} 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_cistatic int soctherm_throt_cfg_parse(struct device *dev, 16438c2ecf20Sopenharmony_ci struct device_node *np, 16448c2ecf20Sopenharmony_ci struct soctherm_throt_cfg *stc) 16458c2ecf20Sopenharmony_ci{ 16468c2ecf20Sopenharmony_ci struct tegra_soctherm *ts = dev_get_drvdata(dev); 16478c2ecf20Sopenharmony_ci int ret; 16488c2ecf20Sopenharmony_ci u32 val; 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "nvidia,priority", &val); 16518c2ecf20Sopenharmony_ci if (ret) { 16528c2ecf20Sopenharmony_ci dev_err(dev, "throttle-cfg: %s: invalid priority\n", stc->name); 16538c2ecf20Sopenharmony_ci return -EINVAL; 16548c2ecf20Sopenharmony_ci } 16558c2ecf20Sopenharmony_ci stc->priority = val; 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, ts->soc->use_ccroc ? 16588c2ecf20Sopenharmony_ci "nvidia,cpu-throt-level" : 16598c2ecf20Sopenharmony_ci "nvidia,cpu-throt-percent", &val); 16608c2ecf20Sopenharmony_ci if (!ret) { 16618c2ecf20Sopenharmony_ci if (ts->soc->use_ccroc && 16628c2ecf20Sopenharmony_ci val <= TEGRA_SOCTHERM_THROT_LEVEL_HIGH) 16638c2ecf20Sopenharmony_ci stc->cpu_throt_level = val; 16648c2ecf20Sopenharmony_ci else if (!ts->soc->use_ccroc && val <= 100) 16658c2ecf20Sopenharmony_ci stc->cpu_throt_depth = val; 16668c2ecf20Sopenharmony_ci else 16678c2ecf20Sopenharmony_ci goto err; 16688c2ecf20Sopenharmony_ci } else { 16698c2ecf20Sopenharmony_ci goto err; 16708c2ecf20Sopenharmony_ci } 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "nvidia,gpu-throt-level", &val); 16738c2ecf20Sopenharmony_ci if (!ret && val <= TEGRA_SOCTHERM_THROT_LEVEL_HIGH) 16748c2ecf20Sopenharmony_ci stc->gpu_throt_level = val; 16758c2ecf20Sopenharmony_ci else 16768c2ecf20Sopenharmony_ci goto err; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci return 0; 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_cierr: 16818c2ecf20Sopenharmony_ci dev_err(dev, "throttle-cfg: %s: no throt prop or invalid prop\n", 16828c2ecf20Sopenharmony_ci stc->name); 16838c2ecf20Sopenharmony_ci return -EINVAL; 16848c2ecf20Sopenharmony_ci} 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci/** 16878c2ecf20Sopenharmony_ci * soctherm_init_hw_throt_cdev() - Parse the HW throttle configurations 16888c2ecf20Sopenharmony_ci * and register them as cooling devices. 16898c2ecf20Sopenharmony_ci * @pdev: Pointer to platform_device struct 16908c2ecf20Sopenharmony_ci */ 16918c2ecf20Sopenharmony_cistatic void soctherm_init_hw_throt_cdev(struct platform_device *pdev) 16928c2ecf20Sopenharmony_ci{ 16938c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 16948c2ecf20Sopenharmony_ci struct tegra_soctherm *ts = dev_get_drvdata(dev); 16958c2ecf20Sopenharmony_ci struct device_node *np_stc, *np_stcc; 16968c2ecf20Sopenharmony_ci const char *name; 16978c2ecf20Sopenharmony_ci int i; 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci for (i = 0; i < THROTTLE_SIZE; i++) { 17008c2ecf20Sopenharmony_ci ts->throt_cfgs[i].name = throt_names[i]; 17018c2ecf20Sopenharmony_ci ts->throt_cfgs[i].id = i; 17028c2ecf20Sopenharmony_ci ts->throt_cfgs[i].init = false; 17038c2ecf20Sopenharmony_ci } 17048c2ecf20Sopenharmony_ci 17058c2ecf20Sopenharmony_ci np_stc = of_get_child_by_name(dev->of_node, "throttle-cfgs"); 17068c2ecf20Sopenharmony_ci if (!np_stc) { 17078c2ecf20Sopenharmony_ci dev_info(dev, 17088c2ecf20Sopenharmony_ci "throttle-cfg: no throttle-cfgs - not enabling\n"); 17098c2ecf20Sopenharmony_ci return; 17108c2ecf20Sopenharmony_ci } 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci for_each_child_of_node(np_stc, np_stcc) { 17138c2ecf20Sopenharmony_ci struct soctherm_throt_cfg *stc; 17148c2ecf20Sopenharmony_ci struct thermal_cooling_device *tcd; 17158c2ecf20Sopenharmony_ci int err; 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci name = np_stcc->name; 17188c2ecf20Sopenharmony_ci stc = find_throttle_cfg_by_name(ts, name); 17198c2ecf20Sopenharmony_ci if (!stc) { 17208c2ecf20Sopenharmony_ci dev_err(dev, 17218c2ecf20Sopenharmony_ci "throttle-cfg: could not find %s\n", name); 17228c2ecf20Sopenharmony_ci continue; 17238c2ecf20Sopenharmony_ci } 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci if (stc->init) { 17268c2ecf20Sopenharmony_ci dev_err(dev, "throttle-cfg: %s: redefined!\n", name); 17278c2ecf20Sopenharmony_ci of_node_put(np_stcc); 17288c2ecf20Sopenharmony_ci break; 17298c2ecf20Sopenharmony_ci } 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci err = soctherm_throt_cfg_parse(dev, np_stcc, stc); 17328c2ecf20Sopenharmony_ci if (err) 17338c2ecf20Sopenharmony_ci continue; 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci if (stc->id >= THROTTLE_OC1) { 17368c2ecf20Sopenharmony_ci soctherm_oc_cfg_parse(dev, np_stcc, stc); 17378c2ecf20Sopenharmony_ci stc->init = true; 17388c2ecf20Sopenharmony_ci } else { 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci tcd = thermal_of_cooling_device_register(np_stcc, 17418c2ecf20Sopenharmony_ci (char *)name, ts, 17428c2ecf20Sopenharmony_ci &throt_cooling_ops); 17438c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(tcd)) { 17448c2ecf20Sopenharmony_ci dev_err(dev, 17458c2ecf20Sopenharmony_ci "throttle-cfg: %s: failed to register cooling device\n", 17468c2ecf20Sopenharmony_ci name); 17478c2ecf20Sopenharmony_ci continue; 17488c2ecf20Sopenharmony_ci } 17498c2ecf20Sopenharmony_ci stc->cdev = tcd; 17508c2ecf20Sopenharmony_ci stc->init = true; 17518c2ecf20Sopenharmony_ci } 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci of_node_put(np_stc); 17568c2ecf20Sopenharmony_ci} 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci/** 17598c2ecf20Sopenharmony_ci * throttlectl_cpu_level_cfg() - programs CCROC NV_THERM level config 17608c2ecf20Sopenharmony_ci * @ts: pointer to a struct tegra_soctherm 17618c2ecf20Sopenharmony_ci * @level: describing the level LOW/MED/HIGH of throttling 17628c2ecf20Sopenharmony_ci * 17638c2ecf20Sopenharmony_ci * It's necessary to set up the CPU-local CCROC NV_THERM instance with 17648c2ecf20Sopenharmony_ci * the M/N values desired for each level. This function does this. 17658c2ecf20Sopenharmony_ci * 17668c2ecf20Sopenharmony_ci * This function pre-programs the CCROC NV_THERM levels in terms of 17678c2ecf20Sopenharmony_ci * pre-configured "Low", "Medium" or "Heavy" throttle levels which are 17688c2ecf20Sopenharmony_ci * mapped to THROT_LEVEL_LOW, THROT_LEVEL_MED and THROT_LEVEL_HVY. 17698c2ecf20Sopenharmony_ci */ 17708c2ecf20Sopenharmony_cistatic void throttlectl_cpu_level_cfg(struct tegra_soctherm *ts, int level) 17718c2ecf20Sopenharmony_ci{ 17728c2ecf20Sopenharmony_ci u8 depth, dividend; 17738c2ecf20Sopenharmony_ci u32 r; 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci switch (level) { 17768c2ecf20Sopenharmony_ci case TEGRA_SOCTHERM_THROT_LEVEL_LOW: 17778c2ecf20Sopenharmony_ci depth = 50; 17788c2ecf20Sopenharmony_ci break; 17798c2ecf20Sopenharmony_ci case TEGRA_SOCTHERM_THROT_LEVEL_MED: 17808c2ecf20Sopenharmony_ci depth = 75; 17818c2ecf20Sopenharmony_ci break; 17828c2ecf20Sopenharmony_ci case TEGRA_SOCTHERM_THROT_LEVEL_HIGH: 17838c2ecf20Sopenharmony_ci depth = 80; 17848c2ecf20Sopenharmony_ci break; 17858c2ecf20Sopenharmony_ci case TEGRA_SOCTHERM_THROT_LEVEL_NONE: 17868c2ecf20Sopenharmony_ci return; 17878c2ecf20Sopenharmony_ci default: 17888c2ecf20Sopenharmony_ci return; 17898c2ecf20Sopenharmony_ci } 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci dividend = THROT_DEPTH_DIVIDEND(depth); 17928c2ecf20Sopenharmony_ci 17938c2ecf20Sopenharmony_ci /* setup PSKIP in ccroc nv_therm registers */ 17948c2ecf20Sopenharmony_ci r = ccroc_readl(ts, CCROC_THROT_PSKIP_RAMP_CPU_REG(level)); 17958c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, CCROC_THROT_PSKIP_RAMP_DURATION_MASK, 0xff); 17968c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, CCROC_THROT_PSKIP_RAMP_STEP_MASK, 0xf); 17978c2ecf20Sopenharmony_ci ccroc_writel(ts, r, CCROC_THROT_PSKIP_RAMP_CPU_REG(level)); 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci r = ccroc_readl(ts, CCROC_THROT_PSKIP_CTRL_CPU_REG(level)); 18008c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, CCROC_THROT_PSKIP_CTRL_ENB_MASK, 1); 18018c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, CCROC_THROT_PSKIP_CTRL_DIVIDEND_MASK, dividend); 18028c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, CCROC_THROT_PSKIP_CTRL_DIVISOR_MASK, 0xff); 18038c2ecf20Sopenharmony_ci ccroc_writel(ts, r, CCROC_THROT_PSKIP_CTRL_CPU_REG(level)); 18048c2ecf20Sopenharmony_ci} 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci/** 18078c2ecf20Sopenharmony_ci * throttlectl_cpu_level_select() - program CPU pulse skipper config 18088c2ecf20Sopenharmony_ci * @ts: pointer to a struct tegra_soctherm 18098c2ecf20Sopenharmony_ci * @throt: the LIGHT/HEAVY of throttle event id 18108c2ecf20Sopenharmony_ci * 18118c2ecf20Sopenharmony_ci * Pulse skippers are used to throttle clock frequencies. This 18128c2ecf20Sopenharmony_ci * function programs the pulse skippers based on @throt and platform 18138c2ecf20Sopenharmony_ci * data. This function is used on SoCs which have CPU-local pulse 18148c2ecf20Sopenharmony_ci * skipper control, such as T13x. It programs soctherm's interface to 18158c2ecf20Sopenharmony_ci * Denver:CCROC NV_THERM in terms of Low, Medium and HIGH throttling 18168c2ecf20Sopenharmony_ci * vectors. PSKIP_BYPASS mode is set as required per HW spec. 18178c2ecf20Sopenharmony_ci */ 18188c2ecf20Sopenharmony_cistatic void throttlectl_cpu_level_select(struct tegra_soctherm *ts, 18198c2ecf20Sopenharmony_ci enum soctherm_throttle_id throt) 18208c2ecf20Sopenharmony_ci{ 18218c2ecf20Sopenharmony_ci u32 r, throt_vect; 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci /* Denver:CCROC NV_THERM interface N:3 Mapping */ 18248c2ecf20Sopenharmony_ci switch (ts->throt_cfgs[throt].cpu_throt_level) { 18258c2ecf20Sopenharmony_ci case TEGRA_SOCTHERM_THROT_LEVEL_LOW: 18268c2ecf20Sopenharmony_ci throt_vect = THROT_VECT_LOW; 18278c2ecf20Sopenharmony_ci break; 18288c2ecf20Sopenharmony_ci case TEGRA_SOCTHERM_THROT_LEVEL_MED: 18298c2ecf20Sopenharmony_ci throt_vect = THROT_VECT_MED; 18308c2ecf20Sopenharmony_ci break; 18318c2ecf20Sopenharmony_ci case TEGRA_SOCTHERM_THROT_LEVEL_HIGH: 18328c2ecf20Sopenharmony_ci throt_vect = THROT_VECT_HIGH; 18338c2ecf20Sopenharmony_ci break; 18348c2ecf20Sopenharmony_ci default: 18358c2ecf20Sopenharmony_ci throt_vect = THROT_VECT_NONE; 18368c2ecf20Sopenharmony_ci break; 18378c2ecf20Sopenharmony_ci } 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci r = readl(ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU)); 18408c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, THROT_PSKIP_CTRL_ENABLE_MASK, 1); 18418c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, THROT_PSKIP_CTRL_VECT_CPU_MASK, throt_vect); 18428c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, THROT_PSKIP_CTRL_VECT2_CPU_MASK, throt_vect); 18438c2ecf20Sopenharmony_ci writel(r, ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU)); 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci /* bypass sequencer in soc_therm as it is programmed in ccroc */ 18468c2ecf20Sopenharmony_ci r = REG_SET_MASK(0, THROT_PSKIP_RAMP_SEQ_BYPASS_MODE_MASK, 1); 18478c2ecf20Sopenharmony_ci writel(r, ts->regs + THROT_PSKIP_RAMP(throt, THROTTLE_DEV_CPU)); 18488c2ecf20Sopenharmony_ci} 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci/** 18518c2ecf20Sopenharmony_ci * throttlectl_cpu_mn() - program CPU pulse skipper configuration 18528c2ecf20Sopenharmony_ci * @ts: pointer to a struct tegra_soctherm 18538c2ecf20Sopenharmony_ci * @throt: the LIGHT/HEAVY of throttle event id 18548c2ecf20Sopenharmony_ci * 18558c2ecf20Sopenharmony_ci * Pulse skippers are used to throttle clock frequencies. This 18568c2ecf20Sopenharmony_ci * function programs the pulse skippers based on @throt and platform 18578c2ecf20Sopenharmony_ci * data. This function is used for CPUs that have "remote" pulse 18588c2ecf20Sopenharmony_ci * skipper control, e.g., the CPU pulse skipper is controlled by the 18598c2ecf20Sopenharmony_ci * SOC_THERM IP block. (SOC_THERM is located outside the CPU 18608c2ecf20Sopenharmony_ci * complex.) 18618c2ecf20Sopenharmony_ci */ 18628c2ecf20Sopenharmony_cistatic void throttlectl_cpu_mn(struct tegra_soctherm *ts, 18638c2ecf20Sopenharmony_ci enum soctherm_throttle_id throt) 18648c2ecf20Sopenharmony_ci{ 18658c2ecf20Sopenharmony_ci u32 r; 18668c2ecf20Sopenharmony_ci int depth; 18678c2ecf20Sopenharmony_ci u8 dividend; 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci depth = ts->throt_cfgs[throt].cpu_throt_depth; 18708c2ecf20Sopenharmony_ci dividend = THROT_DEPTH_DIVIDEND(depth); 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci r = readl(ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU)); 18738c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, THROT_PSKIP_CTRL_ENABLE_MASK, 1); 18748c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, THROT_PSKIP_CTRL_DIVIDEND_MASK, dividend); 18758c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, THROT_PSKIP_CTRL_DIVISOR_MASK, 0xff); 18768c2ecf20Sopenharmony_ci writel(r, ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU)); 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci r = readl(ts->regs + THROT_PSKIP_RAMP(throt, THROTTLE_DEV_CPU)); 18798c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, THROT_PSKIP_RAMP_DURATION_MASK, 0xff); 18808c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, THROT_PSKIP_RAMP_STEP_MASK, 0xf); 18818c2ecf20Sopenharmony_ci writel(r, ts->regs + THROT_PSKIP_RAMP(throt, THROTTLE_DEV_CPU)); 18828c2ecf20Sopenharmony_ci} 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci/** 18858c2ecf20Sopenharmony_ci * throttlectl_gpu_level_select() - selects throttling level for GPU 18868c2ecf20Sopenharmony_ci * @ts: pointer to a struct tegra_soctherm 18878c2ecf20Sopenharmony_ci * @throt: the LIGHT/HEAVY of throttle event id 18888c2ecf20Sopenharmony_ci * 18898c2ecf20Sopenharmony_ci * This function programs soctherm's interface to GK20a NV_THERM to select 18908c2ecf20Sopenharmony_ci * pre-configured "Low", "Medium" or "Heavy" throttle levels. 18918c2ecf20Sopenharmony_ci * 18928c2ecf20Sopenharmony_ci * Return: boolean true if HW was programmed 18938c2ecf20Sopenharmony_ci */ 18948c2ecf20Sopenharmony_cistatic void throttlectl_gpu_level_select(struct tegra_soctherm *ts, 18958c2ecf20Sopenharmony_ci enum soctherm_throttle_id throt) 18968c2ecf20Sopenharmony_ci{ 18978c2ecf20Sopenharmony_ci u32 r, level, throt_vect; 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci level = ts->throt_cfgs[throt].gpu_throt_level; 19008c2ecf20Sopenharmony_ci throt_vect = THROT_LEVEL_TO_DEPTH(level); 19018c2ecf20Sopenharmony_ci r = readl(ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_GPU)); 19028c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, THROT_PSKIP_CTRL_ENABLE_MASK, 1); 19038c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, THROT_PSKIP_CTRL_VECT_GPU_MASK, throt_vect); 19048c2ecf20Sopenharmony_ci writel(r, ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_GPU)); 19058c2ecf20Sopenharmony_ci} 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_cistatic int soctherm_oc_cfg_program(struct tegra_soctherm *ts, 19088c2ecf20Sopenharmony_ci enum soctherm_throttle_id throt) 19098c2ecf20Sopenharmony_ci{ 19108c2ecf20Sopenharmony_ci u32 r; 19118c2ecf20Sopenharmony_ci struct soctherm_oc_cfg *oc = &ts->throt_cfgs[throt].oc_cfg; 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci if (oc->mode == OC_THROTTLE_MODE_DISABLED) 19148c2ecf20Sopenharmony_ci return -EINVAL; 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci r = REG_SET_MASK(0, OC1_CFG_HW_RESTORE_MASK, 1); 19178c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, OC1_CFG_THROTTLE_MODE_MASK, oc->mode); 19188c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, OC1_CFG_ALARM_POLARITY_MASK, oc->active_low); 19198c2ecf20Sopenharmony_ci r = REG_SET_MASK(r, OC1_CFG_EN_THROTTLE_MASK, 1); 19208c2ecf20Sopenharmony_ci writel(r, ts->regs + ALARM_CFG(throt)); 19218c2ecf20Sopenharmony_ci writel(oc->throt_period, ts->regs + ALARM_THROTTLE_PERIOD(throt)); 19228c2ecf20Sopenharmony_ci writel(oc->alarm_cnt_thresh, ts->regs + ALARM_CNT_THRESHOLD(throt)); 19238c2ecf20Sopenharmony_ci writel(oc->alarm_filter, ts->regs + ALARM_FILTER(throt)); 19248c2ecf20Sopenharmony_ci soctherm_oc_intr_enable(ts, throt, oc->intr_en); 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci return 0; 19278c2ecf20Sopenharmony_ci} 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci/** 19308c2ecf20Sopenharmony_ci * soctherm_throttle_program() - programs pulse skippers' configuration 19318c2ecf20Sopenharmony_ci * @ts: pointer to a struct tegra_soctherm 19328c2ecf20Sopenharmony_ci * @throt: the LIGHT/HEAVY of the throttle event id. 19338c2ecf20Sopenharmony_ci * 19348c2ecf20Sopenharmony_ci * Pulse skippers are used to throttle clock frequencies. 19358c2ecf20Sopenharmony_ci * This function programs the pulse skippers. 19368c2ecf20Sopenharmony_ci */ 19378c2ecf20Sopenharmony_cistatic void soctherm_throttle_program(struct tegra_soctherm *ts, 19388c2ecf20Sopenharmony_ci enum soctherm_throttle_id throt) 19398c2ecf20Sopenharmony_ci{ 19408c2ecf20Sopenharmony_ci u32 r; 19418c2ecf20Sopenharmony_ci struct soctherm_throt_cfg stc = ts->throt_cfgs[throt]; 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci if (!stc.init) 19448c2ecf20Sopenharmony_ci return; 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci if ((throt >= THROTTLE_OC1) && (soctherm_oc_cfg_program(ts, throt))) 19478c2ecf20Sopenharmony_ci return; 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_ci /* Setup PSKIP parameters */ 19508c2ecf20Sopenharmony_ci if (ts->soc->use_ccroc) 19518c2ecf20Sopenharmony_ci throttlectl_cpu_level_select(ts, throt); 19528c2ecf20Sopenharmony_ci else 19538c2ecf20Sopenharmony_ci throttlectl_cpu_mn(ts, throt); 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci throttlectl_gpu_level_select(ts, throt); 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci r = REG_SET_MASK(0, THROT_PRIORITY_LITE_PRIO_MASK, stc.priority); 19588c2ecf20Sopenharmony_ci writel(r, ts->regs + THROT_PRIORITY_CTRL(throt)); 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci r = REG_SET_MASK(0, THROT_DELAY_LITE_DELAY_MASK, 0); 19618c2ecf20Sopenharmony_ci writel(r, ts->regs + THROT_DELAY_CTRL(throt)); 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci r = readl(ts->regs + THROT_PRIORITY_LOCK); 19648c2ecf20Sopenharmony_ci r = REG_GET_MASK(r, THROT_PRIORITY_LOCK_PRIORITY_MASK); 19658c2ecf20Sopenharmony_ci if (r >= stc.priority) 19668c2ecf20Sopenharmony_ci return; 19678c2ecf20Sopenharmony_ci r = REG_SET_MASK(0, THROT_PRIORITY_LOCK_PRIORITY_MASK, 19688c2ecf20Sopenharmony_ci stc.priority); 19698c2ecf20Sopenharmony_ci writel(r, ts->regs + THROT_PRIORITY_LOCK); 19708c2ecf20Sopenharmony_ci} 19718c2ecf20Sopenharmony_ci 19728c2ecf20Sopenharmony_cistatic void tegra_soctherm_throttle(struct device *dev) 19738c2ecf20Sopenharmony_ci{ 19748c2ecf20Sopenharmony_ci struct tegra_soctherm *ts = dev_get_drvdata(dev); 19758c2ecf20Sopenharmony_ci u32 v; 19768c2ecf20Sopenharmony_ci int i; 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ci /* configure LOW, MED and HIGH levels for CCROC NV_THERM */ 19798c2ecf20Sopenharmony_ci if (ts->soc->use_ccroc) { 19808c2ecf20Sopenharmony_ci throttlectl_cpu_level_cfg(ts, TEGRA_SOCTHERM_THROT_LEVEL_LOW); 19818c2ecf20Sopenharmony_ci throttlectl_cpu_level_cfg(ts, TEGRA_SOCTHERM_THROT_LEVEL_MED); 19828c2ecf20Sopenharmony_ci throttlectl_cpu_level_cfg(ts, TEGRA_SOCTHERM_THROT_LEVEL_HIGH); 19838c2ecf20Sopenharmony_ci } 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci /* Thermal HW throttle programming */ 19868c2ecf20Sopenharmony_ci for (i = 0; i < THROTTLE_SIZE; i++) 19878c2ecf20Sopenharmony_ci soctherm_throttle_program(ts, i); 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_ci v = REG_SET_MASK(0, THROT_GLOBAL_ENB_MASK, 1); 19908c2ecf20Sopenharmony_ci if (ts->soc->use_ccroc) { 19918c2ecf20Sopenharmony_ci ccroc_writel(ts, v, CCROC_GLOBAL_CFG); 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci v = ccroc_readl(ts, CCROC_SUPER_CCLKG_DIVIDER); 19948c2ecf20Sopenharmony_ci v = REG_SET_MASK(v, CDIVG_USE_THERM_CONTROLS_MASK, 1); 19958c2ecf20Sopenharmony_ci ccroc_writel(ts, v, CCROC_SUPER_CCLKG_DIVIDER); 19968c2ecf20Sopenharmony_ci } else { 19978c2ecf20Sopenharmony_ci writel(v, ts->regs + THROT_GLOBAL_CFG); 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci v = readl(ts->clk_regs + CAR_SUPER_CCLKG_DIVIDER); 20008c2ecf20Sopenharmony_ci v = REG_SET_MASK(v, CDIVG_USE_THERM_CONTROLS_MASK, 1); 20018c2ecf20Sopenharmony_ci writel(v, ts->clk_regs + CAR_SUPER_CCLKG_DIVIDER); 20028c2ecf20Sopenharmony_ci } 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci /* initialize stats collection */ 20058c2ecf20Sopenharmony_ci v = STATS_CTL_CLR_DN | STATS_CTL_EN_DN | 20068c2ecf20Sopenharmony_ci STATS_CTL_CLR_UP | STATS_CTL_EN_UP; 20078c2ecf20Sopenharmony_ci writel(v, ts->regs + THERMCTL_STATS_CTL); 20088c2ecf20Sopenharmony_ci} 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_cistatic int soctherm_interrupts_init(struct platform_device *pdev, 20118c2ecf20Sopenharmony_ci struct tegra_soctherm *tegra) 20128c2ecf20Sopenharmony_ci{ 20138c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 20148c2ecf20Sopenharmony_ci int ret; 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci ret = soctherm_oc_int_init(np, TEGRA_SOC_OC_IRQ_MAX); 20178c2ecf20Sopenharmony_ci if (ret < 0) { 20188c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "soctherm_oc_int_init failed\n"); 20198c2ecf20Sopenharmony_ci return ret; 20208c2ecf20Sopenharmony_ci } 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci tegra->thermal_irq = platform_get_irq(pdev, 0); 20238c2ecf20Sopenharmony_ci if (tegra->thermal_irq < 0) { 20248c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "get 'thermal_irq' failed.\n"); 20258c2ecf20Sopenharmony_ci return 0; 20268c2ecf20Sopenharmony_ci } 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci tegra->edp_irq = platform_get_irq(pdev, 1); 20298c2ecf20Sopenharmony_ci if (tegra->edp_irq < 0) { 20308c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "get 'edp_irq' failed.\n"); 20318c2ecf20Sopenharmony_ci return 0; 20328c2ecf20Sopenharmony_ci } 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, 20358c2ecf20Sopenharmony_ci tegra->thermal_irq, 20368c2ecf20Sopenharmony_ci soctherm_thermal_isr, 20378c2ecf20Sopenharmony_ci soctherm_thermal_isr_thread, 20388c2ecf20Sopenharmony_ci IRQF_ONESHOT, 20398c2ecf20Sopenharmony_ci dev_name(&pdev->dev), 20408c2ecf20Sopenharmony_ci tegra); 20418c2ecf20Sopenharmony_ci if (ret < 0) { 20428c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "request_irq 'thermal_irq' failed.\n"); 20438c2ecf20Sopenharmony_ci return ret; 20448c2ecf20Sopenharmony_ci } 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, 20478c2ecf20Sopenharmony_ci tegra->edp_irq, 20488c2ecf20Sopenharmony_ci soctherm_edp_isr, 20498c2ecf20Sopenharmony_ci soctherm_edp_isr_thread, 20508c2ecf20Sopenharmony_ci IRQF_ONESHOT, 20518c2ecf20Sopenharmony_ci "soctherm_edp", 20528c2ecf20Sopenharmony_ci tegra); 20538c2ecf20Sopenharmony_ci if (ret < 0) { 20548c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "request_irq 'edp_irq' failed.\n"); 20558c2ecf20Sopenharmony_ci return ret; 20568c2ecf20Sopenharmony_ci } 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci return 0; 20598c2ecf20Sopenharmony_ci} 20608c2ecf20Sopenharmony_ci 20618c2ecf20Sopenharmony_cistatic void soctherm_init(struct platform_device *pdev) 20628c2ecf20Sopenharmony_ci{ 20638c2ecf20Sopenharmony_ci struct tegra_soctherm *tegra = platform_get_drvdata(pdev); 20648c2ecf20Sopenharmony_ci const struct tegra_tsensor_group **ttgs = tegra->soc->ttgs; 20658c2ecf20Sopenharmony_ci int i; 20668c2ecf20Sopenharmony_ci u32 pdiv, hotspot; 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci /* Initialize raw sensors */ 20698c2ecf20Sopenharmony_ci for (i = 0; i < tegra->soc->num_tsensors; ++i) 20708c2ecf20Sopenharmony_ci enable_tsensor(tegra, i); 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci /* program pdiv and hotspot offsets per THERM */ 20738c2ecf20Sopenharmony_ci pdiv = readl(tegra->regs + SENSOR_PDIV); 20748c2ecf20Sopenharmony_ci hotspot = readl(tegra->regs + SENSOR_HOTSPOT_OFF); 20758c2ecf20Sopenharmony_ci for (i = 0; i < tegra->soc->num_ttgs; ++i) { 20768c2ecf20Sopenharmony_ci pdiv = REG_SET_MASK(pdiv, ttgs[i]->pdiv_mask, 20778c2ecf20Sopenharmony_ci ttgs[i]->pdiv); 20788c2ecf20Sopenharmony_ci /* hotspot offset from PLLX, doesn't need to configure PLLX */ 20798c2ecf20Sopenharmony_ci if (ttgs[i]->id == TEGRA124_SOCTHERM_SENSOR_PLLX) 20808c2ecf20Sopenharmony_ci continue; 20818c2ecf20Sopenharmony_ci hotspot = REG_SET_MASK(hotspot, 20828c2ecf20Sopenharmony_ci ttgs[i]->pllx_hotspot_mask, 20838c2ecf20Sopenharmony_ci ttgs[i]->pllx_hotspot_diff); 20848c2ecf20Sopenharmony_ci } 20858c2ecf20Sopenharmony_ci writel(pdiv, tegra->regs + SENSOR_PDIV); 20868c2ecf20Sopenharmony_ci writel(hotspot, tegra->regs + SENSOR_HOTSPOT_OFF); 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ci /* Configure hw throttle */ 20898c2ecf20Sopenharmony_ci tegra_soctherm_throttle(&pdev->dev); 20908c2ecf20Sopenharmony_ci} 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_cistatic const struct of_device_id tegra_soctherm_of_match[] = { 20938c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_124_SOC 20948c2ecf20Sopenharmony_ci { 20958c2ecf20Sopenharmony_ci .compatible = "nvidia,tegra124-soctherm", 20968c2ecf20Sopenharmony_ci .data = &tegra124_soctherm, 20978c2ecf20Sopenharmony_ci }, 20988c2ecf20Sopenharmony_ci#endif 20998c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_132_SOC 21008c2ecf20Sopenharmony_ci { 21018c2ecf20Sopenharmony_ci .compatible = "nvidia,tegra132-soctherm", 21028c2ecf20Sopenharmony_ci .data = &tegra132_soctherm, 21038c2ecf20Sopenharmony_ci }, 21048c2ecf20Sopenharmony_ci#endif 21058c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_210_SOC 21068c2ecf20Sopenharmony_ci { 21078c2ecf20Sopenharmony_ci .compatible = "nvidia,tegra210-soctherm", 21088c2ecf20Sopenharmony_ci .data = &tegra210_soctherm, 21098c2ecf20Sopenharmony_ci }, 21108c2ecf20Sopenharmony_ci#endif 21118c2ecf20Sopenharmony_ci { }, 21128c2ecf20Sopenharmony_ci}; 21138c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_soctherm_of_match); 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_cistatic int tegra_soctherm_probe(struct platform_device *pdev) 21168c2ecf20Sopenharmony_ci{ 21178c2ecf20Sopenharmony_ci const struct of_device_id *match; 21188c2ecf20Sopenharmony_ci struct tegra_soctherm *tegra; 21198c2ecf20Sopenharmony_ci struct thermal_zone_device *z; 21208c2ecf20Sopenharmony_ci struct tsensor_shared_calib shared_calib; 21218c2ecf20Sopenharmony_ci struct resource *res; 21228c2ecf20Sopenharmony_ci struct tegra_soctherm_soc *soc; 21238c2ecf20Sopenharmony_ci unsigned int i; 21248c2ecf20Sopenharmony_ci int err; 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci match = of_match_node(tegra_soctherm_of_match, pdev->dev.of_node); 21278c2ecf20Sopenharmony_ci if (!match) 21288c2ecf20Sopenharmony_ci return -ENODEV; 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci soc = (struct tegra_soctherm_soc *)match->data; 21318c2ecf20Sopenharmony_ci if (soc->num_ttgs > TEGRA124_SOCTHERM_SENSOR_NUM) 21328c2ecf20Sopenharmony_ci return -EINVAL; 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ci tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); 21358c2ecf20Sopenharmony_ci if (!tegra) 21368c2ecf20Sopenharmony_ci return -ENOMEM; 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci mutex_init(&tegra->thermctl_lock); 21398c2ecf20Sopenharmony_ci dev_set_drvdata(&pdev->dev, tegra); 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ci tegra->soc = soc; 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 21448c2ecf20Sopenharmony_ci "soctherm-reg"); 21458c2ecf20Sopenharmony_ci tegra->regs = devm_ioremap_resource(&pdev->dev, res); 21468c2ecf20Sopenharmony_ci if (IS_ERR(tegra->regs)) { 21478c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "can't get soctherm registers"); 21488c2ecf20Sopenharmony_ci return PTR_ERR(tegra->regs); 21498c2ecf20Sopenharmony_ci } 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci if (!tegra->soc->use_ccroc) { 21528c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 21538c2ecf20Sopenharmony_ci "car-reg"); 21548c2ecf20Sopenharmony_ci tegra->clk_regs = devm_ioremap_resource(&pdev->dev, res); 21558c2ecf20Sopenharmony_ci if (IS_ERR(tegra->clk_regs)) { 21568c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "can't get car clk registers"); 21578c2ecf20Sopenharmony_ci return PTR_ERR(tegra->clk_regs); 21588c2ecf20Sopenharmony_ci } 21598c2ecf20Sopenharmony_ci } else { 21608c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 21618c2ecf20Sopenharmony_ci "ccroc-reg"); 21628c2ecf20Sopenharmony_ci tegra->ccroc_regs = devm_ioremap_resource(&pdev->dev, res); 21638c2ecf20Sopenharmony_ci if (IS_ERR(tegra->ccroc_regs)) { 21648c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "can't get ccroc registers"); 21658c2ecf20Sopenharmony_ci return PTR_ERR(tegra->ccroc_regs); 21668c2ecf20Sopenharmony_ci } 21678c2ecf20Sopenharmony_ci } 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_ci tegra->reset = devm_reset_control_get(&pdev->dev, "soctherm"); 21708c2ecf20Sopenharmony_ci if (IS_ERR(tegra->reset)) { 21718c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "can't get soctherm reset\n"); 21728c2ecf20Sopenharmony_ci return PTR_ERR(tegra->reset); 21738c2ecf20Sopenharmony_ci } 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci tegra->clock_tsensor = devm_clk_get(&pdev->dev, "tsensor"); 21768c2ecf20Sopenharmony_ci if (IS_ERR(tegra->clock_tsensor)) { 21778c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "can't get tsensor clock\n"); 21788c2ecf20Sopenharmony_ci return PTR_ERR(tegra->clock_tsensor); 21798c2ecf20Sopenharmony_ci } 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci tegra->clock_soctherm = devm_clk_get(&pdev->dev, "soctherm"); 21828c2ecf20Sopenharmony_ci if (IS_ERR(tegra->clock_soctherm)) { 21838c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "can't get soctherm clock\n"); 21848c2ecf20Sopenharmony_ci return PTR_ERR(tegra->clock_soctherm); 21858c2ecf20Sopenharmony_ci } 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_ci tegra->calib = devm_kcalloc(&pdev->dev, 21888c2ecf20Sopenharmony_ci soc->num_tsensors, sizeof(u32), 21898c2ecf20Sopenharmony_ci GFP_KERNEL); 21908c2ecf20Sopenharmony_ci if (!tegra->calib) 21918c2ecf20Sopenharmony_ci return -ENOMEM; 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci /* calculate shared calibration data */ 21948c2ecf20Sopenharmony_ci err = tegra_calc_shared_calib(soc->tfuse, &shared_calib); 21958c2ecf20Sopenharmony_ci if (err) 21968c2ecf20Sopenharmony_ci return err; 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_ci /* calculate tsensor calibaration data */ 21998c2ecf20Sopenharmony_ci for (i = 0; i < soc->num_tsensors; ++i) { 22008c2ecf20Sopenharmony_ci err = tegra_calc_tsensor_calib(&soc->tsensors[i], 22018c2ecf20Sopenharmony_ci &shared_calib, 22028c2ecf20Sopenharmony_ci &tegra->calib[i]); 22038c2ecf20Sopenharmony_ci if (err) 22048c2ecf20Sopenharmony_ci return err; 22058c2ecf20Sopenharmony_ci } 22068c2ecf20Sopenharmony_ci 22078c2ecf20Sopenharmony_ci tegra->thermctl_tzs = devm_kcalloc(&pdev->dev, 22088c2ecf20Sopenharmony_ci soc->num_ttgs, sizeof(z), 22098c2ecf20Sopenharmony_ci GFP_KERNEL); 22108c2ecf20Sopenharmony_ci if (!tegra->thermctl_tzs) 22118c2ecf20Sopenharmony_ci return -ENOMEM; 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci err = soctherm_clk_enable(pdev, true); 22148c2ecf20Sopenharmony_ci if (err) 22158c2ecf20Sopenharmony_ci return err; 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci soctherm_thermtrips_parse(pdev); 22188c2ecf20Sopenharmony_ci 22198c2ecf20Sopenharmony_ci soctherm_init_hw_throt_cdev(pdev); 22208c2ecf20Sopenharmony_ci 22218c2ecf20Sopenharmony_ci soctherm_init(pdev); 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci for (i = 0; i < soc->num_ttgs; ++i) { 22248c2ecf20Sopenharmony_ci struct tegra_thermctl_zone *zone = 22258c2ecf20Sopenharmony_ci devm_kzalloc(&pdev->dev, sizeof(*zone), GFP_KERNEL); 22268c2ecf20Sopenharmony_ci if (!zone) { 22278c2ecf20Sopenharmony_ci err = -ENOMEM; 22288c2ecf20Sopenharmony_ci goto disable_clocks; 22298c2ecf20Sopenharmony_ci } 22308c2ecf20Sopenharmony_ci 22318c2ecf20Sopenharmony_ci zone->reg = tegra->regs + soc->ttgs[i]->sensor_temp_offset; 22328c2ecf20Sopenharmony_ci zone->dev = &pdev->dev; 22338c2ecf20Sopenharmony_ci zone->sg = soc->ttgs[i]; 22348c2ecf20Sopenharmony_ci zone->ts = tegra; 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_ci z = devm_thermal_zone_of_sensor_register(&pdev->dev, 22378c2ecf20Sopenharmony_ci soc->ttgs[i]->id, zone, 22388c2ecf20Sopenharmony_ci &tegra_of_thermal_ops); 22398c2ecf20Sopenharmony_ci if (IS_ERR(z)) { 22408c2ecf20Sopenharmony_ci err = PTR_ERR(z); 22418c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register sensor: %d\n", 22428c2ecf20Sopenharmony_ci err); 22438c2ecf20Sopenharmony_ci goto disable_clocks; 22448c2ecf20Sopenharmony_ci } 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_ci zone->tz = z; 22478c2ecf20Sopenharmony_ci tegra->thermctl_tzs[soc->ttgs[i]->id] = z; 22488c2ecf20Sopenharmony_ci 22498c2ecf20Sopenharmony_ci /* Configure hw trip points */ 22508c2ecf20Sopenharmony_ci err = tegra_soctherm_set_hwtrips(&pdev->dev, soc->ttgs[i], z); 22518c2ecf20Sopenharmony_ci if (err) 22528c2ecf20Sopenharmony_ci goto disable_clocks; 22538c2ecf20Sopenharmony_ci } 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci err = soctherm_interrupts_init(pdev, tegra); 22568c2ecf20Sopenharmony_ci 22578c2ecf20Sopenharmony_ci soctherm_debug_init(pdev); 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_ci return 0; 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_cidisable_clocks: 22628c2ecf20Sopenharmony_ci soctherm_clk_enable(pdev, false); 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_ci return err; 22658c2ecf20Sopenharmony_ci} 22668c2ecf20Sopenharmony_ci 22678c2ecf20Sopenharmony_cistatic int tegra_soctherm_remove(struct platform_device *pdev) 22688c2ecf20Sopenharmony_ci{ 22698c2ecf20Sopenharmony_ci struct tegra_soctherm *tegra = platform_get_drvdata(pdev); 22708c2ecf20Sopenharmony_ci 22718c2ecf20Sopenharmony_ci debugfs_remove_recursive(tegra->debugfs_dir); 22728c2ecf20Sopenharmony_ci 22738c2ecf20Sopenharmony_ci soctherm_clk_enable(pdev, false); 22748c2ecf20Sopenharmony_ci 22758c2ecf20Sopenharmony_ci return 0; 22768c2ecf20Sopenharmony_ci} 22778c2ecf20Sopenharmony_ci 22788c2ecf20Sopenharmony_cistatic int __maybe_unused soctherm_suspend(struct device *dev) 22798c2ecf20Sopenharmony_ci{ 22808c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci soctherm_clk_enable(pdev, false); 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci return 0; 22858c2ecf20Sopenharmony_ci} 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_cistatic int __maybe_unused soctherm_resume(struct device *dev) 22888c2ecf20Sopenharmony_ci{ 22898c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 22908c2ecf20Sopenharmony_ci struct tegra_soctherm *tegra = platform_get_drvdata(pdev); 22918c2ecf20Sopenharmony_ci struct tegra_soctherm_soc *soc = tegra->soc; 22928c2ecf20Sopenharmony_ci int err, i; 22938c2ecf20Sopenharmony_ci 22948c2ecf20Sopenharmony_ci err = soctherm_clk_enable(pdev, true); 22958c2ecf20Sopenharmony_ci if (err) { 22968c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 22978c2ecf20Sopenharmony_ci "Resume failed: enable clocks failed\n"); 22988c2ecf20Sopenharmony_ci return err; 22998c2ecf20Sopenharmony_ci } 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ci soctherm_init(pdev); 23028c2ecf20Sopenharmony_ci 23038c2ecf20Sopenharmony_ci for (i = 0; i < soc->num_ttgs; ++i) { 23048c2ecf20Sopenharmony_ci struct thermal_zone_device *tz; 23058c2ecf20Sopenharmony_ci 23068c2ecf20Sopenharmony_ci tz = tegra->thermctl_tzs[soc->ttgs[i]->id]; 23078c2ecf20Sopenharmony_ci err = tegra_soctherm_set_hwtrips(dev, soc->ttgs[i], tz); 23088c2ecf20Sopenharmony_ci if (err) { 23098c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 23108c2ecf20Sopenharmony_ci "Resume failed: set hwtrips failed\n"); 23118c2ecf20Sopenharmony_ci return err; 23128c2ecf20Sopenharmony_ci } 23138c2ecf20Sopenharmony_ci } 23148c2ecf20Sopenharmony_ci 23158c2ecf20Sopenharmony_ci return 0; 23168c2ecf20Sopenharmony_ci} 23178c2ecf20Sopenharmony_ci 23188c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(tegra_soctherm_pm, soctherm_suspend, soctherm_resume); 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_cistatic struct platform_driver tegra_soctherm_driver = { 23218c2ecf20Sopenharmony_ci .probe = tegra_soctherm_probe, 23228c2ecf20Sopenharmony_ci .remove = tegra_soctherm_remove, 23238c2ecf20Sopenharmony_ci .driver = { 23248c2ecf20Sopenharmony_ci .name = "tegra_soctherm", 23258c2ecf20Sopenharmony_ci .pm = &tegra_soctherm_pm, 23268c2ecf20Sopenharmony_ci .of_match_table = tegra_soctherm_of_match, 23278c2ecf20Sopenharmony_ci }, 23288c2ecf20Sopenharmony_ci}; 23298c2ecf20Sopenharmony_cimodule_platform_driver(tegra_soctherm_driver); 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>"); 23328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NVIDIA Tegra SOCTHERM thermal management driver"); 23338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2334