162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2014 - 2018, NVIDIA CORPORATION. All rights reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: 662306a36Sopenharmony_ci * Mikko Perttunen <mperttunen@nvidia.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This software is licensed under the terms of the GNU General Public 962306a36Sopenharmony_ci * License version 2, as published by the Free Software Foundation, and 1062306a36Sopenharmony_ci * may be copied, distributed, and modified under those terms. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, 1362306a36Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 1462306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1562306a36Sopenharmony_ci * GNU General Public License for more details. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/debugfs.h> 2062306a36Sopenharmony_ci#include <linux/bitops.h> 2162306a36Sopenharmony_ci#include <linux/clk.h> 2262306a36Sopenharmony_ci#include <linux/delay.h> 2362306a36Sopenharmony_ci#include <linux/err.h> 2462306a36Sopenharmony_ci#include <linux/interrupt.h> 2562306a36Sopenharmony_ci#include <linux/io.h> 2662306a36Sopenharmony_ci#include <linux/irq.h> 2762306a36Sopenharmony_ci#include <linux/irqdomain.h> 2862306a36Sopenharmony_ci#include <linux/module.h> 2962306a36Sopenharmony_ci#include <linux/of.h> 3062306a36Sopenharmony_ci#include <linux/platform_device.h> 3162306a36Sopenharmony_ci#include <linux/reset.h> 3262306a36Sopenharmony_ci#include <linux/thermal.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <dt-bindings/thermal/tegra124-soctherm.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include "../thermal_core.h" 3762306a36Sopenharmony_ci#include "soctherm.h" 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define SENSOR_CONFIG0 0 4062306a36Sopenharmony_ci#define SENSOR_CONFIG0_STOP BIT(0) 4162306a36Sopenharmony_ci#define SENSOR_CONFIG0_CPTR_OVER BIT(2) 4262306a36Sopenharmony_ci#define SENSOR_CONFIG0_OVER BIT(3) 4362306a36Sopenharmony_ci#define SENSOR_CONFIG0_TCALC_OVER BIT(4) 4462306a36Sopenharmony_ci#define SENSOR_CONFIG0_TALL_MASK (0xfffff << 8) 4562306a36Sopenharmony_ci#define SENSOR_CONFIG0_TALL_SHIFT 8 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define SENSOR_CONFIG1 4 4862306a36Sopenharmony_ci#define SENSOR_CONFIG1_TSAMPLE_MASK 0x3ff 4962306a36Sopenharmony_ci#define SENSOR_CONFIG1_TSAMPLE_SHIFT 0 5062306a36Sopenharmony_ci#define SENSOR_CONFIG1_TIDDQ_EN_MASK (0x3f << 15) 5162306a36Sopenharmony_ci#define SENSOR_CONFIG1_TIDDQ_EN_SHIFT 15 5262306a36Sopenharmony_ci#define SENSOR_CONFIG1_TEN_COUNT_MASK (0x3f << 24) 5362306a36Sopenharmony_ci#define SENSOR_CONFIG1_TEN_COUNT_SHIFT 24 5462306a36Sopenharmony_ci#define SENSOR_CONFIG1_TEMP_ENABLE BIT(31) 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* 5762306a36Sopenharmony_ci * SENSOR_CONFIG2 is defined in soctherm.h 5862306a36Sopenharmony_ci * because, it will be used by tegra_soctherm_fuse.c 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define SENSOR_STATUS0 0xc 6262306a36Sopenharmony_ci#define SENSOR_STATUS0_VALID_MASK BIT(31) 6362306a36Sopenharmony_ci#define SENSOR_STATUS0_CAPTURE_MASK 0xffff 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define SENSOR_STATUS1 0x10 6662306a36Sopenharmony_ci#define SENSOR_STATUS1_TEMP_VALID_MASK BIT(31) 6762306a36Sopenharmony_ci#define SENSOR_STATUS1_TEMP_MASK 0xffff 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define READBACK_VALUE_MASK 0xff00 7062306a36Sopenharmony_ci#define READBACK_VALUE_SHIFT 8 7162306a36Sopenharmony_ci#define READBACK_ADD_HALF BIT(7) 7262306a36Sopenharmony_ci#define READBACK_NEGATE BIT(0) 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* 7562306a36Sopenharmony_ci * THERMCTL_LEVEL0_GROUP_CPU is defined in soctherm.h 7662306a36Sopenharmony_ci * because it will be used by tegraxxx_soctherm.c 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci#define THERMCTL_LVL0_CPU0_EN_MASK BIT(8) 7962306a36Sopenharmony_ci#define THERMCTL_LVL0_CPU0_CPU_THROT_MASK (0x3 << 5) 8062306a36Sopenharmony_ci#define THERMCTL_LVL0_CPU0_CPU_THROT_LIGHT 0x1 8162306a36Sopenharmony_ci#define THERMCTL_LVL0_CPU0_CPU_THROT_HEAVY 0x2 8262306a36Sopenharmony_ci#define THERMCTL_LVL0_CPU0_GPU_THROT_MASK (0x3 << 3) 8362306a36Sopenharmony_ci#define THERMCTL_LVL0_CPU0_GPU_THROT_LIGHT 0x1 8462306a36Sopenharmony_ci#define THERMCTL_LVL0_CPU0_GPU_THROT_HEAVY 0x2 8562306a36Sopenharmony_ci#define THERMCTL_LVL0_CPU0_MEM_THROT_MASK BIT(2) 8662306a36Sopenharmony_ci#define THERMCTL_LVL0_CPU0_STATUS_MASK 0x3 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#define THERMCTL_LVL0_UP_STATS 0x10 8962306a36Sopenharmony_ci#define THERMCTL_LVL0_DN_STATS 0x14 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define THERMCTL_INTR_STATUS 0x84 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#define TH_INTR_MD0_MASK BIT(25) 9462306a36Sopenharmony_ci#define TH_INTR_MU0_MASK BIT(24) 9562306a36Sopenharmony_ci#define TH_INTR_GD0_MASK BIT(17) 9662306a36Sopenharmony_ci#define TH_INTR_GU0_MASK BIT(16) 9762306a36Sopenharmony_ci#define TH_INTR_CD0_MASK BIT(9) 9862306a36Sopenharmony_ci#define TH_INTR_CU0_MASK BIT(8) 9962306a36Sopenharmony_ci#define TH_INTR_PD0_MASK BIT(1) 10062306a36Sopenharmony_ci#define TH_INTR_PU0_MASK BIT(0) 10162306a36Sopenharmony_ci#define TH_INTR_IGNORE_MASK 0xFCFCFCFC 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define THERMCTL_STATS_CTL 0x94 10462306a36Sopenharmony_ci#define STATS_CTL_CLR_DN 0x8 10562306a36Sopenharmony_ci#define STATS_CTL_EN_DN 0x4 10662306a36Sopenharmony_ci#define STATS_CTL_CLR_UP 0x2 10762306a36Sopenharmony_ci#define STATS_CTL_EN_UP 0x1 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci#define OC1_CFG 0x310 11062306a36Sopenharmony_ci#define OC1_CFG_LONG_LATENCY_MASK BIT(6) 11162306a36Sopenharmony_ci#define OC1_CFG_HW_RESTORE_MASK BIT(5) 11262306a36Sopenharmony_ci#define OC1_CFG_PWR_GOOD_MASK_MASK BIT(4) 11362306a36Sopenharmony_ci#define OC1_CFG_THROTTLE_MODE_MASK (0x3 << 2) 11462306a36Sopenharmony_ci#define OC1_CFG_ALARM_POLARITY_MASK BIT(1) 11562306a36Sopenharmony_ci#define OC1_CFG_EN_THROTTLE_MASK BIT(0) 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#define OC1_CNT_THRESHOLD 0x314 11862306a36Sopenharmony_ci#define OC1_THROTTLE_PERIOD 0x318 11962306a36Sopenharmony_ci#define OC1_ALARM_COUNT 0x31c 12062306a36Sopenharmony_ci#define OC1_FILTER 0x320 12162306a36Sopenharmony_ci#define OC1_STATS 0x3a8 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci#define OC_INTR_STATUS 0x39c 12462306a36Sopenharmony_ci#define OC_INTR_ENABLE 0x3a0 12562306a36Sopenharmony_ci#define OC_INTR_DISABLE 0x3a4 12662306a36Sopenharmony_ci#define OC_STATS_CTL 0x3c4 12762306a36Sopenharmony_ci#define OC_STATS_CTL_CLR_ALL 0x2 12862306a36Sopenharmony_ci#define OC_STATS_CTL_EN_ALL 0x1 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci#define OC_INTR_OC1_MASK BIT(0) 13162306a36Sopenharmony_ci#define OC_INTR_OC2_MASK BIT(1) 13262306a36Sopenharmony_ci#define OC_INTR_OC3_MASK BIT(2) 13362306a36Sopenharmony_ci#define OC_INTR_OC4_MASK BIT(3) 13462306a36Sopenharmony_ci#define OC_INTR_OC5_MASK BIT(4) 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci#define THROT_GLOBAL_CFG 0x400 13762306a36Sopenharmony_ci#define THROT_GLOBAL_ENB_MASK BIT(0) 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci#define CPU_PSKIP_STATUS 0x418 14062306a36Sopenharmony_ci#define XPU_PSKIP_STATUS_M_MASK (0xff << 12) 14162306a36Sopenharmony_ci#define XPU_PSKIP_STATUS_N_MASK (0xff << 4) 14262306a36Sopenharmony_ci#define XPU_PSKIP_STATUS_SW_OVERRIDE_MASK BIT(1) 14362306a36Sopenharmony_ci#define XPU_PSKIP_STATUS_ENABLED_MASK BIT(0) 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci#define THROT_PRIORITY_LOCK 0x424 14662306a36Sopenharmony_ci#define THROT_PRIORITY_LOCK_PRIORITY_MASK 0xff 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci#define THROT_STATUS 0x428 14962306a36Sopenharmony_ci#define THROT_STATUS_BREACH_MASK BIT(12) 15062306a36Sopenharmony_ci#define THROT_STATUS_STATE_MASK (0xff << 4) 15162306a36Sopenharmony_ci#define THROT_STATUS_ENABLED_MASK BIT(0) 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci#define THROT_PSKIP_CTRL_LITE_CPU 0x430 15462306a36Sopenharmony_ci#define THROT_PSKIP_CTRL_ENABLE_MASK BIT(31) 15562306a36Sopenharmony_ci#define THROT_PSKIP_CTRL_DIVIDEND_MASK (0xff << 8) 15662306a36Sopenharmony_ci#define THROT_PSKIP_CTRL_DIVISOR_MASK 0xff 15762306a36Sopenharmony_ci#define THROT_PSKIP_CTRL_VECT_GPU_MASK (0x7 << 16) 15862306a36Sopenharmony_ci#define THROT_PSKIP_CTRL_VECT_CPU_MASK (0x7 << 8) 15962306a36Sopenharmony_ci#define THROT_PSKIP_CTRL_VECT2_CPU_MASK 0x7 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci#define THROT_VECT_NONE 0x0 /* 3'b000 */ 16262306a36Sopenharmony_ci#define THROT_VECT_LOW 0x1 /* 3'b001 */ 16362306a36Sopenharmony_ci#define THROT_VECT_MED 0x3 /* 3'b011 */ 16462306a36Sopenharmony_ci#define THROT_VECT_HIGH 0x7 /* 3'b111 */ 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci#define THROT_PSKIP_RAMP_LITE_CPU 0x434 16762306a36Sopenharmony_ci#define THROT_PSKIP_RAMP_SEQ_BYPASS_MODE_MASK BIT(31) 16862306a36Sopenharmony_ci#define THROT_PSKIP_RAMP_DURATION_MASK (0xffff << 8) 16962306a36Sopenharmony_ci#define THROT_PSKIP_RAMP_STEP_MASK 0xff 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci#define THROT_PRIORITY_LITE 0x444 17262306a36Sopenharmony_ci#define THROT_PRIORITY_LITE_PRIO_MASK 0xff 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci#define THROT_DELAY_LITE 0x448 17562306a36Sopenharmony_ci#define THROT_DELAY_LITE_DELAY_MASK 0xff 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/* car register offsets needed for enabling HW throttling */ 17862306a36Sopenharmony_ci#define CAR_SUPER_CCLKG_DIVIDER 0x36c 17962306a36Sopenharmony_ci#define CDIVG_USE_THERM_CONTROLS_MASK BIT(30) 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/* ccroc register offsets needed for enabling HW throttling for Tegra132 */ 18262306a36Sopenharmony_ci#define CCROC_SUPER_CCLKG_DIVIDER 0x024 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci#define CCROC_GLOBAL_CFG 0x148 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci#define CCROC_THROT_PSKIP_RAMP_CPU 0x150 18762306a36Sopenharmony_ci#define CCROC_THROT_PSKIP_RAMP_SEQ_BYPASS_MODE_MASK BIT(31) 18862306a36Sopenharmony_ci#define CCROC_THROT_PSKIP_RAMP_DURATION_MASK (0xffff << 8) 18962306a36Sopenharmony_ci#define CCROC_THROT_PSKIP_RAMP_STEP_MASK 0xff 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci#define CCROC_THROT_PSKIP_CTRL_CPU 0x154 19262306a36Sopenharmony_ci#define CCROC_THROT_PSKIP_CTRL_ENB_MASK BIT(31) 19362306a36Sopenharmony_ci#define CCROC_THROT_PSKIP_CTRL_DIVIDEND_MASK (0xff << 8) 19462306a36Sopenharmony_ci#define CCROC_THROT_PSKIP_CTRL_DIVISOR_MASK 0xff 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/* get val from register(r) mask bits(m) */ 19762306a36Sopenharmony_ci#define REG_GET_MASK(r, m) (((r) & (m)) >> (ffs(m) - 1)) 19862306a36Sopenharmony_ci/* set val(v) to mask bits(m) of register(r) */ 19962306a36Sopenharmony_ci#define REG_SET_MASK(r, m, v) (((r) & ~(m)) | \ 20062306a36Sopenharmony_ci (((v) & (m >> (ffs(m) - 1))) << (ffs(m) - 1))) 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/* get dividend from the depth */ 20362306a36Sopenharmony_ci#define THROT_DEPTH_DIVIDEND(depth) ((256 * (100 - (depth)) / 100) - 1) 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci/* gk20a nv_therm interface N:3 Mapping. Levels defined in tegra124-soctherm.h 20662306a36Sopenharmony_ci * level vector 20762306a36Sopenharmony_ci * NONE 3'b000 20862306a36Sopenharmony_ci * LOW 3'b001 20962306a36Sopenharmony_ci * MED 3'b011 21062306a36Sopenharmony_ci * HIGH 3'b111 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci#define THROT_LEVEL_TO_DEPTH(level) ((0x1 << (level)) - 1) 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/* get THROT_PSKIP_xxx offset per LIGHT/HEAVY throt and CPU/GPU dev */ 21562306a36Sopenharmony_ci#define THROT_OFFSET 0x30 21662306a36Sopenharmony_ci#define THROT_PSKIP_CTRL(throt, dev) (THROT_PSKIP_CTRL_LITE_CPU + \ 21762306a36Sopenharmony_ci (THROT_OFFSET * throt) + (8 * dev)) 21862306a36Sopenharmony_ci#define THROT_PSKIP_RAMP(throt, dev) (THROT_PSKIP_RAMP_LITE_CPU + \ 21962306a36Sopenharmony_ci (THROT_OFFSET * throt) + (8 * dev)) 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci/* get THROT_xxx_CTRL offset per LIGHT/HEAVY throt */ 22262306a36Sopenharmony_ci#define THROT_PRIORITY_CTRL(throt) (THROT_PRIORITY_LITE + \ 22362306a36Sopenharmony_ci (THROT_OFFSET * throt)) 22462306a36Sopenharmony_ci#define THROT_DELAY_CTRL(throt) (THROT_DELAY_LITE + \ 22562306a36Sopenharmony_ci (THROT_OFFSET * throt)) 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci#define ALARM_OFFSET 0x14 22862306a36Sopenharmony_ci#define ALARM_CFG(throt) (OC1_CFG + \ 22962306a36Sopenharmony_ci (ALARM_OFFSET * (throt - THROTTLE_OC1))) 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci#define ALARM_CNT_THRESHOLD(throt) (OC1_CNT_THRESHOLD + \ 23262306a36Sopenharmony_ci (ALARM_OFFSET * (throt - THROTTLE_OC1))) 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci#define ALARM_THROTTLE_PERIOD(throt) (OC1_THROTTLE_PERIOD + \ 23562306a36Sopenharmony_ci (ALARM_OFFSET * (throt - THROTTLE_OC1))) 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci#define ALARM_ALARM_COUNT(throt) (OC1_ALARM_COUNT + \ 23862306a36Sopenharmony_ci (ALARM_OFFSET * (throt - THROTTLE_OC1))) 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci#define ALARM_FILTER(throt) (OC1_FILTER + \ 24162306a36Sopenharmony_ci (ALARM_OFFSET * (throt - THROTTLE_OC1))) 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci#define ALARM_STATS(throt) (OC1_STATS + \ 24462306a36Sopenharmony_ci (4 * (throt - THROTTLE_OC1))) 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci/* get CCROC_THROT_PSKIP_xxx offset per HIGH/MED/LOW vect*/ 24762306a36Sopenharmony_ci#define CCROC_THROT_OFFSET 0x0c 24862306a36Sopenharmony_ci#define CCROC_THROT_PSKIP_CTRL_CPU_REG(vect) (CCROC_THROT_PSKIP_CTRL_CPU + \ 24962306a36Sopenharmony_ci (CCROC_THROT_OFFSET * vect)) 25062306a36Sopenharmony_ci#define CCROC_THROT_PSKIP_RAMP_CPU_REG(vect) (CCROC_THROT_PSKIP_RAMP_CPU + \ 25162306a36Sopenharmony_ci (CCROC_THROT_OFFSET * vect)) 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci/* get THERMCTL_LEVELx offset per CPU/GPU/MEM/TSENSE rg and LEVEL0~3 lv */ 25462306a36Sopenharmony_ci#define THERMCTL_LVL_REGS_SIZE 0x20 25562306a36Sopenharmony_ci#define THERMCTL_LVL_REG(rg, lv) ((rg) + ((lv) * THERMCTL_LVL_REGS_SIZE)) 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci#define OC_THROTTLE_MODE_DISABLED 0 25862306a36Sopenharmony_ci#define OC_THROTTLE_MODE_BRIEF 2 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic const int min_low_temp = -127000; 26162306a36Sopenharmony_cistatic const int max_high_temp = 127000; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cienum soctherm_throttle_id { 26462306a36Sopenharmony_ci THROTTLE_LIGHT = 0, 26562306a36Sopenharmony_ci THROTTLE_HEAVY, 26662306a36Sopenharmony_ci THROTTLE_OC1, 26762306a36Sopenharmony_ci THROTTLE_OC2, 26862306a36Sopenharmony_ci THROTTLE_OC3, 26962306a36Sopenharmony_ci THROTTLE_OC4, 27062306a36Sopenharmony_ci THROTTLE_OC5, /* OC5 is reserved */ 27162306a36Sopenharmony_ci THROTTLE_SIZE, 27262306a36Sopenharmony_ci}; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cienum soctherm_oc_irq_id { 27562306a36Sopenharmony_ci TEGRA_SOC_OC_IRQ_1, 27662306a36Sopenharmony_ci TEGRA_SOC_OC_IRQ_2, 27762306a36Sopenharmony_ci TEGRA_SOC_OC_IRQ_3, 27862306a36Sopenharmony_ci TEGRA_SOC_OC_IRQ_4, 27962306a36Sopenharmony_ci TEGRA_SOC_OC_IRQ_5, 28062306a36Sopenharmony_ci TEGRA_SOC_OC_IRQ_MAX, 28162306a36Sopenharmony_ci}; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cienum soctherm_throttle_dev_id { 28462306a36Sopenharmony_ci THROTTLE_DEV_CPU = 0, 28562306a36Sopenharmony_ci THROTTLE_DEV_GPU, 28662306a36Sopenharmony_ci THROTTLE_DEV_SIZE, 28762306a36Sopenharmony_ci}; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic const char *const throt_names[] = { 29062306a36Sopenharmony_ci [THROTTLE_LIGHT] = "light", 29162306a36Sopenharmony_ci [THROTTLE_HEAVY] = "heavy", 29262306a36Sopenharmony_ci [THROTTLE_OC1] = "oc1", 29362306a36Sopenharmony_ci [THROTTLE_OC2] = "oc2", 29462306a36Sopenharmony_ci [THROTTLE_OC3] = "oc3", 29562306a36Sopenharmony_ci [THROTTLE_OC4] = "oc4", 29662306a36Sopenharmony_ci [THROTTLE_OC5] = "oc5", 29762306a36Sopenharmony_ci}; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistruct tegra_soctherm; 30062306a36Sopenharmony_cistruct tegra_thermctl_zone { 30162306a36Sopenharmony_ci void __iomem *reg; 30262306a36Sopenharmony_ci struct device *dev; 30362306a36Sopenharmony_ci struct tegra_soctherm *ts; 30462306a36Sopenharmony_ci struct thermal_zone_device *tz; 30562306a36Sopenharmony_ci const struct tegra_tsensor_group *sg; 30662306a36Sopenharmony_ci}; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistruct soctherm_oc_cfg { 30962306a36Sopenharmony_ci u32 active_low; 31062306a36Sopenharmony_ci u32 throt_period; 31162306a36Sopenharmony_ci u32 alarm_cnt_thresh; 31262306a36Sopenharmony_ci u32 alarm_filter; 31362306a36Sopenharmony_ci u32 mode; 31462306a36Sopenharmony_ci bool intr_en; 31562306a36Sopenharmony_ci}; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistruct soctherm_throt_cfg { 31862306a36Sopenharmony_ci const char *name; 31962306a36Sopenharmony_ci unsigned int id; 32062306a36Sopenharmony_ci u8 priority; 32162306a36Sopenharmony_ci u8 cpu_throt_level; 32262306a36Sopenharmony_ci u32 cpu_throt_depth; 32362306a36Sopenharmony_ci u32 gpu_throt_level; 32462306a36Sopenharmony_ci struct soctherm_oc_cfg oc_cfg; 32562306a36Sopenharmony_ci struct thermal_cooling_device *cdev; 32662306a36Sopenharmony_ci bool init; 32762306a36Sopenharmony_ci}; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistruct tegra_soctherm { 33062306a36Sopenharmony_ci struct reset_control *reset; 33162306a36Sopenharmony_ci struct clk *clock_tsensor; 33262306a36Sopenharmony_ci struct clk *clock_soctherm; 33362306a36Sopenharmony_ci void __iomem *regs; 33462306a36Sopenharmony_ci void __iomem *clk_regs; 33562306a36Sopenharmony_ci void __iomem *ccroc_regs; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci int thermal_irq; 33862306a36Sopenharmony_ci int edp_irq; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci u32 *calib; 34162306a36Sopenharmony_ci struct thermal_zone_device **thermctl_tzs; 34262306a36Sopenharmony_ci struct tegra_soctherm_soc *soc; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci struct soctherm_throt_cfg throt_cfgs[THROTTLE_SIZE]; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci struct dentry *debugfs_dir; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci struct mutex thermctl_lock; 34962306a36Sopenharmony_ci}; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistruct soctherm_oc_irq_chip_data { 35262306a36Sopenharmony_ci struct mutex irq_lock; /* serialize OC IRQs */ 35362306a36Sopenharmony_ci struct irq_chip irq_chip; 35462306a36Sopenharmony_ci struct irq_domain *domain; 35562306a36Sopenharmony_ci int irq_enable; 35662306a36Sopenharmony_ci}; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic struct soctherm_oc_irq_chip_data soc_irq_cdata; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci/** 36162306a36Sopenharmony_ci * ccroc_writel() - writes a value to a CCROC register 36262306a36Sopenharmony_ci * @ts: pointer to a struct tegra_soctherm 36362306a36Sopenharmony_ci * @value: the value to write 36462306a36Sopenharmony_ci * @reg: the register offset 36562306a36Sopenharmony_ci * 36662306a36Sopenharmony_ci * Writes @v to @reg. No return value. 36762306a36Sopenharmony_ci */ 36862306a36Sopenharmony_cistatic inline void ccroc_writel(struct tegra_soctherm *ts, u32 value, u32 reg) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci writel(value, (ts->ccroc_regs + reg)); 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci/** 37462306a36Sopenharmony_ci * ccroc_readl() - reads specified register from CCROC IP block 37562306a36Sopenharmony_ci * @ts: pointer to a struct tegra_soctherm 37662306a36Sopenharmony_ci * @reg: register address to be read 37762306a36Sopenharmony_ci * 37862306a36Sopenharmony_ci * Return: the value of the register 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_cistatic inline u32 ccroc_readl(struct tegra_soctherm *ts, u32 reg) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci return readl(ts->ccroc_regs + reg); 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic void enable_tsensor(struct tegra_soctherm *tegra, unsigned int i) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci const struct tegra_tsensor *sensor = &tegra->soc->tsensors[i]; 38862306a36Sopenharmony_ci void __iomem *base = tegra->regs + sensor->base; 38962306a36Sopenharmony_ci unsigned int val; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci val = sensor->config->tall << SENSOR_CONFIG0_TALL_SHIFT; 39262306a36Sopenharmony_ci writel(val, base + SENSOR_CONFIG0); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci val = (sensor->config->tsample - 1) << SENSOR_CONFIG1_TSAMPLE_SHIFT; 39562306a36Sopenharmony_ci val |= sensor->config->tiddq_en << SENSOR_CONFIG1_TIDDQ_EN_SHIFT; 39662306a36Sopenharmony_ci val |= sensor->config->ten_count << SENSOR_CONFIG1_TEN_COUNT_SHIFT; 39762306a36Sopenharmony_ci val |= SENSOR_CONFIG1_TEMP_ENABLE; 39862306a36Sopenharmony_ci writel(val, base + SENSOR_CONFIG1); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci writel(tegra->calib[i], base + SENSOR_CONFIG2); 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci/* 40462306a36Sopenharmony_ci * Translate from soctherm readback format to millicelsius. 40562306a36Sopenharmony_ci * The soctherm readback format in bits is as follows: 40662306a36Sopenharmony_ci * TTTTTTTT H______N 40762306a36Sopenharmony_ci * where T's contain the temperature in Celsius, 40862306a36Sopenharmony_ci * H denotes an addition of 0.5 Celsius and N denotes negation 40962306a36Sopenharmony_ci * of the final value. 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_cistatic int translate_temp(u16 val) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci int t; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci t = ((val & READBACK_VALUE_MASK) >> READBACK_VALUE_SHIFT) * 1000; 41662306a36Sopenharmony_ci if (val & READBACK_ADD_HALF) 41762306a36Sopenharmony_ci t += 500; 41862306a36Sopenharmony_ci if (val & READBACK_NEGATE) 41962306a36Sopenharmony_ci t *= -1; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return t; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic int tegra_thermctl_get_temp(struct thermal_zone_device *tz, int *out_temp) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct tegra_thermctl_zone *zone = thermal_zone_device_priv(tz); 42762306a36Sopenharmony_ci u32 val; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci val = readl(zone->reg); 43062306a36Sopenharmony_ci val = REG_GET_MASK(val, zone->sg->sensor_temp_mask); 43162306a36Sopenharmony_ci *out_temp = translate_temp(val); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci return 0; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci/** 43762306a36Sopenharmony_ci * enforce_temp_range() - check and enforce temperature range [min, max] 43862306a36Sopenharmony_ci * @dev: struct device * of the SOC_THERM instance 43962306a36Sopenharmony_ci * @trip_temp: the trip temperature to check 44062306a36Sopenharmony_ci * 44162306a36Sopenharmony_ci * Checks and enforces the permitted temperature range that SOC_THERM 44262306a36Sopenharmony_ci * HW can support This is 44362306a36Sopenharmony_ci * done while taking care of precision. 44462306a36Sopenharmony_ci * 44562306a36Sopenharmony_ci * Return: The precision adjusted capped temperature in millicelsius. 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_cistatic int enforce_temp_range(struct device *dev, int trip_temp) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci int temp; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci temp = clamp_val(trip_temp, min_low_temp, max_high_temp); 45262306a36Sopenharmony_ci if (temp != trip_temp) 45362306a36Sopenharmony_ci dev_dbg(dev, "soctherm: trip temperature %d forced to %d\n", 45462306a36Sopenharmony_ci trip_temp, temp); 45562306a36Sopenharmony_ci return temp; 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci/** 45962306a36Sopenharmony_ci * thermtrip_program() - Configures the hardware to shut down the 46062306a36Sopenharmony_ci * system if a given sensor group reaches a given temperature 46162306a36Sopenharmony_ci * @dev: ptr to the struct device for the SOC_THERM IP block 46262306a36Sopenharmony_ci * @sg: pointer to the sensor group to set the thermtrip temperature for 46362306a36Sopenharmony_ci * @trip_temp: the temperature in millicelsius to trigger the thermal trip at 46462306a36Sopenharmony_ci * 46562306a36Sopenharmony_ci * Sets the thermal trip threshold of the given sensor group to be the 46662306a36Sopenharmony_ci * @trip_temp. If this threshold is crossed, the hardware will shut 46762306a36Sopenharmony_ci * down. 46862306a36Sopenharmony_ci * 46962306a36Sopenharmony_ci * Note that, although @trip_temp is specified in millicelsius, the 47062306a36Sopenharmony_ci * hardware is programmed in degrees Celsius. 47162306a36Sopenharmony_ci * 47262306a36Sopenharmony_ci * Return: 0 upon success, or %-EINVAL upon failure. 47362306a36Sopenharmony_ci */ 47462306a36Sopenharmony_cistatic int thermtrip_program(struct device *dev, 47562306a36Sopenharmony_ci const struct tegra_tsensor_group *sg, 47662306a36Sopenharmony_ci int trip_temp) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci struct tegra_soctherm *ts = dev_get_drvdata(dev); 47962306a36Sopenharmony_ci int temp; 48062306a36Sopenharmony_ci u32 r; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (!sg || !sg->thermtrip_threshold_mask) 48362306a36Sopenharmony_ci return -EINVAL; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci temp = enforce_temp_range(dev, trip_temp) / ts->soc->thresh_grain; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci r = readl(ts->regs + THERMCTL_THERMTRIP_CTL); 48862306a36Sopenharmony_ci r = REG_SET_MASK(r, sg->thermtrip_threshold_mask, temp); 48962306a36Sopenharmony_ci r = REG_SET_MASK(r, sg->thermtrip_enable_mask, 1); 49062306a36Sopenharmony_ci r = REG_SET_MASK(r, sg->thermtrip_any_en_mask, 0); 49162306a36Sopenharmony_ci writel(r, ts->regs + THERMCTL_THERMTRIP_CTL); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci return 0; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci/** 49762306a36Sopenharmony_ci * throttrip_program() - Configures the hardware to throttle the 49862306a36Sopenharmony_ci * pulse if a given sensor group reaches a given temperature 49962306a36Sopenharmony_ci * @dev: ptr to the struct device for the SOC_THERM IP block 50062306a36Sopenharmony_ci * @sg: pointer to the sensor group to set the thermtrip temperature for 50162306a36Sopenharmony_ci * @stc: pointer to the throttle need to be triggered 50262306a36Sopenharmony_ci * @trip_temp: the temperature in millicelsius to trigger the thermal trip at 50362306a36Sopenharmony_ci * 50462306a36Sopenharmony_ci * Sets the thermal trip threshold and throttle event of the given sensor 50562306a36Sopenharmony_ci * group. If this threshold is crossed, the hardware will trigger the 50662306a36Sopenharmony_ci * throttle. 50762306a36Sopenharmony_ci * 50862306a36Sopenharmony_ci * Note that, although @trip_temp is specified in millicelsius, the 50962306a36Sopenharmony_ci * hardware is programmed in degrees Celsius. 51062306a36Sopenharmony_ci * 51162306a36Sopenharmony_ci * Return: 0 upon success, or %-EINVAL upon failure. 51262306a36Sopenharmony_ci */ 51362306a36Sopenharmony_cistatic int throttrip_program(struct device *dev, 51462306a36Sopenharmony_ci const struct tegra_tsensor_group *sg, 51562306a36Sopenharmony_ci struct soctherm_throt_cfg *stc, 51662306a36Sopenharmony_ci int trip_temp) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct tegra_soctherm *ts = dev_get_drvdata(dev); 51962306a36Sopenharmony_ci int temp, cpu_throt, gpu_throt; 52062306a36Sopenharmony_ci unsigned int throt; 52162306a36Sopenharmony_ci u32 r, reg_off; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if (!sg || !stc || !stc->init) 52462306a36Sopenharmony_ci return -EINVAL; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci temp = enforce_temp_range(dev, trip_temp) / ts->soc->thresh_grain; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* Hardcode LIGHT on LEVEL1 and HEAVY on LEVEL2 */ 52962306a36Sopenharmony_ci throt = stc->id; 53062306a36Sopenharmony_ci reg_off = THERMCTL_LVL_REG(sg->thermctl_lvl0_offset, throt + 1); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (throt == THROTTLE_LIGHT) { 53362306a36Sopenharmony_ci cpu_throt = THERMCTL_LVL0_CPU0_CPU_THROT_LIGHT; 53462306a36Sopenharmony_ci gpu_throt = THERMCTL_LVL0_CPU0_GPU_THROT_LIGHT; 53562306a36Sopenharmony_ci } else { 53662306a36Sopenharmony_ci cpu_throt = THERMCTL_LVL0_CPU0_CPU_THROT_HEAVY; 53762306a36Sopenharmony_ci gpu_throt = THERMCTL_LVL0_CPU0_GPU_THROT_HEAVY; 53862306a36Sopenharmony_ci if (throt != THROTTLE_HEAVY) 53962306a36Sopenharmony_ci dev_warn(dev, 54062306a36Sopenharmony_ci "invalid throt id %d - assuming HEAVY", 54162306a36Sopenharmony_ci throt); 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci r = readl(ts->regs + reg_off); 54562306a36Sopenharmony_ci r = REG_SET_MASK(r, sg->thermctl_lvl0_up_thresh_mask, temp); 54662306a36Sopenharmony_ci r = REG_SET_MASK(r, sg->thermctl_lvl0_dn_thresh_mask, temp); 54762306a36Sopenharmony_ci r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_CPU_THROT_MASK, cpu_throt); 54862306a36Sopenharmony_ci r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_GPU_THROT_MASK, gpu_throt); 54962306a36Sopenharmony_ci r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_EN_MASK, 1); 55062306a36Sopenharmony_ci writel(r, ts->regs + reg_off); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci return 0; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic struct soctherm_throt_cfg * 55662306a36Sopenharmony_cifind_throttle_cfg_by_name(struct tegra_soctherm *ts, const char *name) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci unsigned int i; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci for (i = 0; ts->throt_cfgs[i].name; i++) 56162306a36Sopenharmony_ci if (!strcmp(ts->throt_cfgs[i].name, name)) 56262306a36Sopenharmony_ci return &ts->throt_cfgs[i]; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci return NULL; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic int tsensor_group_thermtrip_get(struct tegra_soctherm *ts, int id) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci int i, temp = min_low_temp; 57062306a36Sopenharmony_ci struct tsensor_group_thermtrips *tt = ts->soc->thermtrips; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (id >= TEGRA124_SOCTHERM_SENSOR_NUM) 57362306a36Sopenharmony_ci return temp; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (tt) { 57662306a36Sopenharmony_ci for (i = 0; i < ts->soc->num_ttgs; i++) { 57762306a36Sopenharmony_ci if (tt[i].id == id) 57862306a36Sopenharmony_ci return tt[i].temp; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci return temp; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic int tegra_thermctl_set_trip_temp(struct thermal_zone_device *tz, int trip_id, int temp) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci struct tegra_thermctl_zone *zone = thermal_zone_device_priv(tz); 58862306a36Sopenharmony_ci struct tegra_soctherm *ts = zone->ts; 58962306a36Sopenharmony_ci struct thermal_trip trip; 59062306a36Sopenharmony_ci const struct tegra_tsensor_group *sg = zone->sg; 59162306a36Sopenharmony_ci struct device *dev = zone->dev; 59262306a36Sopenharmony_ci int ret; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (!tz) 59562306a36Sopenharmony_ci return -EINVAL; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci ret = __thermal_zone_get_trip(tz, trip_id, &trip); 59862306a36Sopenharmony_ci if (ret) 59962306a36Sopenharmony_ci return ret; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (trip.type == THERMAL_TRIP_CRITICAL) { 60262306a36Sopenharmony_ci /* 60362306a36Sopenharmony_ci * If thermtrips property is set in DT, 60462306a36Sopenharmony_ci * doesn't need to program critical type trip to HW, 60562306a36Sopenharmony_ci * if not, program critical trip to HW. 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_ci if (min_low_temp == tsensor_group_thermtrip_get(ts, sg->id)) 60862306a36Sopenharmony_ci return thermtrip_program(dev, sg, temp); 60962306a36Sopenharmony_ci else 61062306a36Sopenharmony_ci return 0; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci } else if (trip.type == THERMAL_TRIP_HOT) { 61362306a36Sopenharmony_ci int i; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci for (i = 0; i < THROTTLE_SIZE; i++) { 61662306a36Sopenharmony_ci struct thermal_cooling_device *cdev; 61762306a36Sopenharmony_ci struct soctherm_throt_cfg *stc; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (!ts->throt_cfgs[i].init) 62062306a36Sopenharmony_ci continue; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci cdev = ts->throt_cfgs[i].cdev; 62362306a36Sopenharmony_ci if (get_thermal_instance(tz, cdev, trip_id)) 62462306a36Sopenharmony_ci stc = find_throttle_cfg_by_name(ts, cdev->type); 62562306a36Sopenharmony_ci else 62662306a36Sopenharmony_ci continue; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci return throttrip_program(dev, sg, stc, temp); 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci return 0; 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic void thermal_irq_enable(struct tegra_thermctl_zone *zn) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci u32 r; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci /* multiple zones could be handling and setting trips at once */ 64062306a36Sopenharmony_ci mutex_lock(&zn->ts->thermctl_lock); 64162306a36Sopenharmony_ci r = readl(zn->ts->regs + THERMCTL_INTR_ENABLE); 64262306a36Sopenharmony_ci r = REG_SET_MASK(r, zn->sg->thermctl_isr_mask, TH_INTR_UP_DN_EN); 64362306a36Sopenharmony_ci writel(r, zn->ts->regs + THERMCTL_INTR_ENABLE); 64462306a36Sopenharmony_ci mutex_unlock(&zn->ts->thermctl_lock); 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic void thermal_irq_disable(struct tegra_thermctl_zone *zn) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci u32 r; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci /* multiple zones could be handling and setting trips at once */ 65262306a36Sopenharmony_ci mutex_lock(&zn->ts->thermctl_lock); 65362306a36Sopenharmony_ci r = readl(zn->ts->regs + THERMCTL_INTR_DISABLE); 65462306a36Sopenharmony_ci r = REG_SET_MASK(r, zn->sg->thermctl_isr_mask, 0); 65562306a36Sopenharmony_ci writel(r, zn->ts->regs + THERMCTL_INTR_DISABLE); 65662306a36Sopenharmony_ci mutex_unlock(&zn->ts->thermctl_lock); 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic int tegra_thermctl_set_trips(struct thermal_zone_device *tz, int lo, int hi) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci struct tegra_thermctl_zone *zone = thermal_zone_device_priv(tz); 66262306a36Sopenharmony_ci u32 r; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci thermal_irq_disable(zone); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci r = readl(zone->ts->regs + zone->sg->thermctl_lvl0_offset); 66762306a36Sopenharmony_ci r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_EN_MASK, 0); 66862306a36Sopenharmony_ci writel(r, zone->ts->regs + zone->sg->thermctl_lvl0_offset); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci lo = enforce_temp_range(zone->dev, lo) / zone->ts->soc->thresh_grain; 67162306a36Sopenharmony_ci hi = enforce_temp_range(zone->dev, hi) / zone->ts->soc->thresh_grain; 67262306a36Sopenharmony_ci dev_dbg(zone->dev, "%s hi:%d, lo:%d\n", __func__, hi, lo); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci r = REG_SET_MASK(r, zone->sg->thermctl_lvl0_up_thresh_mask, hi); 67562306a36Sopenharmony_ci r = REG_SET_MASK(r, zone->sg->thermctl_lvl0_dn_thresh_mask, lo); 67662306a36Sopenharmony_ci r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_EN_MASK, 1); 67762306a36Sopenharmony_ci writel(r, zone->ts->regs + zone->sg->thermctl_lvl0_offset); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci thermal_irq_enable(zone); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci return 0; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic const struct thermal_zone_device_ops tegra_of_thermal_ops = { 68562306a36Sopenharmony_ci .get_temp = tegra_thermctl_get_temp, 68662306a36Sopenharmony_ci .set_trip_temp = tegra_thermctl_set_trip_temp, 68762306a36Sopenharmony_ci .set_trips = tegra_thermctl_set_trips, 68862306a36Sopenharmony_ci}; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cistatic int get_hot_temp(struct thermal_zone_device *tz, int *trip_id, int *temp) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci int i, ret; 69362306a36Sopenharmony_ci struct thermal_trip trip; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci for (i = 0; i < thermal_zone_get_num_trips(tz); i++) { 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci ret = thermal_zone_get_trip(tz, i, &trip); 69862306a36Sopenharmony_ci if (ret) 69962306a36Sopenharmony_ci return -EINVAL; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (trip.type == THERMAL_TRIP_HOT) { 70262306a36Sopenharmony_ci *trip_id = i; 70362306a36Sopenharmony_ci return 0; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci return -EINVAL; 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci/** 71162306a36Sopenharmony_ci * tegra_soctherm_set_hwtrips() - set HW trip point from DT data 71262306a36Sopenharmony_ci * @dev: struct device * of the SOC_THERM instance 71362306a36Sopenharmony_ci * @sg: pointer to the sensor group to set the thermtrip temperature for 71462306a36Sopenharmony_ci * @tz: struct thermal_zone_device * 71562306a36Sopenharmony_ci * 71662306a36Sopenharmony_ci * Configure the SOC_THERM HW trip points, setting "THERMTRIP" 71762306a36Sopenharmony_ci * "THROTTLE" trip points , using "thermtrips", "critical" or "hot" 71862306a36Sopenharmony_ci * type trip_temp 71962306a36Sopenharmony_ci * from thermal zone. 72062306a36Sopenharmony_ci * After they have been configured, THERMTRIP or THROTTLE will take 72162306a36Sopenharmony_ci * action when the configured SoC thermal sensor group reaches a 72262306a36Sopenharmony_ci * certain temperature. 72362306a36Sopenharmony_ci * 72462306a36Sopenharmony_ci * Return: 0 upon success, or a negative error code on failure. 72562306a36Sopenharmony_ci * "Success" does not mean that trips was enabled; it could also 72662306a36Sopenharmony_ci * mean that no node was found in DT. 72762306a36Sopenharmony_ci * THERMTRIP has been enabled successfully when a message similar to 72862306a36Sopenharmony_ci * this one appears on the serial console: 72962306a36Sopenharmony_ci * "thermtrip: will shut down when sensor group XXX reaches YYYYYY mC" 73062306a36Sopenharmony_ci * THROTTLE has been enabled successfully when a message similar to 73162306a36Sopenharmony_ci * this one appears on the serial console: 73262306a36Sopenharmony_ci * ""throttrip: will throttle when sensor group XXX reaches YYYYYY mC" 73362306a36Sopenharmony_ci */ 73462306a36Sopenharmony_cistatic int tegra_soctherm_set_hwtrips(struct device *dev, 73562306a36Sopenharmony_ci const struct tegra_tsensor_group *sg, 73662306a36Sopenharmony_ci struct thermal_zone_device *tz) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci struct tegra_soctherm *ts = dev_get_drvdata(dev); 73962306a36Sopenharmony_ci struct soctherm_throt_cfg *stc; 74062306a36Sopenharmony_ci int i, trip, temperature, ret; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci /* Get thermtrips. If missing, try to get critical trips. */ 74362306a36Sopenharmony_ci temperature = tsensor_group_thermtrip_get(ts, sg->id); 74462306a36Sopenharmony_ci if (min_low_temp == temperature) 74562306a36Sopenharmony_ci if (thermal_zone_get_crit_temp(tz, &temperature)) 74662306a36Sopenharmony_ci temperature = max_high_temp; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci ret = thermtrip_program(dev, sg, temperature); 74962306a36Sopenharmony_ci if (ret) { 75062306a36Sopenharmony_ci dev_err(dev, "thermtrip: %s: error during enable\n", sg->name); 75162306a36Sopenharmony_ci return ret; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci dev_info(dev, "thermtrip: will shut down when %s reaches %d mC\n", 75562306a36Sopenharmony_ci sg->name, temperature); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci ret = get_hot_temp(tz, &trip, &temperature); 75862306a36Sopenharmony_ci if (ret) { 75962306a36Sopenharmony_ci dev_info(dev, "throttrip: %s: missing hot temperature\n", 76062306a36Sopenharmony_ci sg->name); 76162306a36Sopenharmony_ci return 0; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci for (i = 0; i < THROTTLE_OC1; i++) { 76562306a36Sopenharmony_ci struct thermal_cooling_device *cdev; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (!ts->throt_cfgs[i].init) 76862306a36Sopenharmony_ci continue; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci cdev = ts->throt_cfgs[i].cdev; 77162306a36Sopenharmony_ci if (get_thermal_instance(tz, cdev, trip)) 77262306a36Sopenharmony_ci stc = find_throttle_cfg_by_name(ts, cdev->type); 77362306a36Sopenharmony_ci else 77462306a36Sopenharmony_ci continue; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci ret = throttrip_program(dev, sg, stc, temperature); 77762306a36Sopenharmony_ci if (ret) { 77862306a36Sopenharmony_ci dev_err(dev, "throttrip: %s: error during enable\n", 77962306a36Sopenharmony_ci sg->name); 78062306a36Sopenharmony_ci return ret; 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci dev_info(dev, 78462306a36Sopenharmony_ci "throttrip: will throttle when %s reaches %d mC\n", 78562306a36Sopenharmony_ci sg->name, temperature); 78662306a36Sopenharmony_ci break; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (i == THROTTLE_SIZE) 79062306a36Sopenharmony_ci dev_info(dev, "throttrip: %s: missing throttle cdev\n", 79162306a36Sopenharmony_ci sg->name); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci return 0; 79462306a36Sopenharmony_ci} 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_cistatic irqreturn_t soctherm_thermal_isr(int irq, void *dev_id) 79762306a36Sopenharmony_ci{ 79862306a36Sopenharmony_ci struct tegra_soctherm *ts = dev_id; 79962306a36Sopenharmony_ci u32 r; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* Case for no lock: 80262306a36Sopenharmony_ci * Although interrupts are enabled in set_trips, there is still no need 80362306a36Sopenharmony_ci * to lock here because the interrupts are disabled before programming 80462306a36Sopenharmony_ci * new trip points. Hence there cant be a interrupt on the same sensor. 80562306a36Sopenharmony_ci * An interrupt can however occur on a sensor while trips are being 80662306a36Sopenharmony_ci * programmed on a different one. This beign a LEVEL interrupt won't 80762306a36Sopenharmony_ci * cause a new interrupt but this is taken care of by the re-reading of 80862306a36Sopenharmony_ci * the STATUS register in the thread function. 80962306a36Sopenharmony_ci */ 81062306a36Sopenharmony_ci r = readl(ts->regs + THERMCTL_INTR_STATUS); 81162306a36Sopenharmony_ci writel(r, ts->regs + THERMCTL_INTR_DISABLE); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci/** 81762306a36Sopenharmony_ci * soctherm_thermal_isr_thread() - Handles a thermal interrupt request 81862306a36Sopenharmony_ci * @irq: The interrupt number being requested; not used 81962306a36Sopenharmony_ci * @dev_id: Opaque pointer to tegra_soctherm; 82062306a36Sopenharmony_ci * 82162306a36Sopenharmony_ci * Clears the interrupt status register if there are expected 82262306a36Sopenharmony_ci * interrupt bits set. 82362306a36Sopenharmony_ci * The interrupt(s) are then handled by updating the corresponding 82462306a36Sopenharmony_ci * thermal zones. 82562306a36Sopenharmony_ci * 82662306a36Sopenharmony_ci * An error is logged if any unexpected interrupt bits are set. 82762306a36Sopenharmony_ci * 82862306a36Sopenharmony_ci * Disabled interrupts are re-enabled. 82962306a36Sopenharmony_ci * 83062306a36Sopenharmony_ci * Return: %IRQ_HANDLED. Interrupt was handled and no further processing 83162306a36Sopenharmony_ci * is needed. 83262306a36Sopenharmony_ci */ 83362306a36Sopenharmony_cistatic irqreturn_t soctherm_thermal_isr_thread(int irq, void *dev_id) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci struct tegra_soctherm *ts = dev_id; 83662306a36Sopenharmony_ci struct thermal_zone_device *tz; 83762306a36Sopenharmony_ci u32 st, ex = 0, cp = 0, gp = 0, pl = 0, me = 0; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci st = readl(ts->regs + THERMCTL_INTR_STATUS); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci /* deliberately clear expected interrupts handled in SW */ 84262306a36Sopenharmony_ci cp |= st & TH_INTR_CD0_MASK; 84362306a36Sopenharmony_ci cp |= st & TH_INTR_CU0_MASK; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci gp |= st & TH_INTR_GD0_MASK; 84662306a36Sopenharmony_ci gp |= st & TH_INTR_GU0_MASK; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci pl |= st & TH_INTR_PD0_MASK; 84962306a36Sopenharmony_ci pl |= st & TH_INTR_PU0_MASK; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci me |= st & TH_INTR_MD0_MASK; 85262306a36Sopenharmony_ci me |= st & TH_INTR_MU0_MASK; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci ex |= cp | gp | pl | me; 85562306a36Sopenharmony_ci if (ex) { 85662306a36Sopenharmony_ci writel(ex, ts->regs + THERMCTL_INTR_STATUS); 85762306a36Sopenharmony_ci st &= ~ex; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (cp) { 86062306a36Sopenharmony_ci tz = ts->thermctl_tzs[TEGRA124_SOCTHERM_SENSOR_CPU]; 86162306a36Sopenharmony_ci thermal_zone_device_update(tz, 86262306a36Sopenharmony_ci THERMAL_EVENT_UNSPECIFIED); 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if (gp) { 86662306a36Sopenharmony_ci tz = ts->thermctl_tzs[TEGRA124_SOCTHERM_SENSOR_GPU]; 86762306a36Sopenharmony_ci thermal_zone_device_update(tz, 86862306a36Sopenharmony_ci THERMAL_EVENT_UNSPECIFIED); 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (pl) { 87262306a36Sopenharmony_ci tz = ts->thermctl_tzs[TEGRA124_SOCTHERM_SENSOR_PLLX]; 87362306a36Sopenharmony_ci thermal_zone_device_update(tz, 87462306a36Sopenharmony_ci THERMAL_EVENT_UNSPECIFIED); 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci if (me) { 87862306a36Sopenharmony_ci tz = ts->thermctl_tzs[TEGRA124_SOCTHERM_SENSOR_MEM]; 87962306a36Sopenharmony_ci thermal_zone_device_update(tz, 88062306a36Sopenharmony_ci THERMAL_EVENT_UNSPECIFIED); 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci /* deliberately ignore expected interrupts NOT handled in SW */ 88562306a36Sopenharmony_ci ex |= TH_INTR_IGNORE_MASK; 88662306a36Sopenharmony_ci st &= ~ex; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci if (st) { 88962306a36Sopenharmony_ci /* Whine about any other unexpected INTR bits still set */ 89062306a36Sopenharmony_ci pr_err("soctherm: Ignored unexpected INTRs 0x%08x\n", st); 89162306a36Sopenharmony_ci writel(st, ts->regs + THERMCTL_INTR_STATUS); 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci return IRQ_HANDLED; 89562306a36Sopenharmony_ci} 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci/** 89862306a36Sopenharmony_ci * soctherm_oc_intr_enable() - Enables the soctherm over-current interrupt 89962306a36Sopenharmony_ci * @ts: pointer to a struct tegra_soctherm 90062306a36Sopenharmony_ci * @alarm: The soctherm throttle id 90162306a36Sopenharmony_ci * @enable: Flag indicating enable the soctherm over-current 90262306a36Sopenharmony_ci * interrupt or disable it 90362306a36Sopenharmony_ci * 90462306a36Sopenharmony_ci * Enables a specific over-current pins @alarm to raise an interrupt if the flag 90562306a36Sopenharmony_ci * is set and the alarm corresponds to OC1, OC2, OC3, or OC4. 90662306a36Sopenharmony_ci */ 90762306a36Sopenharmony_cistatic void soctherm_oc_intr_enable(struct tegra_soctherm *ts, 90862306a36Sopenharmony_ci enum soctherm_throttle_id alarm, 90962306a36Sopenharmony_ci bool enable) 91062306a36Sopenharmony_ci{ 91162306a36Sopenharmony_ci u32 r; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci if (!enable) 91462306a36Sopenharmony_ci return; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci r = readl(ts->regs + OC_INTR_ENABLE); 91762306a36Sopenharmony_ci switch (alarm) { 91862306a36Sopenharmony_ci case THROTTLE_OC1: 91962306a36Sopenharmony_ci r = REG_SET_MASK(r, OC_INTR_OC1_MASK, 1); 92062306a36Sopenharmony_ci break; 92162306a36Sopenharmony_ci case THROTTLE_OC2: 92262306a36Sopenharmony_ci r = REG_SET_MASK(r, OC_INTR_OC2_MASK, 1); 92362306a36Sopenharmony_ci break; 92462306a36Sopenharmony_ci case THROTTLE_OC3: 92562306a36Sopenharmony_ci r = REG_SET_MASK(r, OC_INTR_OC3_MASK, 1); 92662306a36Sopenharmony_ci break; 92762306a36Sopenharmony_ci case THROTTLE_OC4: 92862306a36Sopenharmony_ci r = REG_SET_MASK(r, OC_INTR_OC4_MASK, 1); 92962306a36Sopenharmony_ci break; 93062306a36Sopenharmony_ci default: 93162306a36Sopenharmony_ci r = 0; 93262306a36Sopenharmony_ci break; 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci writel(r, ts->regs + OC_INTR_ENABLE); 93562306a36Sopenharmony_ci} 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci/** 93862306a36Sopenharmony_ci * soctherm_handle_alarm() - Handles soctherm alarms 93962306a36Sopenharmony_ci * @alarm: The soctherm throttle id 94062306a36Sopenharmony_ci * 94162306a36Sopenharmony_ci * "Handles" over-current alarms (OC1, OC2, OC3, and OC4) by printing 94262306a36Sopenharmony_ci * a warning or informative message. 94362306a36Sopenharmony_ci * 94462306a36Sopenharmony_ci * Return: -EINVAL for @alarm = THROTTLE_OC3, otherwise 0 (success). 94562306a36Sopenharmony_ci */ 94662306a36Sopenharmony_cistatic int soctherm_handle_alarm(enum soctherm_throttle_id alarm) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci int rv = -EINVAL; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci switch (alarm) { 95162306a36Sopenharmony_ci case THROTTLE_OC1: 95262306a36Sopenharmony_ci pr_debug("soctherm: Successfully handled OC1 alarm\n"); 95362306a36Sopenharmony_ci rv = 0; 95462306a36Sopenharmony_ci break; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci case THROTTLE_OC2: 95762306a36Sopenharmony_ci pr_debug("soctherm: Successfully handled OC2 alarm\n"); 95862306a36Sopenharmony_ci rv = 0; 95962306a36Sopenharmony_ci break; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci case THROTTLE_OC3: 96262306a36Sopenharmony_ci pr_debug("soctherm: Successfully handled OC3 alarm\n"); 96362306a36Sopenharmony_ci rv = 0; 96462306a36Sopenharmony_ci break; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci case THROTTLE_OC4: 96762306a36Sopenharmony_ci pr_debug("soctherm: Successfully handled OC4 alarm\n"); 96862306a36Sopenharmony_ci rv = 0; 96962306a36Sopenharmony_ci break; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci default: 97262306a36Sopenharmony_ci break; 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci if (rv) 97662306a36Sopenharmony_ci pr_err("soctherm: ERROR in handling %s alarm\n", 97762306a36Sopenharmony_ci throt_names[alarm]); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci return rv; 98062306a36Sopenharmony_ci} 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci/** 98362306a36Sopenharmony_ci * soctherm_edp_isr_thread() - log an over-current interrupt request 98462306a36Sopenharmony_ci * @irq: OC irq number. Currently not being used. See description 98562306a36Sopenharmony_ci * @arg: a void pointer for callback, currently not being used 98662306a36Sopenharmony_ci * 98762306a36Sopenharmony_ci * Over-current events are handled in hardware. This function is called to log 98862306a36Sopenharmony_ci * and handle any OC events that happened. Additionally, it checks every 98962306a36Sopenharmony_ci * over-current interrupt registers for registers are set but 99062306a36Sopenharmony_ci * was not expected (i.e. any discrepancy in interrupt status) by the function, 99162306a36Sopenharmony_ci * the discrepancy will logged. 99262306a36Sopenharmony_ci * 99362306a36Sopenharmony_ci * Return: %IRQ_HANDLED 99462306a36Sopenharmony_ci */ 99562306a36Sopenharmony_cistatic irqreturn_t soctherm_edp_isr_thread(int irq, void *arg) 99662306a36Sopenharmony_ci{ 99762306a36Sopenharmony_ci struct tegra_soctherm *ts = arg; 99862306a36Sopenharmony_ci u32 st, ex, oc1, oc2, oc3, oc4; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci st = readl(ts->regs + OC_INTR_STATUS); 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci /* deliberately clear expected interrupts handled in SW */ 100362306a36Sopenharmony_ci oc1 = st & OC_INTR_OC1_MASK; 100462306a36Sopenharmony_ci oc2 = st & OC_INTR_OC2_MASK; 100562306a36Sopenharmony_ci oc3 = st & OC_INTR_OC3_MASK; 100662306a36Sopenharmony_ci oc4 = st & OC_INTR_OC4_MASK; 100762306a36Sopenharmony_ci ex = oc1 | oc2 | oc3 | oc4; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci pr_err("soctherm: OC ALARM 0x%08x\n", ex); 101062306a36Sopenharmony_ci if (ex) { 101162306a36Sopenharmony_ci writel(st, ts->regs + OC_INTR_STATUS); 101262306a36Sopenharmony_ci st &= ~ex; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci if (oc1 && !soctherm_handle_alarm(THROTTLE_OC1)) 101562306a36Sopenharmony_ci soctherm_oc_intr_enable(ts, THROTTLE_OC1, true); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci if (oc2 && !soctherm_handle_alarm(THROTTLE_OC2)) 101862306a36Sopenharmony_ci soctherm_oc_intr_enable(ts, THROTTLE_OC2, true); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci if (oc3 && !soctherm_handle_alarm(THROTTLE_OC3)) 102162306a36Sopenharmony_ci soctherm_oc_intr_enable(ts, THROTTLE_OC3, true); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci if (oc4 && !soctherm_handle_alarm(THROTTLE_OC4)) 102462306a36Sopenharmony_ci soctherm_oc_intr_enable(ts, THROTTLE_OC4, true); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci if (oc1 && soc_irq_cdata.irq_enable & BIT(0)) 102762306a36Sopenharmony_ci handle_nested_irq( 102862306a36Sopenharmony_ci irq_find_mapping(soc_irq_cdata.domain, 0)); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (oc2 && soc_irq_cdata.irq_enable & BIT(1)) 103162306a36Sopenharmony_ci handle_nested_irq( 103262306a36Sopenharmony_ci irq_find_mapping(soc_irq_cdata.domain, 1)); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci if (oc3 && soc_irq_cdata.irq_enable & BIT(2)) 103562306a36Sopenharmony_ci handle_nested_irq( 103662306a36Sopenharmony_ci irq_find_mapping(soc_irq_cdata.domain, 2)); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci if (oc4 && soc_irq_cdata.irq_enable & BIT(3)) 103962306a36Sopenharmony_ci handle_nested_irq( 104062306a36Sopenharmony_ci irq_find_mapping(soc_irq_cdata.domain, 3)); 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci if (st) { 104462306a36Sopenharmony_ci pr_err("soctherm: Ignored unexpected OC ALARM 0x%08x\n", st); 104562306a36Sopenharmony_ci writel(st, ts->regs + OC_INTR_STATUS); 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci return IRQ_HANDLED; 104962306a36Sopenharmony_ci} 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci/** 105262306a36Sopenharmony_ci * soctherm_edp_isr() - Disables any active interrupts 105362306a36Sopenharmony_ci * @irq: The interrupt request number 105462306a36Sopenharmony_ci * @arg: Opaque pointer to an argument 105562306a36Sopenharmony_ci * 105662306a36Sopenharmony_ci * Writes to the OC_INTR_DISABLE register the over current interrupt status, 105762306a36Sopenharmony_ci * masking any asserted interrupts. Doing this prevents the same interrupts 105862306a36Sopenharmony_ci * from triggering this isr repeatedly. The thread woken by this isr will 105962306a36Sopenharmony_ci * handle asserted interrupts and subsequently unmask/re-enable them. 106062306a36Sopenharmony_ci * 106162306a36Sopenharmony_ci * The OC_INTR_DISABLE register indicates which OC interrupts 106262306a36Sopenharmony_ci * have been disabled. 106362306a36Sopenharmony_ci * 106462306a36Sopenharmony_ci * Return: %IRQ_WAKE_THREAD, handler requests to wake the handler thread 106562306a36Sopenharmony_ci */ 106662306a36Sopenharmony_cistatic irqreturn_t soctherm_edp_isr(int irq, void *arg) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci struct tegra_soctherm *ts = arg; 106962306a36Sopenharmony_ci u32 r; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci if (!ts) 107262306a36Sopenharmony_ci return IRQ_NONE; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci r = readl(ts->regs + OC_INTR_STATUS); 107562306a36Sopenharmony_ci writel(r, ts->regs + OC_INTR_DISABLE); 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci return IRQ_WAKE_THREAD; 107862306a36Sopenharmony_ci} 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci/** 108162306a36Sopenharmony_ci * soctherm_oc_irq_lock() - locks the over-current interrupt request 108262306a36Sopenharmony_ci * @data: Interrupt request data 108362306a36Sopenharmony_ci * 108462306a36Sopenharmony_ci * Looks up the chip data from @data and locks the mutex associated with 108562306a36Sopenharmony_ci * a particular over-current interrupt request. 108662306a36Sopenharmony_ci */ 108762306a36Sopenharmony_cistatic void soctherm_oc_irq_lock(struct irq_data *data) 108862306a36Sopenharmony_ci{ 108962306a36Sopenharmony_ci struct soctherm_oc_irq_chip_data *d = irq_data_get_irq_chip_data(data); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci mutex_lock(&d->irq_lock); 109262306a36Sopenharmony_ci} 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci/** 109562306a36Sopenharmony_ci * soctherm_oc_irq_sync_unlock() - Unlocks the OC interrupt request 109662306a36Sopenharmony_ci * @data: Interrupt request data 109762306a36Sopenharmony_ci * 109862306a36Sopenharmony_ci * Looks up the interrupt request data @data and unlocks the mutex associated 109962306a36Sopenharmony_ci * with a particular over-current interrupt request. 110062306a36Sopenharmony_ci */ 110162306a36Sopenharmony_cistatic void soctherm_oc_irq_sync_unlock(struct irq_data *data) 110262306a36Sopenharmony_ci{ 110362306a36Sopenharmony_ci struct soctherm_oc_irq_chip_data *d = irq_data_get_irq_chip_data(data); 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci mutex_unlock(&d->irq_lock); 110662306a36Sopenharmony_ci} 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci/** 110962306a36Sopenharmony_ci * soctherm_oc_irq_enable() - Enables the SOC_THERM over-current interrupt queue 111062306a36Sopenharmony_ci * @data: irq_data structure of the chip 111162306a36Sopenharmony_ci * 111262306a36Sopenharmony_ci * Sets the irq_enable bit of SOC_THERM allowing SOC_THERM 111362306a36Sopenharmony_ci * to respond to over-current interrupts. 111462306a36Sopenharmony_ci * 111562306a36Sopenharmony_ci */ 111662306a36Sopenharmony_cistatic void soctherm_oc_irq_enable(struct irq_data *data) 111762306a36Sopenharmony_ci{ 111862306a36Sopenharmony_ci struct soctherm_oc_irq_chip_data *d = irq_data_get_irq_chip_data(data); 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci d->irq_enable |= BIT(data->hwirq); 112162306a36Sopenharmony_ci} 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci/** 112462306a36Sopenharmony_ci * soctherm_oc_irq_disable() - Disables overcurrent interrupt requests 112562306a36Sopenharmony_ci * @data: The interrupt request information 112662306a36Sopenharmony_ci * 112762306a36Sopenharmony_ci * Clears the interrupt request enable bit of the overcurrent 112862306a36Sopenharmony_ci * interrupt request chip data. 112962306a36Sopenharmony_ci * 113062306a36Sopenharmony_ci * Return: Nothing is returned (void) 113162306a36Sopenharmony_ci */ 113262306a36Sopenharmony_cistatic void soctherm_oc_irq_disable(struct irq_data *data) 113362306a36Sopenharmony_ci{ 113462306a36Sopenharmony_ci struct soctherm_oc_irq_chip_data *d = irq_data_get_irq_chip_data(data); 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci d->irq_enable &= ~BIT(data->hwirq); 113762306a36Sopenharmony_ci} 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_cistatic int soctherm_oc_irq_set_type(struct irq_data *data, unsigned int type) 114062306a36Sopenharmony_ci{ 114162306a36Sopenharmony_ci return 0; 114262306a36Sopenharmony_ci} 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci/** 114562306a36Sopenharmony_ci * soctherm_oc_irq_map() - SOC_THERM interrupt request domain mapper 114662306a36Sopenharmony_ci * @h: Interrupt request domain 114762306a36Sopenharmony_ci * @virq: Virtual interrupt request number 114862306a36Sopenharmony_ci * @hw: Hardware interrupt request number 114962306a36Sopenharmony_ci * 115062306a36Sopenharmony_ci * Mapping callback function for SOC_THERM's irq_domain. When a SOC_THERM 115162306a36Sopenharmony_ci * interrupt request is called, the irq_domain takes the request's virtual 115262306a36Sopenharmony_ci * request number (much like a virtual memory address) and maps it to a 115362306a36Sopenharmony_ci * physical hardware request number. 115462306a36Sopenharmony_ci * 115562306a36Sopenharmony_ci * When a mapping doesn't already exist for a virtual request number, the 115662306a36Sopenharmony_ci * irq_domain calls this function to associate the virtual request number with 115762306a36Sopenharmony_ci * a hardware request number. 115862306a36Sopenharmony_ci * 115962306a36Sopenharmony_ci * Return: 0 116062306a36Sopenharmony_ci */ 116162306a36Sopenharmony_cistatic int soctherm_oc_irq_map(struct irq_domain *h, unsigned int virq, 116262306a36Sopenharmony_ci irq_hw_number_t hw) 116362306a36Sopenharmony_ci{ 116462306a36Sopenharmony_ci struct soctherm_oc_irq_chip_data *data = h->host_data; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci irq_set_chip_data(virq, data); 116762306a36Sopenharmony_ci irq_set_chip(virq, &data->irq_chip); 116862306a36Sopenharmony_ci irq_set_nested_thread(virq, 1); 116962306a36Sopenharmony_ci return 0; 117062306a36Sopenharmony_ci} 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci/** 117362306a36Sopenharmony_ci * soctherm_irq_domain_xlate_twocell() - xlate for soctherm interrupts 117462306a36Sopenharmony_ci * @d: Interrupt request domain 117562306a36Sopenharmony_ci * @ctrlr: Controller device tree node 117662306a36Sopenharmony_ci * @intspec: Array of u32s from DTs "interrupt" property 117762306a36Sopenharmony_ci * @intsize: Number of values inside the intspec array 117862306a36Sopenharmony_ci * @out_hwirq: HW IRQ value associated with this interrupt 117962306a36Sopenharmony_ci * @out_type: The IRQ SENSE type for this interrupt. 118062306a36Sopenharmony_ci * 118162306a36Sopenharmony_ci * This Device Tree IRQ specifier translation function will translate a 118262306a36Sopenharmony_ci * specific "interrupt" as defined by 2 DT values where the cell values map 118362306a36Sopenharmony_ci * the hwirq number + 1 and linux irq flags. Since the output is the hwirq 118462306a36Sopenharmony_ci * number, this function will subtract 1 from the value listed in DT. 118562306a36Sopenharmony_ci * 118662306a36Sopenharmony_ci * Return: 0 118762306a36Sopenharmony_ci */ 118862306a36Sopenharmony_cistatic int soctherm_irq_domain_xlate_twocell(struct irq_domain *d, 118962306a36Sopenharmony_ci struct device_node *ctrlr, const u32 *intspec, unsigned int intsize, 119062306a36Sopenharmony_ci irq_hw_number_t *out_hwirq, unsigned int *out_type) 119162306a36Sopenharmony_ci{ 119262306a36Sopenharmony_ci if (WARN_ON(intsize < 2)) 119362306a36Sopenharmony_ci return -EINVAL; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci /* 119662306a36Sopenharmony_ci * The HW value is 1 index less than the DT IRQ values. 119762306a36Sopenharmony_ci * i.e. OC4 goes to HW index 3. 119862306a36Sopenharmony_ci */ 119962306a36Sopenharmony_ci *out_hwirq = intspec[0] - 1; 120062306a36Sopenharmony_ci *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK; 120162306a36Sopenharmony_ci return 0; 120262306a36Sopenharmony_ci} 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_cistatic const struct irq_domain_ops soctherm_oc_domain_ops = { 120562306a36Sopenharmony_ci .map = soctherm_oc_irq_map, 120662306a36Sopenharmony_ci .xlate = soctherm_irq_domain_xlate_twocell, 120762306a36Sopenharmony_ci}; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci/** 121062306a36Sopenharmony_ci * soctherm_oc_int_init() - Initial enabling of the over 121162306a36Sopenharmony_ci * current interrupts 121262306a36Sopenharmony_ci * @np: The devicetree node for soctherm 121362306a36Sopenharmony_ci * @num_irqs: The number of new interrupt requests 121462306a36Sopenharmony_ci * 121562306a36Sopenharmony_ci * Sets the over current interrupt request chip data 121662306a36Sopenharmony_ci * 121762306a36Sopenharmony_ci * Return: 0 on success or if overcurrent interrupts are not enabled, 121862306a36Sopenharmony_ci * -ENOMEM (out of memory), or irq_base if the function failed to 121962306a36Sopenharmony_ci * allocate the irqs 122062306a36Sopenharmony_ci */ 122162306a36Sopenharmony_cistatic int soctherm_oc_int_init(struct device_node *np, int num_irqs) 122262306a36Sopenharmony_ci{ 122362306a36Sopenharmony_ci if (!num_irqs) { 122462306a36Sopenharmony_ci pr_info("%s(): OC interrupts are not enabled\n", __func__); 122562306a36Sopenharmony_ci return 0; 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci mutex_init(&soc_irq_cdata.irq_lock); 122962306a36Sopenharmony_ci soc_irq_cdata.irq_enable = 0; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci soc_irq_cdata.irq_chip.name = "soc_therm_oc"; 123262306a36Sopenharmony_ci soc_irq_cdata.irq_chip.irq_bus_lock = soctherm_oc_irq_lock; 123362306a36Sopenharmony_ci soc_irq_cdata.irq_chip.irq_bus_sync_unlock = 123462306a36Sopenharmony_ci soctherm_oc_irq_sync_unlock; 123562306a36Sopenharmony_ci soc_irq_cdata.irq_chip.irq_disable = soctherm_oc_irq_disable; 123662306a36Sopenharmony_ci soc_irq_cdata.irq_chip.irq_enable = soctherm_oc_irq_enable; 123762306a36Sopenharmony_ci soc_irq_cdata.irq_chip.irq_set_type = soctherm_oc_irq_set_type; 123862306a36Sopenharmony_ci soc_irq_cdata.irq_chip.irq_set_wake = NULL; 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci soc_irq_cdata.domain = irq_domain_add_linear(np, num_irqs, 124162306a36Sopenharmony_ci &soctherm_oc_domain_ops, 124262306a36Sopenharmony_ci &soc_irq_cdata); 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci if (!soc_irq_cdata.domain) { 124562306a36Sopenharmony_ci pr_err("%s: Failed to create IRQ domain\n", __func__); 124662306a36Sopenharmony_ci return -ENOMEM; 124762306a36Sopenharmony_ci } 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci pr_debug("%s(): OC interrupts enabled successful\n", __func__); 125062306a36Sopenharmony_ci return 0; 125162306a36Sopenharmony_ci} 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 125462306a36Sopenharmony_cistatic int regs_show(struct seq_file *s, void *data) 125562306a36Sopenharmony_ci{ 125662306a36Sopenharmony_ci struct platform_device *pdev = s->private; 125762306a36Sopenharmony_ci struct tegra_soctherm *ts = platform_get_drvdata(pdev); 125862306a36Sopenharmony_ci const struct tegra_tsensor *tsensors = ts->soc->tsensors; 125962306a36Sopenharmony_ci const struct tegra_tsensor_group **ttgs = ts->soc->ttgs; 126062306a36Sopenharmony_ci u32 r, state; 126162306a36Sopenharmony_ci int i, level; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci seq_puts(s, "-----TSENSE (convert HW)-----\n"); 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci for (i = 0; i < ts->soc->num_tsensors; i++) { 126662306a36Sopenharmony_ci r = readl(ts->regs + tsensors[i].base + SENSOR_CONFIG1); 126762306a36Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG1_TEMP_ENABLE); 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci seq_printf(s, "%s: ", tsensors[i].name); 127062306a36Sopenharmony_ci seq_printf(s, "En(%d) ", state); 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci if (!state) { 127362306a36Sopenharmony_ci seq_puts(s, "\n"); 127462306a36Sopenharmony_ci continue; 127562306a36Sopenharmony_ci } 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG1_TIDDQ_EN_MASK); 127862306a36Sopenharmony_ci seq_printf(s, "tiddq(%d) ", state); 127962306a36Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG1_TEN_COUNT_MASK); 128062306a36Sopenharmony_ci seq_printf(s, "ten_count(%d) ", state); 128162306a36Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG1_TSAMPLE_MASK); 128262306a36Sopenharmony_ci seq_printf(s, "tsample(%d) ", state + 1); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci r = readl(ts->regs + tsensors[i].base + SENSOR_STATUS1); 128562306a36Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_STATUS1_TEMP_VALID_MASK); 128662306a36Sopenharmony_ci seq_printf(s, "Temp(%d/", state); 128762306a36Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_STATUS1_TEMP_MASK); 128862306a36Sopenharmony_ci seq_printf(s, "%d) ", translate_temp(state)); 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci r = readl(ts->regs + tsensors[i].base + SENSOR_STATUS0); 129162306a36Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_STATUS0_VALID_MASK); 129262306a36Sopenharmony_ci seq_printf(s, "Capture(%d/", state); 129362306a36Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_STATUS0_CAPTURE_MASK); 129462306a36Sopenharmony_ci seq_printf(s, "%d) ", state); 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci r = readl(ts->regs + tsensors[i].base + SENSOR_CONFIG0); 129762306a36Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG0_STOP); 129862306a36Sopenharmony_ci seq_printf(s, "Stop(%d) ", state); 129962306a36Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG0_TALL_MASK); 130062306a36Sopenharmony_ci seq_printf(s, "Tall(%d) ", state); 130162306a36Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG0_TCALC_OVER); 130262306a36Sopenharmony_ci seq_printf(s, "Over(%d/", state); 130362306a36Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG0_OVER); 130462306a36Sopenharmony_ci seq_printf(s, "%d/", state); 130562306a36Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG0_CPTR_OVER); 130662306a36Sopenharmony_ci seq_printf(s, "%d) ", state); 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci r = readl(ts->regs + tsensors[i].base + SENSOR_CONFIG2); 130962306a36Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG2_THERMA_MASK); 131062306a36Sopenharmony_ci seq_printf(s, "Therm_A/B(%d/", state); 131162306a36Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_CONFIG2_THERMB_MASK); 131262306a36Sopenharmony_ci seq_printf(s, "%d)\n", (s16)state); 131362306a36Sopenharmony_ci } 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci r = readl(ts->regs + SENSOR_PDIV); 131662306a36Sopenharmony_ci seq_printf(s, "PDIV: 0x%x\n", r); 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci r = readl(ts->regs + SENSOR_HOTSPOT_OFF); 131962306a36Sopenharmony_ci seq_printf(s, "HOTSPOT: 0x%x\n", r); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci seq_puts(s, "\n"); 132262306a36Sopenharmony_ci seq_puts(s, "-----SOC_THERM-----\n"); 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci r = readl(ts->regs + SENSOR_TEMP1); 132562306a36Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_TEMP1_CPU_TEMP_MASK); 132662306a36Sopenharmony_ci seq_printf(s, "Temperatures: CPU(%d) ", translate_temp(state)); 132762306a36Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_TEMP1_GPU_TEMP_MASK); 132862306a36Sopenharmony_ci seq_printf(s, " GPU(%d) ", translate_temp(state)); 132962306a36Sopenharmony_ci r = readl(ts->regs + SENSOR_TEMP2); 133062306a36Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_TEMP2_PLLX_TEMP_MASK); 133162306a36Sopenharmony_ci seq_printf(s, " PLLX(%d) ", translate_temp(state)); 133262306a36Sopenharmony_ci state = REG_GET_MASK(r, SENSOR_TEMP2_MEM_TEMP_MASK); 133362306a36Sopenharmony_ci seq_printf(s, " MEM(%d)\n", translate_temp(state)); 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci for (i = 0; i < ts->soc->num_ttgs; i++) { 133662306a36Sopenharmony_ci seq_printf(s, "%s:\n", ttgs[i]->name); 133762306a36Sopenharmony_ci for (level = 0; level < 4; level++) { 133862306a36Sopenharmony_ci s32 v; 133962306a36Sopenharmony_ci u32 mask; 134062306a36Sopenharmony_ci u16 off = ttgs[i]->thermctl_lvl0_offset; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci r = readl(ts->regs + THERMCTL_LVL_REG(off, level)); 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci mask = ttgs[i]->thermctl_lvl0_up_thresh_mask; 134562306a36Sopenharmony_ci state = REG_GET_MASK(r, mask); 134662306a36Sopenharmony_ci v = sign_extend32(state, ts->soc->bptt - 1); 134762306a36Sopenharmony_ci v *= ts->soc->thresh_grain; 134862306a36Sopenharmony_ci seq_printf(s, " %d: Up/Dn(%d /", level, v); 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci mask = ttgs[i]->thermctl_lvl0_dn_thresh_mask; 135162306a36Sopenharmony_ci state = REG_GET_MASK(r, mask); 135262306a36Sopenharmony_ci v = sign_extend32(state, ts->soc->bptt - 1); 135362306a36Sopenharmony_ci v *= ts->soc->thresh_grain; 135462306a36Sopenharmony_ci seq_printf(s, "%d ) ", v); 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci mask = THERMCTL_LVL0_CPU0_EN_MASK; 135762306a36Sopenharmony_ci state = REG_GET_MASK(r, mask); 135862306a36Sopenharmony_ci seq_printf(s, "En(%d) ", state); 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci mask = THERMCTL_LVL0_CPU0_CPU_THROT_MASK; 136162306a36Sopenharmony_ci state = REG_GET_MASK(r, mask); 136262306a36Sopenharmony_ci seq_puts(s, "CPU Throt"); 136362306a36Sopenharmony_ci if (!state) 136462306a36Sopenharmony_ci seq_printf(s, "(%s) ", "none"); 136562306a36Sopenharmony_ci else if (state == THERMCTL_LVL0_CPU0_CPU_THROT_LIGHT) 136662306a36Sopenharmony_ci seq_printf(s, "(%s) ", "L"); 136762306a36Sopenharmony_ci else if (state == THERMCTL_LVL0_CPU0_CPU_THROT_HEAVY) 136862306a36Sopenharmony_ci seq_printf(s, "(%s) ", "H"); 136962306a36Sopenharmony_ci else 137062306a36Sopenharmony_ci seq_printf(s, "(%s) ", "H+L"); 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci mask = THERMCTL_LVL0_CPU0_GPU_THROT_MASK; 137362306a36Sopenharmony_ci state = REG_GET_MASK(r, mask); 137462306a36Sopenharmony_ci seq_puts(s, "GPU Throt"); 137562306a36Sopenharmony_ci if (!state) 137662306a36Sopenharmony_ci seq_printf(s, "(%s) ", "none"); 137762306a36Sopenharmony_ci else if (state == THERMCTL_LVL0_CPU0_GPU_THROT_LIGHT) 137862306a36Sopenharmony_ci seq_printf(s, "(%s) ", "L"); 137962306a36Sopenharmony_ci else if (state == THERMCTL_LVL0_CPU0_GPU_THROT_HEAVY) 138062306a36Sopenharmony_ci seq_printf(s, "(%s) ", "H"); 138162306a36Sopenharmony_ci else 138262306a36Sopenharmony_ci seq_printf(s, "(%s) ", "H+L"); 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci mask = THERMCTL_LVL0_CPU0_STATUS_MASK; 138562306a36Sopenharmony_ci state = REG_GET_MASK(r, mask); 138662306a36Sopenharmony_ci seq_printf(s, "Status(%s)\n", 138762306a36Sopenharmony_ci state == 0 ? "LO" : 138862306a36Sopenharmony_ci state == 1 ? "In" : 138962306a36Sopenharmony_ci state == 2 ? "Res" : "HI"); 139062306a36Sopenharmony_ci } 139162306a36Sopenharmony_ci } 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci r = readl(ts->regs + THERMCTL_STATS_CTL); 139462306a36Sopenharmony_ci seq_printf(s, "STATS: Up(%s) Dn(%s)\n", 139562306a36Sopenharmony_ci r & STATS_CTL_EN_UP ? "En" : "--", 139662306a36Sopenharmony_ci r & STATS_CTL_EN_DN ? "En" : "--"); 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci for (level = 0; level < 4; level++) { 139962306a36Sopenharmony_ci u16 off; 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci off = THERMCTL_LVL0_UP_STATS; 140262306a36Sopenharmony_ci r = readl(ts->regs + THERMCTL_LVL_REG(off, level)); 140362306a36Sopenharmony_ci seq_printf(s, " Level_%d Up(%d) ", level, r); 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci off = THERMCTL_LVL0_DN_STATS; 140662306a36Sopenharmony_ci r = readl(ts->regs + THERMCTL_LVL_REG(off, level)); 140762306a36Sopenharmony_ci seq_printf(s, "Dn(%d)\n", r); 140862306a36Sopenharmony_ci } 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci r = readl(ts->regs + THERMCTL_THERMTRIP_CTL); 141162306a36Sopenharmony_ci state = REG_GET_MASK(r, ttgs[0]->thermtrip_any_en_mask); 141262306a36Sopenharmony_ci seq_printf(s, "Thermtrip Any En(%d)\n", state); 141362306a36Sopenharmony_ci for (i = 0; i < ts->soc->num_ttgs; i++) { 141462306a36Sopenharmony_ci state = REG_GET_MASK(r, ttgs[i]->thermtrip_enable_mask); 141562306a36Sopenharmony_ci seq_printf(s, " %s En(%d) ", ttgs[i]->name, state); 141662306a36Sopenharmony_ci state = REG_GET_MASK(r, ttgs[i]->thermtrip_threshold_mask); 141762306a36Sopenharmony_ci state *= ts->soc->thresh_grain; 141862306a36Sopenharmony_ci seq_printf(s, "Thresh(%d)\n", state); 141962306a36Sopenharmony_ci } 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci r = readl(ts->regs + THROT_GLOBAL_CFG); 142262306a36Sopenharmony_ci seq_puts(s, "\n"); 142362306a36Sopenharmony_ci seq_printf(s, "GLOBAL THROTTLE CONFIG: 0x%08x\n", r); 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci seq_puts(s, "---------------------------------------------------\n"); 142662306a36Sopenharmony_ci r = readl(ts->regs + THROT_STATUS); 142762306a36Sopenharmony_ci state = REG_GET_MASK(r, THROT_STATUS_BREACH_MASK); 142862306a36Sopenharmony_ci seq_printf(s, "THROT STATUS: breach(%d) ", state); 142962306a36Sopenharmony_ci state = REG_GET_MASK(r, THROT_STATUS_STATE_MASK); 143062306a36Sopenharmony_ci seq_printf(s, "state(%d) ", state); 143162306a36Sopenharmony_ci state = REG_GET_MASK(r, THROT_STATUS_ENABLED_MASK); 143262306a36Sopenharmony_ci seq_printf(s, "enabled(%d)\n", state); 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci r = readl(ts->regs + CPU_PSKIP_STATUS); 143562306a36Sopenharmony_ci if (ts->soc->use_ccroc) { 143662306a36Sopenharmony_ci state = REG_GET_MASK(r, XPU_PSKIP_STATUS_ENABLED_MASK); 143762306a36Sopenharmony_ci seq_printf(s, "CPU PSKIP STATUS: enabled(%d)\n", state); 143862306a36Sopenharmony_ci } else { 143962306a36Sopenharmony_ci state = REG_GET_MASK(r, XPU_PSKIP_STATUS_M_MASK); 144062306a36Sopenharmony_ci seq_printf(s, "CPU PSKIP STATUS: M(%d) ", state); 144162306a36Sopenharmony_ci state = REG_GET_MASK(r, XPU_PSKIP_STATUS_N_MASK); 144262306a36Sopenharmony_ci seq_printf(s, "N(%d) ", state); 144362306a36Sopenharmony_ci state = REG_GET_MASK(r, XPU_PSKIP_STATUS_ENABLED_MASK); 144462306a36Sopenharmony_ci seq_printf(s, "enabled(%d)\n", state); 144562306a36Sopenharmony_ci } 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci return 0; 144862306a36Sopenharmony_ci} 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(regs); 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_cistatic void soctherm_debug_init(struct platform_device *pdev) 145362306a36Sopenharmony_ci{ 145462306a36Sopenharmony_ci struct tegra_soctherm *tegra = platform_get_drvdata(pdev); 145562306a36Sopenharmony_ci struct dentry *root; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci root = debugfs_create_dir("soctherm", NULL); 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci tegra->debugfs_dir = root; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci debugfs_create_file("reg_contents", 0644, root, pdev, ®s_fops); 146262306a36Sopenharmony_ci} 146362306a36Sopenharmony_ci#else 146462306a36Sopenharmony_cistatic inline void soctherm_debug_init(struct platform_device *pdev) {} 146562306a36Sopenharmony_ci#endif 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_cistatic int soctherm_clk_enable(struct platform_device *pdev, bool enable) 146862306a36Sopenharmony_ci{ 146962306a36Sopenharmony_ci struct tegra_soctherm *tegra = platform_get_drvdata(pdev); 147062306a36Sopenharmony_ci int err; 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci if (!tegra->clock_soctherm || !tegra->clock_tsensor) 147362306a36Sopenharmony_ci return -EINVAL; 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci reset_control_assert(tegra->reset); 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci if (enable) { 147862306a36Sopenharmony_ci err = clk_prepare_enable(tegra->clock_soctherm); 147962306a36Sopenharmony_ci if (err) { 148062306a36Sopenharmony_ci reset_control_deassert(tegra->reset); 148162306a36Sopenharmony_ci return err; 148262306a36Sopenharmony_ci } 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci err = clk_prepare_enable(tegra->clock_tsensor); 148562306a36Sopenharmony_ci if (err) { 148662306a36Sopenharmony_ci clk_disable_unprepare(tegra->clock_soctherm); 148762306a36Sopenharmony_ci reset_control_deassert(tegra->reset); 148862306a36Sopenharmony_ci return err; 148962306a36Sopenharmony_ci } 149062306a36Sopenharmony_ci } else { 149162306a36Sopenharmony_ci clk_disable_unprepare(tegra->clock_tsensor); 149262306a36Sopenharmony_ci clk_disable_unprepare(tegra->clock_soctherm); 149362306a36Sopenharmony_ci } 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci reset_control_deassert(tegra->reset); 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci return 0; 149862306a36Sopenharmony_ci} 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_cistatic int throt_get_cdev_max_state(struct thermal_cooling_device *cdev, 150162306a36Sopenharmony_ci unsigned long *max_state) 150262306a36Sopenharmony_ci{ 150362306a36Sopenharmony_ci *max_state = 1; 150462306a36Sopenharmony_ci return 0; 150562306a36Sopenharmony_ci} 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_cistatic int throt_get_cdev_cur_state(struct thermal_cooling_device *cdev, 150862306a36Sopenharmony_ci unsigned long *cur_state) 150962306a36Sopenharmony_ci{ 151062306a36Sopenharmony_ci struct tegra_soctherm *ts = cdev->devdata; 151162306a36Sopenharmony_ci u32 r; 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci r = readl(ts->regs + THROT_STATUS); 151462306a36Sopenharmony_ci if (REG_GET_MASK(r, THROT_STATUS_STATE_MASK)) 151562306a36Sopenharmony_ci *cur_state = 1; 151662306a36Sopenharmony_ci else 151762306a36Sopenharmony_ci *cur_state = 0; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci return 0; 152062306a36Sopenharmony_ci} 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_cistatic int throt_set_cdev_state(struct thermal_cooling_device *cdev, 152362306a36Sopenharmony_ci unsigned long cur_state) 152462306a36Sopenharmony_ci{ 152562306a36Sopenharmony_ci return 0; 152662306a36Sopenharmony_ci} 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_cistatic const struct thermal_cooling_device_ops throt_cooling_ops = { 152962306a36Sopenharmony_ci .get_max_state = throt_get_cdev_max_state, 153062306a36Sopenharmony_ci .get_cur_state = throt_get_cdev_cur_state, 153162306a36Sopenharmony_ci .set_cur_state = throt_set_cdev_state, 153262306a36Sopenharmony_ci}; 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_cistatic int soctherm_thermtrips_parse(struct platform_device *pdev) 153562306a36Sopenharmony_ci{ 153662306a36Sopenharmony_ci struct device *dev = &pdev->dev; 153762306a36Sopenharmony_ci struct tegra_soctherm *ts = dev_get_drvdata(dev); 153862306a36Sopenharmony_ci struct tsensor_group_thermtrips *tt = ts->soc->thermtrips; 153962306a36Sopenharmony_ci const int max_num_prop = ts->soc->num_ttgs * 2; 154062306a36Sopenharmony_ci u32 *tlb; 154162306a36Sopenharmony_ci int i, j, n, ret; 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci if (!tt) 154462306a36Sopenharmony_ci return -ENOMEM; 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci n = of_property_count_u32_elems(dev->of_node, "nvidia,thermtrips"); 154762306a36Sopenharmony_ci if (n <= 0) { 154862306a36Sopenharmony_ci dev_info(dev, 154962306a36Sopenharmony_ci "missing thermtrips, will use critical trips as shut down temp\n"); 155062306a36Sopenharmony_ci return n; 155162306a36Sopenharmony_ci } 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci n = min(max_num_prop, n); 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci tlb = devm_kcalloc(&pdev->dev, max_num_prop, sizeof(u32), GFP_KERNEL); 155662306a36Sopenharmony_ci if (!tlb) 155762306a36Sopenharmony_ci return -ENOMEM; 155862306a36Sopenharmony_ci ret = of_property_read_u32_array(dev->of_node, "nvidia,thermtrips", 155962306a36Sopenharmony_ci tlb, n); 156062306a36Sopenharmony_ci if (ret) { 156162306a36Sopenharmony_ci dev_err(dev, "invalid num ele: thermtrips:%d\n", ret); 156262306a36Sopenharmony_ci return ret; 156362306a36Sopenharmony_ci } 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci i = 0; 156662306a36Sopenharmony_ci for (j = 0; j < n; j = j + 2) { 156762306a36Sopenharmony_ci if (tlb[j] >= TEGRA124_SOCTHERM_SENSOR_NUM) 156862306a36Sopenharmony_ci continue; 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci tt[i].id = tlb[j]; 157162306a36Sopenharmony_ci tt[i].temp = tlb[j + 1]; 157262306a36Sopenharmony_ci i++; 157362306a36Sopenharmony_ci } 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci return 0; 157662306a36Sopenharmony_ci} 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_cistatic void soctherm_oc_cfg_parse(struct device *dev, 157962306a36Sopenharmony_ci struct device_node *np_oc, 158062306a36Sopenharmony_ci struct soctherm_throt_cfg *stc) 158162306a36Sopenharmony_ci{ 158262306a36Sopenharmony_ci u32 val; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci if (of_property_read_bool(np_oc, "nvidia,polarity-active-low")) 158562306a36Sopenharmony_ci stc->oc_cfg.active_low = 1; 158662306a36Sopenharmony_ci else 158762306a36Sopenharmony_ci stc->oc_cfg.active_low = 0; 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci if (!of_property_read_u32(np_oc, "nvidia,count-threshold", &val)) { 159062306a36Sopenharmony_ci stc->oc_cfg.intr_en = 1; 159162306a36Sopenharmony_ci stc->oc_cfg.alarm_cnt_thresh = val; 159262306a36Sopenharmony_ci } 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci if (!of_property_read_u32(np_oc, "nvidia,throttle-period-us", &val)) 159562306a36Sopenharmony_ci stc->oc_cfg.throt_period = val; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci if (!of_property_read_u32(np_oc, "nvidia,alarm-filter", &val)) 159862306a36Sopenharmony_ci stc->oc_cfg.alarm_filter = val; 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci /* BRIEF throttling by default, do not support STICKY */ 160162306a36Sopenharmony_ci stc->oc_cfg.mode = OC_THROTTLE_MODE_BRIEF; 160262306a36Sopenharmony_ci} 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_cistatic int soctherm_throt_cfg_parse(struct device *dev, 160562306a36Sopenharmony_ci struct device_node *np, 160662306a36Sopenharmony_ci struct soctherm_throt_cfg *stc) 160762306a36Sopenharmony_ci{ 160862306a36Sopenharmony_ci struct tegra_soctherm *ts = dev_get_drvdata(dev); 160962306a36Sopenharmony_ci int ret; 161062306a36Sopenharmony_ci u32 val; 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci ret = of_property_read_u32(np, "nvidia,priority", &val); 161362306a36Sopenharmony_ci if (ret) { 161462306a36Sopenharmony_ci dev_err(dev, "throttle-cfg: %s: invalid priority\n", stc->name); 161562306a36Sopenharmony_ci return -EINVAL; 161662306a36Sopenharmony_ci } 161762306a36Sopenharmony_ci stc->priority = val; 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci ret = of_property_read_u32(np, ts->soc->use_ccroc ? 162062306a36Sopenharmony_ci "nvidia,cpu-throt-level" : 162162306a36Sopenharmony_ci "nvidia,cpu-throt-percent", &val); 162262306a36Sopenharmony_ci if (!ret) { 162362306a36Sopenharmony_ci if (ts->soc->use_ccroc && 162462306a36Sopenharmony_ci val <= TEGRA_SOCTHERM_THROT_LEVEL_HIGH) 162562306a36Sopenharmony_ci stc->cpu_throt_level = val; 162662306a36Sopenharmony_ci else if (!ts->soc->use_ccroc && val <= 100) 162762306a36Sopenharmony_ci stc->cpu_throt_depth = val; 162862306a36Sopenharmony_ci else 162962306a36Sopenharmony_ci goto err; 163062306a36Sopenharmony_ci } else { 163162306a36Sopenharmony_ci goto err; 163262306a36Sopenharmony_ci } 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci ret = of_property_read_u32(np, "nvidia,gpu-throt-level", &val); 163562306a36Sopenharmony_ci if (!ret && val <= TEGRA_SOCTHERM_THROT_LEVEL_HIGH) 163662306a36Sopenharmony_ci stc->gpu_throt_level = val; 163762306a36Sopenharmony_ci else 163862306a36Sopenharmony_ci goto err; 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci return 0; 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_cierr: 164362306a36Sopenharmony_ci dev_err(dev, "throttle-cfg: %s: no throt prop or invalid prop\n", 164462306a36Sopenharmony_ci stc->name); 164562306a36Sopenharmony_ci return -EINVAL; 164662306a36Sopenharmony_ci} 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci/** 164962306a36Sopenharmony_ci * soctherm_init_hw_throt_cdev() - Parse the HW throttle configurations 165062306a36Sopenharmony_ci * and register them as cooling devices. 165162306a36Sopenharmony_ci * @pdev: Pointer to platform_device struct 165262306a36Sopenharmony_ci */ 165362306a36Sopenharmony_cistatic void soctherm_init_hw_throt_cdev(struct platform_device *pdev) 165462306a36Sopenharmony_ci{ 165562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 165662306a36Sopenharmony_ci struct tegra_soctherm *ts = dev_get_drvdata(dev); 165762306a36Sopenharmony_ci struct device_node *np_stc, *np_stcc; 165862306a36Sopenharmony_ci const char *name; 165962306a36Sopenharmony_ci int i; 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci for (i = 0; i < THROTTLE_SIZE; i++) { 166262306a36Sopenharmony_ci ts->throt_cfgs[i].name = throt_names[i]; 166362306a36Sopenharmony_ci ts->throt_cfgs[i].id = i; 166462306a36Sopenharmony_ci ts->throt_cfgs[i].init = false; 166562306a36Sopenharmony_ci } 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci np_stc = of_get_child_by_name(dev->of_node, "throttle-cfgs"); 166862306a36Sopenharmony_ci if (!np_stc) { 166962306a36Sopenharmony_ci dev_info(dev, 167062306a36Sopenharmony_ci "throttle-cfg: no throttle-cfgs - not enabling\n"); 167162306a36Sopenharmony_ci return; 167262306a36Sopenharmony_ci } 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci for_each_child_of_node(np_stc, np_stcc) { 167562306a36Sopenharmony_ci struct soctherm_throt_cfg *stc; 167662306a36Sopenharmony_ci struct thermal_cooling_device *tcd; 167762306a36Sopenharmony_ci int err; 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci name = np_stcc->name; 168062306a36Sopenharmony_ci stc = find_throttle_cfg_by_name(ts, name); 168162306a36Sopenharmony_ci if (!stc) { 168262306a36Sopenharmony_ci dev_err(dev, 168362306a36Sopenharmony_ci "throttle-cfg: could not find %s\n", name); 168462306a36Sopenharmony_ci continue; 168562306a36Sopenharmony_ci } 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci if (stc->init) { 168862306a36Sopenharmony_ci dev_err(dev, "throttle-cfg: %s: redefined!\n", name); 168962306a36Sopenharmony_ci of_node_put(np_stcc); 169062306a36Sopenharmony_ci break; 169162306a36Sopenharmony_ci } 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci err = soctherm_throt_cfg_parse(dev, np_stcc, stc); 169462306a36Sopenharmony_ci if (err) 169562306a36Sopenharmony_ci continue; 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci if (stc->id >= THROTTLE_OC1) { 169862306a36Sopenharmony_ci soctherm_oc_cfg_parse(dev, np_stcc, stc); 169962306a36Sopenharmony_ci stc->init = true; 170062306a36Sopenharmony_ci } else { 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci tcd = thermal_of_cooling_device_register(np_stcc, 170362306a36Sopenharmony_ci (char *)name, ts, 170462306a36Sopenharmony_ci &throt_cooling_ops); 170562306a36Sopenharmony_ci if (IS_ERR_OR_NULL(tcd)) { 170662306a36Sopenharmony_ci dev_err(dev, 170762306a36Sopenharmony_ci "throttle-cfg: %s: failed to register cooling device\n", 170862306a36Sopenharmony_ci name); 170962306a36Sopenharmony_ci continue; 171062306a36Sopenharmony_ci } 171162306a36Sopenharmony_ci stc->cdev = tcd; 171262306a36Sopenharmony_ci stc->init = true; 171362306a36Sopenharmony_ci } 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci } 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci of_node_put(np_stc); 171862306a36Sopenharmony_ci} 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci/** 172162306a36Sopenharmony_ci * throttlectl_cpu_level_cfg() - programs CCROC NV_THERM level config 172262306a36Sopenharmony_ci * @ts: pointer to a struct tegra_soctherm 172362306a36Sopenharmony_ci * @level: describing the level LOW/MED/HIGH of throttling 172462306a36Sopenharmony_ci * 172562306a36Sopenharmony_ci * It's necessary to set up the CPU-local CCROC NV_THERM instance with 172662306a36Sopenharmony_ci * the M/N values desired for each level. This function does this. 172762306a36Sopenharmony_ci * 172862306a36Sopenharmony_ci * This function pre-programs the CCROC NV_THERM levels in terms of 172962306a36Sopenharmony_ci * pre-configured "Low", "Medium" or "Heavy" throttle levels which are 173062306a36Sopenharmony_ci * mapped to THROT_LEVEL_LOW, THROT_LEVEL_MED and THROT_LEVEL_HVY. 173162306a36Sopenharmony_ci */ 173262306a36Sopenharmony_cistatic void throttlectl_cpu_level_cfg(struct tegra_soctherm *ts, int level) 173362306a36Sopenharmony_ci{ 173462306a36Sopenharmony_ci u8 depth, dividend; 173562306a36Sopenharmony_ci u32 r; 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci switch (level) { 173862306a36Sopenharmony_ci case TEGRA_SOCTHERM_THROT_LEVEL_LOW: 173962306a36Sopenharmony_ci depth = 50; 174062306a36Sopenharmony_ci break; 174162306a36Sopenharmony_ci case TEGRA_SOCTHERM_THROT_LEVEL_MED: 174262306a36Sopenharmony_ci depth = 75; 174362306a36Sopenharmony_ci break; 174462306a36Sopenharmony_ci case TEGRA_SOCTHERM_THROT_LEVEL_HIGH: 174562306a36Sopenharmony_ci depth = 80; 174662306a36Sopenharmony_ci break; 174762306a36Sopenharmony_ci case TEGRA_SOCTHERM_THROT_LEVEL_NONE: 174862306a36Sopenharmony_ci return; 174962306a36Sopenharmony_ci default: 175062306a36Sopenharmony_ci return; 175162306a36Sopenharmony_ci } 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci dividend = THROT_DEPTH_DIVIDEND(depth); 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci /* setup PSKIP in ccroc nv_therm registers */ 175662306a36Sopenharmony_ci r = ccroc_readl(ts, CCROC_THROT_PSKIP_RAMP_CPU_REG(level)); 175762306a36Sopenharmony_ci r = REG_SET_MASK(r, CCROC_THROT_PSKIP_RAMP_DURATION_MASK, 0xff); 175862306a36Sopenharmony_ci r = REG_SET_MASK(r, CCROC_THROT_PSKIP_RAMP_STEP_MASK, 0xf); 175962306a36Sopenharmony_ci ccroc_writel(ts, r, CCROC_THROT_PSKIP_RAMP_CPU_REG(level)); 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci r = ccroc_readl(ts, CCROC_THROT_PSKIP_CTRL_CPU_REG(level)); 176262306a36Sopenharmony_ci r = REG_SET_MASK(r, CCROC_THROT_PSKIP_CTRL_ENB_MASK, 1); 176362306a36Sopenharmony_ci r = REG_SET_MASK(r, CCROC_THROT_PSKIP_CTRL_DIVIDEND_MASK, dividend); 176462306a36Sopenharmony_ci r = REG_SET_MASK(r, CCROC_THROT_PSKIP_CTRL_DIVISOR_MASK, 0xff); 176562306a36Sopenharmony_ci ccroc_writel(ts, r, CCROC_THROT_PSKIP_CTRL_CPU_REG(level)); 176662306a36Sopenharmony_ci} 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci/** 176962306a36Sopenharmony_ci * throttlectl_cpu_level_select() - program CPU pulse skipper config 177062306a36Sopenharmony_ci * @ts: pointer to a struct tegra_soctherm 177162306a36Sopenharmony_ci * @throt: the LIGHT/HEAVY of throttle event id 177262306a36Sopenharmony_ci * 177362306a36Sopenharmony_ci * Pulse skippers are used to throttle clock frequencies. This 177462306a36Sopenharmony_ci * function programs the pulse skippers based on @throt and platform 177562306a36Sopenharmony_ci * data. This function is used on SoCs which have CPU-local pulse 177662306a36Sopenharmony_ci * skipper control, such as T13x. It programs soctherm's interface to 177762306a36Sopenharmony_ci * Denver:CCROC NV_THERM in terms of Low, Medium and HIGH throttling 177862306a36Sopenharmony_ci * vectors. PSKIP_BYPASS mode is set as required per HW spec. 177962306a36Sopenharmony_ci */ 178062306a36Sopenharmony_cistatic void throttlectl_cpu_level_select(struct tegra_soctherm *ts, 178162306a36Sopenharmony_ci enum soctherm_throttle_id throt) 178262306a36Sopenharmony_ci{ 178362306a36Sopenharmony_ci u32 r, throt_vect; 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci /* Denver:CCROC NV_THERM interface N:3 Mapping */ 178662306a36Sopenharmony_ci switch (ts->throt_cfgs[throt].cpu_throt_level) { 178762306a36Sopenharmony_ci case TEGRA_SOCTHERM_THROT_LEVEL_LOW: 178862306a36Sopenharmony_ci throt_vect = THROT_VECT_LOW; 178962306a36Sopenharmony_ci break; 179062306a36Sopenharmony_ci case TEGRA_SOCTHERM_THROT_LEVEL_MED: 179162306a36Sopenharmony_ci throt_vect = THROT_VECT_MED; 179262306a36Sopenharmony_ci break; 179362306a36Sopenharmony_ci case TEGRA_SOCTHERM_THROT_LEVEL_HIGH: 179462306a36Sopenharmony_ci throt_vect = THROT_VECT_HIGH; 179562306a36Sopenharmony_ci break; 179662306a36Sopenharmony_ci default: 179762306a36Sopenharmony_ci throt_vect = THROT_VECT_NONE; 179862306a36Sopenharmony_ci break; 179962306a36Sopenharmony_ci } 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci r = readl(ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU)); 180262306a36Sopenharmony_ci r = REG_SET_MASK(r, THROT_PSKIP_CTRL_ENABLE_MASK, 1); 180362306a36Sopenharmony_ci r = REG_SET_MASK(r, THROT_PSKIP_CTRL_VECT_CPU_MASK, throt_vect); 180462306a36Sopenharmony_ci r = REG_SET_MASK(r, THROT_PSKIP_CTRL_VECT2_CPU_MASK, throt_vect); 180562306a36Sopenharmony_ci writel(r, ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU)); 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci /* bypass sequencer in soc_therm as it is programmed in ccroc */ 180862306a36Sopenharmony_ci r = REG_SET_MASK(0, THROT_PSKIP_RAMP_SEQ_BYPASS_MODE_MASK, 1); 180962306a36Sopenharmony_ci writel(r, ts->regs + THROT_PSKIP_RAMP(throt, THROTTLE_DEV_CPU)); 181062306a36Sopenharmony_ci} 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci/** 181362306a36Sopenharmony_ci * throttlectl_cpu_mn() - program CPU pulse skipper configuration 181462306a36Sopenharmony_ci * @ts: pointer to a struct tegra_soctherm 181562306a36Sopenharmony_ci * @throt: the LIGHT/HEAVY of throttle event id 181662306a36Sopenharmony_ci * 181762306a36Sopenharmony_ci * Pulse skippers are used to throttle clock frequencies. This 181862306a36Sopenharmony_ci * function programs the pulse skippers based on @throt and platform 181962306a36Sopenharmony_ci * data. This function is used for CPUs that have "remote" pulse 182062306a36Sopenharmony_ci * skipper control, e.g., the CPU pulse skipper is controlled by the 182162306a36Sopenharmony_ci * SOC_THERM IP block. (SOC_THERM is located outside the CPU 182262306a36Sopenharmony_ci * complex.) 182362306a36Sopenharmony_ci */ 182462306a36Sopenharmony_cistatic void throttlectl_cpu_mn(struct tegra_soctherm *ts, 182562306a36Sopenharmony_ci enum soctherm_throttle_id throt) 182662306a36Sopenharmony_ci{ 182762306a36Sopenharmony_ci u32 r; 182862306a36Sopenharmony_ci int depth; 182962306a36Sopenharmony_ci u8 dividend; 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci depth = ts->throt_cfgs[throt].cpu_throt_depth; 183262306a36Sopenharmony_ci dividend = THROT_DEPTH_DIVIDEND(depth); 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci r = readl(ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU)); 183562306a36Sopenharmony_ci r = REG_SET_MASK(r, THROT_PSKIP_CTRL_ENABLE_MASK, 1); 183662306a36Sopenharmony_ci r = REG_SET_MASK(r, THROT_PSKIP_CTRL_DIVIDEND_MASK, dividend); 183762306a36Sopenharmony_ci r = REG_SET_MASK(r, THROT_PSKIP_CTRL_DIVISOR_MASK, 0xff); 183862306a36Sopenharmony_ci writel(r, ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU)); 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci r = readl(ts->regs + THROT_PSKIP_RAMP(throt, THROTTLE_DEV_CPU)); 184162306a36Sopenharmony_ci r = REG_SET_MASK(r, THROT_PSKIP_RAMP_DURATION_MASK, 0xff); 184262306a36Sopenharmony_ci r = REG_SET_MASK(r, THROT_PSKIP_RAMP_STEP_MASK, 0xf); 184362306a36Sopenharmony_ci writel(r, ts->regs + THROT_PSKIP_RAMP(throt, THROTTLE_DEV_CPU)); 184462306a36Sopenharmony_ci} 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci/** 184762306a36Sopenharmony_ci * throttlectl_gpu_level_select() - selects throttling level for GPU 184862306a36Sopenharmony_ci * @ts: pointer to a struct tegra_soctherm 184962306a36Sopenharmony_ci * @throt: the LIGHT/HEAVY of throttle event id 185062306a36Sopenharmony_ci * 185162306a36Sopenharmony_ci * This function programs soctherm's interface to GK20a NV_THERM to select 185262306a36Sopenharmony_ci * pre-configured "Low", "Medium" or "Heavy" throttle levels. 185362306a36Sopenharmony_ci * 185462306a36Sopenharmony_ci * Return: boolean true if HW was programmed 185562306a36Sopenharmony_ci */ 185662306a36Sopenharmony_cistatic void throttlectl_gpu_level_select(struct tegra_soctherm *ts, 185762306a36Sopenharmony_ci enum soctherm_throttle_id throt) 185862306a36Sopenharmony_ci{ 185962306a36Sopenharmony_ci u32 r, level, throt_vect; 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci level = ts->throt_cfgs[throt].gpu_throt_level; 186262306a36Sopenharmony_ci throt_vect = THROT_LEVEL_TO_DEPTH(level); 186362306a36Sopenharmony_ci r = readl(ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_GPU)); 186462306a36Sopenharmony_ci r = REG_SET_MASK(r, THROT_PSKIP_CTRL_ENABLE_MASK, 1); 186562306a36Sopenharmony_ci r = REG_SET_MASK(r, THROT_PSKIP_CTRL_VECT_GPU_MASK, throt_vect); 186662306a36Sopenharmony_ci writel(r, ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_GPU)); 186762306a36Sopenharmony_ci} 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_cistatic int soctherm_oc_cfg_program(struct tegra_soctherm *ts, 187062306a36Sopenharmony_ci enum soctherm_throttle_id throt) 187162306a36Sopenharmony_ci{ 187262306a36Sopenharmony_ci u32 r; 187362306a36Sopenharmony_ci struct soctherm_oc_cfg *oc = &ts->throt_cfgs[throt].oc_cfg; 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci if (oc->mode == OC_THROTTLE_MODE_DISABLED) 187662306a36Sopenharmony_ci return -EINVAL; 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci r = REG_SET_MASK(0, OC1_CFG_HW_RESTORE_MASK, 1); 187962306a36Sopenharmony_ci r = REG_SET_MASK(r, OC1_CFG_THROTTLE_MODE_MASK, oc->mode); 188062306a36Sopenharmony_ci r = REG_SET_MASK(r, OC1_CFG_ALARM_POLARITY_MASK, oc->active_low); 188162306a36Sopenharmony_ci r = REG_SET_MASK(r, OC1_CFG_EN_THROTTLE_MASK, 1); 188262306a36Sopenharmony_ci writel(r, ts->regs + ALARM_CFG(throt)); 188362306a36Sopenharmony_ci writel(oc->throt_period, ts->regs + ALARM_THROTTLE_PERIOD(throt)); 188462306a36Sopenharmony_ci writel(oc->alarm_cnt_thresh, ts->regs + ALARM_CNT_THRESHOLD(throt)); 188562306a36Sopenharmony_ci writel(oc->alarm_filter, ts->regs + ALARM_FILTER(throt)); 188662306a36Sopenharmony_ci soctherm_oc_intr_enable(ts, throt, oc->intr_en); 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci return 0; 188962306a36Sopenharmony_ci} 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci/** 189262306a36Sopenharmony_ci * soctherm_throttle_program() - programs pulse skippers' configuration 189362306a36Sopenharmony_ci * @ts: pointer to a struct tegra_soctherm 189462306a36Sopenharmony_ci * @throt: the LIGHT/HEAVY of the throttle event id. 189562306a36Sopenharmony_ci * 189662306a36Sopenharmony_ci * Pulse skippers are used to throttle clock frequencies. 189762306a36Sopenharmony_ci * This function programs the pulse skippers. 189862306a36Sopenharmony_ci */ 189962306a36Sopenharmony_cistatic void soctherm_throttle_program(struct tegra_soctherm *ts, 190062306a36Sopenharmony_ci enum soctherm_throttle_id throt) 190162306a36Sopenharmony_ci{ 190262306a36Sopenharmony_ci u32 r; 190362306a36Sopenharmony_ci struct soctherm_throt_cfg stc = ts->throt_cfgs[throt]; 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ci if (!stc.init) 190662306a36Sopenharmony_ci return; 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_ci if ((throt >= THROTTLE_OC1) && (soctherm_oc_cfg_program(ts, throt))) 190962306a36Sopenharmony_ci return; 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci /* Setup PSKIP parameters */ 191262306a36Sopenharmony_ci if (ts->soc->use_ccroc) 191362306a36Sopenharmony_ci throttlectl_cpu_level_select(ts, throt); 191462306a36Sopenharmony_ci else 191562306a36Sopenharmony_ci throttlectl_cpu_mn(ts, throt); 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci throttlectl_gpu_level_select(ts, throt); 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci r = REG_SET_MASK(0, THROT_PRIORITY_LITE_PRIO_MASK, stc.priority); 192062306a36Sopenharmony_ci writel(r, ts->regs + THROT_PRIORITY_CTRL(throt)); 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci r = REG_SET_MASK(0, THROT_DELAY_LITE_DELAY_MASK, 0); 192362306a36Sopenharmony_ci writel(r, ts->regs + THROT_DELAY_CTRL(throt)); 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci r = readl(ts->regs + THROT_PRIORITY_LOCK); 192662306a36Sopenharmony_ci r = REG_GET_MASK(r, THROT_PRIORITY_LOCK_PRIORITY_MASK); 192762306a36Sopenharmony_ci if (r >= stc.priority) 192862306a36Sopenharmony_ci return; 192962306a36Sopenharmony_ci r = REG_SET_MASK(0, THROT_PRIORITY_LOCK_PRIORITY_MASK, 193062306a36Sopenharmony_ci stc.priority); 193162306a36Sopenharmony_ci writel(r, ts->regs + THROT_PRIORITY_LOCK); 193262306a36Sopenharmony_ci} 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_cistatic void tegra_soctherm_throttle(struct device *dev) 193562306a36Sopenharmony_ci{ 193662306a36Sopenharmony_ci struct tegra_soctherm *ts = dev_get_drvdata(dev); 193762306a36Sopenharmony_ci u32 v; 193862306a36Sopenharmony_ci int i; 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci /* configure LOW, MED and HIGH levels for CCROC NV_THERM */ 194162306a36Sopenharmony_ci if (ts->soc->use_ccroc) { 194262306a36Sopenharmony_ci throttlectl_cpu_level_cfg(ts, TEGRA_SOCTHERM_THROT_LEVEL_LOW); 194362306a36Sopenharmony_ci throttlectl_cpu_level_cfg(ts, TEGRA_SOCTHERM_THROT_LEVEL_MED); 194462306a36Sopenharmony_ci throttlectl_cpu_level_cfg(ts, TEGRA_SOCTHERM_THROT_LEVEL_HIGH); 194562306a36Sopenharmony_ci } 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci /* Thermal HW throttle programming */ 194862306a36Sopenharmony_ci for (i = 0; i < THROTTLE_SIZE; i++) 194962306a36Sopenharmony_ci soctherm_throttle_program(ts, i); 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci v = REG_SET_MASK(0, THROT_GLOBAL_ENB_MASK, 1); 195262306a36Sopenharmony_ci if (ts->soc->use_ccroc) { 195362306a36Sopenharmony_ci ccroc_writel(ts, v, CCROC_GLOBAL_CFG); 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci v = ccroc_readl(ts, CCROC_SUPER_CCLKG_DIVIDER); 195662306a36Sopenharmony_ci v = REG_SET_MASK(v, CDIVG_USE_THERM_CONTROLS_MASK, 1); 195762306a36Sopenharmony_ci ccroc_writel(ts, v, CCROC_SUPER_CCLKG_DIVIDER); 195862306a36Sopenharmony_ci } else { 195962306a36Sopenharmony_ci writel(v, ts->regs + THROT_GLOBAL_CFG); 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci v = readl(ts->clk_regs + CAR_SUPER_CCLKG_DIVIDER); 196262306a36Sopenharmony_ci v = REG_SET_MASK(v, CDIVG_USE_THERM_CONTROLS_MASK, 1); 196362306a36Sopenharmony_ci writel(v, ts->clk_regs + CAR_SUPER_CCLKG_DIVIDER); 196462306a36Sopenharmony_ci } 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci /* initialize stats collection */ 196762306a36Sopenharmony_ci v = STATS_CTL_CLR_DN | STATS_CTL_EN_DN | 196862306a36Sopenharmony_ci STATS_CTL_CLR_UP | STATS_CTL_EN_UP; 196962306a36Sopenharmony_ci writel(v, ts->regs + THERMCTL_STATS_CTL); 197062306a36Sopenharmony_ci} 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_cistatic int soctherm_interrupts_init(struct platform_device *pdev, 197362306a36Sopenharmony_ci struct tegra_soctherm *tegra) 197462306a36Sopenharmony_ci{ 197562306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 197662306a36Sopenharmony_ci int ret; 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci ret = soctherm_oc_int_init(np, TEGRA_SOC_OC_IRQ_MAX); 197962306a36Sopenharmony_ci if (ret < 0) { 198062306a36Sopenharmony_ci dev_err(&pdev->dev, "soctherm_oc_int_init failed\n"); 198162306a36Sopenharmony_ci return ret; 198262306a36Sopenharmony_ci } 198362306a36Sopenharmony_ci 198462306a36Sopenharmony_ci tegra->thermal_irq = platform_get_irq(pdev, 0); 198562306a36Sopenharmony_ci if (tegra->thermal_irq < 0) { 198662306a36Sopenharmony_ci dev_dbg(&pdev->dev, "get 'thermal_irq' failed.\n"); 198762306a36Sopenharmony_ci return 0; 198862306a36Sopenharmony_ci } 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_ci tegra->edp_irq = platform_get_irq(pdev, 1); 199162306a36Sopenharmony_ci if (tegra->edp_irq < 0) { 199262306a36Sopenharmony_ci dev_dbg(&pdev->dev, "get 'edp_irq' failed.\n"); 199362306a36Sopenharmony_ci return 0; 199462306a36Sopenharmony_ci } 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, 199762306a36Sopenharmony_ci tegra->thermal_irq, 199862306a36Sopenharmony_ci soctherm_thermal_isr, 199962306a36Sopenharmony_ci soctherm_thermal_isr_thread, 200062306a36Sopenharmony_ci IRQF_ONESHOT, 200162306a36Sopenharmony_ci dev_name(&pdev->dev), 200262306a36Sopenharmony_ci tegra); 200362306a36Sopenharmony_ci if (ret < 0) { 200462306a36Sopenharmony_ci dev_err(&pdev->dev, "request_irq 'thermal_irq' failed.\n"); 200562306a36Sopenharmony_ci return ret; 200662306a36Sopenharmony_ci } 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, 200962306a36Sopenharmony_ci tegra->edp_irq, 201062306a36Sopenharmony_ci soctherm_edp_isr, 201162306a36Sopenharmony_ci soctherm_edp_isr_thread, 201262306a36Sopenharmony_ci IRQF_ONESHOT, 201362306a36Sopenharmony_ci "soctherm_edp", 201462306a36Sopenharmony_ci tegra); 201562306a36Sopenharmony_ci if (ret < 0) { 201662306a36Sopenharmony_ci dev_err(&pdev->dev, "request_irq 'edp_irq' failed.\n"); 201762306a36Sopenharmony_ci return ret; 201862306a36Sopenharmony_ci } 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci return 0; 202162306a36Sopenharmony_ci} 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_cistatic void soctherm_init(struct platform_device *pdev) 202462306a36Sopenharmony_ci{ 202562306a36Sopenharmony_ci struct tegra_soctherm *tegra = platform_get_drvdata(pdev); 202662306a36Sopenharmony_ci const struct tegra_tsensor_group **ttgs = tegra->soc->ttgs; 202762306a36Sopenharmony_ci int i; 202862306a36Sopenharmony_ci u32 pdiv, hotspot; 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci /* Initialize raw sensors */ 203162306a36Sopenharmony_ci for (i = 0; i < tegra->soc->num_tsensors; ++i) 203262306a36Sopenharmony_ci enable_tsensor(tegra, i); 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ci /* program pdiv and hotspot offsets per THERM */ 203562306a36Sopenharmony_ci pdiv = readl(tegra->regs + SENSOR_PDIV); 203662306a36Sopenharmony_ci hotspot = readl(tegra->regs + SENSOR_HOTSPOT_OFF); 203762306a36Sopenharmony_ci for (i = 0; i < tegra->soc->num_ttgs; ++i) { 203862306a36Sopenharmony_ci pdiv = REG_SET_MASK(pdiv, ttgs[i]->pdiv_mask, 203962306a36Sopenharmony_ci ttgs[i]->pdiv); 204062306a36Sopenharmony_ci /* hotspot offset from PLLX, doesn't need to configure PLLX */ 204162306a36Sopenharmony_ci if (ttgs[i]->id == TEGRA124_SOCTHERM_SENSOR_PLLX) 204262306a36Sopenharmony_ci continue; 204362306a36Sopenharmony_ci hotspot = REG_SET_MASK(hotspot, 204462306a36Sopenharmony_ci ttgs[i]->pllx_hotspot_mask, 204562306a36Sopenharmony_ci ttgs[i]->pllx_hotspot_diff); 204662306a36Sopenharmony_ci } 204762306a36Sopenharmony_ci writel(pdiv, tegra->regs + SENSOR_PDIV); 204862306a36Sopenharmony_ci writel(hotspot, tegra->regs + SENSOR_HOTSPOT_OFF); 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci /* Configure hw throttle */ 205162306a36Sopenharmony_ci tegra_soctherm_throttle(&pdev->dev); 205262306a36Sopenharmony_ci} 205362306a36Sopenharmony_ci 205462306a36Sopenharmony_cistatic const struct of_device_id tegra_soctherm_of_match[] = { 205562306a36Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_124_SOC 205662306a36Sopenharmony_ci { 205762306a36Sopenharmony_ci .compatible = "nvidia,tegra124-soctherm", 205862306a36Sopenharmony_ci .data = &tegra124_soctherm, 205962306a36Sopenharmony_ci }, 206062306a36Sopenharmony_ci#endif 206162306a36Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_132_SOC 206262306a36Sopenharmony_ci { 206362306a36Sopenharmony_ci .compatible = "nvidia,tegra132-soctherm", 206462306a36Sopenharmony_ci .data = &tegra132_soctherm, 206562306a36Sopenharmony_ci }, 206662306a36Sopenharmony_ci#endif 206762306a36Sopenharmony_ci#ifdef CONFIG_ARCH_TEGRA_210_SOC 206862306a36Sopenharmony_ci { 206962306a36Sopenharmony_ci .compatible = "nvidia,tegra210-soctherm", 207062306a36Sopenharmony_ci .data = &tegra210_soctherm, 207162306a36Sopenharmony_ci }, 207262306a36Sopenharmony_ci#endif 207362306a36Sopenharmony_ci { }, 207462306a36Sopenharmony_ci}; 207562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_soctherm_of_match); 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_cistatic int tegra_soctherm_probe(struct platform_device *pdev) 207862306a36Sopenharmony_ci{ 207962306a36Sopenharmony_ci const struct of_device_id *match; 208062306a36Sopenharmony_ci struct tegra_soctherm *tegra; 208162306a36Sopenharmony_ci struct thermal_zone_device *z; 208262306a36Sopenharmony_ci struct tsensor_shared_calib shared_calib; 208362306a36Sopenharmony_ci struct tegra_soctherm_soc *soc; 208462306a36Sopenharmony_ci unsigned int i; 208562306a36Sopenharmony_ci int err; 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci match = of_match_node(tegra_soctherm_of_match, pdev->dev.of_node); 208862306a36Sopenharmony_ci if (!match) 208962306a36Sopenharmony_ci return -ENODEV; 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci soc = (struct tegra_soctherm_soc *)match->data; 209262306a36Sopenharmony_ci if (soc->num_ttgs > TEGRA124_SOCTHERM_SENSOR_NUM) 209362306a36Sopenharmony_ci return -EINVAL; 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); 209662306a36Sopenharmony_ci if (!tegra) 209762306a36Sopenharmony_ci return -ENOMEM; 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ci mutex_init(&tegra->thermctl_lock); 210062306a36Sopenharmony_ci dev_set_drvdata(&pdev->dev, tegra); 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci tegra->soc = soc; 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci tegra->regs = devm_platform_ioremap_resource_byname(pdev, "soctherm-reg"); 210562306a36Sopenharmony_ci if (IS_ERR(tegra->regs)) { 210662306a36Sopenharmony_ci dev_err(&pdev->dev, "can't get soctherm registers"); 210762306a36Sopenharmony_ci return PTR_ERR(tegra->regs); 210862306a36Sopenharmony_ci } 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci if (!tegra->soc->use_ccroc) { 211162306a36Sopenharmony_ci tegra->clk_regs = devm_platform_ioremap_resource_byname(pdev, "car-reg"); 211262306a36Sopenharmony_ci if (IS_ERR(tegra->clk_regs)) { 211362306a36Sopenharmony_ci dev_err(&pdev->dev, "can't get car clk registers"); 211462306a36Sopenharmony_ci return PTR_ERR(tegra->clk_regs); 211562306a36Sopenharmony_ci } 211662306a36Sopenharmony_ci } else { 211762306a36Sopenharmony_ci tegra->ccroc_regs = devm_platform_ioremap_resource_byname(pdev, "ccroc-reg"); 211862306a36Sopenharmony_ci if (IS_ERR(tegra->ccroc_regs)) { 211962306a36Sopenharmony_ci dev_err(&pdev->dev, "can't get ccroc registers"); 212062306a36Sopenharmony_ci return PTR_ERR(tegra->ccroc_regs); 212162306a36Sopenharmony_ci } 212262306a36Sopenharmony_ci } 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci tegra->reset = devm_reset_control_get(&pdev->dev, "soctherm"); 212562306a36Sopenharmony_ci if (IS_ERR(tegra->reset)) { 212662306a36Sopenharmony_ci dev_err(&pdev->dev, "can't get soctherm reset\n"); 212762306a36Sopenharmony_ci return PTR_ERR(tegra->reset); 212862306a36Sopenharmony_ci } 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci tegra->clock_tsensor = devm_clk_get(&pdev->dev, "tsensor"); 213162306a36Sopenharmony_ci if (IS_ERR(tegra->clock_tsensor)) { 213262306a36Sopenharmony_ci dev_err(&pdev->dev, "can't get tsensor clock\n"); 213362306a36Sopenharmony_ci return PTR_ERR(tegra->clock_tsensor); 213462306a36Sopenharmony_ci } 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci tegra->clock_soctherm = devm_clk_get(&pdev->dev, "soctherm"); 213762306a36Sopenharmony_ci if (IS_ERR(tegra->clock_soctherm)) { 213862306a36Sopenharmony_ci dev_err(&pdev->dev, "can't get soctherm clock\n"); 213962306a36Sopenharmony_ci return PTR_ERR(tegra->clock_soctherm); 214062306a36Sopenharmony_ci } 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci tegra->calib = devm_kcalloc(&pdev->dev, 214362306a36Sopenharmony_ci soc->num_tsensors, sizeof(u32), 214462306a36Sopenharmony_ci GFP_KERNEL); 214562306a36Sopenharmony_ci if (!tegra->calib) 214662306a36Sopenharmony_ci return -ENOMEM; 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci /* calculate shared calibration data */ 214962306a36Sopenharmony_ci err = tegra_calc_shared_calib(soc->tfuse, &shared_calib); 215062306a36Sopenharmony_ci if (err) 215162306a36Sopenharmony_ci return err; 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci /* calculate tsensor calibration data */ 215462306a36Sopenharmony_ci for (i = 0; i < soc->num_tsensors; ++i) { 215562306a36Sopenharmony_ci err = tegra_calc_tsensor_calib(&soc->tsensors[i], 215662306a36Sopenharmony_ci &shared_calib, 215762306a36Sopenharmony_ci &tegra->calib[i]); 215862306a36Sopenharmony_ci if (err) 215962306a36Sopenharmony_ci return err; 216062306a36Sopenharmony_ci } 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci tegra->thermctl_tzs = devm_kcalloc(&pdev->dev, 216362306a36Sopenharmony_ci soc->num_ttgs, sizeof(z), 216462306a36Sopenharmony_ci GFP_KERNEL); 216562306a36Sopenharmony_ci if (!tegra->thermctl_tzs) 216662306a36Sopenharmony_ci return -ENOMEM; 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci err = soctherm_clk_enable(pdev, true); 216962306a36Sopenharmony_ci if (err) 217062306a36Sopenharmony_ci return err; 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci soctherm_thermtrips_parse(pdev); 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci soctherm_init_hw_throt_cdev(pdev); 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci soctherm_init(pdev); 217762306a36Sopenharmony_ci 217862306a36Sopenharmony_ci for (i = 0; i < soc->num_ttgs; ++i) { 217962306a36Sopenharmony_ci struct tegra_thermctl_zone *zone = 218062306a36Sopenharmony_ci devm_kzalloc(&pdev->dev, sizeof(*zone), GFP_KERNEL); 218162306a36Sopenharmony_ci if (!zone) { 218262306a36Sopenharmony_ci err = -ENOMEM; 218362306a36Sopenharmony_ci goto disable_clocks; 218462306a36Sopenharmony_ci } 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci zone->reg = tegra->regs + soc->ttgs[i]->sensor_temp_offset; 218762306a36Sopenharmony_ci zone->dev = &pdev->dev; 218862306a36Sopenharmony_ci zone->sg = soc->ttgs[i]; 218962306a36Sopenharmony_ci zone->ts = tegra; 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci z = devm_thermal_of_zone_register(&pdev->dev, 219262306a36Sopenharmony_ci soc->ttgs[i]->id, zone, 219362306a36Sopenharmony_ci &tegra_of_thermal_ops); 219462306a36Sopenharmony_ci if (IS_ERR(z)) { 219562306a36Sopenharmony_ci err = PTR_ERR(z); 219662306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register sensor: %d\n", 219762306a36Sopenharmony_ci err); 219862306a36Sopenharmony_ci goto disable_clocks; 219962306a36Sopenharmony_ci } 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci zone->tz = z; 220262306a36Sopenharmony_ci tegra->thermctl_tzs[soc->ttgs[i]->id] = z; 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci /* Configure hw trip points */ 220562306a36Sopenharmony_ci err = tegra_soctherm_set_hwtrips(&pdev->dev, soc->ttgs[i], z); 220662306a36Sopenharmony_ci if (err) 220762306a36Sopenharmony_ci goto disable_clocks; 220862306a36Sopenharmony_ci } 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_ci err = soctherm_interrupts_init(pdev, tegra); 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci soctherm_debug_init(pdev); 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_ci return 0; 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_cidisable_clocks: 221762306a36Sopenharmony_ci soctherm_clk_enable(pdev, false); 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci return err; 222062306a36Sopenharmony_ci} 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_cistatic int tegra_soctherm_remove(struct platform_device *pdev) 222362306a36Sopenharmony_ci{ 222462306a36Sopenharmony_ci struct tegra_soctherm *tegra = platform_get_drvdata(pdev); 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci debugfs_remove_recursive(tegra->debugfs_dir); 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci soctherm_clk_enable(pdev, false); 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ci return 0; 223162306a36Sopenharmony_ci} 223262306a36Sopenharmony_ci 223362306a36Sopenharmony_cistatic int __maybe_unused soctherm_suspend(struct device *dev) 223462306a36Sopenharmony_ci{ 223562306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci soctherm_clk_enable(pdev, false); 223862306a36Sopenharmony_ci 223962306a36Sopenharmony_ci return 0; 224062306a36Sopenharmony_ci} 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_cistatic int __maybe_unused soctherm_resume(struct device *dev) 224362306a36Sopenharmony_ci{ 224462306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 224562306a36Sopenharmony_ci struct tegra_soctherm *tegra = platform_get_drvdata(pdev); 224662306a36Sopenharmony_ci struct tegra_soctherm_soc *soc = tegra->soc; 224762306a36Sopenharmony_ci int err, i; 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci err = soctherm_clk_enable(pdev, true); 225062306a36Sopenharmony_ci if (err) { 225162306a36Sopenharmony_ci dev_err(&pdev->dev, 225262306a36Sopenharmony_ci "Resume failed: enable clocks failed\n"); 225362306a36Sopenharmony_ci return err; 225462306a36Sopenharmony_ci } 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_ci soctherm_init(pdev); 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci for (i = 0; i < soc->num_ttgs; ++i) { 225962306a36Sopenharmony_ci struct thermal_zone_device *tz; 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ci tz = tegra->thermctl_tzs[soc->ttgs[i]->id]; 226262306a36Sopenharmony_ci err = tegra_soctherm_set_hwtrips(dev, soc->ttgs[i], tz); 226362306a36Sopenharmony_ci if (err) { 226462306a36Sopenharmony_ci dev_err(&pdev->dev, 226562306a36Sopenharmony_ci "Resume failed: set hwtrips failed\n"); 226662306a36Sopenharmony_ci return err; 226762306a36Sopenharmony_ci } 226862306a36Sopenharmony_ci } 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci return 0; 227162306a36Sopenharmony_ci} 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(tegra_soctherm_pm, soctherm_suspend, soctherm_resume); 227462306a36Sopenharmony_ci 227562306a36Sopenharmony_cistatic struct platform_driver tegra_soctherm_driver = { 227662306a36Sopenharmony_ci .probe = tegra_soctherm_probe, 227762306a36Sopenharmony_ci .remove = tegra_soctherm_remove, 227862306a36Sopenharmony_ci .driver = { 227962306a36Sopenharmony_ci .name = "tegra_soctherm", 228062306a36Sopenharmony_ci .pm = &tegra_soctherm_pm, 228162306a36Sopenharmony_ci .of_match_table = tegra_soctherm_of_match, 228262306a36Sopenharmony_ci }, 228362306a36Sopenharmony_ci}; 228462306a36Sopenharmony_cimodule_platform_driver(tegra_soctherm_driver); 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_ciMODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>"); 228762306a36Sopenharmony_ciMODULE_DESCRIPTION("NVIDIA Tegra SOCTHERM thermal management driver"); 228862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2289