Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 35ebb639 authored by Manaf Meethalavalappu Pallikunhi's avatar Manaf Meethalavalappu Pallikunhi Committed by Gerrit - the friendly Code Review server
Browse files

drivers: thermal: Add support for limits cpu voltage cooling device



Add support for limits cpu voltage cooling device driver. The LMH CPU
voltage cooling device will be used to place VDD restriction vote to
limits hardware on cold thermal condition. This cooling device driver
can register one cooling device per LLM, which can be used by thermal
zone to place VDD restriction vote.

Change-Id: I81aebcba3ab8d2db17b98b41c554067078edcb9f
Signed-off-by: default avatarManaf Meethalavalappu Pallikunhi <manafm@codeaurora.org>
parent 394ec00f
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -112,3 +112,11 @@ config QTI_CPU_ISOLATE_COOLING_DEVICE
	   a CPU will be used when the CPU frequency mitigation
	   is not good enough to achieve the necessary cooling.

config QTI_LMH_CPU_VDD_COOLING_DEVICE
	tristate "QTI CPU Voltage cooling devices"
	depends on THERMAL_OF
	help
	   This enables the QTI limits hardware CPU VDD cooling devices.
	   These cooling devices will be used by QTI chipset to place a
	   request to limits hardware for a minimum CPU railway voltage
	   corner at cold temperature condition.
+1 −0
Original line number Diff line number Diff line
@@ -10,3 +10,4 @@ obj-$(CONFIG_QTI_THERMAL_LIMITS_DCVS) += msm_lmh_dcvs.o lmh_dbg.o
obj-$(CONFIG_QTI_AOP_REG_COOLING_DEVICE) += regulator_aop_cdev.o
obj-$(CONFIG_REGULATOR_COOLING_DEVICE) += regulator_cdev.o
obj-$(CONFIG_QTI_CPU_ISOLATE_COOLING_DEVICE) += cpu_isolate.o
obj-$(CONFIG_QTI_LMH_CPU_VDD_COOLING_DEVICE) += lmh_cpu_vdd_cdev.o
+179 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2019, The Linux Foundation. All rights reserved.
 */

#define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/thermal.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/io.h>
#include <linux/cpu_cooling.h>
#include <linux/idr.h>

#define LMH_CPU_VDD_MAX_LVL	1
#define LIMITS_CLUSTER_MIN_FREQ_OFFSET	0x3C0

struct lmh_cpu_vdd_cdev {
	struct list_head node;
	int id;
	bool cpu_vdd_state;
	void *min_freq_reg;
	struct thermal_cooling_device *cdev;
};

static DEFINE_IDA(lmh_cpu_vdd_ida);
static DEFINE_MUTEX(lmh_cpu_vdd_lock);
static LIST_HEAD(lmh_cpu_vdd_list);

static int lmh_cpu_vdd_set_cur_state(struct thermal_cooling_device *cdev,
				 unsigned long state)
{
	struct lmh_cpu_vdd_cdev *vdd_cdev = cdev->devdata;

	if (state > LMH_CPU_VDD_MAX_LVL)
		state = LMH_CPU_VDD_MAX_LVL;

	state = !!state;
	/* Check if the old cooling action is same as new cooling action */
	if (vdd_cdev->cpu_vdd_state == state)
		return 0;

	writel_relaxed(state, vdd_cdev->min_freq_reg);
	vdd_cdev->cpu_vdd_state = state;

	pr_debug("%s limits CPU VDD restriction for %s\n",
		state ? "Triggered" : "Cleared", vdd_cdev->cdev->type);

	return 0;
}

static int lmh_cpu_vdd_get_cur_state(struct thermal_cooling_device *cdev,
				 unsigned long *state)
{
	struct lmh_cpu_vdd_cdev *lmh_cpu_vdd_cdev = cdev->devdata;

	*state = (lmh_cpu_vdd_cdev->cpu_vdd_state) ?
			LMH_CPU_VDD_MAX_LVL : 0;

	return 0;
}

static int lmh_cpu_vdd_get_max_state(struct thermal_cooling_device *cdev,
				 unsigned long *state)
{
	*state = LMH_CPU_VDD_MAX_LVL;
	return 0;
}

static struct thermal_cooling_device_ops lmh_cpu_vdd_cooling_ops = {
	.get_max_state = lmh_cpu_vdd_get_max_state,
	.get_cur_state = lmh_cpu_vdd_get_cur_state,
	.set_cur_state = lmh_cpu_vdd_set_cur_state,
};


static int lmh_cpu_vdd_probe(struct platform_device *pdev)
{
	int ret = -1;
	struct lmh_cpu_vdd_cdev *lmh_cpu_vdd_cdev;
	struct device_node *dn = pdev->dev.of_node;
	uint32_t min_reg;
	char cdev_name[THERMAL_NAME_LENGTH] = "";
	const __be32 *addr;

	lmh_cpu_vdd_cdev = devm_kzalloc(&pdev->dev, sizeof(*lmh_cpu_vdd_cdev),
					GFP_KERNEL);
	if (!lmh_cpu_vdd_cdev)
		return -ENOMEM;

	addr = of_get_address(dn, 0, NULL, NULL);
	if (!addr) {
		dev_err(&pdev->dev, "Property llm-base-addr not found\n");
		return -EINVAL;
	}

	min_reg = be32_to_cpu(addr[0]) + LIMITS_CLUSTER_MIN_FREQ_OFFSET;
	lmh_cpu_vdd_cdev->min_freq_reg = devm_ioremap(&pdev->dev, min_reg, 0x4);
	if (!lmh_cpu_vdd_cdev->min_freq_reg) {
		dev_err(&pdev->dev, "lmh cpu vdd register remap failed\n");
		return -ENOMEM;
	}

	mutex_lock(&lmh_cpu_vdd_lock);
	ret = ida_simple_get(&lmh_cpu_vdd_ida, 0, 0, GFP_KERNEL);
	if (ret < 0) {
		dev_err(&pdev->dev, "Failed to create ida\n");
		goto unlock_exit;
	}
	lmh_cpu_vdd_cdev->id = ret;
	ret = 0;

	snprintf(cdev_name, THERMAL_NAME_LENGTH, "lmh-cpu-vdd%d",
			lmh_cpu_vdd_cdev->id);

	lmh_cpu_vdd_cdev->cdev = thermal_of_cooling_device_register(
					dn,
					cdev_name,
					lmh_cpu_vdd_cdev,
					&lmh_cpu_vdd_cooling_ops);
	if (IS_ERR(lmh_cpu_vdd_cdev->cdev)) {
		ret = PTR_ERR(lmh_cpu_vdd_cdev->cdev);
		dev_err(&pdev->dev, "Cooling register failed for %s, ret:%d\n",
			cdev_name, ret);
		lmh_cpu_vdd_cdev->cdev = NULL;
		goto remove_ida;
	}
	list_add(&lmh_cpu_vdd_cdev->node, &lmh_cpu_vdd_list);
	mutex_unlock(&lmh_cpu_vdd_lock);

	pr_debug("Cooling device [%s] registered.\n", cdev_name);

	return ret;

remove_ida:
	ida_simple_remove(&lmh_cpu_vdd_ida, lmh_cpu_vdd_cdev->id);

unlock_exit:
	mutex_unlock(&lmh_cpu_vdd_lock);

	return ret;
}

static int lmh_cpu_vdd_remove(struct platform_device *pdev)
{
	struct lmh_cpu_vdd_cdev *lmh_cpu_vdd, *c_next;

	mutex_lock(&lmh_cpu_vdd_lock);
	list_for_each_entry_safe(lmh_cpu_vdd, c_next,
			&lmh_cpu_vdd_list, node) {
		if (lmh_cpu_vdd->cdev) {
			thermal_cooling_device_unregister(
				lmh_cpu_vdd->cdev);
			lmh_cpu_vdd->cdev = NULL;
		}
		ida_simple_remove(&lmh_cpu_vdd_ida, lmh_cpu_vdd->id);
		list_del(&lmh_cpu_vdd->node);
	}
	mutex_unlock(&lmh_cpu_vdd_lock);

	return 0;
}
static const struct of_device_id lmh_cpu_vdd_match[] = {
	{ .compatible = "qcom,lmh-cpu-vdd", },
	{},
};

static struct platform_driver lmh_cpu_vdd_driver = {
	.probe		= lmh_cpu_vdd_probe,
	.remove         = lmh_cpu_vdd_remove,
	.driver		= {
		.name = KBUILD_MODNAME,
		.of_match_table = lmh_cpu_vdd_match,
	},
};
builtin_platform_driver(lmh_cpu_vdd_driver);