| // SPDX-License-Identifier: GPL-2.0-only |
| /* Copyright (c) 2012-2015, 2018-2020, 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 <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; |
| |
| |
| #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 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_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_smps_mode { |
| RPM_REGULATOR_SMPS_MODE_AUTO = 0, |
| RPM_REGULATOR_SMPS_MODE_IPEAK = 1, |
| RPM_REGULATOR_SMPS_MODE_PWM = 2, |
| }; |
| |
| enum rpm_regulator_ldo_mode { |
| RPM_REGULATOR_LDO_MODE_IPEAK = 0, |
| RPM_REGULATOR_LDO_MODE_HPM = 1, |
| }; |
| |
| enum rpm_regulator_bob_mode { |
| RPM_REGULATOR_BOB_MODE_PASS = 0, |
| RPM_REGULATOR_BOB_MODE_PFM = 1, |
| RPM_REGULATOR_BOB_MODE_AUTO = 2, |
| RPM_REGULATOR_BOB_MODE_PWM = 3, |
| }; |
| |
| #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"), |
| }; |
| |
| struct rpm_regulator_mode_map { |
| int ldo_mode; |
| int smps_mode; |
| }; |
| |
| static struct rpm_regulator_mode_map mode_mapping[] = { |
| [RPM_REGULATOR_MODE_AUTO] |
| = {-1, RPM_REGULATOR_SMPS_MODE_AUTO}, |
| [RPM_REGULATOR_MODE_IPEAK] |
| = {RPM_REGULATOR_LDO_MODE_IPEAK, RPM_REGULATOR_SMPS_MODE_IPEAK}, |
| [RPM_REGULATOR_MODE_HPM] |
| = {RPM_REGULATOR_LDO_MODE_HPM, RPM_REGULATOR_SMPS_MODE_PWM}, |
| }; |
| |
| /* 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_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; |
| 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 prev_mode; |
| |
| rpm_vreg_lock(reg->rpm_vreg); |
| |
| prev_mode = reg->req.param[RPM_REGULATOR_PARAM_MODE_BOB]; |
| |
| switch (mode) { |
| case REGULATOR_MODE_FAST: |
| RPM_VREG_SET_PARAM(reg, MODE_BOB, RPM_REGULATOR_BOB_MODE_PWM); |
| break; |
| case REGULATOR_MODE_NORMAL: |
| RPM_VREG_SET_PARAM(reg, MODE_BOB, RPM_REGULATOR_BOB_MODE_AUTO); |
| break; |
| case REGULATOR_MODE_IDLE: |
| RPM_VREG_SET_PARAM(reg, MODE_BOB, RPM_REGULATOR_BOB_MODE_PFM); |
| break; |
| case REGULATOR_MODE_STANDBY: |
| RPM_VREG_SET_PARAM(reg, MODE_BOB, RPM_REGULATOR_BOB_MODE_PASS); |
| break; |
| default: |
| vreg_err(reg, "invalid mode: %u\n", mode); |
| rpm_vreg_unlock(reg->rpm_vreg); |
| return -EINVAL; |
| } |
| |
| 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; |
| |
| switch (reg->req.param[RPM_REGULATOR_PARAM_MODE_BOB]) { |
| case RPM_REGULATOR_BOB_MODE_PWM: |
| mode = REGULATOR_MODE_FAST; |
| break; |
| case RPM_REGULATOR_BOB_MODE_AUTO: |
| mode = REGULATOR_MODE_NORMAL; |
| break; |
| case RPM_REGULATOR_BOB_MODE_PFM: |
| mode = REGULATOR_MODE_IDLE; |
| break; |
| case RPM_REGULATOR_BOB_MODE_PASS: |
| mode = REGULATOR_MODE_STANDBY; |
| break; |
| default: |
| vreg_err(reg, "BoB mode unknown\n"); |
| mode = REGULATOR_MODE_NORMAL; |
| } |
| |
| return mode; |
| } |
| |
| 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; |
| } |
| |
| /** |
| * 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(*priv_reg), GFP_KERNEL); |
| if (priv_reg == NULL) |
| 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) { |
| kfree(priv_reg); |
| 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->rdesc.ops = framework_reg->rdesc.ops; |
| 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(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(®ulator->list); |
| rpm_vreg_unlock(rpm_vreg); |
| |
| kfree(regulator->rdev); |
| kfree(regulator); |
| } |
| EXPORT_SYMBOL(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(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(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_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 regulator->rdesc.ops->set_voltage(regulator->rdev, uV, uV, NULL); |
| } |
| EXPORT_SYMBOL(rpm_regulator_set_voltage); |
| |
| /** |
| * rpm_regulator_set_mode() - set regulator operating mode |
| * @regulator: RPM regulator handle |
| * @mode: operating mode requested for the regulator |
| * |
| * Requests that the mode of the regulator be set to the mode specified. This |
| * parameter is aggregated using a max function such that AUTO < IPEAK < HPM. |
| * |
| * Returns 0 on success or errno on failure. |
| */ |
| int rpm_regulator_set_mode(struct rpm_regulator *regulator, |
| enum rpm_regulator_mode mode) |
| { |
| int index = 0; |
| u32 new_mode, prev_mode; |
| int rc; |
| |
| rc = rpm_regulator_check_input(regulator); |
| if (rc) |
| return rc; |
| |
| if (mode < 0 || mode >= ARRAY_SIZE(mode_mapping)) { |
| vreg_err(regulator, "invalid mode requested: %d\n", mode); |
| return -EINVAL; |
| } |
| |
| switch (regulator->rpm_vreg->regulator_type) { |
| case RPM_REGULATOR_TYPE_SMPS: |
| index = RPM_REGULATOR_PARAM_MODE_SMPS; |
| new_mode = mode_mapping[mode].smps_mode; |
| break; |
| case RPM_REGULATOR_TYPE_LDO: |
| index = RPM_REGULATOR_PARAM_MODE_LDO; |
| new_mode = mode_mapping[mode].ldo_mode; |
| break; |
| default: |
| vreg_err(regulator, "unsupported regulator type: %d\n", |
| regulator->rpm_vreg->regulator_type); |
| return -EINVAL; |
| } |
| |
| if (new_mode < params[index].min || new_mode > params[index].max) { |
| vreg_err(regulator, "invalid mode requested: %d for type: %d\n", |
| mode, regulator->rpm_vreg->regulator_type); |
| return -EINVAL; |
| } |
| |
| rpm_vreg_lock(regulator->rpm_vreg); |
| |
| prev_mode = regulator->req.param[index]; |
| regulator->req.param[index] = new_mode; |
| regulator->req.modified |= BIT(index); |
| |
| rc = rpm_vreg_aggregate_requests(regulator); |
| if (rc) { |
| vreg_err(regulator, "set mode failed, rc=%d", rc); |
| regulator->req.param[index] = prev_mode; |
| } |
| |
| rpm_vreg_unlock(regulator->rpm_vreg); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(rpm_regulator_set_mode); |
| |
| 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_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); |
| 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; |
| } |
| |
| |
| /* |
| * 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); |
| |
| 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; |
| } |
| |
| 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 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; |
| |
| |
| /* 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; |
| } |
| } |
| |
| /* 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"); |
| |
| 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, |
| }, |
| }; |
| |
| /** |
| * 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. |
| */ |
| 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); |
| } |
| EXPORT_SYMBOL(rpm_smd_regulator_driver_init); |
| |
| 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"); |