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

This commit is contained in:
qctecmdr 2020-07-25 10:33:07 -07:00 • committed by Gerrit - the friendly Code Review server
commit 5bb01a933f
3 changed files with 172 additions and 0 deletions

View file

@ -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.

View file

@ -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

View file

@ -0,0 +1,162 @@
// 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");