Merge "regulator: rpm-smd-regulator: add support for level based voting"
This commit is contained in:
commit
5c147a3b29
|
@ -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:
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue