18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * OF helpers for regulator framework 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2011 Texas Instruments, Inc. 68c2ecf20Sopenharmony_ci * Rajendra Nayak <rnayak@ti.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/regulator/machine.h> 138c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h> 148c2ecf20Sopenharmony_ci#include <linux/regulator/of_regulator.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include "internal.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic const char *const regulator_states[PM_SUSPEND_MAX + 1] = { 198c2ecf20Sopenharmony_ci [PM_SUSPEND_STANDBY] = "regulator-state-standby", 208c2ecf20Sopenharmony_ci [PM_SUSPEND_MEM] = "regulator-state-mem", 218c2ecf20Sopenharmony_ci [PM_SUSPEND_MAX] = "regulator-state-disk", 228c2ecf20Sopenharmony_ci}; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int of_get_regulation_constraints(struct device *dev, 258c2ecf20Sopenharmony_ci struct device_node *np, 268c2ecf20Sopenharmony_ci struct regulator_init_data **init_data, 278c2ecf20Sopenharmony_ci const struct regulator_desc *desc) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci struct regulation_constraints *constraints = &(*init_data)->constraints; 308c2ecf20Sopenharmony_ci struct regulator_state *suspend_state; 318c2ecf20Sopenharmony_ci struct device_node *suspend_np; 328c2ecf20Sopenharmony_ci unsigned int mode; 338c2ecf20Sopenharmony_ci int ret, i, len; 348c2ecf20Sopenharmony_ci int n_phandles; 358c2ecf20Sopenharmony_ci u32 pval; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci n_phandles = of_count_phandle_with_args(np, "regulator-coupled-with", 388c2ecf20Sopenharmony_ci NULL); 398c2ecf20Sopenharmony_ci n_phandles = max(n_phandles, 0); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci constraints->name = of_get_property(np, "regulator-name", NULL); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "regulator-min-microvolt", &pval)) 448c2ecf20Sopenharmony_ci constraints->min_uV = pval; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "regulator-max-microvolt", &pval)) 478c2ecf20Sopenharmony_ci constraints->max_uV = pval; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci /* Voltage change possible? */ 508c2ecf20Sopenharmony_ci if (constraints->min_uV != constraints->max_uV) 518c2ecf20Sopenharmony_ci constraints->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci /* Do we have a voltage range, if so try to apply it? */ 548c2ecf20Sopenharmony_ci if (constraints->min_uV && constraints->max_uV) 558c2ecf20Sopenharmony_ci constraints->apply_uV = true; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "regulator-microvolt-offset", &pval)) 588c2ecf20Sopenharmony_ci constraints->uV_offset = pval; 598c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "regulator-min-microamp", &pval)) 608c2ecf20Sopenharmony_ci constraints->min_uA = pval; 618c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "regulator-max-microamp", &pval)) 628c2ecf20Sopenharmony_ci constraints->max_uA = pval; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "regulator-input-current-limit-microamp", 658c2ecf20Sopenharmony_ci &pval)) 668c2ecf20Sopenharmony_ci constraints->ilim_uA = pval; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci /* Current change possible? */ 698c2ecf20Sopenharmony_ci if (constraints->min_uA != constraints->max_uA) 708c2ecf20Sopenharmony_ci constraints->valid_ops_mask |= REGULATOR_CHANGE_CURRENT; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci constraints->boot_on = of_property_read_bool(np, "regulator-boot-on"); 738c2ecf20Sopenharmony_ci constraints->always_on = of_property_read_bool(np, "regulator-always-on"); 748c2ecf20Sopenharmony_ci if (!constraints->always_on) /* status change should be possible. */ 758c2ecf20Sopenharmony_ci constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci constraints->pull_down = of_property_read_bool(np, "regulator-pull-down"); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "regulator-allow-bypass")) 808c2ecf20Sopenharmony_ci constraints->valid_ops_mask |= REGULATOR_CHANGE_BYPASS; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "regulator-allow-set-load")) 838c2ecf20Sopenharmony_ci constraints->valid_ops_mask |= REGULATOR_CHANGE_DRMS; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "regulator-ramp-delay", &pval); 868c2ecf20Sopenharmony_ci if (!ret) { 878c2ecf20Sopenharmony_ci if (pval) 888c2ecf20Sopenharmony_ci constraints->ramp_delay = pval; 898c2ecf20Sopenharmony_ci else 908c2ecf20Sopenharmony_ci constraints->ramp_disable = true; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "regulator-settling-time-us", &pval); 948c2ecf20Sopenharmony_ci if (!ret) 958c2ecf20Sopenharmony_ci constraints->settling_time = pval; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "regulator-settling-time-up-us", &pval); 988c2ecf20Sopenharmony_ci if (!ret) 998c2ecf20Sopenharmony_ci constraints->settling_time_up = pval; 1008c2ecf20Sopenharmony_ci if (constraints->settling_time_up && constraints->settling_time) { 1018c2ecf20Sopenharmony_ci pr_warn("%pOFn: ambiguous configuration for settling time, ignoring 'regulator-settling-time-up-us'\n", 1028c2ecf20Sopenharmony_ci np); 1038c2ecf20Sopenharmony_ci constraints->settling_time_up = 0; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "regulator-settling-time-down-us", 1078c2ecf20Sopenharmony_ci &pval); 1088c2ecf20Sopenharmony_ci if (!ret) 1098c2ecf20Sopenharmony_ci constraints->settling_time_down = pval; 1108c2ecf20Sopenharmony_ci if (constraints->settling_time_down && constraints->settling_time) { 1118c2ecf20Sopenharmony_ci pr_warn("%pOFn: ambiguous configuration for settling time, ignoring 'regulator-settling-time-down-us'\n", 1128c2ecf20Sopenharmony_ci np); 1138c2ecf20Sopenharmony_ci constraints->settling_time_down = 0; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "regulator-enable-ramp-delay", &pval); 1178c2ecf20Sopenharmony_ci if (!ret) 1188c2ecf20Sopenharmony_ci constraints->enable_time = pval; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci constraints->soft_start = of_property_read_bool(np, 1218c2ecf20Sopenharmony_ci "regulator-soft-start"); 1228c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "regulator-active-discharge", &pval); 1238c2ecf20Sopenharmony_ci if (!ret) { 1248c2ecf20Sopenharmony_ci constraints->active_discharge = 1258c2ecf20Sopenharmony_ci (pval) ? REGULATOR_ACTIVE_DISCHARGE_ENABLE : 1268c2ecf20Sopenharmony_ci REGULATOR_ACTIVE_DISCHARGE_DISABLE; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "regulator-initial-mode", &pval)) { 1308c2ecf20Sopenharmony_ci if (desc && desc->of_map_mode) { 1318c2ecf20Sopenharmony_ci mode = desc->of_map_mode(pval); 1328c2ecf20Sopenharmony_ci if (mode == REGULATOR_MODE_INVALID) 1338c2ecf20Sopenharmony_ci pr_err("%pOFn: invalid mode %u\n", np, pval); 1348c2ecf20Sopenharmony_ci else 1358c2ecf20Sopenharmony_ci constraints->initial_mode = mode; 1368c2ecf20Sopenharmony_ci } else { 1378c2ecf20Sopenharmony_ci pr_warn("%pOFn: mapping for mode %d not defined\n", 1388c2ecf20Sopenharmony_ci np, pval); 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci len = of_property_count_elems_of_size(np, "regulator-allowed-modes", 1438c2ecf20Sopenharmony_ci sizeof(u32)); 1448c2ecf20Sopenharmony_ci if (len > 0) { 1458c2ecf20Sopenharmony_ci if (desc && desc->of_map_mode) { 1468c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 1478c2ecf20Sopenharmony_ci ret = of_property_read_u32_index(np, 1488c2ecf20Sopenharmony_ci "regulator-allowed-modes", i, &pval); 1498c2ecf20Sopenharmony_ci if (ret) { 1508c2ecf20Sopenharmony_ci pr_err("%pOFn: couldn't read allowed modes index %d, ret=%d\n", 1518c2ecf20Sopenharmony_ci np, i, ret); 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci mode = desc->of_map_mode(pval); 1558c2ecf20Sopenharmony_ci if (mode == REGULATOR_MODE_INVALID) 1568c2ecf20Sopenharmony_ci pr_err("%pOFn: invalid regulator-allowed-modes element %u\n", 1578c2ecf20Sopenharmony_ci np, pval); 1588c2ecf20Sopenharmony_ci else 1598c2ecf20Sopenharmony_ci constraints->valid_modes_mask |= mode; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci if (constraints->valid_modes_mask) 1628c2ecf20Sopenharmony_ci constraints->valid_ops_mask 1638c2ecf20Sopenharmony_ci |= REGULATOR_CHANGE_MODE; 1648c2ecf20Sopenharmony_ci } else { 1658c2ecf20Sopenharmony_ci pr_warn("%pOFn: mode mapping not defined\n", np); 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "regulator-system-load", &pval)) 1708c2ecf20Sopenharmony_ci constraints->system_load = pval; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (n_phandles) { 1738c2ecf20Sopenharmony_ci constraints->max_spread = devm_kzalloc(dev, 1748c2ecf20Sopenharmony_ci sizeof(*constraints->max_spread) * n_phandles, 1758c2ecf20Sopenharmony_ci GFP_KERNEL); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (!constraints->max_spread) 1788c2ecf20Sopenharmony_ci return -ENOMEM; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci of_property_read_u32_array(np, "regulator-coupled-max-spread", 1818c2ecf20Sopenharmony_ci constraints->max_spread, n_phandles); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "regulator-max-step-microvolt", 1858c2ecf20Sopenharmony_ci &pval)) 1868c2ecf20Sopenharmony_ci constraints->max_uV_step = pval; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci constraints->over_current_protection = of_property_read_bool(np, 1898c2ecf20Sopenharmony_ci "regulator-over-current-protection"); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(regulator_states); i++) { 1928c2ecf20Sopenharmony_ci switch (i) { 1938c2ecf20Sopenharmony_ci case PM_SUSPEND_MEM: 1948c2ecf20Sopenharmony_ci suspend_state = &constraints->state_mem; 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci case PM_SUSPEND_MAX: 1978c2ecf20Sopenharmony_ci suspend_state = &constraints->state_disk; 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci case PM_SUSPEND_STANDBY: 2008c2ecf20Sopenharmony_ci suspend_state = &constraints->state_standby; 2018c2ecf20Sopenharmony_ci break; 2028c2ecf20Sopenharmony_ci case PM_SUSPEND_ON: 2038c2ecf20Sopenharmony_ci case PM_SUSPEND_TO_IDLE: 2048c2ecf20Sopenharmony_ci default: 2058c2ecf20Sopenharmony_ci continue; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci suspend_np = of_get_child_by_name(np, regulator_states[i]); 2098c2ecf20Sopenharmony_ci if (!suspend_np) 2108c2ecf20Sopenharmony_ci continue; 2118c2ecf20Sopenharmony_ci if (!suspend_state) { 2128c2ecf20Sopenharmony_ci of_node_put(suspend_np); 2138c2ecf20Sopenharmony_ci continue; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (!of_property_read_u32(suspend_np, "regulator-mode", 2178c2ecf20Sopenharmony_ci &pval)) { 2188c2ecf20Sopenharmony_ci if (desc && desc->of_map_mode) { 2198c2ecf20Sopenharmony_ci mode = desc->of_map_mode(pval); 2208c2ecf20Sopenharmony_ci if (mode == REGULATOR_MODE_INVALID) 2218c2ecf20Sopenharmony_ci pr_err("%pOFn: invalid mode %u\n", 2228c2ecf20Sopenharmony_ci np, pval); 2238c2ecf20Sopenharmony_ci else 2248c2ecf20Sopenharmony_ci suspend_state->mode = mode; 2258c2ecf20Sopenharmony_ci } else { 2268c2ecf20Sopenharmony_ci pr_warn("%pOFn: mapping for mode %d not defined\n", 2278c2ecf20Sopenharmony_ci np, pval); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (of_property_read_bool(suspend_np, 2328c2ecf20Sopenharmony_ci "regulator-on-in-suspend")) 2338c2ecf20Sopenharmony_ci suspend_state->enabled = ENABLE_IN_SUSPEND; 2348c2ecf20Sopenharmony_ci else if (of_property_read_bool(suspend_np, 2358c2ecf20Sopenharmony_ci "regulator-off-in-suspend")) 2368c2ecf20Sopenharmony_ci suspend_state->enabled = DISABLE_IN_SUSPEND; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (!of_property_read_u32(suspend_np, 2398c2ecf20Sopenharmony_ci "regulator-suspend-min-microvolt", &pval)) 2408c2ecf20Sopenharmony_ci suspend_state->min_uV = pval; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (!of_property_read_u32(suspend_np, 2438c2ecf20Sopenharmony_ci "regulator-suspend-max-microvolt", &pval)) 2448c2ecf20Sopenharmony_ci suspend_state->max_uV = pval; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (!of_property_read_u32(suspend_np, 2478c2ecf20Sopenharmony_ci "regulator-suspend-microvolt", &pval)) 2488c2ecf20Sopenharmony_ci suspend_state->uV = pval; 2498c2ecf20Sopenharmony_ci else /* otherwise use min_uV as default suspend voltage */ 2508c2ecf20Sopenharmony_ci suspend_state->uV = suspend_state->min_uV; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (of_property_read_bool(suspend_np, 2538c2ecf20Sopenharmony_ci "regulator-changeable-in-suspend")) 2548c2ecf20Sopenharmony_ci suspend_state->changeable = true; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (i == PM_SUSPEND_MEM) 2578c2ecf20Sopenharmony_ci constraints->initial_state = PM_SUSPEND_MEM; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci of_node_put(suspend_np); 2608c2ecf20Sopenharmony_ci suspend_state = NULL; 2618c2ecf20Sopenharmony_ci suspend_np = NULL; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return 0; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci/** 2688c2ecf20Sopenharmony_ci * of_get_regulator_init_data - extract regulator_init_data structure info 2698c2ecf20Sopenharmony_ci * @dev: device requesting for regulator_init_data 2708c2ecf20Sopenharmony_ci * @node: regulator device node 2718c2ecf20Sopenharmony_ci * @desc: regulator description 2728c2ecf20Sopenharmony_ci * 2738c2ecf20Sopenharmony_ci * Populates regulator_init_data structure by extracting data from device 2748c2ecf20Sopenharmony_ci * tree node, returns a pointer to the populated structure or NULL if memory 2758c2ecf20Sopenharmony_ci * alloc fails. 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_cistruct regulator_init_data *of_get_regulator_init_data(struct device *dev, 2788c2ecf20Sopenharmony_ci struct device_node *node, 2798c2ecf20Sopenharmony_ci const struct regulator_desc *desc) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct regulator_init_data *init_data; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (!node) 2848c2ecf20Sopenharmony_ci return NULL; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci init_data = devm_kzalloc(dev, sizeof(*init_data), GFP_KERNEL); 2878c2ecf20Sopenharmony_ci if (!init_data) 2888c2ecf20Sopenharmony_ci return NULL; /* Out of memory? */ 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (of_get_regulation_constraints(dev, node, &init_data, desc)) 2918c2ecf20Sopenharmony_ci return NULL; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci return init_data; 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(of_get_regulator_init_data); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistruct devm_of_regulator_matches { 2988c2ecf20Sopenharmony_ci struct of_regulator_match *matches; 2998c2ecf20Sopenharmony_ci unsigned int num_matches; 3008c2ecf20Sopenharmony_ci}; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic void devm_of_regulator_put_matches(struct device *dev, void *res) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct devm_of_regulator_matches *devm_matches = res; 3058c2ecf20Sopenharmony_ci int i; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci for (i = 0; i < devm_matches->num_matches; i++) 3088c2ecf20Sopenharmony_ci of_node_put(devm_matches->matches[i].of_node); 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci/** 3128c2ecf20Sopenharmony_ci * of_regulator_match - extract multiple regulator init data from device tree. 3138c2ecf20Sopenharmony_ci * @dev: device requesting the data 3148c2ecf20Sopenharmony_ci * @node: parent device node of the regulators 3158c2ecf20Sopenharmony_ci * @matches: match table for the regulators 3168c2ecf20Sopenharmony_ci * @num_matches: number of entries in match table 3178c2ecf20Sopenharmony_ci * 3188c2ecf20Sopenharmony_ci * This function uses a match table specified by the regulator driver to 3198c2ecf20Sopenharmony_ci * parse regulator init data from the device tree. @node is expected to 3208c2ecf20Sopenharmony_ci * contain a set of child nodes, each providing the init data for one 3218c2ecf20Sopenharmony_ci * regulator. The data parsed from a child node will be matched to a regulator 3228c2ecf20Sopenharmony_ci * based on either the deprecated property regulator-compatible if present, 3238c2ecf20Sopenharmony_ci * or otherwise the child node's name. Note that the match table is modified 3248c2ecf20Sopenharmony_ci * in place and an additional of_node reference is taken for each matched 3258c2ecf20Sopenharmony_ci * regulator. 3268c2ecf20Sopenharmony_ci * 3278c2ecf20Sopenharmony_ci * Returns the number of matches found or a negative error code on failure. 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_ciint of_regulator_match(struct device *dev, struct device_node *node, 3308c2ecf20Sopenharmony_ci struct of_regulator_match *matches, 3318c2ecf20Sopenharmony_ci unsigned int num_matches) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci unsigned int count = 0; 3348c2ecf20Sopenharmony_ci unsigned int i; 3358c2ecf20Sopenharmony_ci const char *name; 3368c2ecf20Sopenharmony_ci struct device_node *child; 3378c2ecf20Sopenharmony_ci struct devm_of_regulator_matches *devm_matches; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (!dev || !node) 3408c2ecf20Sopenharmony_ci return -EINVAL; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci devm_matches = devres_alloc(devm_of_regulator_put_matches, 3438c2ecf20Sopenharmony_ci sizeof(struct devm_of_regulator_matches), 3448c2ecf20Sopenharmony_ci GFP_KERNEL); 3458c2ecf20Sopenharmony_ci if (!devm_matches) 3468c2ecf20Sopenharmony_ci return -ENOMEM; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci devm_matches->matches = matches; 3498c2ecf20Sopenharmony_ci devm_matches->num_matches = num_matches; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci devres_add(dev, devm_matches); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci for (i = 0; i < num_matches; i++) { 3548c2ecf20Sopenharmony_ci struct of_regulator_match *match = &matches[i]; 3558c2ecf20Sopenharmony_ci match->init_data = NULL; 3568c2ecf20Sopenharmony_ci match->of_node = NULL; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci for_each_child_of_node(node, child) { 3608c2ecf20Sopenharmony_ci name = of_get_property(child, 3618c2ecf20Sopenharmony_ci "regulator-compatible", NULL); 3628c2ecf20Sopenharmony_ci if (!name) 3638c2ecf20Sopenharmony_ci name = child->name; 3648c2ecf20Sopenharmony_ci for (i = 0; i < num_matches; i++) { 3658c2ecf20Sopenharmony_ci struct of_regulator_match *match = &matches[i]; 3668c2ecf20Sopenharmony_ci if (match->of_node) 3678c2ecf20Sopenharmony_ci continue; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (strcmp(match->name, name)) 3708c2ecf20Sopenharmony_ci continue; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci match->init_data = 3738c2ecf20Sopenharmony_ci of_get_regulator_init_data(dev, child, 3748c2ecf20Sopenharmony_ci match->desc); 3758c2ecf20Sopenharmony_ci if (!match->init_data) { 3768c2ecf20Sopenharmony_ci dev_err(dev, 3778c2ecf20Sopenharmony_ci "failed to parse DT for regulator %pOFn\n", 3788c2ecf20Sopenharmony_ci child); 3798c2ecf20Sopenharmony_ci of_node_put(child); 3808c2ecf20Sopenharmony_ci return -EINVAL; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci match->of_node = of_node_get(child); 3838c2ecf20Sopenharmony_ci count++; 3848c2ecf20Sopenharmony_ci break; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci return count; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(of_regulator_match); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistatic struct 3938c2ecf20Sopenharmony_cidevice_node *regulator_of_get_init_node(struct device *dev, 3948c2ecf20Sopenharmony_ci const struct regulator_desc *desc) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct device_node *search, *child; 3978c2ecf20Sopenharmony_ci const char *name; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (!dev->of_node || !desc->of_match) 4008c2ecf20Sopenharmony_ci return NULL; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (desc->regulators_node) { 4038c2ecf20Sopenharmony_ci search = of_get_child_by_name(dev->of_node, 4048c2ecf20Sopenharmony_ci desc->regulators_node); 4058c2ecf20Sopenharmony_ci } else { 4068c2ecf20Sopenharmony_ci search = of_node_get(dev->of_node); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (!strcmp(desc->of_match, search->name)) 4098c2ecf20Sopenharmony_ci return search; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (!search) { 4138c2ecf20Sopenharmony_ci dev_dbg(dev, "Failed to find regulator container node '%s'\n", 4148c2ecf20Sopenharmony_ci desc->regulators_node); 4158c2ecf20Sopenharmony_ci return NULL; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci for_each_available_child_of_node(search, child) { 4198c2ecf20Sopenharmony_ci name = of_get_property(child, "regulator-compatible", NULL); 4208c2ecf20Sopenharmony_ci if (!name) 4218c2ecf20Sopenharmony_ci name = child->name; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (!strcmp(desc->of_match, name)) { 4248c2ecf20Sopenharmony_ci of_node_put(search); 4258c2ecf20Sopenharmony_ci return of_node_get(child); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci of_node_put(search); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci return NULL; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistruct regulator_init_data *regulator_of_get_init_data(struct device *dev, 4358c2ecf20Sopenharmony_ci const struct regulator_desc *desc, 4368c2ecf20Sopenharmony_ci struct regulator_config *config, 4378c2ecf20Sopenharmony_ci struct device_node **node) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci struct device_node *child; 4408c2ecf20Sopenharmony_ci struct regulator_init_data *init_data = NULL; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci child = regulator_of_get_init_node(dev, desc); 4438c2ecf20Sopenharmony_ci if (!child) 4448c2ecf20Sopenharmony_ci return NULL; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci init_data = of_get_regulator_init_data(dev, child, desc); 4478c2ecf20Sopenharmony_ci if (!init_data) { 4488c2ecf20Sopenharmony_ci dev_err(dev, "failed to parse DT for regulator %pOFn\n", child); 4498c2ecf20Sopenharmony_ci goto error; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (desc->of_parse_cb) { 4538c2ecf20Sopenharmony_ci int ret; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci ret = desc->of_parse_cb(child, desc, config); 4568c2ecf20Sopenharmony_ci if (ret) { 4578c2ecf20Sopenharmony_ci if (ret == -EPROBE_DEFER) { 4588c2ecf20Sopenharmony_ci of_node_put(child); 4598c2ecf20Sopenharmony_ci return ERR_PTR(-EPROBE_DEFER); 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci dev_err(dev, 4628c2ecf20Sopenharmony_ci "driver callback failed to parse DT for regulator %pOFn\n", 4638c2ecf20Sopenharmony_ci child); 4648c2ecf20Sopenharmony_ci goto error; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci *node = child; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return init_data; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cierror: 4738c2ecf20Sopenharmony_ci of_node_put(child); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci return NULL; 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_cistruct regulator_dev *of_find_regulator_by_node(struct device_node *np) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci struct device *dev; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci dev = class_find_device_by_of_node(®ulator_class, np); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci return dev ? dev_to_rdev(dev) : NULL; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci/* 4888c2ecf20Sopenharmony_ci * Returns number of regulators coupled with rdev. 4898c2ecf20Sopenharmony_ci */ 4908c2ecf20Sopenharmony_ciint of_get_n_coupled(struct regulator_dev *rdev) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct device_node *node = rdev->dev.of_node; 4938c2ecf20Sopenharmony_ci int n_phandles; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci n_phandles = of_count_phandle_with_args(node, 4968c2ecf20Sopenharmony_ci "regulator-coupled-with", 4978c2ecf20Sopenharmony_ci NULL); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci return (n_phandles > 0) ? n_phandles : 0; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci/* Looks for "to_find" device_node in src's "regulator-coupled-with" property */ 5038c2ecf20Sopenharmony_cistatic bool of_coupling_find_node(struct device_node *src, 5048c2ecf20Sopenharmony_ci struct device_node *to_find, 5058c2ecf20Sopenharmony_ci int *index) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci int n_phandles, i; 5088c2ecf20Sopenharmony_ci bool found = false; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci n_phandles = of_count_phandle_with_args(src, 5118c2ecf20Sopenharmony_ci "regulator-coupled-with", 5128c2ecf20Sopenharmony_ci NULL); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci for (i = 0; i < n_phandles; i++) { 5158c2ecf20Sopenharmony_ci struct device_node *tmp = of_parse_phandle(src, 5168c2ecf20Sopenharmony_ci "regulator-coupled-with", i); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (!tmp) 5198c2ecf20Sopenharmony_ci break; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci /* found */ 5228c2ecf20Sopenharmony_ci if (tmp == to_find) 5238c2ecf20Sopenharmony_ci found = true; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci of_node_put(tmp); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (found) { 5288c2ecf20Sopenharmony_ci *index = i; 5298c2ecf20Sopenharmony_ci break; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci return found; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci/** 5378c2ecf20Sopenharmony_ci * of_check_coupling_data - Parse rdev's coupling properties and check data 5388c2ecf20Sopenharmony_ci * consistency 5398c2ecf20Sopenharmony_ci * @rdev: pointer to regulator_dev whose data is checked 5408c2ecf20Sopenharmony_ci * 5418c2ecf20Sopenharmony_ci * Function checks if all the following conditions are met: 5428c2ecf20Sopenharmony_ci * - rdev's max_spread is greater than 0 5438c2ecf20Sopenharmony_ci * - all coupled regulators have the same max_spread 5448c2ecf20Sopenharmony_ci * - all coupled regulators have the same number of regulator_dev phandles 5458c2ecf20Sopenharmony_ci * - all regulators are linked to each other 5468c2ecf20Sopenharmony_ci * 5478c2ecf20Sopenharmony_ci * Returns true if all conditions are met. 5488c2ecf20Sopenharmony_ci */ 5498c2ecf20Sopenharmony_cibool of_check_coupling_data(struct regulator_dev *rdev) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci struct device_node *node = rdev->dev.of_node; 5528c2ecf20Sopenharmony_ci int n_phandles = of_get_n_coupled(rdev); 5538c2ecf20Sopenharmony_ci struct device_node *c_node; 5548c2ecf20Sopenharmony_ci int index; 5558c2ecf20Sopenharmony_ci int i; 5568c2ecf20Sopenharmony_ci bool ret = true; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci /* iterate over rdev's phandles */ 5598c2ecf20Sopenharmony_ci for (i = 0; i < n_phandles; i++) { 5608c2ecf20Sopenharmony_ci int max_spread = rdev->constraints->max_spread[i]; 5618c2ecf20Sopenharmony_ci int c_max_spread, c_n_phandles; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (max_spread <= 0) { 5648c2ecf20Sopenharmony_ci dev_err(&rdev->dev, "max_spread value invalid\n"); 5658c2ecf20Sopenharmony_ci return false; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci c_node = of_parse_phandle(node, 5698c2ecf20Sopenharmony_ci "regulator-coupled-with", i); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci if (!c_node) 5728c2ecf20Sopenharmony_ci ret = false; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci c_n_phandles = of_count_phandle_with_args(c_node, 5758c2ecf20Sopenharmony_ci "regulator-coupled-with", 5768c2ecf20Sopenharmony_ci NULL); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (c_n_phandles != n_phandles) { 5798c2ecf20Sopenharmony_ci dev_err(&rdev->dev, "number of coupled reg phandles mismatch\n"); 5808c2ecf20Sopenharmony_ci ret = false; 5818c2ecf20Sopenharmony_ci goto clean; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci if (!of_coupling_find_node(c_node, node, &index)) { 5858c2ecf20Sopenharmony_ci dev_err(&rdev->dev, "missing 2-way linking for coupled regulators\n"); 5868c2ecf20Sopenharmony_ci ret = false; 5878c2ecf20Sopenharmony_ci goto clean; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (of_property_read_u32_index(c_node, "regulator-coupled-max-spread", 5918c2ecf20Sopenharmony_ci index, &c_max_spread)) { 5928c2ecf20Sopenharmony_ci ret = false; 5938c2ecf20Sopenharmony_ci goto clean; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci if (c_max_spread != max_spread) { 5978c2ecf20Sopenharmony_ci dev_err(&rdev->dev, 5988c2ecf20Sopenharmony_ci "coupled regulators max_spread mismatch\n"); 5998c2ecf20Sopenharmony_ci ret = false; 6008c2ecf20Sopenharmony_ci goto clean; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ciclean: 6048c2ecf20Sopenharmony_ci of_node_put(c_node); 6058c2ecf20Sopenharmony_ci if (!ret) 6068c2ecf20Sopenharmony_ci break; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci return ret; 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci/** 6138c2ecf20Sopenharmony_ci * of_parse_coupled regulator - Get regulator_dev pointer from rdev's property 6148c2ecf20Sopenharmony_ci * @rdev: Pointer to regulator_dev, whose DTS is used as a source to parse 6158c2ecf20Sopenharmony_ci * "regulator-coupled-with" property 6168c2ecf20Sopenharmony_ci * @index: Index in phandles array 6178c2ecf20Sopenharmony_ci * 6188c2ecf20Sopenharmony_ci * Returns the regulator_dev pointer parsed from DTS. If it has not been yet 6198c2ecf20Sopenharmony_ci * registered, returns NULL 6208c2ecf20Sopenharmony_ci */ 6218c2ecf20Sopenharmony_cistruct regulator_dev *of_parse_coupled_regulator(struct regulator_dev *rdev, 6228c2ecf20Sopenharmony_ci int index) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci struct device_node *node = rdev->dev.of_node; 6258c2ecf20Sopenharmony_ci struct device_node *c_node; 6268c2ecf20Sopenharmony_ci struct regulator_dev *c_rdev; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci c_node = of_parse_phandle(node, "regulator-coupled-with", index); 6298c2ecf20Sopenharmony_ci if (!c_node) 6308c2ecf20Sopenharmony_ci return NULL; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci c_rdev = of_find_regulator_by_node(c_node); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci of_node_put(c_node); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci return c_rdev; 6378c2ecf20Sopenharmony_ci} 638