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, &regs_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