/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * 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_hw_type - supported PMIC regulator hardware types * This enum defines the specific regulator type along with its PMIC family. */ enum rpmh_regulator_hw_type { RPMH_REGULATOR_HW_TYPE_UNKNOWN, RPMH_REGULATOR_HW_TYPE_PMIC4_LDO, RPMH_REGULATOR_HW_TYPE_PMIC4_HFSMPS, RPMH_REGULATOR_HW_TYPE_PMIC4_FTSMPS, RPMH_REGULATOR_HW_TYPE_PMIC4_BOB, RPMH_REGULATOR_HW_TYPE_PMIC5_LDO, RPMH_REGULATOR_HW_TYPE_PMIC5_HFSMPS, RPMH_REGULATOR_HW_TYPE_PMIC5_FTSMPS, RPMH_REGULATOR_HW_TYPE_PMIC5_BOB, RPMH_REGULATOR_HW_TYPE_MAX, }; /** * 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 /** * 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 * @regulator_hw_type: The regulator hardware type (e.g. LDO or SMPS) * along with PMIC family (i.e. PMIC4 or PMIC5) * @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; enum rpmh_regulator_hw_type regulator_hw_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; }; #define RPMH_REGULATOR_MODE_COUNT 5 #define RPMH_REGULATOR_MODE_PMIC4_LDO_RM 4 #define RPMH_REGULATOR_MODE_PMIC4_LDO_LPM 5 #define RPMH_REGULATOR_MODE_PMIC4_LDO_HPM 7 #define RPMH_REGULATOR_MODE_PMIC4_SMPS_RM 4 #define RPMH_REGULATOR_MODE_PMIC4_SMPS_PFM 5 #define RPMH_REGULATOR_MODE_PMIC4_SMPS_AUTO 6 #define RPMH_REGULATOR_MODE_PMIC4_SMPS_PWM 7 #define RPMH_REGULATOR_MODE_PMIC4_BOB_PASS 0 #define RPMH_REGULATOR_MODE_PMIC4_BOB_PFM 1 #define RPMH_REGULATOR_MODE_PMIC4_BOB_AUTO 2 #define RPMH_REGULATOR_MODE_PMIC4_BOB_PWM 3 #define RPMH_REGULATOR_MODE_PMIC5_LDO_RM 3 #define RPMH_REGULATOR_MODE_PMIC5_LDO_LPM 4 #define RPMH_REGULATOR_MODE_PMIC5_LDO_HPM 7 #define RPMH_REGULATOR_MODE_PMIC5_HFSMPS_RM 3 #define RPMH_REGULATOR_MODE_PMIC5_HFSMPS_PFM 4 #define RPMH_REGULATOR_MODE_PMIC5_HFSMPS_AUTO 6 #define RPMH_REGULATOR_MODE_PMIC5_HFSMPS_PWM 7 #define RPMH_REGULATOR_MODE_PMIC5_FTSMPS_RM 3 #define RPMH_REGULATOR_MODE_PMIC5_FTSMPS_PWM 7 #define RPMH_REGULATOR_MODE_PMIC5_BOB_PASS 2 #define RPMH_REGULATOR_MODE_PMIC5_BOB_PFM 4 #define RPMH_REGULATOR_MODE_PMIC5_BOB_AUTO 6 #define RPMH_REGULATOR_MODE_PMIC5_BOB_PWM 7 /* * Mappings from RPMh generic modes to VRM accelerator modes and regulator * framework modes for each regulator type. */ static const struct rpmh_regulator_mode rpmh_regulator_mode_map_pmic4_ldo[RPMH_REGULATOR_MODE_COUNT] = { [RPMH_REGULATOR_MODE_RET] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC4_LDO_RM, .framework_mode = REGULATOR_MODE_STANDBY, }, [RPMH_REGULATOR_MODE_LPM] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC4_LDO_LPM, .framework_mode = REGULATOR_MODE_IDLE, }, [RPMH_REGULATOR_MODE_HPM] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC4_LDO_HPM, .framework_mode = REGULATOR_MODE_FAST, }, }; static const struct rpmh_regulator_mode rpmh_regulator_mode_map_pmic4_smps[RPMH_REGULATOR_MODE_COUNT] = { [RPMH_REGULATOR_MODE_RET] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC4_SMPS_RM, .framework_mode = REGULATOR_MODE_STANDBY, }, [RPMH_REGULATOR_MODE_LPM] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC4_SMPS_PFM, .framework_mode = REGULATOR_MODE_IDLE, }, [RPMH_REGULATOR_MODE_AUTO] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC4_SMPS_AUTO, .framework_mode = REGULATOR_MODE_NORMAL, }, [RPMH_REGULATOR_MODE_HPM] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC4_SMPS_PWM, .framework_mode = REGULATOR_MODE_FAST, }, }; static const struct rpmh_regulator_mode rpmh_regulator_mode_map_pmic4_bob[RPMH_REGULATOR_MODE_COUNT] = { [RPMH_REGULATOR_MODE_PASS] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC4_BOB_PASS, .framework_mode = REGULATOR_MODE_STANDBY, }, [RPMH_REGULATOR_MODE_LPM] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC4_BOB_PFM, .framework_mode = REGULATOR_MODE_IDLE, }, [RPMH_REGULATOR_MODE_AUTO] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC4_BOB_AUTO, .framework_mode = REGULATOR_MODE_NORMAL, }, [RPMH_REGULATOR_MODE_HPM] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC4_BOB_PWM, .framework_mode = REGULATOR_MODE_FAST, }, }; static const struct rpmh_regulator_mode rpmh_regulator_mode_map_pmic5_ldo[RPMH_REGULATOR_MODE_COUNT] = { [RPMH_REGULATOR_MODE_RET] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC5_LDO_RM, .framework_mode = REGULATOR_MODE_STANDBY, }, [RPMH_REGULATOR_MODE_LPM] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC5_LDO_LPM, .framework_mode = REGULATOR_MODE_IDLE, }, [RPMH_REGULATOR_MODE_HPM] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC5_LDO_HPM, .framework_mode = REGULATOR_MODE_FAST, }, }; static const struct rpmh_regulator_mode rpmh_regulator_mode_map_pmic5_hfsmps[RPMH_REGULATOR_MODE_COUNT] = { [RPMH_REGULATOR_MODE_RET] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC5_HFSMPS_RM, .framework_mode = REGULATOR_MODE_STANDBY, }, [RPMH_REGULATOR_MODE_LPM] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC5_HFSMPS_PFM, .framework_mode = REGULATOR_MODE_IDLE, }, [RPMH_REGULATOR_MODE_AUTO] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC5_HFSMPS_AUTO, .framework_mode = REGULATOR_MODE_NORMAL, }, [RPMH_REGULATOR_MODE_HPM] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC5_HFSMPS_PWM, .framework_mode = REGULATOR_MODE_FAST, }, }; static const struct rpmh_regulator_mode rpmh_regulator_mode_map_pmic5_ftsmps[RPMH_REGULATOR_MODE_COUNT] = { [RPMH_REGULATOR_MODE_RET] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC5_FTSMPS_RM, .framework_mode = REGULATOR_MODE_STANDBY, }, [RPMH_REGULATOR_MODE_HPM] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC5_FTSMPS_PWM, .framework_mode = REGULATOR_MODE_FAST, }, }; static const struct rpmh_regulator_mode rpmh_regulator_mode_map_pmic5_bob[RPMH_REGULATOR_MODE_COUNT] = { [RPMH_REGULATOR_MODE_PASS] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC5_BOB_PASS, .framework_mode = REGULATOR_MODE_STANDBY, }, [RPMH_REGULATOR_MODE_LPM] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC5_BOB_PFM, .framework_mode = REGULATOR_MODE_IDLE, }, [RPMH_REGULATOR_MODE_AUTO] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC5_BOB_AUTO, .framework_mode = REGULATOR_MODE_NORMAL, }, [RPMH_REGULATOR_MODE_HPM] = { .pmic_mode = RPMH_REGULATOR_MODE_PMIC5_BOB_PWM, .framework_mode = REGULATOR_MODE_FAST, }, }; static const struct rpmh_regulator_mode * const rpmh_regulator_mode_map[RPMH_REGULATOR_HW_TYPE_MAX] = { [RPMH_REGULATOR_HW_TYPE_PMIC4_LDO] = rpmh_regulator_mode_map_pmic4_ldo, [RPMH_REGULATOR_HW_TYPE_PMIC4_HFSMPS] = rpmh_regulator_mode_map_pmic4_smps, [RPMH_REGULATOR_HW_TYPE_PMIC4_FTSMPS] = rpmh_regulator_mode_map_pmic4_smps, [RPMH_REGULATOR_HW_TYPE_PMIC4_BOB] = rpmh_regulator_mode_map_pmic4_bob, [RPMH_REGULATOR_HW_TYPE_PMIC5_LDO] = rpmh_regulator_mode_map_pmic5_ldo, [RPMH_REGULATOR_HW_TYPE_PMIC5_HFSMPS] = rpmh_regulator_mode_map_pmic5_hfsmps, [RPMH_REGULATOR_HW_TYPE_PMIC5_FTSMPS] = rpmh_regulator_mode_map_pmic5_ftsmps, [RPMH_REGULATOR_HW_TYPE_PMIC5_BOB] = rpmh_regulator_mode_map_pmic5_bob, }; /* * 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 generic modes to PMIC modes * and 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 *type = ""; const struct rpmh_regulator_mode *map; const char *prop; int i, len, rc; u32 *buf; aggr_vreg->regulator_hw_type = RPMH_REGULATOR_HW_TYPE_UNKNOWN; /* qcom,regulator-type is optional */ prop = "qcom,regulator-type"; if (!of_find_property(node, prop, &len)) return 0; rc = of_property_read_string(node, prop, &type); if (rc) { aggr_vreg_err(aggr_vreg, "unable to read %s, rc=%d\n", prop, rc); return rc; } if (!strcmp(type, "pmic4-ldo")) { aggr_vreg->regulator_hw_type = RPMH_REGULATOR_HW_TYPE_PMIC4_LDO; } else if (!strcmp(type, "pmic4-hfsmps")) { aggr_vreg->regulator_hw_type = RPMH_REGULATOR_HW_TYPE_PMIC4_HFSMPS; } else if (!strcmp(type, "pmic4-ftsmps")) { aggr_vreg->regulator_hw_type = RPMH_REGULATOR_HW_TYPE_PMIC4_FTSMPS; } else if (!strcmp(type, "pmic4-bob")) { aggr_vreg->regulator_hw_type = RPMH_REGULATOR_HW_TYPE_PMIC4_BOB; } else if (!strcmp(type, "pmic5-ldo")) { aggr_vreg->regulator_hw_type = RPMH_REGULATOR_HW_TYPE_PMIC5_LDO; } else if (!strcmp(type, "pmic5-hfsmps")) { aggr_vreg->regulator_hw_type = RPMH_REGULATOR_HW_TYPE_PMIC5_HFSMPS; } else if (!strcmp(type, "pmic5-ftsmps")) { aggr_vreg->regulator_hw_type = RPMH_REGULATOR_HW_TYPE_PMIC5_FTSMPS; } else if (!strcmp(type, "pmic5-bob")) { aggr_vreg->regulator_hw_type = RPMH_REGULATOR_HW_TYPE_PMIC5_BOB; } else { aggr_vreg_err(aggr_vreg, "unknown %s = %s\n", prop, type); return -EINVAL; } map = rpmh_regulator_mode_map[aggr_vreg->regulator_hw_type]; /* qcom,supported-modes is optional */ prop = "qcom,supported-modes"; 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] >= RPMH_REGULATOR_MODE_COUNT) { aggr_vreg_err(aggr_vreg, "element %d of %s = %u is invalid\n", i, prop, buf[i]); rc = -EINVAL; goto done; } if (!map[buf[i]].framework_mode) { aggr_vreg_err(aggr_vreg, "element %d of %s = %u is invalid for regulator type = %s\n", i, prop, buf[i], type); rc = -EINVAL; goto done; } aggr_vreg->mode[i].pmic_mode = map[buf[i]].pmic_mode; aggr_vreg->mode[i].framework_mode = map[buf[i]].framework_mode; 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 struct rpmh_regulator_mode *map; 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_REGULATOR_MODE_COUNT) { vreg_err(vreg, "%s=%u is invalid\n", prop, temp); return -EINVAL; } else if (vreg->aggr_vreg->regulator_hw_type == RPMH_REGULATOR_HW_TYPE_UNKNOWN) { vreg_err(vreg, "qcom,regulator-type missing so %s cannot be used\n", prop); return -EINVAL; } map = rpmh_regulator_mode_map[ vreg->aggr_vreg->regulator_hw_type]; if (!map[temp].framework_mode) { vreg_err(vreg, "%s=%u is not supported by type = %d\n", prop, temp, vreg->aggr_vreg->regulator_hw_type); return -EINVAL; } rpmh_regulator_set_reg(vreg, RPMH_REGULATOR_REG_VRM_MODE, map[temp].pmic_mode); for (i = 0; i < vreg->aggr_vreg->mode_count; i++) { if (vreg->aggr_vreg->mode[i].pmic_mode == map[temp].pmic_mode) { vreg->mode_index = i; break; } } } 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);