Merge "regulator: rpm-smd-regulator: add support for level based voting"

This commit is contained in:
Linux Build Service Account 2015-03-27 07:56:59 -07:00 committed by Gerrit - the friendly Code Review server
commit 5c147a3b29
4 changed files with 303 additions and 32 deletions

View File

@ -76,12 +76,23 @@ Optional properties:
qcom,use-voltage-floor-corner are mutually
exclusive. Only one may be specified for a
given regulator.
- qcom,always-send-voltage: Flag which indicates that updates to the voltage
or voltage corner set point should always be
sent immediately to the RPM. If this flag is
not specified, then voltage set point updates
are only sent if the given regulator has also
been enabled by a Linux consumer.
- qcom,use-voltage-level: Flag that signifies if regulator_set_voltage
calls should modify the level parameter instead
of the voltage parameter.
- qcom,use-voltage-floor-level: Flag that signifies if regulator_set_voltage
calls should modify the floor level parameter
instead of the voltage parameter.
The properties qcom,use-voltage-level and
qcom,use-voltage-floor-level are mutually
exclusive. Only one may be specified for a
given regulator.
- qcom,always-send-voltage: Flag which indicates that updates to the
voltage, voltage corner or voltage level set
point should always be sent immediately to the
RPM. If this flag is not specified, then
voltage set point updates are only sent if the
given regulator has also been enabled by a
Linux consumer.
- qcom,always-send-current: Flag which indicates that updates to the load
current should always be sent immediately to the
RPM. If this flag is not specified, then load
@ -195,6 +206,25 @@ RPM in regulator requests.
processor in the system is awake. This property
supports the same values as
qcom,init-voltage-corner.
- qcom,init-voltage-level: Performance level to use in order to determine
voltage set point. The meaning of level
values is set by the RPM. It is possible that
different regulators on a given platform or
similar regulators on different platforms will
utilize different level values. These are
level values supported on MSM8952 for PMIC
PM8952 SMPS 2 (VDD_Dig); nominal voltages for
these level are also shown:
16 = Retention (0.5000 V)
128 = SVS (1.0500 V)
192 = SVS+ (1.1550 V)
256 = Normal (1.2250 V)
320 = Normal+ (1.2875 V)
384 = Turbo (1.3500 V)
- qcom,init-voltage-floor-level: Minimum performance level to use if any
processor in the system is awake. This property
supports the same values as
qcom,init-voltage-level.
All properties specified within the core regulator framework can also be used in
second level nodes. These bindings can be found in:

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -69,6 +69,8 @@ enum rpm_regulator_param_index {
RPM_REGULATOR_PARAM_CORNER,
RPM_REGULATOR_PARAM_BYPASS,
RPM_REGULATOR_PARAM_FLOOR_CORNER,
RPM_REGULATOR_PARAM_LEVEL,
RPM_REGULATOR_PARAM_FLOOR_LEVEL,
RPM_REGULATOR_PARAM_MAX,
};
@ -126,6 +128,8 @@ static struct rpm_regulator_param params[RPM_REGULATOR_PARAM_MAX] = {
PARAM(CORNER, 1, 1, 0, 0, "corn", 0, 6, "qcom,init-voltage-corner"),
PARAM(BYPASS, 1, 0, 0, 0, "bypa", 0, 1, "qcom,init-disallow-bypass"),
PARAM(FLOOR_CORNER, 1, 1, 0, 0, "vfc", 0, 6, "qcom,init-voltage-floor-corner"),
PARAM(LEVEL, 1, 1, 0, 0, "vlvl", 0, 0xFFFF, "qcom,init-voltage-level"),
PARAM(FLOOR_LEVEL, 1, 1, 0, 0, "vfl", 0, 0xFFFF, "qcom,init-voltage-floor-level"),
};
struct rpm_regulator_mode_map {
@ -483,6 +487,8 @@ static void rpm_vreg_aggregate_params(u32 *param_aggr, const u32 *param_reg)
RPM_VREG_AGGR_MAX(CORNER, param_aggr, param_reg);
RPM_VREG_AGGR_MAX(BYPASS, param_aggr, param_reg);
RPM_VREG_AGGR_MAX(FLOOR_CORNER, param_aggr, param_reg);
RPM_VREG_AGGR_MAX(LEVEL, param_aggr, param_reg);
RPM_VREG_AGGR_MAX(FLOOR_LEVEL, param_aggr, param_reg);
}
static int rpm_vreg_aggregate_requests(struct rpm_regulator *regulator)
@ -848,6 +854,104 @@ static int rpm_vreg_get_voltage_floor_corner(struct regulator_dev *rdev)
+ RPM_REGULATOR_CORNER_NONE;
}
static int rpm_vreg_set_voltage_level(struct regulator_dev *rdev, int min_uV,
int max_uV, unsigned *selector)
{
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
int rc = 0;
int level;
u32 prev_level;
level = min_uV;
if (level < params[RPM_REGULATOR_PARAM_LEVEL].min
|| level > params[RPM_REGULATOR_PARAM_LEVEL].max) {
vreg_err(reg, "level=%d is not within allowed range: [%u, %u]\n",
level, params[RPM_REGULATOR_PARAM_LEVEL].min,
params[RPM_REGULATOR_PARAM_LEVEL].max);
return -EINVAL;
}
rpm_vreg_lock(reg->rpm_vreg);
prev_level = reg->req.param[RPM_REGULATOR_PARAM_LEVEL];
RPM_VREG_SET_PARAM(reg, LEVEL, level);
/*
* Only send a new voltage level if the regulator is currently enabled
* or if the regulator has been configured to always send voltage
* updates.
*/
if (reg->always_send_voltage
|| rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg)
|| rpm_vreg_shared_active_or_sleep_enabled_valid(reg->rpm_vreg))
rc = rpm_vreg_aggregate_requests(reg);
if (rc) {
vreg_err(reg, "set voltage level failed, rc=%d", rc);
RPM_VREG_SET_PARAM(reg, LEVEL, prev_level);
}
rpm_vreg_unlock(reg->rpm_vreg);
return rc;
}
static int rpm_vreg_get_voltage_level(struct regulator_dev *rdev)
{
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
return reg->req.param[RPM_REGULATOR_PARAM_LEVEL];
}
static int rpm_vreg_set_voltage_floor_level(struct regulator_dev *rdev,
int min_uV, int max_uV, unsigned *selector)
{
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
int rc = 0;
int level;
u32 prev_level;
level = min_uV;
if (level < params[RPM_REGULATOR_PARAM_FLOOR_LEVEL].min
|| level > params[RPM_REGULATOR_PARAM_FLOOR_LEVEL].max) {
vreg_err(reg, "level=%d is not within allowed range: [%u, %u]\n",
level, params[RPM_REGULATOR_PARAM_FLOOR_LEVEL].min,
params[RPM_REGULATOR_PARAM_FLOOR_LEVEL].max);
return -EINVAL;
}
rpm_vreg_lock(reg->rpm_vreg);
prev_level = reg->req.param[RPM_REGULATOR_PARAM_FLOOR_LEVEL];
RPM_VREG_SET_PARAM(reg, FLOOR_LEVEL, level);
/*
* Only send a new voltage floor level if the regulator is currently
* enabled or if the regulator has been configured to always send
* voltage updates.
*/
if (reg->always_send_voltage
|| rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg)
|| rpm_vreg_shared_active_or_sleep_enabled_valid(reg->rpm_vreg))
rc = rpm_vreg_aggregate_requests(reg);
if (rc) {
vreg_err(reg, "set voltage floor level failed, rc=%d", rc);
RPM_VREG_SET_PARAM(reg, FLOOR_CORNER, prev_level);
}
rpm_vreg_unlock(reg->rpm_vreg);
return rc;
}
static int rpm_vreg_get_voltage_floor_level(struct regulator_dev *rdev)
{
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
return reg->req.param[RPM_REGULATOR_PARAM_FLOOR_LEVEL];
}
static int rpm_vreg_set_mode(struct regulator_dev *rdev, unsigned int mode)
{
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
@ -1309,6 +1413,30 @@ static struct regulator_ops ldo_floor_corner_ops = {
.enable_time = rpm_vreg_enable_time,
};
static struct regulator_ops ldo_level_ops = {
.enable = rpm_vreg_enable,
.disable = rpm_vreg_disable,
.is_enabled = rpm_vreg_is_enabled,
.set_voltage = rpm_vreg_set_voltage_level,
.get_voltage = rpm_vreg_get_voltage_level,
.set_mode = rpm_vreg_set_mode,
.get_mode = rpm_vreg_get_mode,
.get_optimum_mode = rpm_vreg_get_optimum_mode,
.enable_time = rpm_vreg_enable_time,
};
static struct regulator_ops ldo_floor_level_ops = {
.enable = rpm_vreg_enable,
.disable = rpm_vreg_disable,
.is_enabled = rpm_vreg_is_enabled,
.set_voltage = rpm_vreg_set_voltage_floor_level,
.get_voltage = rpm_vreg_get_voltage_floor_level,
.set_mode = rpm_vreg_set_mode,
.get_mode = rpm_vreg_get_mode,
.get_optimum_mode = rpm_vreg_get_optimum_mode,
.enable_time = rpm_vreg_enable_time,
};
static struct regulator_ops smps_ops = {
.enable = rpm_vreg_enable,
.disable = rpm_vreg_disable,
@ -1345,6 +1473,30 @@ static struct regulator_ops smps_floor_corner_ops = {
.enable_time = rpm_vreg_enable_time,
};
static struct regulator_ops smps_level_ops = {
.enable = rpm_vreg_enable,
.disable = rpm_vreg_disable,
.is_enabled = rpm_vreg_is_enabled,
.set_voltage = rpm_vreg_set_voltage_level,
.get_voltage = rpm_vreg_get_voltage_level,
.set_mode = rpm_vreg_set_mode,
.get_mode = rpm_vreg_get_mode,
.get_optimum_mode = rpm_vreg_get_optimum_mode,
.enable_time = rpm_vreg_enable_time,
};
static struct regulator_ops smps_floor_level_ops = {
.enable = rpm_vreg_enable,
.disable = rpm_vreg_disable,
.is_enabled = rpm_vreg_is_enabled,
.set_voltage = rpm_vreg_set_voltage_floor_level,
.get_voltage = rpm_vreg_get_voltage_floor_level,
.set_mode = rpm_vreg_set_mode,
.get_mode = rpm_vreg_get_mode,
.get_optimum_mode = rpm_vreg_get_optimum_mode,
.enable_time = rpm_vreg_enable_time,
};
static struct regulator_ops switch_ops = {
.enable = rpm_vreg_enable,
.disable = rpm_vreg_disable,
@ -1429,6 +1581,66 @@ static int rpm_vreg_resource_remove(struct platform_device *pdev)
return 0;
}
/*
* Switch regulator ops if one of the following properties is present
* for the device node (SMPS and LDO only):
* use corner ops if 'qcom,use-voltage-corner' is present
* use floor corner ops if 'qcom,use-voltage-floor-corner' is present
* use level ops if 'qcom,use-voltage-level' present
* use floor level ops if 'qcom,use-voltage-floor-level' is present
*/
static int rpm_vreg_device_set_regulator_ops(struct device *dev,
struct rpm_regulator *reg, int type)
{
bool choosen = false;
struct device_node *node = dev->of_node;
reg->rdesc.ops = vreg_ops[type];
if ((type != RPM_REGULATOR_TYPE_SMPS) &&
(type != RPM_REGULATOR_TYPE_LDO))
return 0;
if (of_property_read_bool(node, "qcom,use-voltage-corner")) {
reg->rdesc.ops = (type == RPM_REGULATOR_TYPE_SMPS) ?
&smps_corner_ops : &ldo_corner_ops;
choosen = true;
}
if (of_property_read_bool(node, "qcom,use-voltage-floor-corner")) {
if (choosen)
goto invalid;
reg->rdesc.ops = (type == RPM_REGULATOR_TYPE_SMPS) ?
&smps_floor_corner_ops : &ldo_floor_corner_ops;
choosen = true;
}
if (of_property_read_bool(node, "qcom,use-voltage-level")) {
if (choosen)
goto invalid;
reg->rdesc.ops = (type == RPM_REGULATOR_TYPE_SMPS) ?
&smps_level_ops : &ldo_level_ops;
choosen = true;
}
if (of_property_read_bool(node, "qcom,use-voltage-floor-level")) {
if (choosen)
goto invalid;
reg->rdesc.ops = (type == RPM_REGULATOR_TYPE_SMPS) ?
&smps_floor_level_ops : &ldo_floor_level_ops;
}
return 0;
invalid:
dev_err(dev, "%s: invalid properties: only one of qcom,use-voltage-corner, qcom,use-voltage-floor-corner, qcom,use-voltage-level, or qcom,use-voltage-floor-level may be specified\n",
__func__);
return -EINVAL;
}
/*
* This probe is called for child rpm-regulator devices which have
* properties which are required to configure individual regulator
@ -1472,33 +1684,12 @@ static int rpm_vreg_device_probe(struct platform_device *pdev)
regulator_type = rpm_vreg->regulator_type;
reg->rpm_vreg = rpm_vreg;
reg->rdesc.ops = vreg_ops[regulator_type];
reg->rdesc.owner = THIS_MODULE;
reg->rdesc.type = REGULATOR_VOLTAGE;
/*
* Switch to voltage corner regulator ops if qcom,use-voltage-corner
* is specified in the device node (SMPS and LDO only).
*/
if (of_property_read_bool(node, "qcom,use-voltage-corner")) {
if (of_property_read_bool(node,
"qcom,use-voltage-floor-corner")) {
dev_err(dev, "%s: invalid properties: both qcom,use-voltage-corner and qcom,use-voltage-floor-corner specified\n",
__func__);
goto fail_free_reg;
}
if (regulator_type == RPM_REGULATOR_TYPE_SMPS)
reg->rdesc.ops = &smps_corner_ops;
else if (regulator_type == RPM_REGULATOR_TYPE_LDO)
reg->rdesc.ops = &ldo_corner_ops;
} else if (of_property_read_bool(node,
"qcom,use-voltage-floor-corner")) {
if (regulator_type == RPM_REGULATOR_TYPE_SMPS)
reg->rdesc.ops = &smps_floor_corner_ops;
else if (regulator_type == RPM_REGULATOR_TYPE_LDO)
reg->rdesc.ops = &ldo_floor_corner_ops;
}
rc = rpm_vreg_device_set_regulator_ops(dev, reg, regulator_type);
if (rc)
goto fail_free_reg;
reg->always_send_voltage
= of_property_read_bool(node, "qcom,always-send-voltage");

View File

@ -0,0 +1,27 @@
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __QCOM_RPM_SMD_REGULATOR_H
#define __QCOM_RPM_SMD_REGULATOR_H
#define RPM_SMD_REGULATOR_LEVEL_NONE 0
#define RPM_SMD_REGULATOR_LEVEL_RETENTION 16
#define RPM_SMD_REGULATOR_LEVEL_MIN_SVS 48
#define RPM_SMD_REGULATOR_LEVEL_LOW_SVS 64
#define RPM_SMD_REGULATOR_LEVEL_SVS 128
#define RPM_SMD_REGULATOR_LEVEL_SVS_PLUS 192
#define RPM_SMD_REGULATOR_LEVEL_NOM 256
#define RPM_SMD_REGULATOR_LEVEL_NOM_PLUS 320
#define RPM_SMD_REGULATOR_LEVEL_TURBO 384
#define RPM_SMD_REGULATOR_LEVEL_BINNING 512
#endif

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012-2013, 2015 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -38,6 +38,29 @@ enum rpm_regulator_voltage_corner {
RPM_REGULATOR_CORNER_SUPER_TURBO,
};
/**
* enum rpm_regulator_voltage_level - possible voltage level values
*
* These should be used in regulator_set_voltage() and
* rpm_regulator_set_voltage() calls for level type regulators as if they had
* units of uV.
*
* Note: the meaning of level values is set by the RPM.
*/
enum rpm_regulator_voltage_level {
RPM_REGULATOR_LEVEL_NONE = 0,
RPM_REGULATOR_LEVEL_RETENTION = 16,
RPM_REGULATOR_LEVEL_MIN_SVS = 48,
RPM_REGULATOR_LEVEL_LOW_SVS = 64,
RPM_REGULATOR_LEVEL_SVS = 128,
RPM_REGULATOR_LEVEL_SVS_PLUS = 192,
RPM_REGULATOR_LEVEL_NOM = 256,
RPM_REGULATOR_LEVEL_NOM_PLUS = 320,
RPM_REGULATOR_LEVEL_TURBO = 384,
RPM_REGULATOR_LEVEL_BINNING = 512,
RPM_REGULATOR_LEVEL_MAX = 65535,
};
/**
* enum rpm_regulator_mode - control mode for LDO or SMPS type regulators
* %RPM_REGULATOR_MODE_AUTO: For SMPS type regulators, use SMPS auto mode so