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(&regulator_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