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

Commit e58e95ba authored by Manaf Meethalavalappu Pallikunhi's avatar Manaf Meethalavalappu Pallikunhi
Browse files

drivers: thermal: regulator-cdev: Snapshot of regulator cooling device



Add a snapshot of regulator cooling device driver from msm-4.19 as of
'commit <f2c81ce379ec7> ("drivers: thermal: Handle min state clear
condition properly")'.

Change-Id: If6ab9e571673b01e5aea6fd80f0f4003ae724440
Signed-off-by: default avatarManaf Meethalavalappu Pallikunhi <manafm@codeaurora.org>
parent f02cf8f6
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -118,3 +118,12 @@ config QTI_SDPM_CLOCK_MONITOR
	    for different clock rate change notifications and write the clock
	    rate into the SDPM CSR register. This driver will receive the clock
	    list and the CSR details from devicetree.

config REGULATOR_COOLING_DEVICE
	tristate "Regulator voltage floor cooling device"
	depends on REGULATOR && THERMAL_OF
	help
	  This implements a mitigation device to place a minimum voltage floor
	  on a particular regulator. This mitigation device will be used by low
	  temperature reliability rules to mitigate a regulator at nominal
	  voltage.
+1 −0
Original line number Diff line number Diff line
@@ -19,3 +19,4 @@ obj-$(CONFIG_QTI_THERMAL_LIMITS_DCVS) += msm_lmh_dcvs.o
obj-$(CONFIG_QTI_CPU_VOLTAGE_COOLING_DEVICE) += cpu_voltage_cooling.o
obj-$(CONFIG_QTI_POLICY_ENGINE_SENSOR) += policy_engine.o
obj-${CONFIG_QTI_SDPM_CLOCK_MONITOR} += sdpm_clk.o
obj-$(CONFIG_REGULATOR_COOLING_DEVICE) += regulator_cdev.o
+162 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
 */

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

#define REG_CDEV_DRIVER "reg-cooling-device"

struct reg_cooling_device {
	struct regulator		*reg;
	struct thermal_cooling_device	*cool_dev;
	unsigned int			cur_state;
	unsigned int			*lvl;
	unsigned int			lvl_ct;
	char				reg_name[THERMAL_NAME_LENGTH];
	bool				reg_enable;
};

static int reg_get_max_state(struct thermal_cooling_device *cdev,
				 unsigned long *state)
{
	struct reg_cooling_device *reg_dev = cdev->devdata;

	*state = reg_dev->lvl_ct;
	return 0;
}

static int reg_get_cur_state(struct thermal_cooling_device *cdev,
				 unsigned long *state)
{
	struct reg_cooling_device *reg_dev = cdev->devdata;

	*state = reg_dev->cur_state;
	return 0;
}

static int reg_set_cur_state(struct thermal_cooling_device *cdev,
				 unsigned long state)
{
	struct reg_cooling_device *reg_dev = cdev->devdata;
	int ret = 0;

	if (state > reg_dev->lvl_ct)
		return -EINVAL;

	if (reg_dev->cur_state == state)
		return ret;

	ret = regulator_set_voltage(reg_dev->reg,
			reg_dev->lvl[state], INT_MAX);
	if (ret) {
		dev_err(&cdev->device,
			"switching to floor %lu err:%d\n",
			state, ret);
		return ret;
	}
	if (reg_dev->reg_enable && !state) {
		ret = regulator_disable(reg_dev->reg);
		if (ret) {
			dev_err(&cdev->device,
				"regulator disable err:%d\n", ret);
			return ret;
		}
		reg_dev->reg_enable = false;
	} else if (!reg_dev->reg_enable && state) {
		ret = regulator_enable(reg_dev->reg);
		if (ret) {
			dev_err(&cdev->device,
				"regulator enable err:%d\n", ret);
			return ret;
		}
		reg_dev->reg_enable = true;
	}
	reg_dev->cur_state = state;

	return ret;
}

static struct thermal_cooling_device_ops reg_device_ops = {
	.get_max_state = reg_get_max_state,
	.get_cur_state = reg_get_cur_state,
	.set_cur_state = reg_set_cur_state,
};

static int reg_cdev_probe(struct platform_device *pdev)
{
	struct reg_cooling_device *reg_dev;
	int ret = 0;
	struct device_node *np;

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

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

	reg_dev->reg = devm_regulator_get(&pdev->dev, "regulator-cdev");
	if (IS_ERR_OR_NULL(reg_dev->reg)) {
		ret = PTR_ERR(reg_dev->reg);
		dev_err(&pdev->dev, "regulator register err:%d\n", ret);
		return ret;
	}
	ret = of_property_count_u32_elems(np, "regulator-levels");
	if (ret <= 0) {
		dev_err(&pdev->dev, "Invalid levels err:%d\n", ret);
		return ret;
	}
	reg_dev->lvl_ct = ret;
	reg_dev->lvl = devm_kcalloc(&pdev->dev, reg_dev->lvl_ct,
			sizeof(*reg_dev->lvl), GFP_KERNEL);
	if (!reg_dev->lvl)
		return -ENOMEM;
	ret = of_property_read_u32_array(np, "regulator-levels",
				reg_dev->lvl, reg_dev->lvl_ct);
	if (ret) {
		dev_err(&pdev->dev, "cdev level fetch err:%d\n", ret);
		return ret;
	}
	/* level count is an index and it depicts the max possible index */
	reg_dev->lvl_ct--;
	reg_dev->cur_state = 0;
	reg_dev->reg_enable = false;
	strlcpy(reg_dev->reg_name, np->name, THERMAL_NAME_LENGTH);

	reg_dev->cool_dev = thermal_of_cooling_device_register(
					np, reg_dev->reg_name, reg_dev,
					&reg_device_ops);
	if (IS_ERR(reg_dev->cool_dev)) {
		ret = PTR_ERR(reg_dev->cool_dev);
		dev_err(&pdev->dev, "regulator cdev register err:%d\n",
				ret);
		return ret;
	}

	return ret;
}

static const struct of_device_id reg_cdev_of_match[] = {
	{.compatible = "qcom,regulator-cooling-device", },
	{},
};

static struct platform_driver reg_cdev_driver = {
	.driver = {
		.name = REG_CDEV_DRIVER,
		.of_match_table = reg_cdev_of_match,
	},
	.probe = reg_cdev_probe,
};
builtin_platform_driver(reg_cdev_driver);
MODULE_LICENSE("GPL v2");