msm: Add rpm-regulator-smd driver to support RPM regulators over SMD
The rpm-regulator-smd driver makes use of a key based RPM
resource interface in order to control regulators. Regulator
requests are sent to the RPM over the SMD transport layer.
Provide control of regulators in both the active and sleep sets.
Active set regulator requests take effect immediately. Sleep set
requests take effect when the Apps processor transitions into
RPM assisted power collapse.
Allow for all request parameters to be initialized based upon
configuration data specified in device tree.
Include an API which allows for regulator control from atomic
context in order to support all consumer use cases.
Change-Id: I0417d52bba9eb053b6c552b287aa658d6c8e4b7f
Signed-off-by: David Collins <collinsd@codeaurora.org>
diff --git a/arch/arm/mach-msm/rpm-regulator-smd.c b/arch/arm/mach-msm/rpm-regulator-smd.c
new file mode 100644
index 0000000..b892d05
--- /dev/null
+++ b/arch/arm/mach-msm/rpm-regulator-smd.c
@@ -0,0 +1,1430 @@
+/* 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 *)¶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)
+{
+ 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(¶m_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 (regulator == NULL) {
+ pr_err("could not find regulator for: dev=%s, id=%s\n",
+ (dev ? dev_name(dev) : ""), (supply ? supply : ""));
+ return ERR_PTR(-ENODEV);
+ }
+
+ 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 (regulator == NULL || 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(®ulator->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(®->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(®->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);
+ 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", ®->system_load);
+
+ rpm_vreg_lock(rpm_vreg);
+ list_add(®->list, &rpm_vreg->reg_list);
+ rpm_vreg_unlock(rpm_vreg);
+
+ reg->rdev = regulator_register(®->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(®->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");