regulator: cpr-regulator: Enable/disable closed-loop CPR on temperature

Add logic to enable/disable closed-loop CPR based on TSENS temperature.
Use the thermal sensor interface which allows monitoring TSENS
temperature thresholds and notifies when the configured threshold is hit.

Use the notification callbacks to disable/enable CPR and force open-loop
voltage.

CRs-Fixed: 903646
Change-Id: Id131f7570740dce4445ce1849ae0551d6207c737
Signed-off-by: Anirudh Ghayal <aghayal@codeaurora.org>
This commit is contained in:
Anirudh Ghayal 2015-09-07 19:52:39 +05:30
parent e8641c5aa4
commit 31eccc3394
2 changed files with 234 additions and 1 deletions

View File

@ -708,6 +708,17 @@ Optional properties:
The number of quadruples should be equal to the number of values specified in
the qcom,cpr-aging-sensor-id property. This property is required if
the qcom,cpr-aging-sensor-id property has been specified.
- qcom,cpr-thermal-sensor-id: TSENS hardware sensor-id of the sensor which
needs to be monitored.
- qcom,cpr-disable-temp-threshold: The TSENS temperature threshold in degrees Celsius at which CPR
closed-loop is disabled. CPR closed-loop will stay disabled as long as the
temperature is below this threshold. This property is required
only if 'qcom,cpr-thermal-sensor-id' is present.
- qcom,cpr-enable-temp-threshold: The TSENS temperature threshold in degrees Celsius at which CPR
closed-loop is enabled. CPR closed-loop will stay enabled above this
temperature threshold. This property is required only if
'qcom,cpr-thermal-sensor-id' is present.
Example:
apc_vreg_corner: regulator@f9018000 {
status = "okay";
@ -957,4 +968,8 @@ Example:
qcom,cpr-fuse-aging-init-quot-diff =
<101 0 8 0>,
<101 8 8 0>;
qcom,cpr-thermal-sensor-id = <9>;
qcom,cpr-disable-temp-threshold = <5>;
qcom,cpr-enable-temp-threshold = <10>;
};

View File

@ -36,6 +36,8 @@
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/cpr-regulator.h>
#include <linux/msm_thermal.h>
#include <linux/msm_tsens.h>
#include <soc/qcom/scm.h>
/* Register Offsets for RB-CPR and Bit Definitions */
@ -267,6 +269,7 @@ struct cpr_regulator {
int corner;
int ceiling_max;
struct dentry *debugfs;
struct device *dev;
/* eFuse parameters */
phys_addr_t efuse_addr;
@ -298,6 +301,14 @@ struct cpr_regulator {
/* mem-acc regulator */
struct regulator *mem_acc_vreg;
/* thermal monitor */
int tsens_id;
int cpr_disable_temp_threshold;
int cpr_enable_temp_threshold;
bool cpr_disable_on_temperature;
bool cpr_thermal_disable;
struct threshold_info tsens_threshold_config;
/* CPR parameters */
u32 num_fuse_corners;
u64 cpr_fuse_bits;
@ -532,7 +543,8 @@ static u64 cpr_read_efuse_param(struct cpr_regulator *cpr_vreg, int row_start,
static bool cpr_is_allowed(struct cpr_regulator *cpr_vreg)
{
if (cpr_vreg->cpr_fuse_disable || !cpr_vreg->enable)
if (cpr_vreg->cpr_fuse_disable || !cpr_vreg->enable ||
cpr_vreg->cpr_thermal_disable)
return false;
else
return true;
@ -4889,6 +4901,139 @@ static int cpr_vsens_init(struct platform_device *pdev,
return rc;
}
static int cpr_disable_on_temp(struct cpr_regulator *cpr_vreg, bool disable)
{
int rc = 0;
mutex_lock(&cpr_vreg->cpr_mutex);
if (cpr_vreg->cpr_fuse_disable ||
(cpr_vreg->cpr_thermal_disable == disable))
goto out;
cpr_vreg->cpr_thermal_disable = disable;
if (cpr_vreg->enable && cpr_vreg->corner) {
if (disable) {
cpr_debug(cpr_vreg, "Disabling CPR - below temperature threshold [%d]\n",
cpr_vreg->cpr_disable_temp_threshold);
/* disable CPR and force open-loop */
cpr_ctl_disable(cpr_vreg);
rc = cpr_regulator_set_voltage(cpr_vreg->rdev,
cpr_vreg->corner, false);
if (rc < 0)
cpr_err(cpr_vreg, "Failed to set voltage, rc=%d\n",
rc);
} else {
/* enable CPR */
cpr_debug(cpr_vreg, "Enabling CPR - above temperature thresold [%d]\n",
cpr_vreg->cpr_enable_temp_threshold);
rc = cpr_regulator_set_voltage(cpr_vreg->rdev,
cpr_vreg->corner, true);
if (rc < 0)
cpr_err(cpr_vreg, "Failed to set voltage, rc=%d\n",
rc);
}
}
out:
mutex_unlock(&cpr_vreg->cpr_mutex);
return rc;
}
static void tsens_threshold_notify(struct therm_threshold *tsens_cb_data)
{
struct threshold_info *info = tsens_cb_data->parent;
struct cpr_regulator *cpr_vreg = container_of(info,
struct cpr_regulator, tsens_threshold_config);
int rc = 0;
cpr_debug(cpr_vreg, "Triggered tsens-notification trip_type=%d for thermal_zone_id=%d\n",
tsens_cb_data->trip_triggered, tsens_cb_data->sensor_id);
switch (tsens_cb_data->trip_triggered) {
case THERMAL_TRIP_CONFIGURABLE_HI:
rc = cpr_disable_on_temp(cpr_vreg, false);
if (rc < 0)
cpr_err(cpr_vreg, "Failed to enable CPR, rc=%d\n", rc);
break;
case THERMAL_TRIP_CONFIGURABLE_LOW:
rc = cpr_disable_on_temp(cpr_vreg, true);
if (rc < 0)
cpr_err(cpr_vreg, "Failed to disable CPR, rc=%d\n", rc);
break;
default:
cpr_debug(cpr_vreg, "trip-type %d not supported\n",
tsens_cb_data->trip_triggered);
break;
}
rc = sensor_mgr_set_threshold(tsens_cb_data->sensor_id,
tsens_cb_data->threshold);
if (rc < 0)
cpr_err(cpr_vreg, "Failed to set temp. threshold, rc=%d\n", rc);
}
static int cpr_check_tsens(struct cpr_regulator *cpr_vreg)
{
int rc = 0;
struct tsens_device tsens_dev;
unsigned long temp = 0;
bool disable;
if (tsens_is_ready() > 0) {
tsens_dev.sensor_num = cpr_vreg->tsens_id;
rc = tsens_get_temp(&tsens_dev, &temp);
if (rc < 0) {
cpr_err(cpr_vreg, "Faled to read tsens, rc=%d\n", rc);
return rc;
}
disable = (int) temp <= cpr_vreg->cpr_disable_temp_threshold;
rc = cpr_disable_on_temp(cpr_vreg, disable);
if (rc)
cpr_err(cpr_vreg, "Failed to %s CPR, rc=%d\n",
disable ? "disable" : "enable", rc);
}
return rc;
}
static int cpr_thermal_init(struct cpr_regulator *cpr_vreg)
{
int rc;
struct device_node *of_node = cpr_vreg->dev->of_node;
if (!of_find_property(of_node, "qcom,cpr-thermal-sensor-id", NULL))
return 0;
CPR_PROP_READ_U32(cpr_vreg, of_node, "cpr-thermal-sensor-id",
&cpr_vreg->tsens_id, rc);
if (rc < 0)
return rc;
CPR_PROP_READ_U32(cpr_vreg, of_node, "cpr-disable-temp-threshold",
&cpr_vreg->cpr_disable_temp_threshold, rc);
if (rc < 0)
return rc;
CPR_PROP_READ_U32(cpr_vreg, of_node, "cpr-enable-temp-threshold",
&cpr_vreg->cpr_enable_temp_threshold, rc);
if (rc < 0)
return rc;
if (cpr_vreg->cpr_disable_temp_threshold >=
cpr_vreg->cpr_enable_temp_threshold) {
cpr_err(cpr_vreg, "Invalid temperature threshold cpr_disable_temp[%d] >= cpr_enable_temp[%d]\n",
cpr_vreg->cpr_disable_temp_threshold,
cpr_vreg->cpr_enable_temp_threshold);
return -EINVAL;
}
cpr_vreg->cpr_disable_on_temperature = true;
return 0;
}
static int cpr_init_cpr(struct platform_device *pdev,
struct cpr_regulator *cpr_vreg)
{
@ -5636,6 +5781,7 @@ static int cpr_regulator_probe(struct platform_device *pdev)
return -ENOMEM;
}
cpr_vreg->dev = &pdev->dev;
cpr_vreg->rdesc.name = init_data->constraints.name;
if (cpr_vreg->rdesc.name == NULL) {
dev_err(dev, "regulator-name missing\n");
@ -5732,6 +5878,12 @@ static int cpr_regulator_probe(struct platform_device *pdev)
return rc;
}
rc = cpr_thermal_init(cpr_vreg);
if (rc) {
cpr_err(cpr_vreg, "Thermal intialization failed rc=%d\n", rc);
return rc;
}
/* Load per-online CPU adjustment data */
rc = cpr_init_per_cpu_adjustments(cpr_vreg, &pdev->dev);
if (rc) {
@ -5781,6 +5933,17 @@ static int cpr_regulator_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, cpr_vreg);
cpr_debugfs_init(cpr_vreg);
if (cpr_vreg->cpr_disable_on_temperature) {
rc = cpr_check_tsens(cpr_vreg);
if (rc < 0) {
cpr_err(cpr_vreg, "Unable to config CPR on tsens, rc=%d\n",
rc);
cpr_apc_exit(cpr_vreg);
cpr_debugfs_remove(cpr_vreg);
return rc;
}
}
mutex_lock(&cpr_regulator_list_mutex);
list_add(&cpr_vreg->list, &cpr_regulator_list);
mutex_unlock(&cpr_regulator_list_mutex);
@ -5811,6 +5974,10 @@ static int cpr_regulator_remove(struct platform_device *pdev)
if (cpr_vreg->cpu_notifier.notifier_call)
unregister_hotcpu_notifier(&cpr_vreg->cpu_notifier);
if (cpr_vreg->cpr_disable_on_temperature)
sensor_mgr_remove_threshold(cpr_vreg->dev,
&cpr_vreg->tsens_threshold_config);
cpr_apc_exit(cpr_vreg);
cpr_debugfs_remove(cpr_vreg);
regulator_unregister(cpr_vreg->rdev);
@ -5836,6 +6003,57 @@ static struct platform_driver cpr_regulator_driver = {
.resume = cpr_regulator_resume,
};
static int initialize_tsens_monitor(struct cpr_regulator *cpr_vreg)
{
int rc;
rc = cpr_check_tsens(cpr_vreg);
if (rc < 0) {
cpr_err(cpr_vreg, "Unable to check tsens, rc=%d\n", rc);
return rc;
}
rc = sensor_mgr_init_threshold(cpr_vreg->dev,
&cpr_vreg->tsens_threshold_config,
cpr_vreg->tsens_id,
cpr_vreg->cpr_enable_temp_threshold, /* high */
cpr_vreg->cpr_disable_temp_threshold, /* low */
tsens_threshold_notify);
if (rc < 0) {
cpr_err(cpr_vreg, "Failed to init tsens monitor, rc=%d\n", rc);
return rc;
}
rc = sensor_mgr_convert_id_and_set_threshold(
&cpr_vreg->tsens_threshold_config);
if (rc < 0)
cpr_err(cpr_vreg, "Failed to set tsens threshold, rc=%d\n",
rc);
return rc;
}
int __init cpr_regulator_late_init(void)
{
int rc;
struct cpr_regulator *cpr_vreg;
mutex_lock(&cpr_regulator_list_mutex);
list_for_each_entry(cpr_vreg, &cpr_regulator_list, list) {
if (cpr_vreg->cpr_disable_on_temperature) {
rc = initialize_tsens_monitor(cpr_vreg);
if (rc)
cpr_err(cpr_vreg, "Failed to initialize temperature monitor, rc=%d\n",
rc);
}
}
mutex_unlock(&cpr_regulator_list_mutex);
return 0;
}
late_initcall(cpr_regulator_late_init);
/**
* cpr_regulator_init() - register cpr-regulator driver
*