Loading drivers/thermal/qcom/Kconfig +9 −0 Original line number Diff line number Diff line Loading @@ -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. drivers/thermal/qcom/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -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 drivers/thermal/qcom/regulator_cdev.c 0 → 100644 +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, ®_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"); Loading
drivers/thermal/qcom/Kconfig +9 −0 Original line number Diff line number Diff line Loading @@ -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.
drivers/thermal/qcom/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -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
drivers/thermal/qcom/regulator_cdev.c 0 → 100644 +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, ®_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");