f1e72c4946
Correct existing logic to ensure that minimum dropout voltage does not get read in place of active/sleep set property. Change-Id: I37239e99de849a0f756eb12d0614d297cfdeacd4 Signed-off-by: Jishnu Prakash <jprakash@codeaurora.org>
1977 lines
54 KiB
C
1977 lines
54 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/* Copyright (c) 2012-2015, 2018-2021, The Linux Foundation. All rights reserved. */
|
|
|
|
#define pr_fmt(fmt) "%s: " fmt, __func__
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/err.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/string.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/regulator/driver.h>
|
|
#include <linux/regulator/machine.h>
|
|
#include <linux/regulator/of_regulator.h>
|
|
#include <linux/regulator/rpm-smd-regulator.h>
|
|
#include <linux/regulator/proxy-consumer.h>
|
|
#include <dt-bindings/regulator/qcom,rpm-smd-regulator.h>
|
|
#include <soc/qcom/rpm-smd.h>
|
|
#include <linux/debugfs.h>
|
|
/* Debug Definitions */
|
|
|
|
enum {
|
|
RPM_VREG_DEBUG_REQUEST = BIT(0),
|
|
RPM_VREG_DEBUG_FULL_REQUEST = BIT(1),
|
|
RPM_VREG_DEBUG_DUPLICATE = BIT(2),
|
|
};
|
|
|
|
static int rpm_vreg_debug_mask;
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
static bool is_debugfs_created;
|
|
#endif
|
|
|
|
#define vreg_err(req, fmt, ...) \
|
|
pr_err("%s: " fmt, req->rdesc.name, ##__VA_ARGS__)
|
|
|
|
/* RPM regulator request types */
|
|
enum rpm_regulator_type {
|
|
RPM_REGULATOR_TYPE_LDO,
|
|
RPM_REGULATOR_TYPE_SMPS,
|
|
RPM_REGULATOR_TYPE_VS,
|
|
RPM_REGULATOR_TYPE_NCP,
|
|
RPM_REGULATOR_TYPE_BOB,
|
|
RPM_REGULATOR_TYPE_MAX,
|
|
};
|
|
|
|
/* Supported PMIC regulator LDO and BOB types */
|
|
enum rpm_regulator_hw_type {
|
|
RPM_REGULATOR_HW_TYPE_UNKNOWN,
|
|
RPM_REGULATOR_HW_TYPE_PMIC4_LDO,
|
|
RPM_REGULATOR_HW_TYPE_PMIC5_LDO,
|
|
RPM_REGULATOR_HW_TYPE_PMIC4_BOB,
|
|
RPM_REGULATOR_HW_TYPE_PMIC5_BOB,
|
|
RPM_REGULATOR_HW_TYPE_MAX,
|
|
};
|
|
|
|
/* RPM resource parameters */
|
|
enum rpm_regulator_param_index {
|
|
RPM_REGULATOR_PARAM_ENABLE,
|
|
RPM_REGULATOR_PARAM_VOLTAGE,
|
|
RPM_REGULATOR_PARAM_CURRENT,
|
|
RPM_REGULATOR_PARAM_MODE_LDO,
|
|
RPM_REGULATOR_PARAM_MODE_SMPS,
|
|
RPM_REGULATOR_PARAM_PIN_CTRL_ENABLE,
|
|
RPM_REGULATOR_PARAM_PIN_CTRL_MODE,
|
|
RPM_REGULATOR_PARAM_FREQUENCY,
|
|
RPM_REGULATOR_PARAM_HEAD_ROOM,
|
|
RPM_REGULATOR_PARAM_QUIET_MODE,
|
|
RPM_REGULATOR_PARAM_FREQ_REASON,
|
|
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_MODE_BOB,
|
|
RPM_REGULATOR_PARAM_PIN_CTRL_VOLTAGE1,
|
|
RPM_REGULATOR_PARAM_PIN_CTRL_VOLTAGE2,
|
|
RPM_REGULATOR_PARAM_PIN_CTRL_VOLTAGE3,
|
|
RPM_REGULATOR_PARAM_MAX,
|
|
};
|
|
|
|
enum rpm_regulator_bob_mode_pmic4 {
|
|
RPM_REGULATOR_PMIC4_BOB_MODE_PASS = 0,
|
|
RPM_REGULATOR_PMIC4_BOB_MODE_PFM = 1,
|
|
RPM_REGULATOR_PMIC4_BOB_MODE_AUTO = 2,
|
|
RPM_REGULATOR_PMIC4_BOB_MODE_PWM = 3,
|
|
};
|
|
|
|
enum rpm_regulator_bob_mode_pmic5 {
|
|
RPM_REGULATOR_PMIC5_BOB_MODE_PASS = 2,
|
|
RPM_REGULATOR_PMIC5_BOB_MODE_PFM = 4,
|
|
RPM_REGULATOR_PMIC5_BOB_MODE_AUTO = 6,
|
|
RPM_REGULATOR_PMIC5_BOB_MODE_PWM = 7,
|
|
};
|
|
|
|
#define RPM_SET_CONFIG_ACTIVE BIT(0)
|
|
#define RPM_SET_CONFIG_SLEEP BIT(1)
|
|
#define RPM_SET_CONFIG_BOTH (RPM_SET_CONFIG_ACTIVE \
|
|
| RPM_SET_CONFIG_SLEEP)
|
|
struct rpm_regulator_param {
|
|
char *name;
|
|
char *property_name;
|
|
u32 key;
|
|
u32 min;
|
|
u32 max;
|
|
u32 supported_regulator_types;
|
|
};
|
|
|
|
#define PARAM(_idx, _support_ldo, _support_smps, _support_vs, _support_ncp, \
|
|
_support_bob, _name, _min, _max, _property_name) \
|
|
[RPM_REGULATOR_PARAM_##_idx] = { \
|
|
.name = _name, \
|
|
.property_name = _property_name, \
|
|
.min = _min, \
|
|
.max = _max, \
|
|
.supported_regulator_types = \
|
|
_support_ldo << RPM_REGULATOR_TYPE_LDO | \
|
|
_support_smps << RPM_REGULATOR_TYPE_SMPS | \
|
|
_support_vs << RPM_REGULATOR_TYPE_VS | \
|
|
_support_ncp << RPM_REGULATOR_TYPE_NCP | \
|
|
_support_bob << RPM_REGULATOR_TYPE_BOB, \
|
|
}
|
|
|
|
static struct rpm_regulator_param params[RPM_REGULATOR_PARAM_MAX] = {
|
|
/* ID LDO SMPS VS NCP BOB name min max property-name */
|
|
PARAM(ENABLE, 1, 1, 1, 1, 1, "swen", 0, 1, "qcom,init-enable"),
|
|
PARAM(VOLTAGE, 1, 1, 0, 1, 1, "uv", 0, 0x7FFFFFF, "qcom,init-voltage"),
|
|
PARAM(CURRENT, 0, 1, 0, 0, 0, "ma", 0, 0x1FFF, "qcom,init-current"),
|
|
PARAM(MODE_LDO, 1, 0, 0, 0, 0, "lsmd", 0, 1, "qcom,init-ldo-mode"),
|
|
PARAM(MODE_SMPS, 0, 1, 0, 0, 0, "ssmd", 0, 2, "qcom,init-smps-mode"),
|
|
PARAM(PIN_CTRL_ENABLE, 1, 1, 1, 0, 0, "pcen", 0, 0xF, "qcom,init-pin-ctrl-enable"),
|
|
PARAM(PIN_CTRL_MODE, 0, 1, 1, 0, 0, "pcmd", 0, 0x1F, "qcom,init-pin-ctrl-mode"),
|
|
PARAM(FREQUENCY, 0, 1, 0, 1, 0, "freq", 0, 31, "qcom,init-frequency"),
|
|
PARAM(HEAD_ROOM, 0, 0, 0, 1, 0, "hr", 0, 0x7FFFFFFF, "qcom,init-head-room"),
|
|
PARAM(QUIET_MODE, 0, 1, 0, 0, 0, "qm", 0, 2, "qcom,init-quiet-mode"),
|
|
PARAM(FREQ_REASON, 0, 1, 0, 1, 0, "resn", 0, 8, "qcom,init-freq-reason"),
|
|
PARAM(CORNER, 0, 1, 0, 0, 0, "corn", 0, 6, "qcom,init-voltage-corner"),
|
|
PARAM(BYPASS, 0, 0, 0, 0, 0, "bypa", 0, 1, "qcom,init-disallow-bypass"),
|
|
PARAM(FLOOR_CORNER, 0, 1, 0, 0, 0, "vfc", 0, 6, "qcom,init-voltage-floor-corner"),
|
|
PARAM(LEVEL, 0, 1, 0, 0, 0, "vlvl", 0, 0xFFFF, "qcom,init-voltage-level"),
|
|
PARAM(FLOOR_LEVEL, 0, 1, 0, 0, 0, "vfl", 0, 0xFFFF, "qcom,init-voltage-floor-level"),
|
|
PARAM(MODE_BOB, 0, 0, 0, 0, 1, "bobm", 0, 3, "qcom,init-bob-mode"),
|
|
PARAM(PIN_CTRL_VOLTAGE1, 0, 0, 0, 0, 1, "pcv1", 0, 0x7FFFFFF, "qcom,init-pin-ctrl-voltage1"),
|
|
PARAM(PIN_CTRL_VOLTAGE2, 0, 0, 0, 0, 1, "pcv2", 0, 0x7FFFFFF, "qcom,init-pin-ctrl-voltage2"),
|
|
PARAM(PIN_CTRL_VOLTAGE3, 0, 0, 0, 0, 1, "pcv3", 0, 0x7FFFFFF, "qcom,init-pin-ctrl-voltage3"),
|
|
};
|
|
|
|
/* Indices for use with pin control enable via enable/disable feature. */
|
|
#define RPM_VREG_PIN_CTRL_STATE_DISABLE 0
|
|
#define RPM_VREG_PIN_CTRL_STATE_ENABLE 1
|
|
#define RPM_VREG_PIN_CTRL_STATE_COUNT 2
|
|
|
|
struct rpm_vreg_request {
|
|
u32 param[RPM_REGULATOR_PARAM_MAX];
|
|
u32 valid;
|
|
u32 modified;
|
|
};
|
|
|
|
struct rpm_reg_mode_info {
|
|
u32 mode;
|
|
int min_load_ua;
|
|
};
|
|
|
|
struct rpm_vreg {
|
|
struct rpm_vreg_request aggr_req_active;
|
|
struct rpm_vreg_request aggr_req_sleep;
|
|
struct list_head reg_list;
|
|
const char *resource_name;
|
|
u32 resource_id;
|
|
bool allow_atomic;
|
|
int regulator_type;
|
|
int hpm_min_load;
|
|
struct rpm_reg_mode_info *mode;
|
|
int mode_count;
|
|
int enable_time;
|
|
spinlock_t slock;
|
|
struct mutex mlock;
|
|
unsigned long flags;
|
|
bool sleep_request_sent;
|
|
bool wait_for_ack_active;
|
|
bool wait_for_ack_sleep;
|
|
bool always_wait_for_ack;
|
|
bool apps_only;
|
|
struct msm_rpm_request *handle_active;
|
|
struct msm_rpm_request *handle_sleep;
|
|
enum rpm_regulator_hw_type regulator_hw_type;
|
|
};
|
|
|
|
struct rpm_regulator {
|
|
struct regulator_desc rdesc;
|
|
struct regulator_dev *rdev;
|
|
struct rpm_vreg *rpm_vreg;
|
|
struct dentry *dfs_root;
|
|
struct list_head list;
|
|
bool set_active;
|
|
bool set_sleep;
|
|
bool always_send_voltage;
|
|
bool always_send_current;
|
|
bool use_pin_ctrl_for_enable;
|
|
struct rpm_vreg_request req;
|
|
int system_load;
|
|
int min_uV;
|
|
int max_uV;
|
|
u32 pin_ctrl_mask[RPM_VREG_PIN_CTRL_STATE_COUNT];
|
|
enum rpm_regulator_param_index voltage_index;
|
|
int voltage_offset;
|
|
};
|
|
|
|
/*
|
|
* This voltage in uV is returned by get_voltage functions when there is no way
|
|
* to determine the current voltage level. It is needed because the regulator
|
|
* framework treats a 0 uV voltage as an error.
|
|
*/
|
|
#define VOLTAGE_UNKNOWN 1
|
|
|
|
/*
|
|
* Regulator requests sent in the active set take effect immediately. Requests
|
|
* sent in the sleep set take effect when the Apps processor transitions into
|
|
* RPM assisted power collapse. For any given regulator, if an active set
|
|
* request is present, but not a sleep set request, then the active set request
|
|
* is used at all times, even when the Apps processor is power collapsed.
|
|
*
|
|
* The rpm-regulator-smd takes advantage of this default usage of the active set
|
|
* request by only sending a sleep set request if it differs from the
|
|
* corresponding active set request.
|
|
*/
|
|
#define RPM_SET_ACTIVE MSM_RPM_CTX_ACTIVE_SET
|
|
#define RPM_SET_SLEEP MSM_RPM_CTX_SLEEP_SET
|
|
|
|
static u32 rpm_vreg_string_to_int(const u8 *str)
|
|
{
|
|
int i, len;
|
|
u32 output = 0;
|
|
|
|
len = strnlen(str, sizeof(u32));
|
|
for (i = 0; i < len; i++)
|
|
output |= str[i] << (i * 8);
|
|
|
|
return output;
|
|
}
|
|
|
|
static inline void rpm_vreg_lock(struct rpm_vreg *rpm_vreg)
|
|
{
|
|
if (rpm_vreg->allow_atomic)
|
|
spin_lock_irqsave(&rpm_vreg->slock, rpm_vreg->flags);
|
|
else
|
|
mutex_lock(&rpm_vreg->mlock);
|
|
}
|
|
|
|
static inline void rpm_vreg_unlock(struct rpm_vreg *rpm_vreg)
|
|
{
|
|
if (rpm_vreg->allow_atomic)
|
|
spin_unlock_irqrestore(&rpm_vreg->slock, rpm_vreg->flags);
|
|
else
|
|
mutex_unlock(&rpm_vreg->mlock);
|
|
}
|
|
|
|
static inline bool rpm_vreg_active_or_sleep_enabled(struct rpm_vreg *rpm_vreg)
|
|
{
|
|
return (rpm_vreg->aggr_req_active.param[RPM_REGULATOR_PARAM_ENABLE]
|
|
&& (rpm_vreg->aggr_req_active.valid
|
|
& BIT(RPM_REGULATOR_PARAM_ENABLE)))
|
|
|| ((rpm_vreg->aggr_req_sleep.param[RPM_REGULATOR_PARAM_ENABLE])
|
|
&& (rpm_vreg->aggr_req_sleep.valid
|
|
& BIT(RPM_REGULATOR_PARAM_ENABLE)));
|
|
}
|
|
|
|
static inline bool rpm_vreg_shared_active_or_sleep_enabled_valid
|
|
(struct rpm_vreg *rpm_vreg)
|
|
{
|
|
return !rpm_vreg->apps_only &&
|
|
((rpm_vreg->aggr_req_active.valid
|
|
& BIT(RPM_REGULATOR_PARAM_ENABLE))
|
|
|| (rpm_vreg->aggr_req_sleep.valid
|
|
& BIT(RPM_REGULATOR_PARAM_ENABLE)));
|
|
}
|
|
|
|
static const u32 power_level_params =
|
|
BIT(RPM_REGULATOR_PARAM_ENABLE) |
|
|
BIT(RPM_REGULATOR_PARAM_VOLTAGE) |
|
|
BIT(RPM_REGULATOR_PARAM_CURRENT) |
|
|
BIT(RPM_REGULATOR_PARAM_CORNER) |
|
|
BIT(RPM_REGULATOR_PARAM_BYPASS) |
|
|
BIT(RPM_REGULATOR_PARAM_FLOOR_CORNER) |
|
|
BIT(RPM_REGULATOR_PARAM_LEVEL) |
|
|
BIT(RPM_REGULATOR_PARAM_FLOOR_LEVEL);
|
|
|
|
static bool rpm_vreg_ack_required(struct rpm_vreg *rpm_vreg, u32 set,
|
|
const u32 *prev_param, const u32 *param,
|
|
u32 prev_valid, u32 modified)
|
|
{
|
|
u32 mask;
|
|
int i;
|
|
|
|
if (rpm_vreg->always_wait_for_ack
|
|
|| (set == RPM_SET_ACTIVE && rpm_vreg->wait_for_ack_active)
|
|
|| (set == RPM_SET_SLEEP && rpm_vreg->wait_for_ack_sleep))
|
|
return true;
|
|
|
|
for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++) {
|
|
mask = BIT(i);
|
|
if (modified & mask) {
|
|
if ((prev_valid & mask) && (power_level_params & mask)
|
|
&& (param[i] <= prev_param[i]))
|
|
continue;
|
|
else
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void rpm_vreg_check_param_max(struct rpm_regulator *regulator, int index,
|
|
u32 new_max)
|
|
{
|
|
struct rpm_vreg *rpm_vreg = regulator->rpm_vreg;
|
|
|
|
if (regulator->set_active
|
|
&& (rpm_vreg->aggr_req_active.valid & BIT(index))
|
|
&& rpm_vreg->aggr_req_active.param[index] > new_max)
|
|
rpm_vreg->wait_for_ack_active = true;
|
|
|
|
if (regulator->set_sleep
|
|
&& (rpm_vreg->aggr_req_sleep.valid & BIT(index))
|
|
&& rpm_vreg->aggr_req_sleep.param[index] > new_max)
|
|
rpm_vreg->wait_for_ack_sleep = true;
|
|
}
|
|
|
|
/*
|
|
* This is used when voting for LPM or HPM by subtracting or adding to the
|
|
* hpm_min_load of a regulator. It has units of uA.
|
|
*/
|
|
#define LOAD_THRESHOLD_STEP 1000
|
|
|
|
static inline int rpm_vreg_hpm_min_uA(struct rpm_vreg *rpm_vreg)
|
|
{
|
|
return rpm_vreg->hpm_min_load;
|
|
}
|
|
|
|
static inline int rpm_vreg_lpm_max_uA(struct rpm_vreg *rpm_vreg)
|
|
{
|
|
return rpm_vreg->hpm_min_load - LOAD_THRESHOLD_STEP;
|
|
}
|
|
|
|
#define MICRO_TO_MILLI(uV) ((uV) / 1000)
|
|
#define MILLI_TO_MICRO(uV) ((uV) * 1000)
|
|
|
|
#define DEBUG_PRINT_BUFFER_SIZE 512
|
|
#define REQ_SENT 0
|
|
#define REQ_PREV 1
|
|
#define REQ_CACHED 2
|
|
#define REQ_TYPES 3
|
|
|
|
static void rpm_regulator_req(struct rpm_regulator *regulator, int set,
|
|
bool sent)
|
|
{
|
|
char buf[DEBUG_PRINT_BUFFER_SIZE];
|
|
size_t buflen = DEBUG_PRINT_BUFFER_SIZE;
|
|
struct rpm_vreg *rpm_vreg = regulator->rpm_vreg;
|
|
struct rpm_vreg_request *aggr;
|
|
bool first;
|
|
u32 mask[REQ_TYPES] = {0, 0, 0};
|
|
const char *req_names[REQ_TYPES] = {"sent", "prev", "cached"};
|
|
int pos = 0;
|
|
int i, j;
|
|
|
|
aggr = (set == RPM_SET_ACTIVE)
|
|
? &rpm_vreg->aggr_req_active : &rpm_vreg->aggr_req_sleep;
|
|
|
|
if (rpm_vreg_debug_mask & RPM_VREG_DEBUG_DUPLICATE) {
|
|
mask[REQ_SENT] = aggr->modified;
|
|
mask[REQ_PREV] = aggr->valid & ~aggr->modified;
|
|
} else if (sent
|
|
&& (rpm_vreg_debug_mask & RPM_VREG_DEBUG_FULL_REQUEST)) {
|
|
mask[REQ_SENT] = aggr->modified;
|
|
mask[REQ_PREV] = aggr->valid & ~aggr->modified;
|
|
} else if (sent && (rpm_vreg_debug_mask & RPM_VREG_DEBUG_REQUEST)) {
|
|
mask[REQ_SENT] = aggr->modified;
|
|
}
|
|
|
|
if (!(mask[REQ_SENT] | mask[REQ_PREV]))
|
|
return;
|
|
|
|
if (set == RPM_SET_SLEEP && !rpm_vreg->sleep_request_sent) {
|
|
mask[REQ_CACHED] = mask[REQ_SENT] | mask[REQ_PREV];
|
|
mask[REQ_SENT] = 0;
|
|
mask[REQ_PREV] = 0;
|
|
}
|
|
|
|
pos += scnprintf(buf + pos, buflen - pos, "%s%s: ",
|
|
KERN_INFO, __func__);
|
|
|
|
pos += scnprintf(buf + pos, buflen - pos, "%s %u (%s): s=%s",
|
|
rpm_vreg->resource_name, rpm_vreg->resource_id,
|
|
regulator->rdesc.name,
|
|
(set == RPM_SET_ACTIVE ? "act" : "slp"));
|
|
|
|
for (i = 0; i < REQ_TYPES; i++) {
|
|
if (mask[i])
|
|
pos += scnprintf(buf + pos, buflen - pos, "; %s: ",
|
|
req_names[i]);
|
|
|
|
first = true;
|
|
for (j = 0; j < RPM_REGULATOR_PARAM_MAX; j++) {
|
|
if (mask[i] & BIT(j)) {
|
|
pos += scnprintf(buf + pos, buflen - pos,
|
|
"%s%s=%u", (first ? "" : ", "),
|
|
params[j].name, aggr->param[j]);
|
|
first = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
pos += scnprintf(buf + pos, buflen - pos, "\n");
|
|
pr_info("%s\n", buf);
|
|
}
|
|
|
|
#define RPM_VREG_SET_PARAM(_regulator, _param, _val) \
|
|
{ \
|
|
(_regulator)->req.param[RPM_REGULATOR_PARAM_##_param] = _val; \
|
|
(_regulator)->req.modified |= BIT(RPM_REGULATOR_PARAM_##_param); \
|
|
} \
|
|
|
|
static int rpm_vreg_add_kvp_to_request(struct rpm_vreg *rpm_vreg,
|
|
const u32 *param, int idx, u32 set)
|
|
{
|
|
struct msm_rpm_request *handle;
|
|
|
|
handle = (set == RPM_SET_ACTIVE ? rpm_vreg->handle_active
|
|
: rpm_vreg->handle_sleep);
|
|
|
|
if (rpm_vreg->allow_atomic)
|
|
return msm_rpm_add_kvp_data_noirq(handle, params[idx].key,
|
|
(u8 *)¶m[idx], 4);
|
|
else
|
|
return msm_rpm_add_kvp_data(handle, params[idx].key,
|
|
(u8 *)¶m[idx], 4);
|
|
}
|
|
|
|
static void rpm_vreg_check_modified_requests(const u32 *prev_param,
|
|
const u32 *param, u32 prev_valid, u32 *modified)
|
|
{
|
|
u32 value_changed = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++) {
|
|
if (param[i] != prev_param[i])
|
|
value_changed |= BIT(i);
|
|
}
|
|
|
|
/*
|
|
* Only keep bits that are for changed parameters or previously
|
|
* invalid parameters.
|
|
*/
|
|
*modified &= value_changed | ~prev_valid;
|
|
}
|
|
|
|
static int rpm_vreg_add_modified_requests(struct rpm_regulator *regulator,
|
|
u32 set, const u32 *param, u32 modified)
|
|
{
|
|
struct rpm_vreg *rpm_vreg = regulator->rpm_vreg;
|
|
int rc = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++) {
|
|
/* Only send requests for modified parameters. */
|
|
if (modified & BIT(i)) {
|
|
rc = rpm_vreg_add_kvp_to_request(rpm_vreg, param, i,
|
|
set);
|
|
if (rc) {
|
|
vreg_err(regulator,
|
|
"add KVP failed: %s %u; %s, rc=%d\n",
|
|
rpm_vreg->resource_name,
|
|
rpm_vreg->resource_id, params[i].name,
|
|
rc);
|
|
return rc;
|
|
}
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int rpm_vreg_send_request(struct rpm_regulator *regulator, u32 set,
|
|
bool wait_for_ack)
|
|
{
|
|
struct rpm_vreg *rpm_vreg = regulator->rpm_vreg;
|
|
struct msm_rpm_request *handle
|
|
= (set == RPM_SET_ACTIVE ? rpm_vreg->handle_active
|
|
: rpm_vreg->handle_sleep);
|
|
int rc = 0;
|
|
void *temp;
|
|
|
|
if (unlikely(rpm_vreg->allow_atomic)) {
|
|
rc = msm_rpm_wait_for_ack_noirq(msm_rpm_send_request_noirq(
|
|
handle));
|
|
} else if (wait_for_ack) {
|
|
rc = msm_rpm_wait_for_ack(msm_rpm_send_request(handle));
|
|
} else {
|
|
temp = msm_rpm_send_request_noack(handle);
|
|
if (IS_ERR(temp))
|
|
rc = PTR_ERR(temp);
|
|
}
|
|
|
|
if (rc)
|
|
vreg_err(regulator,
|
|
"msm rpm send failed: %s %u; set=%s, rc=%d\n",
|
|
rpm_vreg->resource_name,
|
|
rpm_vreg->resource_id,
|
|
(set == RPM_SET_ACTIVE ? "act" : "slp"), rc);
|
|
|
|
return rc;
|
|
}
|
|
|
|
#define RPM_VREG_AGGR_MIN(_idx, _param_aggr, _param_reg) \
|
|
{ \
|
|
_param_aggr[RPM_REGULATOR_PARAM_##_idx] \
|
|
= min(_param_aggr[RPM_REGULATOR_PARAM_##_idx], \
|
|
_param_reg[RPM_REGULATOR_PARAM_##_idx]); \
|
|
}
|
|
|
|
#define RPM_VREG_AGGR_MAX(_idx, _param_aggr, _param_reg) \
|
|
{ \
|
|
_param_aggr[RPM_REGULATOR_PARAM_##_idx] \
|
|
= max(_param_aggr[RPM_REGULATOR_PARAM_##_idx], \
|
|
_param_reg[RPM_REGULATOR_PARAM_##_idx]); \
|
|
}
|
|
|
|
#define RPM_VREG_AGGR_SUM(_idx, _param_aggr, _param_reg) \
|
|
{ \
|
|
_param_aggr[RPM_REGULATOR_PARAM_##_idx] \
|
|
+= _param_reg[RPM_REGULATOR_PARAM_##_idx]; \
|
|
}
|
|
|
|
#define RPM_VREG_AGGR_OR(_idx, _param_aggr, _param_reg) \
|
|
{ \
|
|
_param_aggr[RPM_REGULATOR_PARAM_##_idx] \
|
|
|= _param_reg[RPM_REGULATOR_PARAM_##_idx]; \
|
|
}
|
|
|
|
/*
|
|
* Aggregation is performed on each parameter based on the way that the RPM
|
|
* aggregates that type internally between RPM masters.
|
|
*/
|
|
static void rpm_vreg_aggregate_params(u32 *param_aggr, const u32 *param_reg)
|
|
{
|
|
RPM_VREG_AGGR_MAX(ENABLE, param_aggr, param_reg);
|
|
RPM_VREG_AGGR_MAX(VOLTAGE, param_aggr, param_reg);
|
|
RPM_VREG_AGGR_SUM(CURRENT, param_aggr, param_reg);
|
|
RPM_VREG_AGGR_MAX(MODE_LDO, param_aggr, param_reg);
|
|
RPM_VREG_AGGR_MAX(MODE_SMPS, param_aggr, param_reg);
|
|
RPM_VREG_AGGR_OR(PIN_CTRL_ENABLE, param_aggr, param_reg);
|
|
RPM_VREG_AGGR_OR(PIN_CTRL_MODE, param_aggr, param_reg);
|
|
RPM_VREG_AGGR_MIN(FREQUENCY, param_aggr, param_reg);
|
|
RPM_VREG_AGGR_MAX(HEAD_ROOM, param_aggr, param_reg);
|
|
RPM_VREG_AGGR_MAX(QUIET_MODE, param_aggr, param_reg);
|
|
RPM_VREG_AGGR_MAX(FREQ_REASON, param_aggr, 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);
|
|
RPM_VREG_AGGR_MAX(MODE_BOB, param_aggr, param_reg);
|
|
RPM_VREG_AGGR_MAX(PIN_CTRL_VOLTAGE1, param_aggr, param_reg);
|
|
RPM_VREG_AGGR_MAX(PIN_CTRL_VOLTAGE2, param_aggr, param_reg);
|
|
RPM_VREG_AGGR_MAX(PIN_CTRL_VOLTAGE3, param_aggr, param_reg);
|
|
}
|
|
|
|
static int rpm_vreg_aggregate_requests(struct rpm_regulator *regulator)
|
|
{
|
|
struct rpm_vreg *rpm_vreg = regulator->rpm_vreg;
|
|
u32 param_active[RPM_REGULATOR_PARAM_MAX];
|
|
u32 param_sleep[RPM_REGULATOR_PARAM_MAX];
|
|
u32 modified_active, modified_sleep;
|
|
struct rpm_regulator *reg;
|
|
bool sleep_set_differs = false;
|
|
bool send_active = false;
|
|
bool send_sleep = false;
|
|
bool wait_for_ack;
|
|
int rc = 0;
|
|
int i;
|
|
|
|
memset(param_active, 0, sizeof(param_active));
|
|
memset(param_sleep, 0, sizeof(param_sleep));
|
|
modified_active = rpm_vreg->aggr_req_active.modified;
|
|
modified_sleep = rpm_vreg->aggr_req_sleep.modified;
|
|
|
|
/*
|
|
* Aggregate all of the requests for this regulator in both active
|
|
* and sleep sets.
|
|
*/
|
|
list_for_each_entry(reg, &rpm_vreg->reg_list, list) {
|
|
if (reg->set_active) {
|
|
rpm_vreg_aggregate_params(param_active, reg->req.param);
|
|
modified_active |= reg->req.modified;
|
|
}
|
|
if (reg->set_sleep) {
|
|
rpm_vreg_aggregate_params(param_sleep, reg->req.param);
|
|
modified_sleep |= reg->req.modified;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if the aggregated sleep set parameter values differ from the
|
|
* aggregated active set parameter values.
|
|
*/
|
|
if (!rpm_vreg->sleep_request_sent) {
|
|
for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++) {
|
|
if ((param_active[i] != param_sleep[i])
|
|
&& (modified_sleep & BIT(i))) {
|
|
sleep_set_differs = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Add KVPs to the active set RPM request if they have new values. */
|
|
rpm_vreg_check_modified_requests(rpm_vreg->aggr_req_active.param,
|
|
param_active, rpm_vreg->aggr_req_active.valid,
|
|
&modified_active);
|
|
rc = rpm_vreg_add_modified_requests(regulator, RPM_SET_ACTIVE,
|
|
param_active, modified_active);
|
|
if (rc)
|
|
return rc;
|
|
send_active = modified_active;
|
|
|
|
/*
|
|
* Sleep set configurations are only sent if they differ from the
|
|
* active set values. This is because the active set values will take
|
|
* effect during rpm assisted power collapse in the absence of sleep set
|
|
* values.
|
|
*
|
|
* However, once a sleep set request is sent for a given regulator,
|
|
* additional sleep set requests must be sent in the future even if they
|
|
* match the corresponding active set requests.
|
|
*/
|
|
if (rpm_vreg->sleep_request_sent || sleep_set_differs) {
|
|
/* Add KVPs to the sleep set RPM request if they are new. */
|
|
rpm_vreg_check_modified_requests(rpm_vreg->aggr_req_sleep.param,
|
|
param_sleep, rpm_vreg->aggr_req_sleep.valid,
|
|
&modified_sleep);
|
|
rc = rpm_vreg_add_modified_requests(regulator, RPM_SET_SLEEP,
|
|
param_sleep, modified_sleep);
|
|
if (rc)
|
|
return rc;
|
|
send_sleep = modified_sleep;
|
|
}
|
|
|
|
/* Send active set request to the RPM if it contains new KVPs. */
|
|
if (send_active) {
|
|
wait_for_ack = rpm_vreg_ack_required(rpm_vreg, RPM_SET_ACTIVE,
|
|
rpm_vreg->aggr_req_active.param,
|
|
param_active,
|
|
rpm_vreg->aggr_req_active.valid,
|
|
modified_active);
|
|
rc = rpm_vreg_send_request(regulator, RPM_SET_ACTIVE,
|
|
wait_for_ack);
|
|
if (rc)
|
|
return rc;
|
|
rpm_vreg->aggr_req_active.valid |= modified_active;
|
|
rpm_vreg->wait_for_ack_active = false;
|
|
}
|
|
/* Store the results of the aggregation. */
|
|
rpm_vreg->aggr_req_active.modified = modified_active;
|
|
memcpy(rpm_vreg->aggr_req_active.param, param_active,
|
|
sizeof(param_active));
|
|
|
|
/* Handle debug printing of the active set request. */
|
|
rpm_regulator_req(regulator, RPM_SET_ACTIVE, send_active);
|
|
if (send_active)
|
|
rpm_vreg->aggr_req_active.modified = 0;
|
|
|
|
/* Send sleep set request to the RPM if it contains new KVPs. */
|
|
if (send_sleep) {
|
|
wait_for_ack = rpm_vreg_ack_required(rpm_vreg, RPM_SET_SLEEP,
|
|
rpm_vreg->aggr_req_sleep.param,
|
|
param_sleep,
|
|
rpm_vreg->aggr_req_sleep.valid,
|
|
modified_sleep);
|
|
rc = rpm_vreg_send_request(regulator, RPM_SET_SLEEP,
|
|
wait_for_ack);
|
|
if (rc)
|
|
return rc;
|
|
|
|
rpm_vreg->sleep_request_sent = true;
|
|
rpm_vreg->aggr_req_sleep.valid |= modified_sleep;
|
|
rpm_vreg->wait_for_ack_sleep = false;
|
|
}
|
|
/* Store the results of the aggregation. */
|
|
rpm_vreg->aggr_req_sleep.modified = modified_sleep;
|
|
memcpy(rpm_vreg->aggr_req_sleep.param, param_sleep,
|
|
sizeof(param_sleep));
|
|
|
|
/* Handle debug printing of the sleep set request. */
|
|
rpm_regulator_req(regulator, RPM_SET_SLEEP, send_sleep);
|
|
if (send_sleep)
|
|
rpm_vreg->aggr_req_sleep.modified = 0;
|
|
|
|
/*
|
|
* Loop over all requests for this regulator to update the valid and
|
|
* modified values for use in future aggregation.
|
|
*/
|
|
list_for_each_entry(reg, &rpm_vreg->reg_list, list) {
|
|
reg->req.valid |= reg->req.modified;
|
|
reg->req.modified = 0;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int rpm_vreg_is_enabled(struct regulator_dev *rdev)
|
|
{
|
|
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
|
|
|
|
if (likely(!reg->use_pin_ctrl_for_enable))
|
|
return reg->req.param[RPM_REGULATOR_PARAM_ENABLE];
|
|
else
|
|
return reg->req.param[RPM_REGULATOR_PARAM_PIN_CTRL_ENABLE]
|
|
== reg->pin_ctrl_mask[RPM_VREG_PIN_CTRL_STATE_ENABLE];
|
|
}
|
|
|
|
static int rpm_vreg_enable(struct regulator_dev *rdev)
|
|
{
|
|
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
|
|
int rc;
|
|
u32 prev_enable;
|
|
|
|
rpm_vreg_lock(reg->rpm_vreg);
|
|
|
|
if (likely(!reg->use_pin_ctrl_for_enable)) {
|
|
/* Enable using swen KVP. */
|
|
prev_enable = reg->req.param[RPM_REGULATOR_PARAM_ENABLE];
|
|
RPM_VREG_SET_PARAM(reg, ENABLE, 1);
|
|
rc = rpm_vreg_aggregate_requests(reg);
|
|
if (rc) {
|
|
vreg_err(reg, "enable failed, rc=%d\n", rc);
|
|
RPM_VREG_SET_PARAM(reg, ENABLE, prev_enable);
|
|
}
|
|
} else {
|
|
/* Enable using pcen KVP. */
|
|
prev_enable
|
|
= reg->req.param[RPM_REGULATOR_PARAM_PIN_CTRL_ENABLE];
|
|
RPM_VREG_SET_PARAM(reg, PIN_CTRL_ENABLE,
|
|
reg->pin_ctrl_mask[RPM_VREG_PIN_CTRL_STATE_ENABLE]);
|
|
rc = rpm_vreg_aggregate_requests(reg);
|
|
if (rc) {
|
|
vreg_err(reg, "enable failed, rc=%d\n", rc);
|
|
RPM_VREG_SET_PARAM(reg, PIN_CTRL_ENABLE, prev_enable);
|
|
}
|
|
}
|
|
|
|
rpm_vreg_unlock(reg->rpm_vreg);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int rpm_vreg_disable(struct regulator_dev *rdev)
|
|
{
|
|
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
|
|
int rc;
|
|
u32 prev_enable;
|
|
|
|
rpm_vreg_lock(reg->rpm_vreg);
|
|
|
|
if (likely(!reg->use_pin_ctrl_for_enable)) {
|
|
/* Disable using swen KVP. */
|
|
prev_enable = reg->req.param[RPM_REGULATOR_PARAM_ENABLE];
|
|
RPM_VREG_SET_PARAM(reg, ENABLE, 0);
|
|
rc = rpm_vreg_aggregate_requests(reg);
|
|
if (rc) {
|
|
vreg_err(reg, "disable failed, rc=%d\n", rc);
|
|
RPM_VREG_SET_PARAM(reg, ENABLE, prev_enable);
|
|
}
|
|
} else {
|
|
/* Disable using pcen KVP. */
|
|
prev_enable
|
|
= reg->req.param[RPM_REGULATOR_PARAM_PIN_CTRL_ENABLE];
|
|
RPM_VREG_SET_PARAM(reg, PIN_CTRL_ENABLE,
|
|
reg->pin_ctrl_mask[RPM_VREG_PIN_CTRL_STATE_DISABLE]);
|
|
rc = rpm_vreg_aggregate_requests(reg);
|
|
if (rc) {
|
|
vreg_err(reg, "disable failed, rc=%d\n", rc);
|
|
RPM_VREG_SET_PARAM(reg, PIN_CTRL_ENABLE, prev_enable);
|
|
}
|
|
}
|
|
|
|
rpm_vreg_unlock(reg->rpm_vreg);
|
|
|
|
return rc;
|
|
}
|
|
|
|
#define RPM_VREG_SET_VOLTAGE(_regulator, _val) \
|
|
{ \
|
|
(_regulator)->req.param[(_regulator)->voltage_index] = _val; \
|
|
(_regulator)->req.modified |= BIT((_regulator)->voltage_index); \
|
|
} \
|
|
|
|
static int rpm_vreg_set_voltage(struct regulator_dev *rdev, int min_uV,
|
|
int max_uV, unsigned int *selector)
|
|
{
|
|
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
|
|
int rc = 0;
|
|
int voltage;
|
|
u32 prev_voltage;
|
|
|
|
voltage = min_uV - reg->voltage_offset;
|
|
|
|
if (voltage < params[reg->voltage_index].min
|
|
|| voltage > params[reg->voltage_index].max) {
|
|
vreg_err(reg, "voltage=%d for key=%s is not within allowed range: [%u, %u]\n",
|
|
voltage, params[reg->voltage_index].name,
|
|
params[reg->voltage_index].min,
|
|
params[reg->voltage_index].max);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rpm_vreg_lock(reg->rpm_vreg);
|
|
|
|
prev_voltage = reg->req.param[reg->voltage_index];
|
|
RPM_VREG_SET_VOLTAGE(reg, voltage);
|
|
|
|
rpm_vreg_check_param_max(reg, reg->voltage_index,
|
|
max_uV - reg->voltage_offset);
|
|
|
|
/*
|
|
* Only send a new voltage 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 for key=%s failed, rc=%d\n",
|
|
params[reg->voltage_index].name, rc);
|
|
RPM_VREG_SET_VOLTAGE(reg, prev_voltage);
|
|
}
|
|
|
|
rpm_vreg_unlock(reg->rpm_vreg);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int rpm_vreg_get_voltage(struct regulator_dev *rdev)
|
|
{
|
|
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
|
|
int uV;
|
|
|
|
uV = reg->req.param[reg->voltage_index] + reg->voltage_offset;
|
|
if (uV == 0)
|
|
uV = VOLTAGE_UNKNOWN;
|
|
|
|
return uV;
|
|
}
|
|
|
|
#define REGULATOR_MODE_PMIC4_LDO_LPM 0
|
|
#define REGULATOR_MODE_PMIC4_LDO_HPM 1
|
|
#define REGULATOR_MODE_PMIC5_LDO_LPM 4
|
|
#define REGULATOR_MODE_PMIC5_LDO_HPM 7
|
|
|
|
static int _rpm_vreg_ldo_set_mode(struct regulator_dev *rdev, unsigned int mode)
|
|
{
|
|
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
|
|
u32 hw_mode;
|
|
int rc = 0;
|
|
|
|
if (mode == REGULATOR_MODE_NORMAL) {
|
|
switch (reg->rpm_vreg->regulator_hw_type) {
|
|
case RPM_REGULATOR_HW_TYPE_PMIC4_LDO:
|
|
hw_mode = REGULATOR_MODE_PMIC4_LDO_HPM;
|
|
break;
|
|
|
|
case RPM_REGULATOR_HW_TYPE_PMIC5_LDO:
|
|
hw_mode = REGULATOR_MODE_PMIC5_LDO_HPM;
|
|
break;
|
|
|
|
default:
|
|
vreg_err(reg, "unsupported ldo hw type: %d\n",
|
|
reg->rpm_vreg->regulator_hw_type);
|
|
return -EINVAL;
|
|
}
|
|
} else if (mode == REGULATOR_MODE_IDLE) {
|
|
switch (reg->rpm_vreg->regulator_hw_type) {
|
|
case RPM_REGULATOR_HW_TYPE_PMIC4_LDO:
|
|
hw_mode = REGULATOR_MODE_PMIC4_LDO_LPM;
|
|
break;
|
|
|
|
case RPM_REGULATOR_HW_TYPE_PMIC5_LDO:
|
|
hw_mode = REGULATOR_MODE_PMIC5_LDO_LPM;
|
|
break;
|
|
|
|
default:
|
|
vreg_err(reg, "unsupported ldo hw type: %d\n",
|
|
reg->rpm_vreg->regulator_hw_type);
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
vreg_err(reg, "invalid mode: %u\n", mode);
|
|
return -EINVAL;
|
|
}
|
|
|
|
RPM_VREG_SET_PARAM(reg, MODE_LDO, hw_mode);
|
|
|
|
/*
|
|
* Only send the mode if the regulator is currently enabled or if the
|
|
* regulator has been configured to always send current updates.
|
|
*/
|
|
if (reg->always_send_current
|
|
|| 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 mode failed, rc=%d\n", rc);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int rpm_vreg_ldo_set_mode(struct regulator_dev *rdev, unsigned int mode)
|
|
{
|
|
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
|
|
int rc = 0;
|
|
|
|
rpm_vreg_lock(reg->rpm_vreg);
|
|
|
|
rc = _rpm_vreg_ldo_set_mode(rdev, mode);
|
|
|
|
rpm_vreg_unlock(reg->rpm_vreg);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static unsigned int rpm_vreg_ldo_get_mode(struct regulator_dev *rdev)
|
|
{
|
|
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
|
|
u32 hw_mode;
|
|
|
|
hw_mode = reg->req.param[RPM_REGULATOR_PARAM_MODE_LDO];
|
|
if (hw_mode == REGULATOR_MODE_PMIC4_LDO_HPM ||
|
|
hw_mode == REGULATOR_MODE_PMIC5_LDO_HPM)
|
|
return REGULATOR_MODE_NORMAL;
|
|
|
|
return REGULATOR_MODE_IDLE;
|
|
}
|
|
|
|
static int rpm_vreg_ldo_set_load(struct regulator_dev *rdev, int load_uA)
|
|
{
|
|
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
|
|
unsigned int mode;
|
|
int rc = 0;
|
|
|
|
rpm_vreg_lock(reg->rpm_vreg);
|
|
|
|
mode = (load_uA + reg->system_load >= reg->rpm_vreg->hpm_min_load)
|
|
? REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE;
|
|
rc = _rpm_vreg_ldo_set_mode(rdev, mode);
|
|
|
|
rpm_vreg_unlock(reg->rpm_vreg);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int rpm_vreg_set_mode(struct regulator_dev *rdev, unsigned int mode)
|
|
{
|
|
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
|
|
int rc = 0;
|
|
u32 prev_current;
|
|
int prev_uA;
|
|
|
|
rpm_vreg_lock(reg->rpm_vreg);
|
|
|
|
prev_current = reg->req.param[RPM_REGULATOR_PARAM_CURRENT];
|
|
prev_uA = MILLI_TO_MICRO(prev_current);
|
|
|
|
if (mode == REGULATOR_MODE_NORMAL) {
|
|
/* Make sure that request current is in HPM range. */
|
|
if (prev_uA < rpm_vreg_hpm_min_uA(reg->rpm_vreg))
|
|
RPM_VREG_SET_PARAM(reg, CURRENT,
|
|
MICRO_TO_MILLI(rpm_vreg_hpm_min_uA(reg->rpm_vreg)));
|
|
} else if (REGULATOR_MODE_IDLE) {
|
|
/* Make sure that request current is in LPM range. */
|
|
if (prev_uA > rpm_vreg_lpm_max_uA(reg->rpm_vreg))
|
|
RPM_VREG_SET_PARAM(reg, CURRENT,
|
|
MICRO_TO_MILLI(rpm_vreg_lpm_max_uA(reg->rpm_vreg)));
|
|
} else {
|
|
vreg_err(reg, "invalid mode: %u\n", mode);
|
|
rpm_vreg_unlock(reg->rpm_vreg);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Only send a new load current value if the regulator is currently
|
|
* enabled or if the regulator has been configured to always send
|
|
* current updates.
|
|
*/
|
|
if (reg->always_send_current
|
|
|| 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 mode failed, rc=%d\n", rc);
|
|
RPM_VREG_SET_PARAM(reg, CURRENT, prev_current);
|
|
}
|
|
|
|
rpm_vreg_unlock(reg->rpm_vreg);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static unsigned int rpm_vreg_get_mode(struct regulator_dev *rdev)
|
|
{
|
|
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
|
|
|
|
return (reg->req.param[RPM_REGULATOR_PARAM_CURRENT]
|
|
>= MICRO_TO_MILLI(reg->rpm_vreg->hpm_min_load))
|
|
? REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE;
|
|
}
|
|
|
|
static unsigned int rpm_vreg_get_optimum_mode(struct regulator_dev *rdev,
|
|
int input_uV, int output_uV, int load_uA)
|
|
{
|
|
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
|
|
u32 load_mA;
|
|
|
|
load_uA += reg->system_load;
|
|
|
|
load_mA = MICRO_TO_MILLI(load_uA);
|
|
if (load_mA > params[RPM_REGULATOR_PARAM_CURRENT].max)
|
|
load_mA = params[RPM_REGULATOR_PARAM_CURRENT].max;
|
|
|
|
rpm_vreg_lock(reg->rpm_vreg);
|
|
RPM_VREG_SET_PARAM(reg, CURRENT, load_mA);
|
|
rpm_vreg_unlock(reg->rpm_vreg);
|
|
|
|
return (load_uA >= reg->rpm_vreg->hpm_min_load)
|
|
? REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE;
|
|
}
|
|
|
|
static int rpm_vreg_set_bob_mode(struct regulator_dev *rdev, unsigned int mode)
|
|
{
|
|
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
|
|
int rc;
|
|
u32 hw_mode, hw_type, prev_mode;
|
|
|
|
rpm_vreg_lock(reg->rpm_vreg);
|
|
|
|
prev_mode = reg->req.param[RPM_REGULATOR_PARAM_MODE_BOB];
|
|
hw_type = reg->rpm_vreg->regulator_hw_type;
|
|
|
|
if (hw_type == RPM_REGULATOR_HW_TYPE_PMIC4_BOB) {
|
|
switch (mode) {
|
|
case REGULATOR_MODE_FAST:
|
|
hw_mode = RPM_REGULATOR_PMIC4_BOB_MODE_PWM;
|
|
break;
|
|
case REGULATOR_MODE_NORMAL:
|
|
hw_mode = RPM_REGULATOR_PMIC4_BOB_MODE_AUTO;
|
|
break;
|
|
case REGULATOR_MODE_IDLE:
|
|
hw_mode = RPM_REGULATOR_PMIC4_BOB_MODE_PFM;
|
|
break;
|
|
case REGULATOR_MODE_STANDBY:
|
|
hw_mode = RPM_REGULATOR_PMIC4_BOB_MODE_PASS;
|
|
break;
|
|
default:
|
|
vreg_err(reg, "invalid mode: %u\n", mode);
|
|
rpm_vreg_unlock(reg->rpm_vreg);
|
|
return -EINVAL;
|
|
}
|
|
} else if (hw_type == RPM_REGULATOR_HW_TYPE_PMIC5_BOB) {
|
|
switch (mode) {
|
|
case REGULATOR_MODE_FAST:
|
|
hw_mode = RPM_REGULATOR_PMIC5_BOB_MODE_PWM;
|
|
break;
|
|
case REGULATOR_MODE_NORMAL:
|
|
hw_mode = RPM_REGULATOR_PMIC5_BOB_MODE_AUTO;
|
|
break;
|
|
case REGULATOR_MODE_IDLE:
|
|
hw_mode = RPM_REGULATOR_PMIC5_BOB_MODE_PFM;
|
|
break;
|
|
case REGULATOR_MODE_STANDBY:
|
|
hw_mode = RPM_REGULATOR_PMIC5_BOB_MODE_PASS;
|
|
break;
|
|
default:
|
|
vreg_err(reg, "invalid mode: %u\n", mode);
|
|
rpm_vreg_unlock(reg->rpm_vreg);
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
vreg_err(reg, "unsupported bob hw type: %d\n",
|
|
reg->rpm_vreg->regulator_hw_type);
|
|
rpm_vreg_unlock(reg->rpm_vreg);
|
|
return -EINVAL;
|
|
}
|
|
|
|
RPM_VREG_SET_PARAM(reg, MODE_BOB, hw_mode);
|
|
|
|
rc = rpm_vreg_aggregate_requests(reg);
|
|
if (rc) {
|
|
vreg_err(reg, "set BoB mode failed, rc=%d\n", rc);
|
|
RPM_VREG_SET_PARAM(reg, MODE_BOB, prev_mode);
|
|
}
|
|
|
|
rpm_vreg_unlock(reg->rpm_vreg);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static unsigned int rpm_vreg_get_bob_mode(struct regulator_dev *rdev)
|
|
{
|
|
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
|
|
unsigned int mode = REGULATOR_MODE_NORMAL;
|
|
u32 hw_type = reg->rpm_vreg->regulator_hw_type;
|
|
|
|
if (hw_type == RPM_REGULATOR_HW_TYPE_PMIC4_BOB) {
|
|
switch (reg->req.param[RPM_REGULATOR_PARAM_MODE_BOB]) {
|
|
case RPM_REGULATOR_PMIC4_BOB_MODE_PWM:
|
|
mode = REGULATOR_MODE_FAST;
|
|
break;
|
|
case RPM_REGULATOR_PMIC4_BOB_MODE_AUTO:
|
|
mode = REGULATOR_MODE_NORMAL;
|
|
break;
|
|
case RPM_REGULATOR_PMIC4_BOB_MODE_PFM:
|
|
mode = REGULATOR_MODE_IDLE;
|
|
break;
|
|
case RPM_REGULATOR_PMIC4_BOB_MODE_PASS:
|
|
mode = REGULATOR_MODE_STANDBY;
|
|
break;
|
|
default:
|
|
vreg_err(reg, "BoB mode unknown\n");
|
|
}
|
|
} else if (hw_type == RPM_REGULATOR_HW_TYPE_PMIC4_BOB) {
|
|
switch (reg->req.param[RPM_REGULATOR_PARAM_MODE_BOB]) {
|
|
case RPM_REGULATOR_PMIC5_BOB_MODE_PWM:
|
|
mode = REGULATOR_MODE_FAST;
|
|
break;
|
|
case RPM_REGULATOR_PMIC5_BOB_MODE_AUTO:
|
|
mode = REGULATOR_MODE_NORMAL;
|
|
break;
|
|
case RPM_REGULATOR_PMIC5_BOB_MODE_PFM:
|
|
mode = REGULATOR_MODE_IDLE;
|
|
break;
|
|
case RPM_REGULATOR_PMIC5_BOB_MODE_PASS:
|
|
mode = REGULATOR_MODE_STANDBY;
|
|
break;
|
|
default:
|
|
vreg_err(reg, "BoB mode unknown\n");
|
|
}
|
|
}
|
|
|
|
return mode;
|
|
}
|
|
|
|
#define RPM_SMD_REGULATOR_MAX_MODES 5
|
|
|
|
static const int bob_supported_modes[RPM_SMD_REGULATOR_MAX_MODES] = {
|
|
[RPM_SMD_REGULATOR_MODE_PASS] = REGULATOR_MODE_STANDBY,
|
|
[RPM_SMD_REGULATOR_MODE_RET] = REGULATOR_MODE_INVALID,
|
|
[RPM_SMD_REGULATOR_MODE_LPM] = REGULATOR_MODE_IDLE,
|
|
[RPM_SMD_REGULATOR_MODE_AUTO] = REGULATOR_MODE_NORMAL,
|
|
[RPM_SMD_REGULATOR_MODE_HPM] = REGULATOR_MODE_FAST,
|
|
};
|
|
|
|
static int rpm_vreg_bob_set_load(struct regulator_dev *rdev, int load_ua)
|
|
{
|
|
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
|
|
int rc = 0, i;
|
|
unsigned int mode;
|
|
|
|
for (i = reg->rpm_vreg->mode_count - 1; i > 0; i--)
|
|
if (reg->rpm_vreg->mode[i].min_load_ua <= load_ua)
|
|
break;
|
|
|
|
mode = reg->rpm_vreg->mode[i].mode;
|
|
|
|
rc = rpm_vreg_set_bob_mode(rdev, mode);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int rpm_vreg_enable_time(struct regulator_dev *rdev)
|
|
{
|
|
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
|
|
|
|
return reg->rpm_vreg->enable_time;
|
|
}
|
|
|
|
static int rpm_vreg_send_defaults(struct rpm_regulator *reg)
|
|
{
|
|
int rc;
|
|
|
|
rpm_vreg_lock(reg->rpm_vreg);
|
|
rc = rpm_vreg_aggregate_requests(reg);
|
|
if (rc)
|
|
vreg_err(reg, "RPM request failed, rc=%d", rc);
|
|
rpm_vreg_unlock(reg->rpm_vreg);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int rpm_vreg_configure_pin_control_enable(struct rpm_regulator *reg,
|
|
struct device_node *node)
|
|
{
|
|
struct rpm_regulator_param *pcen_param =
|
|
¶ms[RPM_REGULATOR_PARAM_PIN_CTRL_ENABLE];
|
|
int rc, i;
|
|
|
|
if (!of_find_property(node, "qcom,enable-with-pin-ctrl", NULL))
|
|
return 0;
|
|
|
|
if (pcen_param->supported_regulator_types
|
|
& BIT(reg->rpm_vreg->regulator_type)) {
|
|
rc = of_property_read_u32_array(node,
|
|
"qcom,enable-with-pin-ctrl", reg->pin_ctrl_mask,
|
|
RPM_VREG_PIN_CTRL_STATE_COUNT);
|
|
if (rc) {
|
|
vreg_err(reg, "could not read qcom,enable-with-pin-ctrl, rc=%d\n",
|
|
rc);
|
|
return rc;
|
|
}
|
|
|
|
/* Verify that the mask values are valid. */
|
|
for (i = 0; i < RPM_VREG_PIN_CTRL_STATE_COUNT; i++) {
|
|
if (reg->pin_ctrl_mask[i] < pcen_param->min
|
|
|| reg->pin_ctrl_mask[i] > pcen_param->max) {
|
|
vreg_err(reg, "device tree property: qcom,enable-with-pin-ctrl[%d]=%u is outside allowed range [%u, %u]\n",
|
|
i, reg->pin_ctrl_mask[i],
|
|
pcen_param->min, pcen_param->max);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
reg->use_pin_ctrl_for_enable = true;
|
|
} else {
|
|
pr_warn("%s: regulator type=%d does not support device tree property: qcom,enable-with-pin-ctrl\n",
|
|
reg->rdesc.name, reg->rpm_vreg->regulator_type);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct regulator_ops ldo_ops = {
|
|
.enable = rpm_vreg_enable,
|
|
.disable = rpm_vreg_disable,
|
|
.is_enabled = rpm_vreg_is_enabled,
|
|
.set_voltage = rpm_vreg_set_voltage,
|
|
.get_voltage = rpm_vreg_get_voltage,
|
|
.set_load = rpm_vreg_ldo_set_load,
|
|
.set_mode = rpm_vreg_ldo_set_mode,
|
|
.get_mode = rpm_vreg_ldo_get_mode,
|
|
.enable_time = rpm_vreg_enable_time,
|
|
};
|
|
|
|
static struct regulator_ops smps_ops = {
|
|
.enable = rpm_vreg_enable,
|
|
.disable = rpm_vreg_disable,
|
|
.is_enabled = rpm_vreg_is_enabled,
|
|
.set_voltage = rpm_vreg_set_voltage,
|
|
.get_voltage = rpm_vreg_get_voltage,
|
|
.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,
|
|
.is_enabled = rpm_vreg_is_enabled,
|
|
.enable_time = rpm_vreg_enable_time,
|
|
};
|
|
|
|
static struct regulator_ops ncp_ops = {
|
|
.enable = rpm_vreg_enable,
|
|
.disable = rpm_vreg_disable,
|
|
.is_enabled = rpm_vreg_is_enabled,
|
|
.set_voltage = rpm_vreg_set_voltage,
|
|
.get_voltage = rpm_vreg_get_voltage,
|
|
.enable_time = rpm_vreg_enable_time,
|
|
};
|
|
|
|
static struct regulator_ops bob_ops = {
|
|
.enable = rpm_vreg_enable,
|
|
.disable = rpm_vreg_disable,
|
|
.is_enabled = rpm_vreg_is_enabled,
|
|
.set_voltage = rpm_vreg_set_voltage,
|
|
.get_voltage = rpm_vreg_get_voltage,
|
|
.set_load = rpm_vreg_bob_set_load,
|
|
.set_mode = rpm_vreg_set_bob_mode,
|
|
.get_mode = rpm_vreg_get_bob_mode,
|
|
.enable_time = rpm_vreg_enable_time,
|
|
};
|
|
|
|
static struct regulator_ops *vreg_ops[] = {
|
|
[RPM_REGULATOR_TYPE_LDO] = &ldo_ops,
|
|
[RPM_REGULATOR_TYPE_SMPS] = &smps_ops,
|
|
[RPM_REGULATOR_TYPE_VS] = &switch_ops,
|
|
[RPM_REGULATOR_TYPE_NCP] = &ncp_ops,
|
|
[RPM_REGULATOR_TYPE_BOB] = &bob_ops,
|
|
};
|
|
|
|
static int rpm_vreg_device_remove(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct rpm_regulator *reg;
|
|
struct rpm_vreg *rpm_vreg;
|
|
|
|
reg = platform_get_drvdata(pdev);
|
|
if (reg) {
|
|
rpm_vreg = reg->rpm_vreg;
|
|
rpm_vreg_lock(rpm_vreg);
|
|
regulator_unregister(reg->rdev);
|
|
devm_regulator_proxy_consumer_unregister(pdev->dev.parent);
|
|
list_del(®->list);
|
|
kfree(reg);
|
|
rpm_vreg_unlock(rpm_vreg);
|
|
} else {
|
|
dev_err(dev, "%s: drvdata missing\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
platform_set_drvdata(pdev, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rpm_vreg_resource_remove(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct rpm_regulator *reg, *reg_temp;
|
|
struct rpm_vreg *rpm_vreg;
|
|
|
|
rpm_vreg = platform_get_drvdata(pdev);
|
|
if (rpm_vreg) {
|
|
rpm_vreg_lock(rpm_vreg);
|
|
list_for_each_entry_safe(reg, reg_temp, &rpm_vreg->reg_list,
|
|
list) {
|
|
/* Only touch data for private consumers. */
|
|
if (reg->rdev->desc == NULL) {
|
|
list_del(®->list);
|
|
kfree(reg->rdev);
|
|
kfree(reg);
|
|
} else {
|
|
dev_err(dev, "%s: not all child devices have been removed\n",
|
|
__func__);
|
|
}
|
|
}
|
|
rpm_vreg_unlock(rpm_vreg);
|
|
|
|
msm_rpm_free_request(rpm_vreg->handle_active);
|
|
msm_rpm_free_request(rpm_vreg->handle_sleep);
|
|
|
|
kfree(rpm_vreg);
|
|
} else {
|
|
dev_err(dev, "%s: drvdata missing\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
platform_set_drvdata(pdev, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rpm_vreg_set_smps_ldo_voltage_index(struct device *dev,
|
|
struct rpm_regulator *reg)
|
|
{
|
|
struct device_node *node = dev->of_node;
|
|
int chosen = 0;
|
|
|
|
if (of_property_read_bool(node, "qcom,use-voltage-corner")) {
|
|
reg->voltage_index = RPM_REGULATOR_PARAM_CORNER;
|
|
reg->voltage_offset = RPM_REGULATOR_CORNER_NONE;
|
|
chosen++;
|
|
}
|
|
|
|
if (of_property_read_bool(node, "qcom,use-voltage-floor-corner")) {
|
|
reg->voltage_index = RPM_REGULATOR_PARAM_FLOOR_CORNER;
|
|
reg->voltage_offset = RPM_REGULATOR_CORNER_NONE;
|
|
chosen++;
|
|
}
|
|
|
|
if (of_property_read_bool(node, "qcom,use-voltage-level")) {
|
|
reg->voltage_index = RPM_REGULATOR_PARAM_LEVEL;
|
|
chosen++;
|
|
}
|
|
|
|
if (of_property_read_bool(node, "qcom,use-voltage-floor-level")) {
|
|
reg->voltage_index = RPM_REGULATOR_PARAM_FLOOR_LEVEL;
|
|
chosen++;
|
|
}
|
|
|
|
if (chosen > 1) {
|
|
dev_err(dev, "only one qcom,use-voltage-* may be specified\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rpm_vreg_set_bob_voltage_index(struct device *dev,
|
|
struct rpm_regulator *reg)
|
|
{
|
|
struct device_node *node = dev->of_node;
|
|
int chosen = 0;
|
|
|
|
if (of_property_read_bool(node, "qcom,use-pin-ctrl-voltage1")) {
|
|
reg->voltage_index = RPM_REGULATOR_PARAM_PIN_CTRL_VOLTAGE1;
|
|
chosen++;
|
|
}
|
|
|
|
if (of_property_read_bool(node, "qcom,use-pin-ctrl-voltage2")) {
|
|
reg->voltage_index = RPM_REGULATOR_PARAM_PIN_CTRL_VOLTAGE2;
|
|
chosen++;
|
|
}
|
|
|
|
if (of_property_read_bool(node, "qcom,use-pin-ctrl-voltage3")) {
|
|
reg->voltage_index = RPM_REGULATOR_PARAM_PIN_CTRL_VOLTAGE3;
|
|
chosen++;
|
|
}
|
|
|
|
if (chosen > 1) {
|
|
dev_err(dev, "only one qcom,use-pin-ctrl-voltage* may be specified\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rpm_vreg_device_set_voltage_index(struct device *dev,
|
|
struct rpm_regulator *reg, int type)
|
|
{
|
|
int rc = 0;
|
|
|
|
reg->voltage_index = RPM_REGULATOR_PARAM_VOLTAGE;
|
|
|
|
switch (type) {
|
|
case RPM_REGULATOR_TYPE_SMPS:
|
|
case RPM_REGULATOR_TYPE_LDO:
|
|
rc = rpm_vreg_set_smps_ldo_voltage_index(dev, reg);
|
|
break;
|
|
case RPM_REGULATOR_TYPE_BOB:
|
|
rc = rpm_vreg_set_bob_voltage_index(dev, reg);
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
static void rpm_vreg_create_debugfs(struct rpm_regulator *reg)
|
|
{
|
|
struct dentry *entry;
|
|
|
|
if (!is_debugfs_created) {
|
|
reg->dfs_root = debugfs_create_dir("rpm_vreg_debugfs", NULL);
|
|
if (IS_ERR_OR_NULL(reg->dfs_root)) {
|
|
pr_err("Failed to create debugfs directory rc=%ld\n",
|
|
(long)reg->dfs_root);
|
|
return;
|
|
}
|
|
entry = debugfs_create_u32("debug_mask", 0600, reg->dfs_root,
|
|
&rpm_vreg_debug_mask);
|
|
if (IS_ERR_OR_NULL(entry)) {
|
|
pr_err("Failed to create debug_mask rc=%ld\n",
|
|
(long)entry);
|
|
debugfs_remove_recursive(reg->dfs_root);
|
|
}
|
|
is_debugfs_created = true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* This probe is called for child rpm-regulator devices which have
|
|
* properties which are required to configure individual regulator
|
|
* framework regulators for a given RPM regulator resource.
|
|
*/
|
|
static int rpm_vreg_device_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *node = dev->of_node;
|
|
struct regulator_init_data *init_data;
|
|
struct rpm_vreg *rpm_vreg;
|
|
struct rpm_regulator *reg;
|
|
struct regulator_config reg_config = {};
|
|
int rc = 0;
|
|
int i, regulator_type;
|
|
u32 val;
|
|
|
|
if (pdev->dev.parent == NULL) {
|
|
dev_err(dev, "%s: parent device missing\n", __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
rpm_vreg = dev_get_drvdata(pdev->dev.parent);
|
|
if (rpm_vreg == NULL) {
|
|
dev_err(dev, "%s: rpm_vreg not found in parent device\n",
|
|
__func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
reg = kzalloc(sizeof(*reg), GFP_KERNEL);
|
|
if (reg == NULL)
|
|
return -ENOMEM;
|
|
|
|
regulator_type = rpm_vreg->regulator_type;
|
|
reg->rpm_vreg = rpm_vreg;
|
|
reg->rdesc.owner = THIS_MODULE;
|
|
reg->rdesc.type = REGULATOR_VOLTAGE;
|
|
reg->rdesc.ops = vreg_ops[regulator_type];
|
|
|
|
rc = rpm_vreg_device_set_voltage_index(dev, reg, regulator_type);
|
|
if (rc)
|
|
goto fail_free_reg;
|
|
|
|
reg->always_send_voltage
|
|
= of_property_read_bool(node, "qcom,always-send-voltage");
|
|
reg->always_send_current
|
|
= of_property_read_bool(node, "qcom,always-send-current");
|
|
|
|
if (regulator_type == RPM_REGULATOR_TYPE_VS)
|
|
reg->rdesc.n_voltages = 0;
|
|
else
|
|
reg->rdesc.n_voltages = 2;
|
|
|
|
rc = of_property_read_u32(node, "qcom,set", &val);
|
|
if (rc) {
|
|
dev_err(dev, "%s: sleep set and/or active set must be configured via qcom,set property, rc=%d\n",
|
|
__func__, rc);
|
|
goto fail_free_reg;
|
|
} else if (!(val & RPM_SET_CONFIG_BOTH)) {
|
|
dev_err(dev, "%s: qcom,set=%u property is invalid\n", __func__,
|
|
val);
|
|
rc = -EINVAL;
|
|
goto fail_free_reg;
|
|
}
|
|
|
|
reg->set_active = !!(val & RPM_SET_CONFIG_ACTIVE);
|
|
reg->set_sleep = !!(val & RPM_SET_CONFIG_SLEEP);
|
|
|
|
rc = of_property_read_u32(node, "qcom,min-dropout-voltage",
|
|
&val);
|
|
if (!rc)
|
|
reg->rdesc.min_dropout_uV = val;
|
|
|
|
init_data = of_get_regulator_init_data(dev, node, ®->rdesc);
|
|
if (init_data == NULL) {
|
|
dev_err(dev, "%s: failed to populate regulator_init_data\n",
|
|
__func__);
|
|
rc = -ENOMEM;
|
|
goto fail_free_reg;
|
|
}
|
|
if (init_data->constraints.name == NULL) {
|
|
dev_err(dev, "%s: regulator name not specified\n", __func__);
|
|
rc = -EINVAL;
|
|
goto fail_free_reg;
|
|
}
|
|
|
|
init_data->constraints.input_uV = init_data->constraints.max_uV;
|
|
|
|
if (of_get_property(node, "parent-supply", NULL))
|
|
init_data->supply_regulator = "parent";
|
|
|
|
/*
|
|
* Fill in ops and mode masks based on callbacks specified for
|
|
* this type of regulator.
|
|
*/
|
|
if (reg->rdesc.ops->enable)
|
|
init_data->constraints.valid_ops_mask
|
|
|= REGULATOR_CHANGE_STATUS;
|
|
if (reg->rdesc.ops->get_voltage)
|
|
init_data->constraints.valid_ops_mask
|
|
|= REGULATOR_CHANGE_VOLTAGE;
|
|
if (reg->rdesc.ops->get_mode) {
|
|
init_data->constraints.valid_ops_mask
|
|
|= REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_DRMS;
|
|
init_data->constraints.valid_modes_mask
|
|
|= REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE;
|
|
}
|
|
|
|
reg->rdesc.name = init_data->constraints.name;
|
|
reg->min_uV = init_data->constraints.min_uV;
|
|
reg->max_uV = init_data->constraints.max_uV;
|
|
|
|
/* Initialize the param array based on optional properties. */
|
|
for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++) {
|
|
rc = of_property_read_u32(node, params[i].property_name, &val);
|
|
if (rc == 0) {
|
|
if (params[i].supported_regulator_types
|
|
& BIT(regulator_type)) {
|
|
if (val < params[i].min
|
|
|| val > params[i].max) {
|
|
pr_warn("%s: device tree property: %s=%u is outsided allowed range [%u, %u]\n",
|
|
reg->rdesc.name,
|
|
params[i].property_name, val,
|
|
params[i].min, params[i].max);
|
|
continue;
|
|
}
|
|
reg->req.param[i] = val;
|
|
reg->req.modified |= BIT(i);
|
|
} else {
|
|
pr_warn("%s: regulator type=%d does not support device tree property: %s\n",
|
|
reg->rdesc.name, regulator_type,
|
|
params[i].property_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
of_property_read_u32(node, "qcom,system-load", ®->system_load);
|
|
|
|
rc = rpm_vreg_configure_pin_control_enable(reg, node);
|
|
if (rc) {
|
|
vreg_err(reg, "could not configure pin control enable, rc=%d\n",
|
|
rc);
|
|
goto fail_free_reg;
|
|
}
|
|
|
|
rpm_vreg_lock(rpm_vreg);
|
|
list_add(®->list, &rpm_vreg->reg_list);
|
|
rpm_vreg_unlock(rpm_vreg);
|
|
|
|
if (of_property_read_bool(node, "qcom,send-defaults")) {
|
|
rc = rpm_vreg_send_defaults(reg);
|
|
if (rc) {
|
|
vreg_err(reg, "could not send defaults, rc=%d\n", rc);
|
|
goto fail_remove_from_list;
|
|
}
|
|
}
|
|
|
|
reg_config.dev = dev;
|
|
reg_config.init_data = init_data;
|
|
reg_config.of_node = node;
|
|
reg_config.driver_data = reg;
|
|
reg->rdev = regulator_register(®->rdesc, ®_config);
|
|
if (IS_ERR(reg->rdev)) {
|
|
rc = PTR_ERR(reg->rdev);
|
|
reg->rdev = NULL;
|
|
pr_err("regulator_register failed: %s, rc=%d\n",
|
|
reg->rdesc.name, rc);
|
|
goto fail_remove_from_list;
|
|
}
|
|
|
|
rc = devm_regulator_proxy_consumer_register(pdev->dev.parent, node);
|
|
if (rc)
|
|
vreg_err(reg, "failed to register proxy consumer, rc=%d\n",
|
|
rc);
|
|
|
|
platform_set_drvdata(pdev, reg);
|
|
|
|
rpm_vreg_create_debugfs(reg);
|
|
|
|
pr_debug("successfully probed: %s\n", reg->rdesc.name);
|
|
|
|
return 0;
|
|
|
|
fail_remove_from_list:
|
|
rpm_vreg_lock(rpm_vreg);
|
|
list_del(®->list);
|
|
rpm_vreg_unlock(rpm_vreg);
|
|
|
|
fail_free_reg:
|
|
kfree(reg);
|
|
return rc;
|
|
}
|
|
|
|
int init_thresholds(struct device_node *node, struct device *dev,
|
|
struct rpm_vreg *rpm_vreg, int len)
|
|
{
|
|
int rc, i;
|
|
u32 *buf;
|
|
const char *prop = "qcom,supported-modes";
|
|
|
|
len /= sizeof(u32);
|
|
rpm_vreg->mode = devm_kcalloc(dev, len,
|
|
sizeof(*rpm_vreg->mode), GFP_KERNEL);
|
|
if (!rpm_vreg->mode)
|
|
return -ENOMEM;
|
|
rpm_vreg->mode_count = len;
|
|
|
|
buf = kcalloc(len, sizeof(*buf), GFP_KERNEL);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
rc = of_property_read_u32_array(node, prop, buf, len);
|
|
if (rc) {
|
|
pr_err("unable to read %s, rc=%d\n",
|
|
prop, rc);
|
|
return rc;
|
|
}
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (buf[i] < RPM_SMD_REGULATOR_MODE_PASS ||
|
|
buf[i] > RPM_SMD_REGULATOR_MODE_HPM) {
|
|
dev_err(dev, "element %d of %s = %u is invalid for BOB\n",
|
|
i, prop, buf[i]);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rpm_vreg->mode[i].mode = bob_supported_modes[buf[i]];
|
|
|
|
if (i > 0 && buf[i] <= buf[i - 1]) {
|
|
dev_err(dev, "%s elements are not in ascending order\n",
|
|
prop);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
prop = "qcom,mode-threshold-currents";
|
|
|
|
rc = of_property_read_u32_array(node, prop, buf, len);
|
|
if (rc) {
|
|
dev_err(dev, "unable to read %s, rc=%d\n",
|
|
prop, rc);
|
|
return rc;
|
|
}
|
|
|
|
for (i = 0; i < len; i++) {
|
|
rpm_vreg->mode[i].min_load_ua = buf[i];
|
|
|
|
if (i > 0 && rpm_vreg->mode[i].min_load_ua
|
|
<= rpm_vreg->mode[i - 1].min_load_ua) {
|
|
dev_err(dev, "%s elements are not in ascending order\n",
|
|
prop);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* This probe is called for parent rpm-regulator devices which have
|
|
* properties which are required to identify a given RPM resource.
|
|
*/
|
|
static int rpm_vreg_resource_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *node = dev->of_node;
|
|
struct rpm_vreg *rpm_vreg;
|
|
const char *type = "";
|
|
const char *prop;
|
|
int val = 0;
|
|
u32 resource_type;
|
|
int rc, len;
|
|
|
|
|
|
/* Create new rpm_vreg entry. */
|
|
rpm_vreg = kzalloc(sizeof(*rpm_vreg), GFP_KERNEL);
|
|
if (rpm_vreg == NULL)
|
|
return -ENOMEM;
|
|
|
|
/* Required device tree properties: */
|
|
rc = of_property_read_string(node, "qcom,resource-name",
|
|
&rpm_vreg->resource_name);
|
|
if (rc) {
|
|
dev_err(dev, "%s: qcom,resource-name missing in DT node\n",
|
|
__func__);
|
|
goto fail_free_vreg;
|
|
}
|
|
resource_type = rpm_vreg_string_to_int(rpm_vreg->resource_name);
|
|
|
|
rc = of_property_read_u32(node, "qcom,resource-id",
|
|
&rpm_vreg->resource_id);
|
|
if (rc) {
|
|
dev_err(dev, "%s: qcom,resource-id missing in DT node\n",
|
|
__func__);
|
|
goto fail_free_vreg;
|
|
}
|
|
|
|
rc = of_property_read_u32(node, "qcom,regulator-type",
|
|
&rpm_vreg->regulator_type);
|
|
if (rc) {
|
|
dev_err(dev, "%s: qcom,regulator-type missing in DT node\n",
|
|
__func__);
|
|
goto fail_free_vreg;
|
|
}
|
|
|
|
if ((rpm_vreg->regulator_type < 0)
|
|
|| (rpm_vreg->regulator_type >= RPM_REGULATOR_TYPE_MAX)) {
|
|
dev_err(dev, "%s: invalid regulator type: %d\n", __func__,
|
|
rpm_vreg->regulator_type);
|
|
rc = -EINVAL;
|
|
goto fail_free_vreg;
|
|
}
|
|
|
|
if (rpm_vreg->regulator_type == RPM_REGULATOR_TYPE_LDO) {
|
|
prop = "qcom,regulator-hw-type";
|
|
rpm_vreg->regulator_hw_type = RPM_REGULATOR_HW_TYPE_UNKNOWN;
|
|
rc = of_property_read_string(node, prop, &type);
|
|
if (rc) {
|
|
dev_err(dev, "%s is missing in DT node rc=%d\n",
|
|
prop, rc);
|
|
goto fail_free_vreg;
|
|
}
|
|
|
|
if (!strcmp(type, "pmic4-ldo")) {
|
|
rpm_vreg->regulator_hw_type
|
|
= RPM_REGULATOR_HW_TYPE_PMIC4_LDO;
|
|
} else if (!strcmp(type, "pmic5-ldo")) {
|
|
rpm_vreg->regulator_hw_type
|
|
= RPM_REGULATOR_HW_TYPE_PMIC5_LDO;
|
|
} else {
|
|
dev_err(dev, "unknown %s = %s\n",
|
|
prop, type);
|
|
goto fail_free_vreg;
|
|
}
|
|
}
|
|
|
|
if (rpm_vreg->regulator_type == RPM_REGULATOR_TYPE_BOB) {
|
|
prop = "qcom,regulator-hw-type";
|
|
rpm_vreg->regulator_hw_type = RPM_REGULATOR_HW_TYPE_UNKNOWN;
|
|
rc = of_property_read_string(node, prop, &type);
|
|
if (rc) {
|
|
dev_err(dev, "%s is missing in DT node rc=%d\n",
|
|
prop, rc);
|
|
goto fail_free_vreg;
|
|
}
|
|
|
|
if (!strcmp(type, "pmic4-bob")) {
|
|
rpm_vreg->regulator_hw_type
|
|
= RPM_REGULATOR_HW_TYPE_PMIC4_BOB;
|
|
} else if (!strcmp(type, "pmic5-bob")) {
|
|
rpm_vreg->regulator_hw_type
|
|
= RPM_REGULATOR_HW_TYPE_PMIC5_BOB;
|
|
} else {
|
|
dev_err(dev, "unknown %s = %s\n",
|
|
prop, type);
|
|
goto fail_free_vreg;
|
|
}
|
|
}
|
|
|
|
/* Optional device tree properties: */
|
|
of_property_read_u32(node, "qcom,allow-atomic", &val);
|
|
rpm_vreg->allow_atomic = !!val;
|
|
of_property_read_u32(node, "qcom,enable-time", &rpm_vreg->enable_time);
|
|
of_property_read_u32(node, "qcom,hpm-min-load",
|
|
&rpm_vreg->hpm_min_load);
|
|
rpm_vreg->apps_only = of_property_read_bool(node, "qcom,apps-only");
|
|
rpm_vreg->always_wait_for_ack
|
|
= of_property_read_bool(node, "qcom,always-wait-for-ack");
|
|
|
|
if (of_find_property(node, "qcom,supported-modes", &len)) {
|
|
rc = init_thresholds(node, dev, rpm_vreg, len);
|
|
if (rc < 0)
|
|
goto fail_free_vreg;
|
|
}
|
|
|
|
rpm_vreg->handle_active = msm_rpm_create_request(RPM_SET_ACTIVE,
|
|
resource_type, rpm_vreg->resource_id, RPM_REGULATOR_PARAM_MAX);
|
|
if (rpm_vreg->handle_active == NULL
|
|
|| IS_ERR(rpm_vreg->handle_active)) {
|
|
rc = PTR_ERR(rpm_vreg->handle_active);
|
|
if (rc != -EPROBE_DEFER)
|
|
dev_err(dev, "%s: failed to create active RPM handle, rc=%d\n",
|
|
__func__, rc);
|
|
goto fail_free_vreg;
|
|
}
|
|
|
|
rpm_vreg->handle_sleep = msm_rpm_create_request(RPM_SET_SLEEP,
|
|
resource_type, rpm_vreg->resource_id, RPM_REGULATOR_PARAM_MAX);
|
|
if (rpm_vreg->handle_sleep == NULL || IS_ERR(rpm_vreg->handle_sleep)) {
|
|
rc = PTR_ERR(rpm_vreg->handle_sleep);
|
|
if (rc != -EPROBE_DEFER)
|
|
dev_err(dev, "%s: failed to create sleep RPM handle, rc=%d\n",
|
|
__func__, rc);
|
|
goto fail_free_handle_active;
|
|
}
|
|
|
|
INIT_LIST_HEAD(&rpm_vreg->reg_list);
|
|
|
|
if (rpm_vreg->allow_atomic)
|
|
spin_lock_init(&rpm_vreg->slock);
|
|
else
|
|
mutex_init(&rpm_vreg->mlock);
|
|
|
|
platform_set_drvdata(pdev, rpm_vreg);
|
|
|
|
rc = of_platform_populate(node, NULL, NULL, dev);
|
|
if (rc) {
|
|
dev_err(dev, "%s: failed to add child nodes, rc=%d\n", __func__,
|
|
rc);
|
|
goto fail_unset_drvdata;
|
|
}
|
|
|
|
pr_debug("successfully probed: %s (%08X) %u\n", rpm_vreg->resource_name,
|
|
resource_type, rpm_vreg->resource_id);
|
|
|
|
return rc;
|
|
|
|
fail_unset_drvdata:
|
|
platform_set_drvdata(pdev, NULL);
|
|
msm_rpm_free_request(rpm_vreg->handle_sleep);
|
|
|
|
fail_free_handle_active:
|
|
msm_rpm_free_request(rpm_vreg->handle_active);
|
|
|
|
fail_free_vreg:
|
|
kfree(rpm_vreg);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static const struct of_device_id rpm_vreg_match_table_device[] = {
|
|
{ .compatible = "qcom,rpm-smd-regulator", },
|
|
{}
|
|
};
|
|
|
|
static const struct of_device_id rpm_vreg_match_table_resource[] = {
|
|
{ .compatible = "qcom,rpm-smd-regulator-resource", },
|
|
{}
|
|
};
|
|
|
|
static struct platform_driver rpm_vreg_device_driver = {
|
|
.probe = rpm_vreg_device_probe,
|
|
.remove = rpm_vreg_device_remove,
|
|
.driver = {
|
|
.name = "qcom,rpm-smd-regulator",
|
|
.of_match_table = rpm_vreg_match_table_device,
|
|
},
|
|
};
|
|
|
|
static struct platform_driver rpm_vreg_resource_driver = {
|
|
.probe = rpm_vreg_resource_probe,
|
|
.remove = rpm_vreg_resource_remove,
|
|
.driver = {
|
|
.name = "qcom,rpm-smd-regulator-resource",
|
|
.of_match_table = rpm_vreg_match_table_resource,
|
|
.sync_state = regulator_proxy_consumer_sync_state,
|
|
},
|
|
};
|
|
|
|
/**
|
|
* rpm_smd_regulator_driver_init() - initialize the RPM SMD regulator drivers
|
|
*
|
|
* This function registers the RPM SMD regulator platform drivers.
|
|
*
|
|
* Returns 0 on success or errno on failure.
|
|
*/
|
|
static int __init rpm_smd_regulator_driver_init(void)
|
|
{
|
|
static bool initialized;
|
|
int i, rc;
|
|
|
|
if (initialized)
|
|
return 0;
|
|
|
|
initialized = true;
|
|
|
|
/* Store parameter string names as integers */
|
|
for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++)
|
|
params[i].key = rpm_vreg_string_to_int(params[i].name);
|
|
|
|
rc = platform_driver_register(&rpm_vreg_device_driver);
|
|
if (rc)
|
|
return rc;
|
|
|
|
return platform_driver_register(&rpm_vreg_resource_driver);
|
|
}
|
|
|
|
static void __exit rpm_vreg_exit(void)
|
|
{
|
|
platform_driver_unregister(&rpm_vreg_device_driver);
|
|
platform_driver_unregister(&rpm_vreg_resource_driver);
|
|
}
|
|
|
|
arch_initcall(rpm_smd_regulator_driver_init);
|
|
module_exit(rpm_vreg_exit);
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_DESCRIPTION("MSM RPM SMD regulator driver");
|