| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. |
| */ |
| |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/of_irq.h> |
| #include <linux/of_device.h> |
| #include <linux/slab.h> |
| #include <linux/regulator/consumer.h> |
| #include <asoc/msm-cdc-supply.h> |
| |
| #define CODEC_DT_MAX_PROP_SIZE 40 |
| |
| static int msm_cdc_dt_parse_vreg_info(struct device *dev, |
| struct cdc_regulator *cdc_vreg, |
| const char *name, bool is_ond) |
| { |
| char prop_name[CODEC_DT_MAX_PROP_SIZE]; |
| struct device_node *regulator_node = NULL; |
| const __be32 *prop; |
| int len, rc; |
| u32 prop_val; |
| |
| /* Parse supply name */ |
| snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "%s-supply", name); |
| |
| regulator_node = of_parse_phandle(dev->of_node, prop_name, 0); |
| if (!regulator_node) { |
| dev_err(dev, "%s: Looking up %s property in node %s failed", |
| __func__, prop_name, dev->of_node->full_name); |
| rc = -EINVAL; |
| goto done; |
| } |
| cdc_vreg->name = name; |
| cdc_vreg->ondemand = is_ond; |
| |
| /* Parse supply - voltage */ |
| snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "qcom,%s-voltage", name); |
| prop = of_get_property(dev->of_node, prop_name, &len); |
| if (!prop || (len != (2 * sizeof(__be32)))) { |
| dev_err(dev, "%s: %s %s property\n", __func__, |
| prop ? "invalid format" : "no", prop_name); |
| rc = -EINVAL; |
| goto done; |
| } else { |
| cdc_vreg->min_uV = be32_to_cpup(&prop[0]); |
| cdc_vreg->max_uV = be32_to_cpup(&prop[1]); |
| } |
| |
| /* Parse supply - current */ |
| snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "qcom,%s-current", name); |
| rc = of_property_read_u32(dev->of_node, prop_name, &prop_val); |
| if (rc) { |
| dev_err(dev, "%s: Looking up %s property in node %s failed", |
| __func__, prop_name, dev->of_node->full_name); |
| goto done; |
| } |
| cdc_vreg->optimum_uA = prop_val; |
| |
| /* Parse supply - LPM or NOM mode(default NOM) */ |
| snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "qcom,%s-lpm-supported", name); |
| rc = of_property_read_u32(dev->of_node, prop_name, &prop_val); |
| if (rc) { |
| dev_dbg(dev, "%s: Looking up %s property in node %s failed", |
| __func__, prop_name, dev->of_node->full_name); |
| cdc_vreg->lpm_supported = 0; |
| rc = 0; |
| } else { |
| cdc_vreg->lpm_supported = prop_val; |
| } |
| |
| dev_info(dev, "%s: %s: vol=[%d %d]uV, curr=[%d]uA, ond %d lpm %d\n", |
| __func__, cdc_vreg->name, cdc_vreg->min_uV, cdc_vreg->max_uV, |
| cdc_vreg->optimum_uA, cdc_vreg->ondemand, |
| cdc_vreg->lpm_supported); |
| |
| done: |
| return rc; |
| } |
| |
| static int msm_cdc_parse_supplies(struct device *dev, |
| struct cdc_regulator *cdc_reg, |
| const char *sup_list, int sup_cnt, |
| bool is_ond) |
| { |
| int idx, rc = 0; |
| const char *name = NULL; |
| |
| for (idx = 0; idx < sup_cnt; idx++) { |
| rc = of_property_read_string_index(dev->of_node, sup_list, idx, |
| &name); |
| if (rc) { |
| dev_err(dev, "%s: read string %s[%d] error (%d)\n", |
| __func__, sup_list, idx, rc); |
| goto done; |
| } |
| |
| dev_dbg(dev, "%s: Found cdc supply %s as part of %s\n", |
| __func__, name, sup_list); |
| |
| rc = msm_cdc_dt_parse_vreg_info(dev, &cdc_reg[idx], name, |
| is_ond); |
| if (rc) { |
| dev_err(dev, "%s: parse %s vreg info failed (%d)\n", |
| __func__, name, rc); |
| goto done; |
| } |
| } |
| |
| done: |
| return rc; |
| } |
| |
| static int msm_cdc_check_supply_param(struct device *dev, |
| struct cdc_regulator *cdc_vreg, |
| int num_supplies) |
| { |
| if (!dev) { |
| pr_err("%s: device is NULL\n", __func__); |
| return -ENODEV; |
| } |
| |
| if (!cdc_vreg || (num_supplies <= 0)) { |
| dev_err(dev, "%s: supply check failed: vreg: %pK, num_supplies: %d\n", |
| __func__, cdc_vreg, num_supplies); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * msm_cdc_is_ondemand_supply: |
| * return if ondemand supply true or not |
| * |
| * @dev: pointer to codec device |
| * @supplies: pointer to regulator bulk data |
| * @cdc_vreg: pointer to platform regulator data |
| * @num_supplies: number of supplies |
| * @supply_name: supply name to be checked |
| * |
| * Return true/false |
| */ |
| bool msm_cdc_is_ondemand_supply(struct device *dev, |
| struct regulator_bulk_data *supplies, |
| struct cdc_regulator *cdc_vreg, |
| int num_supplies, |
| char *supply_name) |
| { |
| bool rc = false; |
| int ret, i; |
| |
| if ((!supply_name) || (!supplies)) { |
| pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", |
| __func__); |
| return rc; |
| } |
| /* input parameter validation */ |
| ret = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); |
| if (ret) |
| return rc; |
| |
| for (i = 0; i < num_supplies; i++) { |
| if (cdc_vreg[i].ondemand && |
| !strcmp(cdc_vreg[i].name, supply_name)) |
| return true; |
| } |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(msm_cdc_is_ondemand_supply); |
| |
| /* |
| * msm_cdc_set_supply_min_voltage: |
| * Set min supply voltage for particular supply |
| * |
| * @dev: pointer to codec device |
| * @supplies: pointer to regulator bulk data |
| * @cdc_vreg: pointer to platform regulator data |
| * @num_supplies: number of supplies |
| * @supply_name: Supply name to change voltage for |
| * @vval_min: Min voltage to be set in uV |
| * @override_min_vol: True if override min voltage from default |
| * Return error code if unable to set voltage |
| */ |
| int msm_cdc_set_supply_min_voltage(struct device *dev, |
| struct regulator_bulk_data *supplies, |
| struct cdc_regulator *cdc_vreg, |
| int num_supplies, char *supply_name, |
| int vval_min, bool override_min_vol) |
| { |
| int rc = 0, i; |
| |
| if ((!supply_name) || (!supplies)) { |
| pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", |
| __func__); |
| return -EINVAL; |
| } |
| /* input parameter validation */ |
| rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); |
| if (rc) |
| return rc; |
| for (i = 0; i < num_supplies; i++) { |
| if (!strcmp(cdc_vreg[i].name, supply_name)) { |
| if (override_min_vol) |
| regulator_set_voltage(supplies[i].consumer, |
| vval_min, cdc_vreg[i].max_uV); |
| else |
| regulator_set_voltage(supplies[i].consumer, |
| cdc_vreg[i].min_uV, cdc_vreg[i].max_uV); |
| break; |
| } |
| } |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(msm_cdc_set_supply_min_voltage); |
| |
| /* |
| * msm_cdc_disable_ondemand_supply: |
| * Disable codec ondemand supply |
| * |
| * @dev: pointer to codec device |
| * @supplies: pointer to regulator bulk data |
| * @cdc_vreg: pointer to platform regulator data |
| * @num_supplies: number of supplies |
| * @supply_name: Ondemand supply name to be enabled |
| * |
| * Return error code if supply disable is failed |
| */ |
| int msm_cdc_disable_ondemand_supply(struct device *dev, |
| struct regulator_bulk_data *supplies, |
| struct cdc_regulator *cdc_vreg, |
| int num_supplies, |
| char *supply_name) |
| { |
| int rc, i; |
| |
| if ((!supply_name) || (!supplies)) { |
| pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", |
| __func__); |
| return -EINVAL; |
| } |
| /* input parameter validation */ |
| rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); |
| if (rc) |
| return rc; |
| |
| for (i = 0; i < num_supplies; i++) { |
| if (cdc_vreg[i].ondemand && |
| !strcmp(cdc_vreg[i].name, supply_name)) { |
| rc = regulator_disable(supplies[i].consumer); |
| if (rc) |
| dev_err(dev, "%s: failed to disable supply %s, err:%d\n", |
| __func__, supplies[i].supply, rc); |
| break; |
| } |
| } |
| if (i == num_supplies) { |
| dev_err(dev, "%s: not able to find supply %s\n", |
| __func__, supply_name); |
| rc = -EINVAL; |
| } |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(msm_cdc_disable_ondemand_supply); |
| |
| /* |
| * msm_cdc_enable_ondemand_supply: |
| * Enable codec ondemand supply |
| * |
| * @dev: pointer to codec device |
| * @supplies: pointer to regulator bulk data |
| * @cdc_vreg: pointer to platform regulator data |
| * @num_supplies: number of supplies |
| * @supply_name: Ondemand supply name to be enabled |
| * |
| * Return error code if supply enable is failed |
| */ |
| int msm_cdc_enable_ondemand_supply(struct device *dev, |
| struct regulator_bulk_data *supplies, |
| struct cdc_regulator *cdc_vreg, |
| int num_supplies, |
| char *supply_name) |
| { |
| int rc, i; |
| |
| if ((!supply_name) || (!supplies)) { |
| pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", |
| __func__); |
| return -EINVAL; |
| } |
| /* input parameter validation */ |
| rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); |
| if (rc) |
| return rc; |
| |
| for (i = 0; i < num_supplies; i++) { |
| if (cdc_vreg[i].ondemand && |
| !strcmp(cdc_vreg[i].name, supply_name)) { |
| rc = regulator_enable(supplies[i].consumer); |
| if (rc) |
| dev_err(dev, "%s: failed to enable supply %s, rc: %d\n", |
| __func__, supplies[i].supply, rc); |
| break; |
| } |
| } |
| if (i == num_supplies) { |
| dev_err(dev, "%s: not able to find supply %s\n", |
| __func__, supply_name); |
| rc = -EINVAL; |
| } |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(msm_cdc_enable_ondemand_supply); |
| |
| /* |
| * msm_cdc_set_supplies_lpm_mode: |
| * Update load for given supply string |
| * |
| * @dev: pointer to codec device |
| * @supplies: pointer to regulator bulk data |
| * @cdc_vreg: pointer to platform regulator data |
| * @num_supplies: number of supplies |
| * @supply_name: supply name to be checked |
| * @min_max: Apply optimum or 0 current |
| * |
| * Return error code if set current fail |
| */ |
| int msm_cdc_set_supplies_lpm_mode(struct device *dev, |
| struct regulator_bulk_data *supplies, |
| struct cdc_regulator *cdc_vreg, |
| int num_supplies, |
| bool flag) |
| { |
| int rc = 0, i; |
| |
| if (!supplies) { |
| pr_err("%s: supplies is NULL\n", |
| __func__); |
| return -EINVAL; |
| } |
| /* input parameter validation */ |
| rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); |
| if (rc) |
| return rc; |
| |
| for (i = 0; i < num_supplies; i++) { |
| if (cdc_vreg[i].lpm_supported) { |
| rc = regulator_set_load( |
| supplies[i].consumer, |
| flag ? 0 : cdc_vreg[i].optimum_uA); |
| if (rc) |
| dev_err(dev, |
| "%s: failed to set supply %s to %s, err:%d\n", |
| __func__, supplies[i].supply, |
| flag ? "LPM" : "NOM", |
| rc); |
| else |
| dev_dbg(dev, "%s: regulator %s load set to %s\n", |
| __func__, supplies[i].supply, |
| flag ? "LPM" : "NOM"); |
| } |
| } |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(msm_cdc_set_supplies_lpm_mode); |
| |
| /* |
| * msm_cdc_disable_static_supplies: |
| * Disable codec static supplies |
| * |
| * @dev: pointer to codec device |
| * @supplies: pointer to regulator bulk data |
| * @cdc_vreg: pointer to platform regulator data |
| * @num_supplies: number of supplies |
| * |
| * Return error code if supply disable is failed |
| */ |
| int msm_cdc_disable_static_supplies(struct device *dev, |
| struct regulator_bulk_data *supplies, |
| struct cdc_regulator *cdc_vreg, |
| int num_supplies) |
| { |
| int rc, i; |
| |
| if ((!dev) || (!supplies) || (!cdc_vreg)) { |
| pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", |
| __func__); |
| return -EINVAL; |
| } |
| /* input parameter validation */ |
| rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); |
| if (rc) |
| return rc; |
| |
| for (i = 0; i < num_supplies; i++) { |
| if (cdc_vreg[i].ondemand) |
| continue; |
| |
| rc = regulator_disable(supplies[i].consumer); |
| if (rc) |
| dev_err(dev, "%s: failed to disable supply %s, err:%d\n", |
| __func__, supplies[i].supply, rc); |
| else |
| dev_dbg(dev, "%s: disabled regulator %s\n", |
| __func__, supplies[i].supply); |
| } |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(msm_cdc_disable_static_supplies); |
| |
| /* |
| * msm_cdc_release_supplies: |
| * Release codec power supplies |
| * |
| * @dev: pointer to codec device |
| * @supplies: pointer to regulator bulk data |
| * @cdc_vreg: pointer to platform regulator data |
| * @num_supplies: number of supplies |
| * |
| * Return error code if supply disable is failed |
| */ |
| int msm_cdc_release_supplies(struct device *dev, |
| struct regulator_bulk_data *supplies, |
| struct cdc_regulator *cdc_vreg, |
| int num_supplies) |
| { |
| int rc = 0; |
| int i; |
| |
| if ((!dev) || (!supplies) || (!cdc_vreg)) { |
| pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", |
| __func__); |
| return -EINVAL; |
| } |
| /* input parameter validation */ |
| rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); |
| if (rc) |
| return rc; |
| |
| msm_cdc_disable_static_supplies(dev, supplies, cdc_vreg, |
| num_supplies); |
| for (i = 0; i < num_supplies; i++) { |
| if (regulator_count_voltages(supplies[i].consumer) < 0) |
| continue; |
| |
| regulator_set_voltage(supplies[i].consumer, 0, |
| cdc_vreg[i].max_uV); |
| regulator_set_load(supplies[i].consumer, 0); |
| } |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(msm_cdc_release_supplies); |
| |
| /* |
| * msm_cdc_enable_static_supplies: |
| * Enable codec static supplies |
| * |
| * @dev: pointer to codec device |
| * @supplies: pointer to regulator bulk data |
| * @cdc_vreg: pointer to platform regulator data |
| * @num_supplies: number of supplies |
| * |
| * Return error code if supply enable is failed |
| */ |
| int msm_cdc_enable_static_supplies(struct device *dev, |
| struct regulator_bulk_data *supplies, |
| struct cdc_regulator *cdc_vreg, |
| int num_supplies) |
| { |
| int rc, i; |
| |
| if ((!dev) || (!supplies) || (!cdc_vreg)) { |
| pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", |
| __func__); |
| return -EINVAL; |
| } |
| /* input parameter validation */ |
| rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); |
| if (rc) |
| return rc; |
| |
| for (i = 0; i < num_supplies; i++) { |
| if (cdc_vreg[i].ondemand) |
| continue; |
| |
| rc = regulator_enable(supplies[i].consumer); |
| if (rc) { |
| dev_err(dev, "%s: failed to enable supply %s, rc: %d\n", |
| __func__, supplies[i].supply, rc); |
| break; |
| } |
| } |
| |
| while (rc && i--) |
| if (!cdc_vreg[i].ondemand) |
| regulator_disable(supplies[i].consumer); |
| |
| return rc; |
| } |
| EXPORT_SYMBOL(msm_cdc_enable_static_supplies); |
| |
| /* |
| * msm_cdc_init_supplies: |
| * Initialize codec static supplies |
| * |
| * @dev: pointer to codec device |
| * @supplies: pointer to regulator bulk data |
| * @cdc_vreg: pointer to platform regulator data |
| * @num_supplies: number of supplies |
| * |
| * Return error code if supply init is failed |
| */ |
| int msm_cdc_init_supplies(struct device *dev, |
| struct regulator_bulk_data **supplies, |
| struct cdc_regulator *cdc_vreg, |
| int num_supplies) |
| { |
| return msm_cdc_init_supplies_v2(dev, supplies, cdc_vreg, |
| num_supplies, false); |
| } |
| EXPORT_SYMBOL(msm_cdc_init_supplies); |
| |
| /* |
| * msm_cdc_init_supplies_v2: |
| * Initialize codec static supplies. |
| * Initialize codec dynamic supplies based on vote_regulator_on_demand |
| * |
| * @dev: pointer to codec device |
| * @supplies: pointer to regulator bulk data |
| * @cdc_vreg: pointer to platform regulator data |
| * @num_supplies: number of supplies |
| * @vote_regulator_on_demand: initialize codec dynamic supplies at runtime |
| * |
| * Return error code if supply init is failed |
| */ |
| int msm_cdc_init_supplies_v2(struct device *dev, |
| struct regulator_bulk_data **supplies, |
| struct cdc_regulator *cdc_vreg, |
| int num_supplies, u32 vote_regulator_on_demand) |
| { |
| struct regulator_bulk_data *vsup; |
| int rc; |
| int i; |
| |
| if (!dev || !cdc_vreg) { |
| pr_err("%s: device pointer or dce_vreg is NULL\n", |
| __func__); |
| return -EINVAL; |
| } |
| /* input parameter validation */ |
| rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); |
| if (rc) |
| return rc; |
| |
| vsup = devm_kcalloc(dev, num_supplies, |
| sizeof(struct regulator_bulk_data), |
| GFP_KERNEL); |
| if (!vsup) |
| return -ENOMEM; |
| |
| for (i = 0; i < num_supplies; i++) { |
| if (!cdc_vreg[i].name) { |
| dev_err(dev, "%s: supply name not defined\n", |
| __func__); |
| rc = -EINVAL; |
| goto err_supply; |
| } |
| vsup[i].supply = cdc_vreg[i].name; |
| } |
| |
| rc = devm_regulator_bulk_get(dev, num_supplies, vsup); |
| if (rc) { |
| dev_err(dev, "%s: failed to get supplies (%d)\n", |
| __func__, rc); |
| goto err_supply; |
| } |
| |
| /* Set voltage and current on regulators */ |
| for (i = 0; i < num_supplies; i++) { |
| if (regulator_count_voltages(vsup[i].consumer) < 0) |
| continue; |
| |
| if (cdc_vreg[i].ondemand && vote_regulator_on_demand) |
| continue; |
| |
| rc = regulator_set_voltage(vsup[i].consumer, |
| cdc_vreg[i].min_uV, |
| cdc_vreg[i].max_uV); |
| if (rc) { |
| dev_err(dev, "%s: set regulator voltage failed for %s, err:%d\n", |
| __func__, vsup[i].supply, rc); |
| goto err_supply; |
| } |
| rc = regulator_set_load(vsup[i].consumer, |
| cdc_vreg[i].optimum_uA); |
| if (rc < 0) { |
| dev_err(dev, "%s: set regulator optimum mode failed for %s, err:%d\n", |
| __func__, vsup[i].supply, rc); |
| goto err_supply; |
| } |
| } |
| |
| *supplies = vsup; |
| |
| return 0; |
| |
| err_supply: |
| return rc; |
| } |
| EXPORT_SYMBOL(msm_cdc_init_supplies_v2); |
| |
| /* |
| * msm_cdc_get_power_supplies: |
| * Get codec power supplies from device tree. |
| * Allocate memory to hold regulator data for |
| * all power supplies. |
| * |
| * @dev: pointer to codec device |
| * @cdc_vreg: pointer to codec regulator |
| * @total_num_supplies: total number of supplies read from DT |
| * |
| * Return error code if supply disable is failed |
| */ |
| int msm_cdc_get_power_supplies(struct device *dev, |
| struct cdc_regulator **cdc_vreg, |
| int *total_num_supplies) |
| { |
| const char *static_prop_name = "qcom,cdc-static-supplies"; |
| const char *ond_prop_name = "qcom,cdc-on-demand-supplies"; |
| const char *cp_prop_name = "qcom,cdc-cp-supplies"; |
| int static_sup_cnt = 0; |
| int ond_sup_cnt = 0; |
| int cp_sup_cnt = 0; |
| int num_supplies = 0; |
| struct cdc_regulator *cdc_reg; |
| int rc; |
| |
| if (!dev) { |
| pr_err("%s: device pointer is NULL\n", __func__); |
| return -EINVAL; |
| } |
| static_sup_cnt = of_property_count_strings(dev->of_node, |
| static_prop_name); |
| if (static_sup_cnt < 0) { |
| dev_err(dev, "%s: Failed to get static supplies(%d)\n", |
| __func__, static_sup_cnt); |
| rc = static_sup_cnt; |
| goto err_supply_cnt; |
| } |
| ond_sup_cnt = of_property_count_strings(dev->of_node, ond_prop_name); |
| if (ond_sup_cnt < 0) |
| ond_sup_cnt = 0; |
| |
| cp_sup_cnt = of_property_count_strings(dev->of_node, |
| cp_prop_name); |
| if (cp_sup_cnt < 0) |
| cp_sup_cnt = 0; |
| |
| num_supplies = static_sup_cnt + ond_sup_cnt + cp_sup_cnt; |
| if (num_supplies <= 0) { |
| dev_err(dev, "%s: supply count is 0 or negative\n", __func__); |
| rc = -EINVAL; |
| goto err_supply_cnt; |
| } |
| |
| cdc_reg = devm_kcalloc(dev, num_supplies, |
| sizeof(struct cdc_regulator), |
| GFP_KERNEL); |
| if (!cdc_reg) { |
| rc = -ENOMEM; |
| goto err_mem_alloc; |
| } |
| |
| rc = msm_cdc_parse_supplies(dev, cdc_reg, static_prop_name, |
| static_sup_cnt, false); |
| if (rc) { |
| dev_err(dev, "%s: failed to parse static supplies(%d)\n", |
| __func__, rc); |
| goto err_sup; |
| } |
| |
| rc = msm_cdc_parse_supplies(dev, &cdc_reg[static_sup_cnt], |
| ond_prop_name, ond_sup_cnt, |
| true); |
| if (rc) { |
| dev_err(dev, "%s: failed to parse demand supplies(%d)\n", |
| __func__, rc); |
| goto err_sup; |
| } |
| |
| rc = msm_cdc_parse_supplies(dev, |
| &cdc_reg[static_sup_cnt + ond_sup_cnt], |
| cp_prop_name, cp_sup_cnt, true); |
| if (rc) { |
| dev_err(dev, "%s: failed to parse cp supplies(%d)\n", |
| __func__, rc); |
| goto err_sup; |
| } |
| |
| *cdc_vreg = cdc_reg; |
| *total_num_supplies = num_supplies; |
| |
| return 0; |
| |
| err_sup: |
| err_supply_cnt: |
| err_mem_alloc: |
| return rc; |
| } |
| EXPORT_SYMBOL(msm_cdc_get_power_supplies); |