blob: 152b6e5f3ae3fabc209e187219b2713fd1d60ea2 [file] [log] [blame]
/* Copyright (c) 2012, Code Aurora Forum. 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.
*/
#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 <mach/rpm-smd.h>
#include <mach/rpm-regulator-smd.h>
#include <mach/socinfo.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;
module_param_named(
debug_mask, rpm_vreg_debug_mask, int, S_IRUSR | S_IWUSR
);
#define vreg_err(req, fmt, ...) \
pr_err("%s: " fmt, req->rdesc.name, ##__VA_ARGS__)
/* RPM regulator request types */
enum rpm_regulator_smd_type {
RPM_REGULATOR_SMD_TYPE_LDO,
RPM_REGULATOR_SMD_TYPE_SMPS,
RPM_REGULATOR_SMD_TYPE_VS,
RPM_REGULATOR_SMD_TYPE_NCP,
RPM_REGULATOR_SMD_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_MAX,
};
#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, \
_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_SMD_TYPE_LDO | \
_support_smps << RPM_REGULATOR_SMD_TYPE_SMPS | \
_support_vs << RPM_REGULATOR_SMD_TYPE_VS | \
_support_ncp << RPM_REGULATOR_SMD_TYPE_NCP, \
}
static struct rpm_regulator_param params[RPM_REGULATOR_PARAM_MAX] = {
/* ID LDO SMPS VS NCP name min max property-name */
PARAM(ENABLE, 1, 1, 1, 1, "swen", 0, 1, "qcom,init-enable"),
PARAM(VOLTAGE, 1, 1, 0, 1, "uv", 0, 0x7FFFFFF, "qcom,init-voltage"),
PARAM(CURRENT, 1, 1, 0, 0, "ma", 0, 0x1FFF, "qcom,init-current"),
PARAM(MODE_LDO, 1, 0, 0, 0, "lsmd", 0, 1, "qcom,init-ldo-mode"),
PARAM(MODE_SMPS, 0, 1, 0, 0, "ssmd", 0, 2, "qcom,init-smps-mode"),
PARAM(PIN_CTRL_ENABLE, 1, 1, 1, 0, "pcen", 0, 0xF, "qcom,init-pin-ctrl-enable"),
PARAM(PIN_CTRL_MODE, 1, 1, 1, 0, "pcmd", 0, 0x1F, "qcom,init-pin-ctrl-mode"),
PARAM(FREQUENCY, 0, 1, 0, 1, "freq", 0, 16, "qcom,init-frequency"),
PARAM(HEAD_ROOM, 1, 0, 0, 1, "hr", 0, 0x7FFFFFFF, "qcom,init-head-room"),
PARAM(QUIET_MODE, 0, 1, 0, 0, "qm", 0, 2, "qcom,init-quiet-mode"),
PARAM(FREQ_REASON, 0, 1, 0, 1, "resn", 0, 8, "qcom,init-freq-reason"),
};
struct rpm_vreg_request {
u32 param[RPM_REGULATOR_PARAM_MAX];
u32 valid;
u32 modified;
};
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;
int enable_time;
struct spinlock slock;
struct mutex mlock;
unsigned long flags;
bool sleep_request_sent;
struct msm_rpm_request *handle_active;
struct msm_rpm_request *handle_sleep;
};
struct rpm_regulator {
struct regulator_desc rdesc;
struct regulator_dev *rdev;
struct rpm_vreg *rpm_vreg;
struct list_head list;
bool set_active;
bool set_sleep;
struct rpm_vreg_request req;
int system_load;
int min_uV;
int max_uV;
};
/*
* 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)));
}
/*
* 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");
printk(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 *)&param[idx], 4);
else
return msm_rpm_add_kvp_data(handle, params[idx].key,
(u8 *)&param[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)
{
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;
if (rpm_vreg->allow_atomic)
rc = msm_rpm_wait_for_ack_noirq(msm_rpm_send_request_noirq(
handle));
else
rc = msm_rpm_wait_for_ack(msm_rpm_send_request(handle));
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_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]; \
}
/*
* The RPM treats freq=0 as a special value meaning that this consumer does not
* care what the SMPS switching freqency is.
*/
#define RPM_REGULATOR_FREQ_DONT_CARE 0
static inline void rpm_vreg_freqency_aggr(u32 *freq, u32 consumer_freq)
{
if (consumer_freq != RPM_REGULATOR_FREQ_DONT_CARE
&& (consumer_freq < *freq
|| *freq == RPM_REGULATOR_FREQ_DONT_CARE))
*freq = consumer_freq;
}
/*
* 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_freqency_aggr(&param_aggr[RPM_REGULATOR_PARAM_FREQUENCY],
param_reg[RPM_REGULATOR_PARAM_FREQUENCY]);
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);
}
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;
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) {
rc = rpm_vreg_send_request(regulator, RPM_SET_ACTIVE);
if (rc)
return rc;
rpm_vreg->aggr_req_active.valid |= modified_active;
}
/* 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) {
rc = rpm_vreg_send_request(regulator, RPM_SET_SLEEP);
if (rc)
return rc;
else
rpm_vreg->sleep_request_sent = true;
rpm_vreg->aggr_req_sleep.valid |= modified_sleep;
}
/* 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);
return reg->req.param[RPM_REGULATOR_PARAM_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);
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", rc);
RPM_VREG_SET_PARAM(reg, 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);
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, "enable failed, rc=%d", rc);
RPM_VREG_SET_PARAM(reg, ENABLE, prev_enable);
}
rpm_vreg_unlock(reg->rpm_vreg);
return rc;
}
static int rpm_vreg_set_voltage(struct regulator_dev *rdev, int min_uV,
int max_uV, unsigned *selector)
{
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
int rc = 0;
u32 prev_voltage;
rpm_vreg_lock(reg->rpm_vreg);
prev_voltage = reg->req.param[RPM_REGULATOR_PARAM_VOLTAGE];
RPM_VREG_SET_PARAM(reg, VOLTAGE, min_uV);
/* Only send a new voltage if the regulator is currently enabled. */
if (rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg))
rc = rpm_vreg_aggregate_requests(reg);
if (rc) {
vreg_err(reg, "set voltage failed, rc=%d", rc);
RPM_VREG_SET_PARAM(reg, VOLTAGE, 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[RPM_REGULATOR_PARAM_VOLTAGE];
if (uV == 0)
uV = VOLTAGE_UNKNOWN;
return uV;
}
static int rpm_vreg_list_voltage(struct regulator_dev *rdev, unsigned selector)
{
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
int uV = 0;
if (selector == 0)
uV = reg->min_uV;
else if (selector == 1)
uV = reg->max_uV;
return uV;
}
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 mode value if the regulator is currently enabled. */
if (rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg))
rc = rpm_vreg_aggregate_requests(reg);
if (rc) {
vreg_err(reg, "set mode failed, rc=%d", 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, MICRO_TO_MILLI(load_uA));
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_enable_time(struct regulator_dev *rdev)
{
struct rpm_regulator *reg = rdev_get_drvdata(rdev);
return reg->rpm_vreg->enable_time;
}
/**
* rpm_regulator_get() - lookup and obtain a handle to an RPM regulator
* @dev: device for regulator consumer
* @supply: supply name
*
* Returns a struct rpm_regulator corresponding to the regulator producer,
* or ERR_PTR() containing errno.
*
* This function may only be called from nonatomic context.
*/
struct rpm_regulator *rpm_regulator_get(struct device *dev, const char *supply)
{
struct rpm_regulator *framework_reg;
struct rpm_regulator *priv_reg = NULL;
struct regulator *regulator;
struct rpm_vreg *rpm_vreg;
regulator = regulator_get(dev, supply);
if (IS_ERR(regulator)) {
pr_err("could not find regulator for: dev=%s, supply=%s, rc=%ld\n",
(dev ? dev_name(dev) : ""), (supply ? supply : ""),
PTR_ERR(regulator));
return ERR_CAST(regulator);
}
framework_reg = regulator_get_drvdata(regulator);
if (framework_reg == NULL) {
pr_err("regulator structure not found.\n");
regulator_put(regulator);
return ERR_PTR(-ENODEV);
}
regulator_put(regulator);
rpm_vreg = framework_reg->rpm_vreg;
priv_reg = kzalloc(sizeof(struct rpm_regulator), GFP_KERNEL);
if (priv_reg == NULL) {
vreg_err(framework_reg, "could not allocate memory for "
"regulator\n");
rpm_vreg_unlock(rpm_vreg);
return ERR_PTR(-ENOMEM);
}
/*
* Allocate a regulator_dev struct so that framework callback functions
* can be called from the private API functions.
*/
priv_reg->rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL);
if (priv_reg->rdev == NULL) {
vreg_err(framework_reg, "could not allocate memory for "
"regulator_dev\n");
kfree(priv_reg);
rpm_vreg_unlock(rpm_vreg);
return ERR_PTR(-ENOMEM);
}
priv_reg->rdev->reg_data = priv_reg;
priv_reg->rpm_vreg = rpm_vreg;
priv_reg->rdesc.name = framework_reg->rdesc.name;
priv_reg->set_active = framework_reg->set_active;
priv_reg->set_sleep = framework_reg->set_sleep;
priv_reg->min_uV = framework_reg->min_uV;
priv_reg->max_uV = framework_reg->max_uV;
priv_reg->system_load = framework_reg->system_load;
might_sleep_if(!rpm_vreg->allow_atomic);
rpm_vreg_lock(rpm_vreg);
list_add(&priv_reg->list, &rpm_vreg->reg_list);
rpm_vreg_unlock(rpm_vreg);
return priv_reg;
}
EXPORT_SYMBOL_GPL(rpm_regulator_get);
static int rpm_regulator_check_input(struct rpm_regulator *regulator)
{
if (IS_ERR_OR_NULL(regulator) || regulator->rpm_vreg == NULL) {
pr_err("invalid rpm_regulator pointer\n");
return -EINVAL;
}
might_sleep_if(!regulator->rpm_vreg->allow_atomic);
return 0;
}
/**
* rpm_regulator_put() - free the RPM regulator handle
* @regulator: RPM regulator handle
*
* Parameter reaggregation does not take place when rpm_regulator_put is called.
* Therefore, regulator enable state and voltage must be configured
* appropriately before calling rpm_regulator_put.
*
* This function may be called from either atomic or nonatomic context. If this
* function is called from atomic context, then the regulator being operated on
* must be configured via device tree with qcom,allow-atomic == 1.
*/
void rpm_regulator_put(struct rpm_regulator *regulator)
{
struct rpm_vreg *rpm_vreg;
int rc = rpm_regulator_check_input(regulator);
if (rc)
return;
rpm_vreg = regulator->rpm_vreg;
might_sleep_if(!rpm_vreg->allow_atomic);
rpm_vreg_lock(rpm_vreg);
list_del(&regulator->list);
rpm_vreg_unlock(rpm_vreg);
kfree(regulator->rdev);
kfree(regulator);
}
EXPORT_SYMBOL_GPL(rpm_regulator_put);
/**
* rpm_regulator_enable() - enable regulator output
* @regulator: RPM regulator handle
*
* Returns 0 on success or errno on failure.
*
* This function may be called from either atomic or nonatomic context. If this
* function is called from atomic context, then the regulator being operated on
* must be configured via device tree with qcom,allow-atomic == 1.
*/
int rpm_regulator_enable(struct rpm_regulator *regulator)
{
int rc = rpm_regulator_check_input(regulator);
if (rc)
return rc;
return rpm_vreg_enable(regulator->rdev);
}
EXPORT_SYMBOL_GPL(rpm_regulator_enable);
/**
* rpm_regulator_disable() - disable regulator output
* @regulator: RPM regulator handle
*
* Returns 0 on success or errno on failure.
*
* The enable state of the regulator is determined by aggregating the requests
* of all consumers. Therefore, it is possible that the regulator will remain
* enabled even after rpm_regulator_disable is called.
*
* This function may be called from either atomic or nonatomic context. If this
* function is called from atomic context, then the regulator being operated on
* must be configured via device tree with qcom,allow-atomic == 1.
*/
int rpm_regulator_disable(struct rpm_regulator *regulator)
{
int rc = rpm_regulator_check_input(regulator);
if (rc)
return rc;
return rpm_vreg_disable(regulator->rdev);
}
EXPORT_SYMBOL_GPL(rpm_regulator_disable);
/**
* rpm_regulator_set_voltage() - set regulator output voltage
* @regulator: RPM regulator handle
* @min_uV: minimum required voltage in uV
* @max_uV: maximum acceptable voltage in uV
*
* Sets a voltage regulator to the desired output voltage. This can be set
* while the regulator is disabled or enabled. If the regulator is enabled then
* the voltage will change to the new value immediately; otherwise, if the
* regulator is disabled, then the regulator will output at the new voltage when
* enabled.
*
* The min_uV to max_uV voltage range requested must intersect with the
* voltage constraint range configured for the regulator.
*
* Returns 0 on success or errno on failure.
*
* The final voltage value that is sent to the RPM is aggregated based upon the
* values requested by all consumers of the regulator. This corresponds to the
* maximum min_uV value.
*
* This function may be called from either atomic or nonatomic context. If this
* function is called from atomic context, then the regulator being operated on
* must be configured via device tree with qcom,allow-atomic == 1.
*/
int rpm_regulator_set_voltage(struct rpm_regulator *regulator, int min_uV,
int max_uV)
{
int rc = rpm_regulator_check_input(regulator);
int uV = min_uV;
if (rc)
return rc;
if (regulator->rpm_vreg->regulator_type == RPM_REGULATOR_SMD_TYPE_VS) {
vreg_err(regulator, "unsupported regulator type: %d\n",
regulator->rpm_vreg->regulator_type);
return -EINVAL;
}
if (min_uV > max_uV) {
vreg_err(regulator, "min_uV=%d must be less than max_uV=%d\n",
min_uV, max_uV);
return -EINVAL;
}
if (uV < regulator->min_uV && max_uV >= regulator->min_uV)
uV = regulator->min_uV;
if (uV < regulator->min_uV || uV > regulator->max_uV) {
vreg_err(regulator, "request v=[%d, %d] is outside allowed "
"v=[%d, %d]\n", min_uV, max_uV, regulator->min_uV,
regulator->max_uV);
return -EINVAL;
}
return rpm_vreg_set_voltage(regulator->rdev, uV, uV, NULL);
}
EXPORT_SYMBOL_GPL(rpm_regulator_set_voltage);
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,
.list_voltage = rpm_vreg_list_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 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,
.list_voltage = rpm_vreg_list_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,
.list_voltage = rpm_vreg_list_voltage,
.enable_time = rpm_vreg_enable_time,
};
static struct regulator_ops *vreg_ops[] = {
[RPM_REGULATOR_SMD_TYPE_LDO] = &ldo_ops,
[RPM_REGULATOR_SMD_TYPE_SMPS] = &smps_ops,
[RPM_REGULATOR_SMD_TYPE_VS] = &switch_ops,
[RPM_REGULATOR_SMD_TYPE_NCP] = &ncp_ops,
};
static int __devexit rpm_vreg_device_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rpm_regulator *reg;
reg = platform_get_drvdata(pdev);
if (reg) {
rpm_vreg_lock(reg->rpm_vreg);
regulator_unregister(reg->rdev);
list_del(&reg->list);
kfree(reg);
rpm_vreg_unlock(reg->rpm_vreg);
} else {
dev_err(dev, "%s: drvdata missing\n", __func__);
return -EINVAL;
}
platform_set_drvdata(pdev, NULL);
return 0;
}
static int __devexit 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(&reg->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;
}
/*
* 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 __devinit 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;
int rc = 0;
int i, regulator_type;
u32 val;
if (!dev->of_node) {
dev_err(dev, "%s: device tree information missing\n", __func__);
return -ENODEV;
}
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(struct rpm_regulator), GFP_KERNEL);
if (reg == NULL) {
dev_err(dev, "%s: could not allocate memory for reg\n",
__func__);
return -ENOMEM;
}
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;
if (regulator_type == RPM_REGULATOR_SMD_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);
init_data = of_get_regulator_init_data(dev, node);
if (init_data == NULL) {
dev_err(dev, "%s: unable to allocate memory\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", &reg->system_load);
rpm_vreg_lock(rpm_vreg);
list_add(&reg->list, &rpm_vreg->reg_list);
rpm_vreg_unlock(rpm_vreg);
reg->rdev = regulator_register(&reg->rdesc, dev, init_data, reg, node);
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;
}
platform_set_drvdata(pdev, reg);
pr_debug("successfully probed: %s\n", reg->rdesc.name);
return 0;
fail_remove_from_list:
rpm_vreg_lock(rpm_vreg);
list_del(&reg->list);
rpm_vreg_unlock(rpm_vreg);
fail_free_reg:
kfree(reg);
return rc;
}
/*
* This probe is called for parent rpm-regulator devices which have
* properties which are required to identify a given RPM resource.
*/
static int __devinit 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;
int val = 0;
u32 resource_type;
int rc;
if (!dev->of_node) {
dev_err(dev, "%s: device tree information missing\n", __func__);
return -ENODEV;
}
/* Create new rpm_vreg entry. */
rpm_vreg = kzalloc(sizeof(struct rpm_vreg), GFP_KERNEL);
if (rpm_vreg == NULL) {
dev_err(dev, "%s: could not allocate memory for vreg\n",
__func__);
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_SMD_TYPE_MAX)) {
dev_err(dev, "%s: invalid regulator type: %d\n", __func__,
rpm_vreg->regulator_type);
rc = -EINVAL;
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->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);
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);
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 struct of_device_id rpm_vreg_match_table_device[] = {
{ .compatible = "qcom,rpm-regulator-smd", },
{}
};
static struct of_device_id rpm_vreg_match_table_resource[] = {
{ .compatible = "qcom,rpm-regulator-smd-resource", },
{}
};
static struct platform_driver rpm_vreg_device_driver = {
.probe = rpm_vreg_device_probe,
.remove = __devexit_p(rpm_vreg_device_remove),
.driver = {
.name = "qcom,rpm-regulator-smd",
.owner = THIS_MODULE,
.of_match_table = rpm_vreg_match_table_device,
},
};
static struct platform_driver rpm_vreg_resource_driver = {
.probe = rpm_vreg_resource_probe,
.remove = __devexit_p(rpm_vreg_resource_remove),
.driver = {
.name = "qcom,rpm-regulator-smd-resource",
.owner = THIS_MODULE,
.of_match_table = rpm_vreg_match_table_resource,
},
};
/**
* rpm_regulator_smd_driver_init() - initialized SMD RPM regulator driver
*
* This function registers the SMD RPM regulator platform drivers.
*
* Returns 0 on success or errno on failure.
*/
int __init rpm_regulator_smd_driver_init(void)
{
static bool initialized;
int i, rc;
if (initialized)
return 0;
else
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);
}
EXPORT_SYMBOL_GPL(rpm_regulator_smd_driver_init);
static void __exit rpm_vreg_exit(void)
{
platform_driver_unregister(&rpm_vreg_device_driver);
platform_driver_unregister(&rpm_vreg_resource_driver);
}
module_init(rpm_regulator_smd_driver_init);
module_exit(rpm_vreg_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MSM SMD RPM regulator driver");