Commit 6ca29b7e authored by Wei Ni's avatar Wei Ni Committed by Eduardo Valentin
Browse files

thermal: tegra: add support for gpu hw-throttle



Add support to trigger pulse skippers on the GPU
when a HOT trip point is triggered. The pulse skippers
can be signalled to throttle at low, medium and high
depths\levels.

Signed-off-by: default avatarWei Ni <wni@nvidia.com>
Signed-off-by: default avatarEduardo Valentin <edubezval@gmail.com>
parent 7d8ac6b2
Loading
Loading
Loading
Loading
+85 −33
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
 * Copyright (c) 2014 - 2018, NVIDIA CORPORATION.  All rights reserved.
 *
 * Author:
 *	Mikko Perttunen <mperttunen@nvidia.com>
@@ -160,6 +161,15 @@
/* get dividend from the depth */
#define THROT_DEPTH_DIVIDEND(depth)	((256 * (100 - (depth)) / 100) - 1)

/* gk20a nv_therm interface N:3 Mapping. Levels defined in tegra124-sochterm.h
 * level	vector
 * NONE		3'b000
 * LOW		3'b001
 * MED		3'b011
 * HIGH		3'b111
 */
#define THROT_LEVEL_TO_DEPTH(level)	((0x1 << (level)) - 1)

/* get THROT_PSKIP_xxx offset per LIGHT/HEAVY throt and CPU/GPU dev */
#define THROT_OFFSET			0x30
#define THROT_PSKIP_CTRL(throt, dev)	(THROT_PSKIP_CTRL_LITE_CPU + \
@@ -219,6 +229,7 @@ struct soctherm_throt_cfg {
	u8 priority;
	u8 cpu_throt_level;
	u32 cpu_throt_depth;
	u32 gpu_throt_level;
	struct thermal_cooling_device *cdev;
	bool init;
};
@@ -996,6 +1007,50 @@ static int soctherm_thermtrips_parse(struct platform_device *pdev)
	return 0;
}

static int soctherm_throt_cfg_parse(struct device *dev,
				    struct device_node *np,
				    struct soctherm_throt_cfg *stc)
{
	struct tegra_soctherm *ts = dev_get_drvdata(dev);
	int ret;
	u32 val;

	ret = of_property_read_u32(np, "nvidia,priority", &val);
	if (ret) {
		dev_err(dev, "throttle-cfg: %s: invalid priority\n", stc->name);
		return -EINVAL;
	}
	stc->priority = val;

	ret = of_property_read_u32(np, ts->soc->use_ccroc ?
				   "nvidia,cpu-throt-level" :
				   "nvidia,cpu-throt-percent", &val);
	if (!ret) {
		if (ts->soc->use_ccroc &&
		    val <= TEGRA_SOCTHERM_THROT_LEVEL_HIGH)
			stc->cpu_throt_level = val;
		else if (!ts->soc->use_ccroc && val <= 100)
			stc->cpu_throt_depth = val;
		else
			goto err;
	} else {
		goto err;
	}

	ret = of_property_read_u32(np, "nvidia,gpu-throt-level", &val);
	if (!ret && val <= TEGRA_SOCTHERM_THROT_LEVEL_HIGH)
		stc->gpu_throt_level = val;
	else
		goto err;

	return 0;

err:
	dev_err(dev, "throttle-cfg: %s: no throt prop or invalid prop\n",
		stc->name);
	return -EINVAL;
}

/**
 * soctherm_init_hw_throt_cdev() - Parse the HW throttle configurations
 * and register them as cooling devices.
@@ -1006,8 +1061,7 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev)
	struct tegra_soctherm *ts = dev_get_drvdata(dev);
	struct device_node *np_stc, *np_stcc;
	const char *name;
	u32 val;
	int i, r;
	int i;

	for (i = 0; i < THROTTLE_SIZE; i++) {
		ts->throt_cfgs[i].name = throt_names[i];
@@ -1025,6 +1079,7 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev)
	for_each_child_of_node(np_stc, np_stcc) {
		struct soctherm_throt_cfg *stc;
		struct thermal_cooling_device *tcd;
		int err;

		name = np_stcc->name;
		stc = find_throttle_cfg_by_name(ts, name);
@@ -1034,37 +1089,10 @@ static void soctherm_init_hw_throt_cdev(struct platform_device *pdev)
			continue;
		}

		r = of_property_read_u32(np_stcc, "nvidia,priority", &val);
		if (r) {
			dev_info(dev,
				 "throttle-cfg: %s: missing priority\n", name);
			continue;
		}
		stc->priority = val;

		if (ts->soc->use_ccroc) {
			r = of_property_read_u32(np_stcc,
						 "nvidia,cpu-throt-level",
						 &val);
			if (r) {
				dev_info(dev,
					 "throttle-cfg: %s: missing cpu-throt-level\n",
					 name);
				continue;
			}
			stc->cpu_throt_level = val;
		} else {
			r = of_property_read_u32(np_stcc,
						 "nvidia,cpu-throt-percent",
						 &val);
			if (r) {
				dev_info(dev,
					 "throttle-cfg: %s: missing cpu-throt-percent\n",
					 name);
		err = soctherm_throt_cfg_parse(dev, np_stcc, stc);
		if (err)
			continue;
			}
			stc->cpu_throt_depth = val;
		}

		tcd = thermal_of_cooling_device_register(np_stcc,
							 (char *)name, ts,
@@ -1207,6 +1235,28 @@ static void throttlectl_cpu_mn(struct tegra_soctherm *ts,
	writel(r, ts->regs + THROT_PSKIP_RAMP(throt, THROTTLE_DEV_CPU));
}

/**
 * throttlectl_gpu_level_select() - selects throttling level for GPU
 * @throt: the LIGHT/HEAVY of throttle event id
 *
 * This function programs soctherm's interface to GK20a NV_THERM to select
 * pre-configured "Low", "Medium" or "Heavy" throttle levels.
 *
 * Return: boolean true if HW was programmed
 */
static void throttlectl_gpu_level_select(struct tegra_soctherm *ts,
					 enum soctherm_throttle_id throt)
{
	u32 r, level, throt_vect;

	level = ts->throt_cfgs[throt].gpu_throt_level;
	throt_vect = THROT_LEVEL_TO_DEPTH(level);
	r = readl(ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_GPU));
	r = REG_SET_MASK(r, THROT_PSKIP_CTRL_ENABLE_MASK, 1);
	r = REG_SET_MASK(r, THROT_PSKIP_CTRL_VECT_GPU_MASK, throt_vect);
	writel(r, ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_GPU));
}

/**
 * soctherm_throttle_program() - programs pulse skippers' configuration
 * @throt: the LIGHT/HEAVY of the throttle event id.
@@ -1229,6 +1279,8 @@ static void soctherm_throttle_program(struct tegra_soctherm *ts,
	else
		throttlectl_cpu_mn(ts, throt);

	throttlectl_gpu_level_select(ts, throt);

	r = REG_SET_MASK(0, THROT_PRIORITY_LITE_PRIO_MASK, stc.priority);
	writel(r, ts->regs + THROT_PRIORITY_CTRL(throt));