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

Commit 8f7f5fd1 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "defconfig: bengal: Enable thermal drivers"

parents 27fc0419 23e27bb2
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -321,12 +321,25 @@ CONFIG_QPNP_SMB5=y
CONFIG_SMB1355_SLAVE_CHARGER=y
CONFIG_QPNP_QG=y
CONFIG_THERMAL=y
CONFIG_THERMAL_STATISTICS=y
CONFIG_THERMAL_WRITABLE_TRIPS=y
CONFIG_THERMAL_GOV_USER_SPACE=y
CONFIG_THERMAL_GOV_LOW_LIMITS=y
CONFIG_CPU_THERMAL=y
CONFIG_DEVFREQ_THERMAL=y
CONFIG_QCOM_SPMI_TEMP_ALARM=y
CONFIG_THERMAL_TSENS=y
CONFIG_QTI_ADC_TM=y
CONFIG_QTI_VIRTUAL_SENSOR=y
CONFIG_QTI_QMI_SENSOR=y
CONFIG_QTI_BCL_PMIC5=y
CONFIG_QTI_BCL_SOC_DRIVER=y
CONFIG_QTI_QMI_COOLING_DEVICE=y
CONFIG_QTI_THERMAL_LIMITS_DCVS=y
CONFIG_REGULATOR_COOLING_DEVICE=y
CONFIG_QTI_CPU_ISOLATE_COOLING_DEVICE=y
CONFIG_QTI_LMH_CPU_VDD_COOLING_DEVICE=y
CONFIG_QTI_CX_IPEAK_COOLING_DEVICE=y
CONFIG_MFD_I2C_PMIC=y
CONFIG_MFD_SPMI_PMIC=y
CONFIG_REGULATOR=y
+13 −0
Original line number Diff line number Diff line
@@ -333,12 +333,25 @@ CONFIG_QPNP_SMB5=y
CONFIG_SMB1355_SLAVE_CHARGER=y
CONFIG_QPNP_QG=y
CONFIG_THERMAL=y
CONFIG_THERMAL_STATISTICS=y
CONFIG_THERMAL_WRITABLE_TRIPS=y
CONFIG_THERMAL_GOV_USER_SPACE=y
CONFIG_THERMAL_GOV_LOW_LIMITS=y
CONFIG_CPU_THERMAL=y
CONFIG_DEVFREQ_THERMAL=y
CONFIG_QCOM_SPMI_TEMP_ALARM=y
CONFIG_THERMAL_TSENS=y
CONFIG_QTI_ADC_TM=y
CONFIG_QTI_VIRTUAL_SENSOR=y
CONFIG_QTI_QMI_SENSOR=y
CONFIG_QTI_BCL_PMIC5=y
CONFIG_QTI_BCL_SOC_DRIVER=y
CONFIG_QTI_QMI_COOLING_DEVICE=y
CONFIG_QTI_THERMAL_LIMITS_DCVS=y
CONFIG_REGULATOR_COOLING_DEVICE=y
CONFIG_QTI_CPU_ISOLATE_COOLING_DEVICE=y
CONFIG_QTI_LMH_CPU_VDD_COOLING_DEVICE=y
CONFIG_QTI_CX_IPEAK_COOLING_DEVICE=y
CONFIG_MFD_I2C_PMIC=y
CONFIG_MFD_SPMI_PMIC=y
CONFIG_REGULATOR=y
+11 −0
Original line number Diff line number Diff line
@@ -129,3 +129,14 @@ config QTI_LIMITS_ISENSE_CDSP
          shared memory and enable sysfs file support to access this data. This
          driver is required for the chipsets where isense hardware is present
          as part of cdsp subsystem.

config QTI_CX_IPEAK_COOLING_DEVICE
	bool "CX IPeak cooling device"
	depends on THERMAL_OF
	help
	  This implements a mitigation device to place a thermal client vote
	  to CXIP LM hardware. When all pre-defined clients on CX rail including
	  thermal client set their vote, CXIP LM hardware throttles the clients
	  on the CX rail.

	  If you want this support, you should say Y here.
+1 −0
Original line number Diff line number Diff line
@@ -12,3 +12,4 @@ 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
obj-$(CONFIG_QTI_LIMITS_ISENSE_CDSP) += msm_isense_cdsp.o
obj-$(CONFIG_QTI_CX_IPEAK_COOLING_DEVICE) += cx_ipeak_cdev.o
+252 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
 */

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

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/thermal.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/io.h>

#define CXIP_LM_CDEV_DRIVER "cx-ipeak-cooling-device"
#define CXIP_LM_CDEV_MAX_STATE 1

#define CXIP_LM_VOTE_STATUS       0x0
#define CXIP_LM_BYPASS            0x4
#define CXIP_LM_VOTE_CLEAR        0x8
#define CXIP_LM_VOTE_SET          0xc
#define CXIP_LM_FEATURE_EN        0x10
#define CXIP_LM_BYPASS_VAL        0xff20
#define CXIP_LM_THERM_VOTE_VAL    0x80
#define CXIP_LM_FEATURE_EN_VAL    0x1

struct cxip_lm_cooling_device {
	struct thermal_cooling_device	*cool_dev;
	char				cdev_name[THERMAL_NAME_LENGTH];
	void				*cx_ip_reg_base;
	unsigned int			therm_clnt;
	unsigned int			*bypass_clnts;
	unsigned int			bypass_clnt_cnt;
	bool				state;
};

static void cxip_lm_therm_vote_apply(struct cxip_lm_cooling_device *cxip_dev,
					bool vote)
{
	int vote_offset = 0, val = 0, sts_offset = 0;

	if (!cxip_dev->therm_clnt) {
		vote_offset = vote ? CXIP_LM_VOTE_SET : CXIP_LM_VOTE_CLEAR;
		val = CXIP_LM_THERM_VOTE_VAL;
		sts_offset = CXIP_LM_VOTE_STATUS;
	} else {
		vote_offset = cxip_dev->therm_clnt;
		val = vote ? 0x1 : 0x0;
		sts_offset = vote_offset;
	}

	writel_relaxed(val, cxip_dev->cx_ip_reg_base + vote_offset);
	pr_debug("%s vote for cxip_lm. vote:0x%x\n",
		vote ? "Applied" : "Cleared",
		readl_relaxed(cxip_dev->cx_ip_reg_base + sts_offset));
}

static void cxip_lm_initialize_cxip_hw(struct cxip_lm_cooling_device *cxip_dev)
{
	int i = 0;

	/* Set CXIP LM proxy vote for clients who are not participating */
	if (cxip_dev->bypass_clnt_cnt)
		for (i = 0; i < cxip_dev->bypass_clnt_cnt; i++)
			writel_relaxed(0x1, cxip_dev->cx_ip_reg_base +
					cxip_dev->bypass_clnts[i]);
	else if (!cxip_dev->therm_clnt)
		writel_relaxed(CXIP_LM_BYPASS_VAL,
			cxip_dev->cx_ip_reg_base + CXIP_LM_BYPASS);

	/* Enable CXIP LM HW */
	writel_relaxed(CXIP_LM_FEATURE_EN_VAL, cxip_dev->cx_ip_reg_base +
			CXIP_LM_FEATURE_EN);
}

static int cxip_lm_get_max_state(struct thermal_cooling_device *cdev,
				 unsigned long *state)
{
	*state = CXIP_LM_CDEV_MAX_STATE;

	return 0;
}

static int cxip_lm_set_cur_state(struct thermal_cooling_device *cdev,
				 unsigned long state)
{
	struct cxip_lm_cooling_device *cxip_dev = cdev->devdata;
	int ret = 0;

	if (state > CXIP_LM_CDEV_MAX_STATE)
		state = CXIP_LM_CDEV_MAX_STATE;

	if (cxip_dev->state == state)
		return 0;

	cxip_lm_therm_vote_apply(cxip_dev, state);
	cxip_dev->state = state;

	return ret;
}

static int cxip_lm_get_cur_state(struct thermal_cooling_device *cdev,
				 unsigned long *state)
{
	struct cxip_lm_cooling_device *cxip_dev = cdev->devdata;

	*state = cxip_dev->state;

	return 0;
}

static struct thermal_cooling_device_ops cxip_lm_device_ops = {
	.get_max_state = cxip_lm_get_max_state,
	.get_cur_state = cxip_lm_get_cur_state,
	.set_cur_state = cxip_lm_set_cur_state,
};

static int cxip_lm_cdev_remove(struct platform_device *pdev)
{
	struct cxip_lm_cooling_device *cxip_dev =
		(struct cxip_lm_cooling_device *)dev_get_drvdata(&pdev->dev);

	if (cxip_dev) {
		if (cxip_dev->cool_dev)
			thermal_cooling_device_unregister(cxip_dev->cool_dev);

		if (cxip_dev->cx_ip_reg_base)
			cxip_lm_therm_vote_apply(cxip_dev->cx_ip_reg_base,
							false);
	}

	return 0;
}

static int cxip_lm_get_devicetree_data(struct platform_device *pdev,
					struct cxip_lm_cooling_device *cxip_dev,
					struct device_node *np)
{
	int ret = 0;

	ret = of_property_read_u32(np, "qcom,thermal-client-offset",
			&cxip_dev->therm_clnt);
	if (ret) {
		dev_dbg(&pdev->dev,
			"error for qcom,thermal-client-offset. ret:%d\n",
			ret);
		cxip_dev->therm_clnt = 0;
		ret = 0;
		return ret;
	}

	ret = of_property_count_u32_elems(np, "qcom,bypass-client-list");
	if (ret <= 0) {
		dev_dbg(&pdev->dev, "Invalid number of clients err:%d\n", ret);
		ret = 0;
		return ret;
	}
	cxip_dev->bypass_clnt_cnt = ret;

	cxip_dev->bypass_clnts = devm_kcalloc(&pdev->dev,
				cxip_dev->bypass_clnt_cnt,
				sizeof(*cxip_dev->bypass_clnts), GFP_KERNEL);
	if (!cxip_dev->bypass_clnts)
		return -ENOMEM;

	ret = of_property_read_u32_array(np, "qcom,bypass-client-list",
		cxip_dev->bypass_clnts, cxip_dev->bypass_clnt_cnt);
	if (ret) {
		dev_dbg(&pdev->dev, "bypass client list err:%d, cnt:%d\n",
			ret, cxip_dev->bypass_clnt_cnt);
		cxip_dev->bypass_clnt_cnt = 0;
		ret = 0;
	}

	return ret;
}

static int cxip_lm_cdev_probe(struct platform_device *pdev)
{
	struct cxip_lm_cooling_device *cxip_dev = NULL;
	int ret = 0;
	struct device_node *np;
	struct resource *res = NULL;

	np = dev_of_node(&pdev->dev);
	if (!np) {
		dev_err(&pdev->dev,
			"of node not available for cxip_lm cdev\n");
		return -EINVAL;
	}

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

	ret = cxip_lm_get_devicetree_data(pdev, cxip_dev, np);
	if (ret)
		return ret;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res) {
		dev_err(&pdev->dev,
			"cxip_lm platform get resource failed\n");
		return -ENODEV;
	}

	cxip_dev->cx_ip_reg_base = devm_ioremap(&pdev->dev, res->start,
						resource_size(res));
	if (!cxip_dev->cx_ip_reg_base) {
		dev_err(&pdev->dev, "cxip_lm reg remap failed\n");
		return -ENOMEM;
	}

	cxip_lm_initialize_cxip_hw(cxip_dev);

	/* Set thermal vote till we get first vote from TF */
	cxip_dev->state = true;
	cxip_lm_therm_vote_apply(cxip_dev, cxip_dev->state);

	strlcpy(cxip_dev->cdev_name, np->name, THERMAL_NAME_LENGTH);
	cxip_dev->cool_dev = thermal_of_cooling_device_register(
					np, cxip_dev->cdev_name, cxip_dev,
					&cxip_lm_device_ops);
	if (IS_ERR(cxip_dev->cool_dev)) {
		ret = PTR_ERR(cxip_dev->cool_dev);
		dev_err(&pdev->dev, "cxip_lm cdev register err:%d\n",
				ret);
		cxip_dev->cool_dev = NULL;
		cxip_lm_therm_vote_apply(cxip_dev->cx_ip_reg_base,
						false);
		return ret;
	}

	dev_set_drvdata(&pdev->dev, cxip_dev);

	return ret;
}

static const struct of_device_id cxip_lm_cdev_of_match[] = {
	{.compatible = "qcom,cxip-lm-cooling-device", },
	{}
};

static struct platform_driver cxip_lm_cdev_driver = {
	.driver = {
		.name = CXIP_LM_CDEV_DRIVER,
		.of_match_table = cxip_lm_cdev_of_match,
	},
	.probe = cxip_lm_cdev_probe,
	.remove = cxip_lm_cdev_remove,
};
builtin_platform_driver(cxip_lm_cdev_driver);