| /* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 and |
| * only version 2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #define pr_fmt(fmt) "%s: " fmt, __func__ |
| |
| #include <linux/bitops.h> |
| #include <linux/err.h> |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/of.h> |
| #include <linux/of_device.h> |
| #include <linux/platform_device.h> |
| #include <linux/slab.h> |
| #include <linux/string.h> |
| #include <linux/regulator/driver.h> |
| #include <linux/regulator/machine.h> |
| #include <linux/regulator/of_regulator.h> |
| #include <soc/qcom/cmd-db.h> |
| #include <soc/qcom/rpmh.h> |
| #include <dt-bindings/regulator/qcom,rpmh-regulator.h> |
| |
| /** |
| * enum rpmh_regulator_type - supported RPMh accelerator types |
| * %RPMH_REGULATOR_TYPE_VRM: RPMh VRM accelerator which supports voting on |
| * enable, voltage, mode, and headroom voltage of |
| * LDO, SMPS, VS, and BOB type PMIC regulators. |
| * %RPMH_REGULATOR_TYPE_ARC: RPMh ARC accelerator which supports voting on |
| * the CPR managed voltage level of LDO and SMPS |
| * type PMIC regulators. |
| * %RPMH_REGULATOR_TYPE_XOB: RPMh XOB accelerator which supports voting on |
| * the enable state of PMIC regulators. |
| */ |
| enum rpmh_regulator_type { |
| RPMH_REGULATOR_TYPE_VRM, |
| RPMH_REGULATOR_TYPE_ARC, |
| RPMH_REGULATOR_TYPE_XOB, |
| }; |
| |
| /** |
| * enum rpmh_regulator_reg_index - RPMh accelerator register indices |
| * %RPMH_REGULATOR_REG_VRM_VOLTAGE: VRM voltage voting register index |
| * %RPMH_REGULATOR_REG_ARC_LEVEL: ARC voltage level voting register index |
| * %RPMH_REGULATOR_REG_VRM_ENABLE: VRM enable voltage voting register index |
| * %RPMH_REGULATOR_REG_ARC_PSEUDO_ENABLE: Place-holder for enable aggregation. |
| * ARC does not have a specific register |
| * for enable voting. Instead, ARC level |
| * 0 corresponds to "disabled" for a given |
| * ARC regulator resource if supported. |
| * %RPMH_REGULATOR_REG_XOB_ENABLE: XOB enable voting register index |
| * %RPMH_REGULATOR_REG_ENABLE: Common enable index used in callback |
| * functions for both ARC and VRM. |
| * %RPMH_REGULATOR_REG_VRM_MODE: VRM regulator mode voting register index |
| * %RPMH_REGULATOR_REG_VRM_HEADROOM: VRM headroom voltage voting register |
| * index |
| * %RPMH_REGULATOR_REG_ARC_REAL_MAX: Upper limit of real existent ARC |
| * register indices |
| * %RPMH_REGULATOR_REG_ARC_MAX: Exclusive upper limit of ARC register |
| * indices |
| * %RPMH_REGULATOR_REG_XOB_MAX: Exclusive upper limit of XOB register |
| * indices |
| * %RPMH_REGULATOR_REG_VRM_MAX: Exclusive upper limit of VRM register |
| * indices |
| * %RPMH_REGULATOR_REG_MAX: Combined exclusive upper limit of ARC |
| * and VRM register indices |
| * |
| * Register addresses are calculated as: base_addr + sizeof(u32) * reg_index |
| */ |
| enum rpmh_regulator_reg_index { |
| RPMH_REGULATOR_REG_VRM_VOLTAGE = 0, |
| RPMH_REGULATOR_REG_ARC_LEVEL = 0, |
| RPMH_REGULATOR_REG_VRM_ENABLE = 1, |
| RPMH_REGULATOR_REG_ARC_PSEUDO_ENABLE = RPMH_REGULATOR_REG_VRM_ENABLE, |
| RPMH_REGULATOR_REG_XOB_ENABLE = RPMH_REGULATOR_REG_VRM_ENABLE, |
| RPMH_REGULATOR_REG_ENABLE = RPMH_REGULATOR_REG_VRM_ENABLE, |
| RPMH_REGULATOR_REG_VRM_MODE = 2, |
| RPMH_REGULATOR_REG_VRM_HEADROOM = 3, |
| RPMH_REGULATOR_REG_ARC_REAL_MAX = 1, |
| RPMH_REGULATOR_REG_ARC_MAX = 2, |
| RPMH_REGULATOR_REG_XOB_MAX = 2, |
| RPMH_REGULATOR_REG_VRM_MAX = 4, |
| RPMH_REGULATOR_REG_MAX = 4, |
| }; |
| |
| /* |
| * This is the number of bytes used for each command DB aux data entry of an |
| * ARC resource. |
| */ |
| #define RPMH_ARC_LEVEL_SIZE 2 |
| |
| /* |
| * This is the maximum number of voltage levels that may be defined for an ARC |
| * resource. |
| */ |
| #define RPMH_ARC_MAX_LEVELS 16 |
| |
| /* Min and max limits of VRM resource request parameters */ |
| #define RPMH_VRM_MIN_UV 0 |
| #define RPMH_VRM_MAX_UV 8191000 |
| |
| #define RPMH_VRM_HEADROOM_MIN_UV 0 |
| #define RPMH_VRM_HEADROOM_MAX_UV 511000 |
| |
| #define RPMH_VRM_MODE_MIN 0 |
| #define RPMH_VRM_MODE_MAX 7 |
| |
| /* XOB voting registers are found in the VRM hardware module */ |
| #define CMD_DB_HW_XOB CMD_DB_HW_VRM |
| |
| /* |
| * Mapping from RPMh VRM accelerator modes to regulator framework modes |
| * Assumes that SMPS PFM mode == LDO LPM mode and SMPS PWM mode == LDO HPM mode |
| */ |
| static const int rpmh_regulator_mode_map[] = { |
| [RPMH_REGULATOR_MODE_SMPS_PFM] = REGULATOR_MODE_IDLE, |
| [RPMH_REGULATOR_MODE_SMPS_AUTO] = REGULATOR_MODE_NORMAL, |
| [RPMH_REGULATOR_MODE_SMPS_PWM] = REGULATOR_MODE_FAST, |
| [RPMH_REGULATOR_MODE_BOB_PASS] = REGULATOR_MODE_STANDBY, |
| [RPMH_REGULATOR_MODE_BOB_PFM] = REGULATOR_MODE_IDLE, |
| [RPMH_REGULATOR_MODE_BOB_AUTO] = REGULATOR_MODE_NORMAL, |
| [RPMH_REGULATOR_MODE_BOB_PWM] = REGULATOR_MODE_FAST, |
| }; |
| |
| /** |
| * struct rpmh_regulator_request - rpmh request data |
| * @reg: Array of RPMh accelerator register values |
| * @valid: Bitmask identifying which of the register values |
| * are valid/initialized |
| */ |
| struct rpmh_regulator_request { |
| u32 reg[RPMH_REGULATOR_REG_MAX]; |
| u32 valid; |
| }; |
| |
| /** |
| * struct rpmh_regulator_mode - RPMh VRM mode attributes |
| * @pmic_mode: Raw PMIC mode value written into VRM mode voting |
| * register (i.e. RPMH_REGULATOR_MODE_*) |
| * @framework_mode: Regulator framework mode value |
| * (i.e. REGULATOR_MODE_*) |
| * @min_load_ua: The minimum load current in microamps which |
| * would utilize this mode |
| * |
| * Software selects the lowest mode for which aggr_load_ua >= min_load_ua. |
| */ |
| struct rpmh_regulator_mode { |
| u32 pmic_mode; |
| u32 framework_mode; |
| int min_load_ua; |
| }; |
| |
| struct rpmh_vreg; |
| |
| /** |
| * struct rpmh_aggr_vreg - top level aggregated rpmh regulator resource data |
| * structure |
| * @dev: Device pointer to the rpmh aggregated regulator |
| * device |
| * @resource_name: Name of rpmh regulator resource which is mapped |
| * to an RPMh accelerator address via command DB. |
| * This name must match to one that is defined by |
| * the bootloader. |
| * @addr: Base address of the regulator resource within |
| * an RPMh accelerator |
| * @rpmh_client: Handle used for rpmh communications |
| * @lock: Mutex lock used for locking between regulators |
| * common to a single aggregated resource |
| * @regulator_type: RPMh accelerator type for this regulator |
| * resource |
| * @level: Mapping from ARC resource specific voltage |
| * levels (0 to RPMH_ARC_MAX_LEVELS - 1) to common |
| * consumer voltage levels (i.e. |
| * RPMH_REGULATOR_LEVEL_*). These values are read |
| * out of the AUX data found in command DB for a |
| * given ARC resource. Note that the values in |
| * this array are equal to those read from AUX data |
| * + RPMH_REGULATOR_LEVEL_OFFSET. |
| * @level_count: The number of valid entries in the level array |
| * @always_wait_for_ack: Boolean flag indicating if a request must always |
| * wait for an ACK from RPMh before continuing even |
| * if it corresponds to a strictly lower power |
| * state (e.g. enabled --> disabled). |
| * @next_wait_for_ack: Boolean flag indicating that the next request |
| * sent must wait for an ACK. This is used to |
| * ensure that the driver waits for the voltage to |
| * slew down in the case that the requested max_uV |
| * value is lower than the last requested voltage. |
| * @sleep_request_sent: Boolean flag indicating that a sleep set request |
| * has been sent at some point due to it diverging |
| * from the active set request. After that point, |
| * the sleep set requests must always be sent for |
| * a given resource. |
| * @use_awake_state: Boolean flag indicating that active set requests |
| * should be made using the awake state instead of |
| * the active-only state. This should be used for |
| * RSC's which do not have an AMC. |
| * @vreg: Array of rpmh regulator structs representing the |
| * individual regulators sharing the aggregated |
| * regulator resource. |
| * @vreg_count: The number of entries in the vreg array. |
| * @mode: An array of modes supported by an RPMh VRM |
| * regulator resource. |
| * @mode_count: The number of entries in the mode array. |
| * @aggr_req_active: Aggregated active set RPMh accelerator register |
| * request |
| * @aggr_req_sleep: Aggregated sleep set RPMh accelerator register |
| * request |
| */ |
| struct rpmh_aggr_vreg { |
| struct device *dev; |
| const char *resource_name; |
| u32 addr; |
| struct rpmh_client *rpmh_client; |
| struct mutex lock; |
| enum rpmh_regulator_type regulator_type; |
| u32 level[RPMH_ARC_MAX_LEVELS]; |
| int level_count; |
| bool always_wait_for_ack; |
| bool next_wait_for_ack; |
| bool sleep_request_sent; |
| bool use_awake_state; |
| struct rpmh_vreg *vreg; |
| int vreg_count; |
| struct rpmh_regulator_mode *mode; |
| int mode_count; |
| struct rpmh_regulator_request aggr_req_active; |
| struct rpmh_regulator_request aggr_req_sleep; |
| }; |
| |
| /** |
| * struct rpmh_vreg - individual rpmh regulator data structure encapsulating a |
| * regulator framework regulator device and its corresponding |
| * rpmh request |
| * @of_node: Device node pointer for the individual rpmh |
| * regulator |
| * @name: Name of the regulator |
| * @rdesc: Regulator descriptor |
| * @rdev: Regulator device pointer returned by |
| * devm_regulator_register() |
| * @aggr_vreg: Pointer to the aggregated rpmh regulator |
| * resource |
| * @set_active: Boolean flag indicating that requests made by |
| * this regulator should take affect in the active |
| * set |
| * @set_sleep: Boolean flag indicating that requests made by |
| * this regulator should take affect in the sleep |
| * set |
| * @req: RPMh accelerator register request |
| * @mode_index: RPMh VRM regulator mode selected by index into |
| * aggr_vreg->mode |
| */ |
| struct rpmh_vreg { |
| struct device_node *of_node; |
| struct regulator_desc rdesc; |
| struct regulator_dev *rdev; |
| struct rpmh_aggr_vreg *aggr_vreg; |
| bool set_active; |
| bool set_sleep; |
| struct rpmh_regulator_request req; |
| int mode_index; |
| }; |
| |
| /* |
| * 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 |
| |
| #define vreg_err(vreg, message, ...) \ |
| pr_err("%s: " message, (vreg)->rdesc.name, ##__VA_ARGS__) |
| #define vreg_info(vreg, message, ...) \ |
| pr_info("%s: " message, (vreg)->rdesc.name, ##__VA_ARGS__) |
| #define vreg_debug(vreg, message, ...) \ |
| pr_debug("%s: " message, (vreg)->rdesc.name, ##__VA_ARGS__) |
| |
| #define aggr_vreg_err(aggr_vreg, message, ...) \ |
| pr_err("%s: " message, (aggr_vreg)->resource_name, ##__VA_ARGS__) |
| #define aggr_vreg_info(aggr_vreg, message, ...) \ |
| pr_info("%s: " message, (aggr_vreg)->resource_name, ##__VA_ARGS__) |
| #define aggr_vreg_debug(aggr_vreg, message, ...) \ |
| pr_debug("%s: " message, (aggr_vreg)->resource_name, ##__VA_ARGS__) |
| |
| #define DEBUG_PRINT_BUFFER_SIZE 256 |
| static const char *const rpmh_regulator_state_names[] = { |
| [RPMH_SLEEP_STATE] = "sleep ", |
| [RPMH_WAKE_ONLY_STATE] = "wake ", |
| [RPMH_ACTIVE_ONLY_STATE] = "active", |
| [RPMH_AWAKE_STATE] = "awake ", |
| }; |
| |
| static const char *const rpmh_regulator_vrm_param_names[] = { |
| [RPMH_REGULATOR_REG_VRM_VOLTAGE] = "mv", |
| [RPMH_REGULATOR_REG_VRM_ENABLE] = "en", |
| [RPMH_REGULATOR_REG_VRM_MODE] = "mode", |
| [RPMH_REGULATOR_REG_VRM_HEADROOM] = "hr_mv", |
| }; |
| |
| static const char *const rpmh_regulator_arc_param_names[] = { |
| [RPMH_REGULATOR_REG_ARC_LEVEL] = "hlvl", |
| }; |
| |
| static const char *const rpmh_regulator_xob_param_names[] = { |
| [RPMH_REGULATOR_REG_XOB_ENABLE] = "en", |
| }; |
| |
| /** |
| * rpmh_regulator_req() - print the rpmh regulator request to the kernel log |
| * @vreg: Pointer to the RPMh regulator |
| * @current_req: Pointer to the new request |
| * @prev_req: Pointer to the last request |
| * @sent_mask: Bitmask which specifies the parameters sent in this |
| * request |
| * @state: The rpmh state that the request was sent for |
| * |
| * Return: none |
| */ |
| static void rpmh_regulator_req(struct rpmh_vreg *vreg, |
| struct rpmh_regulator_request *current_req, |
| struct rpmh_regulator_request *prev_req, |
| u32 sent_mask, |
| enum rpmh_state state) |
| { |
| struct rpmh_aggr_vreg *aggr_vreg = vreg->aggr_vreg; |
| char buf[DEBUG_PRINT_BUFFER_SIZE]; |
| size_t buflen = DEBUG_PRINT_BUFFER_SIZE; |
| const char *const *param_name; |
| int i, max_reg_index; |
| int pos = 0; |
| u32 valid; |
| bool first; |
| |
| switch (aggr_vreg->regulator_type) { |
| case RPMH_REGULATOR_TYPE_VRM: |
| max_reg_index = RPMH_REGULATOR_REG_VRM_MAX; |
| param_name = rpmh_regulator_vrm_param_names; |
| break; |
| case RPMH_REGULATOR_TYPE_ARC: |
| max_reg_index = RPMH_REGULATOR_REG_ARC_REAL_MAX; |
| param_name = rpmh_regulator_arc_param_names; |
| break; |
| case RPMH_REGULATOR_TYPE_XOB: |
| max_reg_index = RPMH_REGULATOR_REG_XOB_MAX; |
| param_name = rpmh_regulator_xob_param_names; |
| break; |
| default: |
| return; |
| } |
| |
| pos += scnprintf(buf + pos, buflen - pos, |
| "%s (%s), addr=0x%05X: s=%s; sent: ", |
| aggr_vreg->resource_name, vreg->rdesc.name, |
| aggr_vreg->addr, rpmh_regulator_state_names[state]); |
| |
| valid = sent_mask; |
| first = true; |
| for (i = 0; i < max_reg_index; i++) { |
| if (valid & BIT(i)) { |
| pos += scnprintf(buf + pos, buflen - pos, "%s%s=%u", |
| (first ? "" : ", "), param_name[i], |
| current_req->reg[i]); |
| first = false; |
| if (aggr_vreg->regulator_type |
| == RPMH_REGULATOR_TYPE_ARC |
| && i == RPMH_REGULATOR_REG_ARC_LEVEL) |
| pos += scnprintf(buf + pos, buflen - pos, |
| " (vlvl=%u)", |
| aggr_vreg->level[current_req->reg[i]]); |
| } |
| } |
| |
| valid = prev_req->valid & ~sent_mask; |
| |
| if (valid) |
| pos += scnprintf(buf + pos, buflen - pos, "; prev: "); |
| first = true; |
| for (i = 0; i < max_reg_index; i++) { |
| if (valid & BIT(i)) { |
| pos += scnprintf(buf + pos, buflen - pos, "%s%s=%u", |
| (first ? "" : ", "), param_name[i], |
| current_req->reg[i]); |
| first = false; |
| if (aggr_vreg->regulator_type |
| == RPMH_REGULATOR_TYPE_ARC |
| && i == RPMH_REGULATOR_REG_ARC_LEVEL) |
| pos += scnprintf(buf + pos, buflen - pos, |
| " (vlvl=%u)", |
| aggr_vreg->level[current_req->reg[i]]); |
| } |
| } |
| |
| pr_debug("%s\n", buf); |
| } |
| |
| /** |
| * rpmh_regulator_handle_arc_enable() - handle masking of the voltage level |
| * request based on the pseudo-enable value |
| * @aggr_vreg: Pointer to the aggregated rpmh regulator resource |
| * @req Pointer to the newly aggregated request |
| * |
| * Return: none |
| */ |
| static void rpmh_regulator_handle_arc_enable(struct rpmh_aggr_vreg *aggr_vreg, |
| struct rpmh_regulator_request *req) |
| { |
| if (aggr_vreg->regulator_type != RPMH_REGULATOR_TYPE_ARC) |
| return; |
| |
| /* |
| * Mask the voltage level if "off" level is supported and the regulator |
| * has not been enabled. |
| */ |
| if (aggr_vreg->level[0] == RPMH_REGULATOR_LEVEL_OFF) { |
| if (req->valid & BIT(RPMH_REGULATOR_REG_ARC_PSEUDO_ENABLE)) { |
| if (!req->reg[RPMH_REGULATOR_REG_ARC_PSEUDO_ENABLE]) |
| req->reg[RPMH_REGULATOR_REG_ARC_LEVEL] = 0; |
| } else { |
| /* Invalidate voltage level if enable is invalid. */ |
| req->valid &= ~BIT(RPMH_REGULATOR_REG_ARC_LEVEL); |
| } |
| } |
| |
| /* |
| * Mark the pseudo enable bit as invalid so that it is not accidentally |
| * included in an RPMh command. |
| */ |
| req->valid &= ~BIT(RPMH_REGULATOR_REG_ARC_PSEUDO_ENABLE); |
| } |
| |
| /** |
| * rpmh_regulator_send_aggregate_requests() - aggregate the requests from all |
| * regulators associated with an RPMh resource and send the request |
| * to RPMh |
| * @vreg: Pointer to the RPMh regulator |
| * |
| * This function aggregates the requests from the different regulators |
| * associated with the aggr_vreg resource independently in both the active set |
| * and sleep set. The requests are only sent for the sleep set if they differ, |
| * or have differed in the past, from those of the active set. |
| * |
| * Return: 0 on success, errno on failure |
| */ |
| static int |
| rpmh_regulator_send_aggregate_requests(struct rpmh_vreg *vreg) |
| { |
| struct rpmh_aggr_vreg *aggr_vreg = vreg->aggr_vreg; |
| struct rpmh_regulator_request req_active = { {0} }; |
| struct rpmh_regulator_request req_sleep = { {0} }; |
| struct tcs_cmd cmd[RPMH_REGULATOR_REG_MAX] = { {0} }; |
| bool sleep_set_differs = aggr_vreg->sleep_request_sent; |
| bool wait_for_ack = aggr_vreg->always_wait_for_ack |
| || aggr_vreg->next_wait_for_ack; |
| bool resend_active = false; |
| int i, j, max_reg_index, rc; |
| enum rpmh_state state; |
| u32 sent_mask; |
| |
| switch (aggr_vreg->regulator_type) { |
| case RPMH_REGULATOR_TYPE_VRM: |
| max_reg_index = RPMH_REGULATOR_REG_VRM_MAX; |
| break; |
| case RPMH_REGULATOR_TYPE_ARC: |
| max_reg_index = RPMH_REGULATOR_REG_ARC_MAX; |
| break; |
| case RPMH_REGULATOR_TYPE_XOB: |
| max_reg_index = RPMH_REGULATOR_REG_XOB_MAX; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| /* |
| * Perform max aggregration of each register value across all regulators |
| * which use this RPMh resource. |
| */ |
| for (i = 0; i < aggr_vreg->vreg_count; i++) { |
| if (aggr_vreg->vreg[i].set_active) { |
| for (j = 0; j < max_reg_index; j++) |
| req_active.reg[j] = max(req_active.reg[j], |
| aggr_vreg->vreg[i].req.reg[j]); |
| req_active.valid |= aggr_vreg->vreg[i].req.valid; |
| } |
| if (aggr_vreg->vreg[i].set_sleep) { |
| for (j = 0; j < max_reg_index; j++) |
| req_sleep.reg[j] = max(req_sleep.reg[j], |
| aggr_vreg->vreg[i].req.reg[j]); |
| req_sleep.valid |= aggr_vreg->vreg[i].req.valid; |
| } |
| } |
| |
| rpmh_regulator_handle_arc_enable(aggr_vreg, &req_active); |
| rpmh_regulator_handle_arc_enable(aggr_vreg, &req_sleep); |
| |
| /* |
| * Check if the aggregated sleep set parameter values differ from the |
| * aggregated active set parameter values. |
| */ |
| if (!aggr_vreg->sleep_request_sent) { |
| for (i = 0; i < max_reg_index; i++) { |
| if ((req_active.reg[i] != req_sleep.reg[i]) |
| && (req_sleep.valid & BIT(i))) { |
| sleep_set_differs = true; |
| /* |
| * Resend full active set request so that |
| * all parameters are specified in the wake-only |
| * state request. |
| */ |
| resend_active = !aggr_vreg->use_awake_state; |
| break; |
| } |
| } |
| } |
| |
| if (sleep_set_differs) { |
| /* |
| * Generate an rpmh command consisting of only those registers |
| * which have new values or which have never been touched before |
| * (i.e. those that were previously not valid). |
| */ |
| sent_mask = 0; |
| for (i = 0, j = 0; i < max_reg_index; i++) { |
| if ((req_sleep.valid & BIT(i)) |
| && (!(aggr_vreg->aggr_req_sleep.valid & BIT(i)) |
| || aggr_vreg->aggr_req_sleep.reg[i] |
| != req_sleep.reg[i])) { |
| cmd[j].addr = aggr_vreg->addr + i * 4; |
| cmd[j].data = req_sleep.reg[i]; |
| j++; |
| sent_mask |= BIT(i); |
| } |
| } |
| |
| /* Send the rpmh command if any register values differ. */ |
| if (j > 0) { |
| rc = rpmh_write_async(aggr_vreg->rpmh_client, |
| RPMH_SLEEP_STATE, cmd, j); |
| if (rc) { |
| aggr_vreg_err(aggr_vreg, "sleep state rpmh_write_async() failed, rc=%d\n", |
| rc); |
| return rc; |
| } |
| rpmh_regulator_req(vreg, &req_sleep, |
| &aggr_vreg->aggr_req_sleep, |
| sent_mask, |
| RPMH_SLEEP_STATE); |
| aggr_vreg->sleep_request_sent = true; |
| aggr_vreg->aggr_req_sleep = req_sleep; |
| } |
| } |
| |
| /* |
| * Generate an rpmh command consisting of only those registers |
| * which have new values or which have never been touched before |
| * (i.e. those that were previously not valid). |
| */ |
| sent_mask = 0; |
| for (i = 0, j = 0; i < max_reg_index; i++) { |
| if ((req_active.valid & BIT(i)) |
| && (!(aggr_vreg->aggr_req_active.valid & BIT(i)) |
| || aggr_vreg->aggr_req_active.reg[i] |
| != req_active.reg[i] || resend_active)) { |
| cmd[j].addr = aggr_vreg->addr + i * 4; |
| cmd[j].data = req_active.reg[i]; |
| j++; |
| sent_mask |= BIT(i); |
| |
| /* |
| * Must wait for ACK from RPMh if power state is |
| * increasing |
| */ |
| if (req_active.reg[i] |
| > aggr_vreg->aggr_req_active.reg[i]) |
| wait_for_ack = true; |
| } |
| } |
| |
| /* Send the rpmh command if any register values differ. */ |
| if (j > 0) { |
| if (sleep_set_differs && !aggr_vreg->use_awake_state) { |
| state = RPMH_WAKE_ONLY_STATE; |
| rc = rpmh_write_async(aggr_vreg->rpmh_client, state, |
| cmd, j); |
| if (rc) { |
| aggr_vreg_err(aggr_vreg, "%s state rpmh_write_async() failed, rc=%d\n", |
| rpmh_regulator_state_names[state], rc); |
| return rc; |
| } |
| rpmh_regulator_req(vreg, &req_active, |
| &aggr_vreg->aggr_req_active, sent_mask, state); |
| } |
| |
| state = aggr_vreg->use_awake_state ? RPMH_AWAKE_STATE |
| : RPMH_ACTIVE_ONLY_STATE; |
| if (wait_for_ack) |
| rc = rpmh_write(aggr_vreg->rpmh_client, state, cmd, j); |
| else |
| rc = rpmh_write_async(aggr_vreg->rpmh_client, state, |
| cmd, j); |
| if (rc) { |
| aggr_vreg_err(aggr_vreg, "%s state rpmh_write() failed, rc=%d\n", |
| rpmh_regulator_state_names[state], rc); |
| return rc; |
| } |
| rpmh_regulator_req(vreg, &req_active, |
| &aggr_vreg->aggr_req_active, sent_mask, state); |
| |
| aggr_vreg->aggr_req_active = req_active; |
| aggr_vreg->next_wait_for_ack = false; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * rpmh_regulator_set_reg() - set a register value within the request for an |
| * RPMh regulator and return the previous value |
| * @vreg: Pointer to the RPMh regulator |
| * @reg_index: Index of the register value to update |
| * @value: New register value to set |
| * |
| * Return: old register value |
| */ |
| static u32 rpmh_regulator_set_reg(struct rpmh_vreg *vreg, int reg_index, |
| u32 value) |
| { |
| u32 old_value; |
| |
| old_value = vreg->req.reg[reg_index]; |
| vreg->req.reg[reg_index] = value; |
| vreg->req.valid |= BIT(reg_index); |
| |
| return old_value; |
| } |
| |
| /** |
| * rpmh_regulator_check_param_max() - sets if the next request must wait for |
| * an ACK based on the previously sent reg[index] value and the new |
| * max value |
| * @aggr_vreg: Pointer to the aggregated rpmh regulator resource |
| * @index: Register index |
| * @new_max: Newly requested maximum allowed value for the parameter |
| * |
| * This function is used to handle the case when a consumer makes a new |
| * (min_uv, max_uv) range request in which the new max_uv is lower than the |
| * previously requested min_uv. In this case, the driver must wait for an ACK |
| * from RPMh to ensure that the voltage has completed reducing to the new min_uv |
| * value since the consumer cannot operate at the old min_uv value. |
| * |
| * Return: none |
| */ |
| static void rpmh_regulator_check_param_max(struct rpmh_aggr_vreg *aggr_vreg, |
| int index, u32 new_max) |
| { |
| if ((aggr_vreg->aggr_req_active.valid & BIT(index)) |
| && aggr_vreg->aggr_req_active.reg[index] > new_max) |
| aggr_vreg->next_wait_for_ack = true; |
| } |
| |
| /** |
| * rpmh_regulator_is_enabled() - return the enable state of the RPMh |
| * regulator |
| * @rdev: Regulator device pointer for the rpmh-regulator |
| * |
| * This function is passed as a callback function into the regulator ops that |
| * are registered for each rpmh-regulator device. |
| * |
| * Note that for ARC resources, this value is effectively a flag indicating if |
| * the requested voltage level is masked or unmasked since "disabled" = voltage |
| * level 0 (if supported). |
| * |
| * Return: true if regulator is enabled, false if regulator is disabled |
| */ |
| static int rpmh_regulator_is_enabled(struct regulator_dev *rdev) |
| { |
| struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); |
| |
| return !!vreg->req.reg[RPMH_REGULATOR_REG_ENABLE]; |
| } |
| |
| /** |
| * rpmh_regulator_enable() - enable the RPMh regulator |
| * @rdev: Regulator device pointer for the rpmh-regulator |
| * |
| * This function is passed as a callback function into the regulator ops that |
| * are registered for each rpmh-regulator device. |
| * |
| * Note that for ARC devices the enable state is handled via the voltage level |
| * parameter. Therefore, this enable value effectively masks or unmasks the |
| * enabled voltage level. |
| * |
| * Return: 0 on success, errno on failure |
| */ |
| static int rpmh_regulator_enable(struct regulator_dev *rdev) |
| { |
| struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); |
| u32 prev_enable; |
| int rc; |
| |
| mutex_lock(&vreg->aggr_vreg->lock); |
| |
| prev_enable |
| = rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_ENABLE, 1); |
| |
| rc = rpmh_regulator_send_aggregate_requests(vreg); |
| if (rc) { |
| vreg_err(vreg, "enable failed, rc=%d\n", rc); |
| rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_ENABLE, |
| prev_enable); |
| } |
| |
| mutex_unlock(&vreg->aggr_vreg->lock); |
| |
| return rc; |
| } |
| |
| /** |
| * rpmh_regulator_disable() - disable the RPMh regulator |
| * @rdev: Regulator device pointer for the rpmh-regulator |
| * |
| * This function is passed as a callback function into the regulator ops that |
| * are registered for each rpmh-regulator device. |
| * |
| * Note that for ARC devices the enable state is handled via the voltage level |
| * parameter. Therefore, this enable value effectively masks or unmasks the |
| * enabled voltage level. |
| * |
| * Return: 0 on success, errno on failure |
| */ |
| static int rpmh_regulator_disable(struct regulator_dev *rdev) |
| { |
| struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); |
| u32 prev_enable; |
| int rc; |
| |
| mutex_lock(&vreg->aggr_vreg->lock); |
| |
| prev_enable |
| = rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_ENABLE, 0); |
| |
| rc = rpmh_regulator_send_aggregate_requests(vreg); |
| if (rc) { |
| vreg_err(vreg, "disable failed, rc=%d\n", rc); |
| rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_ENABLE, |
| prev_enable); |
| } |
| |
| mutex_unlock(&vreg->aggr_vreg->lock); |
| |
| return rc; |
| } |
| |
| /** |
| * rpmh_regulator_vrm_set_voltage() - set the voltage of the VRM rpmh-regulator |
| * @rdev: Regulator device pointer for the rpmh-regulator |
| * @min_uv: New voltage in microvolts to set |
| * @max_uv: Maximum voltage in microvolts allowed |
| * @selector: Unused |
| * |
| * This function is passed as a callback function into the regulator ops that |
| * are registered for each VRM rpmh-regulator device. |
| * |
| * Return: 0 on success, errno on failure |
| */ |
| static int rpmh_regulator_vrm_set_voltage(struct regulator_dev *rdev, |
| int min_uv, int max_uv, unsigned int *selector) |
| { |
| struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); |
| u32 prev_voltage; |
| int mv; |
| int rc = 0; |
| |
| mv = DIV_ROUND_UP(min_uv, 1000); |
| if (mv * 1000 > max_uv) { |
| vreg_err(vreg, "no set points available in range %d-%d uV\n", |
| min_uv, max_uv); |
| return -EINVAL; |
| } |
| |
| mutex_lock(&vreg->aggr_vreg->lock); |
| |
| prev_voltage |
| = rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_VRM_VOLTAGE, mv); |
| rpmh_regulator_check_param_max(vreg->aggr_vreg, |
| RPMH_REGULATOR_REG_VRM_VOLTAGE, max_uv); |
| |
| rc = rpmh_regulator_send_aggregate_requests(vreg); |
| if (rc) { |
| vreg_err(vreg, "set voltage=%d mV failed, rc=%d\n", mv, rc); |
| rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_VRM_VOLTAGE, |
| prev_voltage); |
| } |
| |
| mutex_unlock(&vreg->aggr_vreg->lock); |
| |
| return rc; |
| } |
| |
| /** |
| * rpmh_regulator_vrm_get_voltage() - get the voltage of the VRM rpmh-regulator |
| * @rdev: Regulator device pointer for the rpmh-regulator |
| * |
| * This function is passed as a callback function into the regulator ops that |
| * are registered for each VRM rpmh-regulator device. |
| * |
| * Return: regulator voltage in microvolts |
| */ |
| static int rpmh_regulator_vrm_get_voltage(struct regulator_dev *rdev) |
| { |
| struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); |
| int uv; |
| |
| uv = vreg->req.reg[RPMH_REGULATOR_REG_VRM_VOLTAGE] * 1000; |
| if (uv == 0) |
| uv = VOLTAGE_UNKNOWN; |
| |
| return uv; |
| } |
| |
| /** |
| * rpmh_regulator_vrm_set_mode_index() - set the mode of a VRM regulator to the |
| * mode mapped to mode_index |
| * @vreg: Pointer to the RPMh regulator |
| * @mode_index: Index into aggr_vreg->mode[] array |
| * |
| * Return: 0 on success, errno on failure |
| */ |
| static int rpmh_regulator_vrm_set_mode_index(struct rpmh_vreg *vreg, |
| int mode_index) |
| { |
| u32 prev_mode; |
| int rc; |
| |
| mutex_lock(&vreg->aggr_vreg->lock); |
| |
| prev_mode = rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_VRM_MODE, |
| vreg->aggr_vreg->mode[mode_index].pmic_mode); |
| |
| rc = rpmh_regulator_send_aggregate_requests(vreg); |
| if (rc) { |
| vreg_err(vreg, "set mode=%u failed, rc=%d\n", |
| vreg->req.reg[RPMH_REGULATOR_REG_VRM_MODE], |
| rc); |
| rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_VRM_MODE, |
| prev_mode); |
| } else { |
| vreg->mode_index = mode_index; |
| } |
| |
| mutex_unlock(&vreg->aggr_vreg->lock); |
| |
| return rc; |
| } |
| |
| /** |
| * rpmh_regulator_vrm_set_mode() - set the mode of the VRM rpmh-regulator |
| * @rdev: Regulator device pointer for the rpmh-regulator |
| * @mode: The regulator framework mode to set |
| * |
| * This function is passed as a callback function into the regulator ops that |
| * are registered for each VRM rpmh-regulator device. |
| * |
| * This function sets the PMIC mode corresponding to the specified framework |
| * mode. The set of PMIC modes allowed is defined in device tree for a given |
| * RPMh regulator resource. The full mapping from PMIC modes to framework modes |
| * is defined in the rpmh_regulator_mode_map[] array. The RPMh resource |
| * specific mapping is defined in the aggr_vreg->mode[] array. |
| * |
| * Return: 0 on success, errno on failure |
| */ |
| static int rpmh_regulator_vrm_set_mode(struct regulator_dev *rdev, |
| unsigned int mode) |
| { |
| struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); |
| int i; |
| |
| for (i = 0; i < vreg->aggr_vreg->mode_count; i++) |
| if (vreg->aggr_vreg->mode[i].framework_mode == mode) |
| break; |
| if (i >= vreg->aggr_vreg->mode_count) { |
| vreg_err(vreg, "invalid mode=%u\n", mode); |
| return -EINVAL; |
| } |
| |
| return rpmh_regulator_vrm_set_mode_index(vreg, i); |
| } |
| |
| /** |
| * rpmh_regulator_vrm_get_mode() - get the mode of the VRM rpmh-regulator |
| * @rdev: Regulator device pointer for the rpmh-regulator |
| * |
| * This function is passed as a callback function into the regulator ops that |
| * are registered for each VRM rpmh-regulator device. |
| * |
| * Return: the regulator framework mode of the regulator |
| */ |
| static unsigned int rpmh_regulator_vrm_get_mode(struct regulator_dev *rdev) |
| { |
| struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); |
| |
| return vreg->aggr_vreg->mode[vreg->mode_index].framework_mode; |
| } |
| |
| /** |
| * rpmh_regulator_vrm_set_load() - set the PMIC mode based upon the maximum load |
| * required from the VRM rpmh-regulator |
| * @rdev: Regulator device pointer for the rpmh-regulator |
| * @load_ua: Maximum current required from all consumers in microamps |
| * |
| * This function is passed as a callback function into the regulator ops that |
| * are registered for each VRM rpmh-regulator device. |
| * |
| * This function sets the mode of the regulator to that which has the highest |
| * min support load less than or equal to load_ua. Example: |
| * mode_count = 3 |
| * mode[].min_load_ua = 0, 100000, 6000000 |
| * |
| * load_ua = 10000 --> mode_index = 0 |
| * load_ua = 250000 --> mode_index = 1 |
| * load_ua = 7000000 --> mode_index = 2 |
| * |
| * Return: 0 on success, errno on failure |
| */ |
| static int rpmh_regulator_vrm_set_load(struct regulator_dev *rdev, int load_ua) |
| { |
| struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); |
| int i; |
| |
| /* No need to check element 0 as it will be the default. */ |
| for (i = vreg->aggr_vreg->mode_count - 1; i > 0; i--) |
| if (vreg->aggr_vreg->mode[i].min_load_ua <= load_ua) |
| break; |
| |
| return rpmh_regulator_vrm_set_mode_index(vreg, i); |
| } |
| |
| /** |
| * rpmh_regulator_arc_set_voltage_sel() - set the voltage level of the ARC |
| * rpmh-regulator device |
| * @rdev: Regulator device pointer for the rpmh-regulator |
| * @selector: ARC voltage level to set |
| * |
| * This function is passed as a callback function into the regulator ops that |
| * are registered for each ARC rpmh-regulator device. |
| * |
| * Return: 0 on success, errno on failure |
| */ |
| static int rpmh_regulator_arc_set_voltage_sel(struct regulator_dev *rdev, |
| unsigned int selector) |
| { |
| struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); |
| u32 prev_level; |
| int rc; |
| |
| mutex_lock(&vreg->aggr_vreg->lock); |
| |
| prev_level = rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_ARC_LEVEL, |
| selector); |
| |
| rc = rpmh_regulator_send_aggregate_requests(vreg); |
| if (rc) { |
| vreg_err(vreg, "set level=%d failed, rc=%d\n", |
| vreg->req.reg[RPMH_REGULATOR_REG_ARC_LEVEL], |
| rc); |
| rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_ARC_LEVEL, |
| prev_level); |
| } |
| |
| mutex_unlock(&vreg->aggr_vreg->lock); |
| |
| return rc; |
| } |
| |
| /** |
| * rpmh_regulator_arc_get_voltage_sel() - get the voltage level of the ARC |
| * rpmh-regulator device |
| * @rdev: Regulator device pointer for the rpmh-regulator |
| * |
| * This function is passed as a callback function into the regulator ops that |
| * are registered for each ARC rpmh-regulator device. |
| * |
| * Return: ARC voltage level |
| */ |
| static int rpmh_regulator_arc_get_voltage_sel(struct regulator_dev *rdev) |
| { |
| struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); |
| |
| return vreg->req.reg[RPMH_REGULATOR_REG_ARC_LEVEL]; |
| } |
| |
| /** |
| * rpmh_regulator_arc_list_voltage() - return the consumer voltage level mapped |
| * to a given ARC voltage level |
| * @rdev: Regulator device pointer for the rpmh-regulator |
| * @selector: ARC voltage level |
| * |
| * This function is passed as a callback function into the regulator ops that |
| * are registered for each ARC rpmh-regulator device. |
| * |
| * Data ranges: |
| * ARC voltage level: 0 - 15 (fixed in hardware) |
| * Consumer voltage level: 1 - 513 (could be expanded to larger values) |
| * |
| * Return: consumer voltage level |
| */ |
| static int rpmh_regulator_arc_list_voltage(struct regulator_dev *rdev, |
| unsigned int selector) |
| { |
| struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); |
| |
| if (selector >= vreg->aggr_vreg->level_count) |
| return 0; |
| |
| return vreg->aggr_vreg->level[selector]; |
| } |
| |
| static const struct regulator_ops rpmh_regulator_vrm_ops = { |
| .enable = rpmh_regulator_enable, |
| .disable = rpmh_regulator_disable, |
| .is_enabled = rpmh_regulator_is_enabled, |
| .set_voltage = rpmh_regulator_vrm_set_voltage, |
| .get_voltage = rpmh_regulator_vrm_get_voltage, |
| .set_mode = rpmh_regulator_vrm_set_mode, |
| .get_mode = rpmh_regulator_vrm_get_mode, |
| .set_load = rpmh_regulator_vrm_set_load, |
| }; |
| |
| static const struct regulator_ops rpmh_regulator_arc_ops = { |
| .enable = rpmh_regulator_enable, |
| .disable = rpmh_regulator_disable, |
| .is_enabled = rpmh_regulator_is_enabled, |
| .set_voltage_sel = rpmh_regulator_arc_set_voltage_sel, |
| .get_voltage_sel = rpmh_regulator_arc_get_voltage_sel, |
| .list_voltage = rpmh_regulator_arc_list_voltage, |
| }; |
| |
| static const struct regulator_ops rpmh_regulator_xob_ops = { |
| .enable = rpmh_regulator_enable, |
| .disable = rpmh_regulator_disable, |
| .is_enabled = rpmh_regulator_is_enabled, |
| }; |
| |
| static const struct regulator_ops *rpmh_regulator_ops[] = { |
| [RPMH_REGULATOR_TYPE_VRM] = &rpmh_regulator_vrm_ops, |
| [RPMH_REGULATOR_TYPE_ARC] = &rpmh_regulator_arc_ops, |
| [RPMH_REGULATOR_TYPE_XOB] = &rpmh_regulator_xob_ops, |
| }; |
| |
| /** |
| * rpmh_regulator_load_arc_level_mapping() - load the RPMh ARC resource's |
| * voltage level mapping from command db |
| * @aggr_vreg: Pointer to the aggregated rpmh regulator resource |
| * |
| * The set of supported RPMH_REGULATOR_LEVEL_* voltage levels (0 - ~512) that |
| * map to ARC operating levels (0 - 15) is defined in aux data per ARC resource |
| * in the command db SMEM data structure. It is in a u16 array with 1 to 16 |
| * elements. Note that the aux data array may be zero padded at the end for |
| * data alignment purposes. Such padding entries are invalid and must be |
| * ignored. |
| * |
| * Return: 0 on success, errno on failure |
| */ |
| static int |
| rpmh_regulator_load_arc_level_mapping(struct rpmh_aggr_vreg *aggr_vreg) |
| { |
| int i, j, len, rc; |
| u8 *buf; |
| |
| len = cmd_db_get_aux_data_len(aggr_vreg->resource_name); |
| if (len < 0) { |
| aggr_vreg_err(aggr_vreg, "could not get ARC aux data len, rc=%d\n", |
| len); |
| return len; |
| } else if (len == 0) { |
| aggr_vreg_err(aggr_vreg, "ARC level mapping data missing in command db\n"); |
| return -EINVAL; |
| } else if (len > RPMH_ARC_MAX_LEVELS * RPMH_ARC_LEVEL_SIZE) { |
| aggr_vreg_err(aggr_vreg, "more ARC levels defined than allowed: %d > %d\n", |
| len, RPMH_ARC_MAX_LEVELS * RPMH_ARC_LEVEL_SIZE); |
| return -EINVAL; |
| } else if (len % RPMH_ARC_LEVEL_SIZE) { |
| aggr_vreg_err(aggr_vreg, "invalid ARC aux data size: %d\n", |
| len); |
| return -EINVAL; |
| } |
| |
| buf = kzalloc(len, GFP_KERNEL); |
| if (!buf) |
| return -ENOMEM; |
| |
| rc = cmd_db_get_aux_data(aggr_vreg->resource_name, buf, len); |
| if (rc < 0) { |
| aggr_vreg_err(aggr_vreg, "could not retrieve ARC aux data, rc=%d\n", |
| rc); |
| goto done; |
| } else if (rc != len) { |
| aggr_vreg_err(aggr_vreg, "could not retrieve all ARC aux data, %d != %d\n", |
| rc, len); |
| rc = -EINVAL; |
| goto done; |
| } |
| rc = 0; |
| |
| aggr_vreg->level_count = len / RPMH_ARC_LEVEL_SIZE; |
| |
| for (i = 0; i < aggr_vreg->level_count; i++) { |
| for (j = 0; j < RPMH_ARC_LEVEL_SIZE; j++) |
| aggr_vreg->level[i] |= |
| buf[i * RPMH_ARC_LEVEL_SIZE + j] << (8 * j); |
| |
| /* |
| * The AUX data may be zero padded. These 0 valued entries at |
| * the end of the map must be ignored. |
| */ |
| if (i > 0 && aggr_vreg->level[i] == 0) { |
| aggr_vreg->level_count = i; |
| break; |
| } |
| |
| /* Add consumer offset to avoid voltage = 0. */ |
| aggr_vreg->level[i] += RPMH_REGULATOR_LEVEL_OFFSET; |
| aggr_vreg_debug(aggr_vreg, "ARC hlvl=%2d --> vlvl=%4u\n", |
| i, aggr_vreg->level[i]); |
| } |
| |
| done: |
| kfree(buf); |
| return rc; |
| } |
| |
| /** |
| * rpmh_regulator_parse_vrm_modes() - parse the supported mode configurations |
| * for a VRM RPMh resource from device tree |
| * @aggr_vreg: Pointer to the aggregated rpmh regulator resource |
| * |
| * This function initializes the mode[] array of aggr_vreg based upon the values |
| * of optional device tree properties. |
| * |
| * Return: 0 on success, errno on failure |
| */ |
| static int rpmh_regulator_parse_vrm_modes(struct rpmh_aggr_vreg *aggr_vreg) |
| { |
| struct device_node *node = aggr_vreg->dev->of_node; |
| const char *prop = "qcom,supported-modes"; |
| int i, len, rc; |
| u32 *buf; |
| |
| /* qcom,supported-modes is optional */ |
| if (!of_find_property(node, prop, &len)) |
| return 0; |
| |
| len /= sizeof(u32); |
| aggr_vreg->mode = devm_kcalloc(aggr_vreg->dev, len, |
| sizeof(*aggr_vreg->mode), GFP_KERNEL); |
| if (!aggr_vreg->mode) |
| return -ENOMEM; |
| aggr_vreg->mode_count = len; |
| |
| |
| buf = kcalloc(len, sizeof(*buf), GFP_KERNEL); |
| if (!buf) |
| return -ENOMEM; |
| |
| rc = of_property_read_u32_array(node, prop, buf, len); |
| if (rc) { |
| aggr_vreg_err(aggr_vreg, "unable to read %s, rc=%d\n", |
| prop, rc); |
| goto done; |
| } |
| |
| for (i = 0; i < len; i++) { |
| if (buf[i] >= ARRAY_SIZE(rpmh_regulator_mode_map)) { |
| aggr_vreg_err(aggr_vreg, "element %d of %s = %u is invalid\n", |
| i, prop, buf[i]); |
| rc = -EINVAL; |
| goto done; |
| } |
| aggr_vreg->mode[i].pmic_mode = buf[i]; |
| aggr_vreg->mode[i].framework_mode |
| = rpmh_regulator_mode_map[buf[i]]; |
| |
| if (i > 0 && aggr_vreg->mode[i].pmic_mode |
| <= aggr_vreg->mode[i - 1].pmic_mode) { |
| aggr_vreg_err(aggr_vreg, "%s elements are not in ascending order\n", |
| prop); |
| rc = -EINVAL; |
| goto done; |
| } |
| } |
| |
| prop = "qcom,mode-threshold-currents"; |
| |
| rc = of_property_read_u32_array(node, prop, buf, len); |
| if (rc) { |
| aggr_vreg_err(aggr_vreg, "unable to read %s, rc=%d\n", |
| prop, rc); |
| goto done; |
| } |
| |
| for (i = 0; i < len; i++) { |
| aggr_vreg->mode[i].min_load_ua = buf[i]; |
| |
| if (i > 0 && aggr_vreg->mode[i].min_load_ua |
| <= aggr_vreg->mode[i - 1].min_load_ua) { |
| aggr_vreg_err(aggr_vreg, "%s elements are not in ascending order\n", |
| prop); |
| rc = -EINVAL; |
| goto done; |
| } |
| } |
| |
| done: |
| kfree(buf); |
| return rc; |
| } |
| |
| /** |
| * rpmh_regulator_allocate_vreg() - allocate space for the regulators associated |
| * with the RPMh regulator resource and initialize important |
| * pointers for each regulator |
| * @aggr_vreg: Pointer to the aggregated rpmh regulator resource |
| * |
| * Return: 0 on success, errno on failure |
| */ |
| static int rpmh_regulator_allocate_vreg(struct rpmh_aggr_vreg *aggr_vreg) |
| { |
| struct device_node *node; |
| int i, rc; |
| |
| aggr_vreg->vreg_count = 0; |
| |
| for_each_available_child_of_node(aggr_vreg->dev->of_node, node) { |
| /* Skip child nodes handled by other drivers. */ |
| if (of_find_property(node, "compatible", NULL)) |
| continue; |
| aggr_vreg->vreg_count++; |
| } |
| |
| if (aggr_vreg->vreg_count == 0) { |
| aggr_vreg_err(aggr_vreg, "could not find any regulator subnodes\n"); |
| return -ENODEV; |
| } |
| |
| aggr_vreg->vreg = devm_kcalloc(aggr_vreg->dev, aggr_vreg->vreg_count, |
| sizeof(*aggr_vreg->vreg), GFP_KERNEL); |
| if (!aggr_vreg->vreg) |
| return -ENOMEM; |
| |
| i = 0; |
| for_each_available_child_of_node(aggr_vreg->dev->of_node, node) { |
| /* Skip child nodes handled by other drivers. */ |
| if (of_find_property(node, "compatible", NULL)) |
| continue; |
| |
| aggr_vreg->vreg[i].of_node = node; |
| aggr_vreg->vreg[i].aggr_vreg = aggr_vreg; |
| |
| rc = of_property_read_string(node, "regulator-name", |
| &aggr_vreg->vreg[i].rdesc.name); |
| if (rc) { |
| aggr_vreg_err(aggr_vreg, "could not read regulator-name property, rc=%d\n", |
| rc); |
| return rc; |
| } |
| |
| i++; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * rpmh_regulator_load_default_parameters() - initialize the RPMh resource |
| * request for this regulator based on optional device tree |
| * properties |
| * @vreg: Pointer to the RPMh regulator |
| * |
| * Return: 0 on success, errno on failure |
| */ |
| static int rpmh_regulator_load_default_parameters(struct rpmh_vreg *vreg) |
| { |
| enum rpmh_regulator_type type = vreg->aggr_vreg->regulator_type; |
| const char *prop; |
| int i, rc; |
| u32 temp; |
| |
| if (type == RPMH_REGULATOR_TYPE_ARC) { |
| prop = "qcom,init-voltage-level"; |
| rc = of_property_read_u32(vreg->of_node, prop, &temp); |
| if (!rc) { |
| for (i = 0; i < vreg->aggr_vreg->level_count; i++) |
| if (temp <= vreg->aggr_vreg->level[i]) |
| break; |
| if (i < vreg->aggr_vreg->level_count) { |
| rpmh_regulator_set_reg(vreg, |
| RPMH_REGULATOR_REG_ARC_LEVEL, i); |
| } else { |
| vreg_err(vreg, "%s=%u is invalid\n", |
| prop, temp); |
| return -EINVAL; |
| } |
| } |
| |
| prop = "qcom,min-dropout-voltage-level"; |
| rc = of_property_read_u32(vreg->of_node, prop, &temp); |
| if (!rc) |
| vreg->rdesc.min_dropout_uV = temp; |
| } else if (type == RPMH_REGULATOR_TYPE_VRM) { |
| prop = "qcom,init-enable"; |
| rc = of_property_read_u32(vreg->of_node, prop, &temp); |
| if (!rc) |
| rpmh_regulator_set_reg(vreg, |
| RPMH_REGULATOR_REG_VRM_ENABLE, |
| !!temp); |
| |
| prop = "qcom,init-voltage"; |
| rc = of_property_read_u32(vreg->of_node, prop, &temp); |
| if (!rc) { |
| if (temp < RPMH_VRM_MIN_UV || temp > RPMH_VRM_MAX_UV) { |
| vreg_err(vreg, "%s=%u is invalid\n", |
| prop, temp); |
| return -EINVAL; |
| } |
| rpmh_regulator_set_reg(vreg, |
| RPMH_REGULATOR_REG_VRM_VOLTAGE, |
| temp / 1000); |
| } |
| |
| prop = "qcom,init-mode"; |
| rc = of_property_read_u32(vreg->of_node, prop, &temp); |
| if (!rc) { |
| if (temp < RPMH_VRM_MODE_MIN || |
| temp > RPMH_VRM_MODE_MAX) { |
| vreg_err(vreg, "%s=%u is invalid\n", |
| prop, temp); |
| return -EINVAL; |
| } |
| rpmh_regulator_set_reg(vreg, |
| RPMH_REGULATOR_REG_VRM_MODE, |
| temp); |
| } |
| |
| prop = "qcom,init-headroom-voltage"; |
| rc = of_property_read_u32(vreg->of_node, prop, &temp); |
| if (!rc) { |
| if (temp < RPMH_VRM_HEADROOM_MIN_UV || |
| temp > RPMH_VRM_HEADROOM_MAX_UV) { |
| vreg_err(vreg, "%s=%u is invalid\n", |
| prop, temp); |
| return -EINVAL; |
| } |
| rpmh_regulator_set_reg(vreg, |
| RPMH_REGULATOR_REG_VRM_HEADROOM, |
| temp / 1000); |
| } |
| |
| prop = "qcom,min-dropout-voltage"; |
| rc = of_property_read_u32(vreg->of_node, prop, &temp); |
| if (!rc) |
| vreg->rdesc.min_dropout_uV = temp; |
| } else if (type == RPMH_REGULATOR_TYPE_XOB) { |
| prop = "qcom,init-enable"; |
| rc = of_property_read_u32(vreg->of_node, prop, &temp); |
| if (!rc) |
| rpmh_regulator_set_reg(vreg, |
| RPMH_REGULATOR_REG_XOB_ENABLE, |
| !!temp); |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * rpmh_regulator_init_vreg_supply() - initialize the regulator's parent supply |
| * mapping based on optional DT parent supply property |
| * @vreg: Pointer to the RPMh regulator |
| * |
| * Return: 0 on success, errno on failure |
| */ |
| static int rpmh_regulator_init_vreg_supply(struct rpmh_vreg *vreg) |
| { |
| char *buf; |
| size_t len; |
| |
| len = strlen(vreg->rdesc.name) + 16; |
| buf = kzalloc(len, GFP_KERNEL); |
| if (!buf) |
| return -ENOMEM; |
| scnprintf(buf, len, "%s-parent-supply", vreg->rdesc.name); |
| |
| if (of_find_property(vreg->aggr_vreg->dev->of_node, buf, NULL)) { |
| kfree(buf); |
| |
| len = strlen(vreg->rdesc.name) + 10; |
| buf = devm_kzalloc(vreg->aggr_vreg->dev, len, GFP_KERNEL); |
| if (!buf) |
| return -ENOMEM; |
| scnprintf(buf, len, "%s-parent", vreg->rdesc.name); |
| |
| vreg->rdesc.supply_name = buf; |
| } else { |
| kfree(buf); |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * rpmh_regulator_init_vreg() - initialize all abbributes of an rpmh-regulator |
| * @vreg: Pointer to the RPMh regulator |
| * |
| * Return: 0 on success, errno on failure |
| */ |
| static int rpmh_regulator_init_vreg(struct rpmh_vreg *vreg) |
| { |
| struct device *dev = vreg->aggr_vreg->dev; |
| enum rpmh_regulator_type type = vreg->aggr_vreg->regulator_type; |
| struct regulator_config reg_config = {}; |
| struct regulator_init_data *init_data; |
| struct regulator_ops *ops; |
| int rc, i; |
| u32 set; |
| |
| ops = devm_kzalloc(dev, sizeof(*ops), GFP_KERNEL); |
| if (!ops) |
| return -ENOMEM; |
| |
| *ops = *rpmh_regulator_ops[type]; |
| vreg->rdesc.owner = THIS_MODULE; |
| vreg->rdesc.type = REGULATOR_VOLTAGE; |
| vreg->rdesc.ops = ops; |
| |
| init_data = of_get_regulator_init_data(dev, |
| vreg->of_node, &vreg->rdesc); |
| if (init_data == NULL) |
| return -ENOMEM; |
| |
| init_data->constraints.input_uV = init_data->constraints.max_uV; |
| if (type == RPMH_REGULATOR_TYPE_VRM) { |
| init_data->constraints.min_uV |
| = max(init_data->constraints.min_uV, RPMH_VRM_MIN_UV); |
| init_data->constraints.min_uV |
| = min(init_data->constraints.min_uV, RPMH_VRM_MAX_UV); |
| init_data->constraints.max_uV |
| = max(init_data->constraints.max_uV, RPMH_VRM_MIN_UV); |
| init_data->constraints.max_uV |
| = min(init_data->constraints.max_uV, RPMH_VRM_MAX_UV); |
| } |
| |
| if (ops->set_voltage || ops->set_voltage_sel) |
| init_data->constraints.valid_ops_mask |
| |= REGULATOR_CHANGE_VOLTAGE; |
| |
| if (type == RPMH_REGULATOR_TYPE_XOB |
| && init_data->constraints.min_uV == init_data->constraints.max_uV) |
| vreg->rdesc.fixed_uV = init_data->constraints.min_uV; |
| |
| if (vreg->aggr_vreg->mode_count) { |
| init_data->constraints.valid_ops_mask |
| |= REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_DRMS; |
| for (i = 0; i < vreg->aggr_vreg->mode_count; i++) |
| init_data->constraints.valid_modes_mask |
| |= vreg->aggr_vreg->mode[i].framework_mode; |
| } else { |
| ops->get_mode = NULL; |
| ops->set_mode = NULL; |
| ops->set_load = NULL; |
| } |
| |
| /* |
| * Remove enable state control if the ARC resource does not support the |
| * off level. |
| */ |
| if (type == RPMH_REGULATOR_TYPE_ARC |
| && vreg->aggr_vreg->level[0] != RPMH_REGULATOR_LEVEL_OFF) { |
| ops->enable = NULL; |
| ops->disable = NULL; |
| ops->is_enabled = NULL; |
| } |
| if (ops->enable) |
| init_data->constraints.valid_ops_mask |
| |= REGULATOR_CHANGE_STATUS; |
| |
| switch (type) { |
| case RPMH_REGULATOR_TYPE_VRM: |
| vreg->rdesc.n_voltages = 2; |
| break; |
| case RPMH_REGULATOR_TYPE_ARC: |
| vreg->rdesc.n_voltages = vreg->aggr_vreg->level_count; |
| break; |
| case RPMH_REGULATOR_TYPE_XOB: |
| vreg->rdesc.n_voltages = 1; |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| rc = of_property_read_u32(vreg->of_node, "qcom,set", &set); |
| if (rc) { |
| vreg_err(vreg, "qcom,set property missing, rc=%d\n", rc); |
| return rc; |
| } else if (!(set & RPMH_REGULATOR_SET_ALL)) { |
| vreg_err(vreg, "qcom,set=%u property is invalid\n", set); |
| return rc; |
| } |
| |
| vreg->set_active = !!(set & RPMH_REGULATOR_SET_ACTIVE); |
| vreg->set_sleep = !!(set & RPMH_REGULATOR_SET_SLEEP); |
| |
| rc = rpmh_regulator_init_vreg_supply(vreg); |
| if (rc) { |
| vreg_err(vreg, "unable to initialize regulator supply name, rc=%d\n", |
| rc); |
| return rc; |
| } |
| |
| reg_config.dev = dev; |
| reg_config.init_data = init_data; |
| reg_config.of_node = vreg->of_node; |
| reg_config.driver_data = vreg; |
| |
| rc = rpmh_regulator_load_default_parameters(vreg); |
| if (rc) { |
| vreg_err(vreg, "unable to load default parameters, rc=%d\n", |
| rc); |
| return rc; |
| } |
| |
| vreg->rdev = devm_regulator_register(dev, &vreg->rdesc, ®_config); |
| if (IS_ERR(vreg->rdev)) { |
| rc = PTR_ERR(vreg->rdev); |
| vreg->rdev = NULL; |
| vreg_err(vreg, "devm_regulator_register() failed, rc=%d\n", rc); |
| return rc; |
| } |
| |
| vreg_debug(vreg, "successfully registered; set=%s\n", |
| vreg->set_active && vreg->set_sleep |
| ? "active + sleep" |
| : vreg->set_active ? "active" : "sleep"); |
| |
| return rc; |
| } |
| |
| static const struct of_device_id rpmh_regulator_match_table[] = { |
| { |
| .compatible = "qcom,rpmh-vrm-regulator", |
| .data = (void *)(uintptr_t)RPMH_REGULATOR_TYPE_VRM, |
| }, |
| { |
| .compatible = "qcom,rpmh-arc-regulator", |
| .data = (void *)(uintptr_t)RPMH_REGULATOR_TYPE_ARC, |
| }, |
| { |
| .compatible = "qcom,rpmh-xob-regulator", |
| .data = (void *)(uintptr_t)RPMH_REGULATOR_TYPE_XOB, |
| }, |
| {} |
| }; |
| |
| /** |
| * rpmh_regulator_probe() - probe an aggregated RPMh regulator resource and |
| * register regulators for each of the regulator nodes associated |
| * with it |
| * @pdev: Pointer to the platform device of the aggregated rpmh |
| * regulator resource |
| * |
| * Return: 0 on success, errno on failure |
| */ |
| static int rpmh_regulator_probe(struct platform_device *pdev) |
| { |
| struct device *dev = &pdev->dev; |
| const struct of_device_id *match; |
| struct rpmh_aggr_vreg *aggr_vreg; |
| struct device_node *node; |
| int rc, i, sid; |
| |
| node = dev->of_node; |
| |
| if (!node) { |
| dev_err(dev, "Device tree node is missing\n"); |
| return -EINVAL; |
| } |
| |
| aggr_vreg = devm_kzalloc(dev, sizeof(*aggr_vreg), GFP_KERNEL); |
| if (!aggr_vreg) |
| return -ENOMEM; |
| |
| aggr_vreg->dev = dev; |
| mutex_init(&aggr_vreg->lock); |
| |
| match = of_match_node(rpmh_regulator_match_table, node); |
| if (match) { |
| aggr_vreg->regulator_type = (uintptr_t)match->data; |
| } else { |
| dev_err(dev, "could not find compatible string match\n"); |
| return -ENODEV; |
| } |
| |
| rc = of_property_read_string(node, "qcom,resource-name", |
| &aggr_vreg->resource_name); |
| if (rc) { |
| dev_err(dev, "qcom,resource-name missing in DT node\n"); |
| return rc; |
| } |
| |
| aggr_vreg->use_awake_state = of_property_read_bool(node, |
| "qcom,use-awake-state"); |
| |
| rc = cmd_db_ready(); |
| if (rc) { |
| if (rc != -EPROBE_DEFER) |
| aggr_vreg_err(aggr_vreg, "Command DB not available, rc=%d\n", |
| rc); |
| return rc; |
| } |
| |
| aggr_vreg->addr = cmd_db_get_addr(aggr_vreg->resource_name); |
| if (!aggr_vreg->addr) { |
| aggr_vreg_err(aggr_vreg, "could not find RPMh address for resource\n"); |
| return -ENODEV; |
| } |
| |
| sid = cmd_db_get_slave_id(aggr_vreg->resource_name); |
| if (sid < 0) { |
| aggr_vreg_err(aggr_vreg, "could not find RPMh slave id for resource, rc=%d\n", |
| sid); |
| return sid; |
| } |
| |
| /* Confirm slave ID listed in command DB matches DT configuration. */ |
| if ((aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_ARC |
| && sid != CMD_DB_HW_ARC) |
| || (aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_VRM |
| && sid != CMD_DB_HW_VRM) |
| || (aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_XOB |
| && sid != CMD_DB_HW_XOB)) { |
| aggr_vreg_err(aggr_vreg, "RPMh slave ID mismatch; config=%d (%s) != cmd-db=%d\n", |
| aggr_vreg->regulator_type, |
| aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_ARC |
| ? "ARC" : (aggr_vreg->regulator_type |
| == RPMH_REGULATOR_TYPE_VRM |
| ? "VRM" : "XOB"), |
| sid); |
| return -EINVAL; |
| } |
| |
| aggr_vreg->rpmh_client = rpmh_get_byindex(pdev, 0); |
| if (IS_ERR(aggr_vreg->rpmh_client)) { |
| rc = PTR_ERR(aggr_vreg->rpmh_client); |
| if (rc != -EPROBE_DEFER) |
| aggr_vreg_err(aggr_vreg, "failed to request RPMh client, rc=%d\n", |
| rc); |
| return rc; |
| } |
| |
| if (aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_ARC) { |
| rc = rpmh_regulator_load_arc_level_mapping(aggr_vreg); |
| if (rc) { |
| aggr_vreg_err(aggr_vreg, "could not load arc level mapping, rc=%d\n", |
| rc); |
| goto free_client; |
| } |
| } else if (aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_VRM) { |
| rc = rpmh_regulator_parse_vrm_modes(aggr_vreg); |
| if (rc) { |
| aggr_vreg_err(aggr_vreg, "could not parse vrm mode mapping, rc=%d\n", |
| rc); |
| goto free_client; |
| } |
| } |
| |
| aggr_vreg->always_wait_for_ack |
| = of_property_read_bool(node, "qcom,always-wait-for-ack"); |
| |
| rc = rpmh_regulator_allocate_vreg(aggr_vreg); |
| if (rc) { |
| aggr_vreg_err(aggr_vreg, "failed to allocate regulator subnode array, rc=%d\n", |
| rc); |
| goto free_client; |
| } |
| |
| for (i = 0; i < aggr_vreg->vreg_count; i++) { |
| rc = rpmh_regulator_init_vreg(&aggr_vreg->vreg[i]); |
| if (rc) { |
| pr_err("unable to initialize rpmh-regulator vreg %s for resource %s, rc=%d\n", |
| aggr_vreg->vreg[i].rdesc.name, |
| aggr_vreg->resource_name, rc); |
| goto free_client; |
| } |
| } |
| |
| if (of_property_read_bool(node, "qcom,send-defaults")) { |
| mutex_lock(&aggr_vreg->lock); |
| rc = rpmh_regulator_send_aggregate_requests( |
| &aggr_vreg->vreg[0]); |
| if (rc) { |
| aggr_vreg_err(aggr_vreg, "error while sending default request, rc=%d\n", |
| rc); |
| mutex_unlock(&aggr_vreg->lock); |
| goto free_client; |
| } |
| mutex_unlock(&aggr_vreg->lock); |
| } |
| |
| of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); |
| platform_set_drvdata(pdev, aggr_vreg); |
| |
| aggr_vreg_debug(aggr_vreg, "successfully probed; addr=0x%05X, type=%s\n", |
| aggr_vreg->addr, |
| aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_ARC |
| ? "ARC" |
| : (aggr_vreg->regulator_type |
| == RPMH_REGULATOR_TYPE_VRM |
| ? "VRM" : "XOB")); |
| |
| return rc; |
| |
| free_client: |
| rpmh_release(aggr_vreg->rpmh_client); |
| |
| return rc; |
| } |
| |
| static int rpmh_regulator_remove(struct platform_device *pdev) |
| { |
| struct rpmh_aggr_vreg *aggr_vreg = platform_get_drvdata(pdev); |
| |
| rpmh_release(aggr_vreg->rpmh_client); |
| |
| return 0; |
| } |
| |
| static struct platform_driver rpmh_regulator_driver = { |
| .driver = { |
| .name = "qcom,rpmh-regulator", |
| .of_match_table = rpmh_regulator_match_table, |
| .owner = THIS_MODULE, |
| }, |
| .probe = rpmh_regulator_probe, |
| .remove = rpmh_regulator_remove, |
| }; |
| |
| static int rpmh_regulator_init(void) |
| { |
| return platform_driver_register(&rpmh_regulator_driver); |
| } |
| |
| static void rpmh_regulator_exit(void) |
| { |
| platform_driver_unregister(&rpmh_regulator_driver); |
| } |
| |
| MODULE_DESCRIPTION("RPMh regulator driver"); |
| MODULE_LICENSE("GPL v2"); |
| |
| arch_initcall(rpmh_regulator_init); |
| module_exit(rpmh_regulator_exit); |