mirror of
https://github.com/joel16/android_kernel_sony_msm8994_rework.git
synced 2024-11-23 03:50:08 +00:00
regulator: rpm-smd-regulator: add support for level based voting
In the corner based voting scheme the major corners (SVS, NOM, TURBO) are sequential and there is no room for adding any intermediate corners in-between these major corners. Add support for a level based voting mechanism where all the levels are sparsely distributed between [1 - 65535] leaving enough room to add intermediate levels as per system requirements. Change-Id: I4ab78614d90edf020b90de0dad88599859c4f21c Signed-off-by: Ashay Jaiswal <ashayj@codeaurora.org>
This commit is contained in:
parent
fce847567b
commit
bbaaaf8532
@ -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");
|
||||
|
27
include/dt-bindings/regulator/qcom,rpm-smd-regulator.h
Normal file
27
include/dt-bindings/regulator/qcom,rpm-smd-regulator.h
Normal 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
|
@ -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
Block a user