18f446e6fSRajendra Nayak /*
28f446e6fSRajendra Nayak  * OF helpers for regulator framework
38f446e6fSRajendra Nayak  *
48f446e6fSRajendra Nayak  * Copyright (C) 2011 Texas Instruments, Inc.
58f446e6fSRajendra Nayak  * Rajendra Nayak <rnayak@ti.com>
68f446e6fSRajendra Nayak  *
78f446e6fSRajendra Nayak  * This program is free software; you can redistribute it and/or modify
88f446e6fSRajendra Nayak  * it under the terms of the GNU General Public License as published by
98f446e6fSRajendra Nayak  * the Free Software Foundation; either version 2 of the License, or
108f446e6fSRajendra Nayak  * (at your option) any later version.
118f446e6fSRajendra Nayak  */
128f446e6fSRajendra Nayak 
13e69af5e9SAxel Lin #include <linux/module.h>
148f446e6fSRajendra Nayak #include <linux/slab.h>
158f446e6fSRajendra Nayak #include <linux/of.h>
168f446e6fSRajendra Nayak #include <linux/regulator/machine.h>
17a0c7b164SMark Brown #include <linux/regulator/driver.h>
181c8fa58fSThierry Reding #include <linux/regulator/of_regulator.h>
198f446e6fSRajendra Nayak 
20a0c7b164SMark Brown #include "internal.h"
21a0c7b164SMark Brown 
22f32fa89cSKrzysztof Kozlowski static const char *const regulator_states[PM_SUSPEND_MAX + 1] = {
2340e20d68SChanwoo Choi 	[PM_SUSPEND_MEM]	= "regulator-state-mem",
2440e20d68SChanwoo Choi 	[PM_SUSPEND_MAX]	= "regulator-state-disk",
2540e20d68SChanwoo Choi };
2640e20d68SChanwoo Choi 
278f446e6fSRajendra Nayak static void of_get_regulation_constraints(struct device_node *np,
285e5e3a42SJavier Martinez Canillas 					struct regulator_init_data **init_data,
295e5e3a42SJavier Martinez Canillas 					const struct regulator_desc *desc)
308f446e6fSRajendra Nayak {
318f446e6fSRajendra Nayak 	struct regulation_constraints *constraints = &(*init_data)->constraints;
3240e20d68SChanwoo Choi 	struct regulator_state *suspend_state;
3340e20d68SChanwoo Choi 	struct device_node *suspend_np;
3440e20d68SChanwoo Choi 	int ret, i;
3500c877c6SLaxman Dewangan 	u32 pval;
368f446e6fSRajendra Nayak 
378f446e6fSRajendra Nayak 	constraints->name = of_get_property(np, "regulator-name", NULL);
388f446e6fSRajendra Nayak 
39a34785f1SLaxman Dewangan 	if (!of_property_read_u32(np, "regulator-min-microvolt", &pval))
40a34785f1SLaxman Dewangan 		constraints->min_uV = pval;
41a34785f1SLaxman Dewangan 
42a34785f1SLaxman Dewangan 	if (!of_property_read_u32(np, "regulator-max-microvolt", &pval))
43a34785f1SLaxman Dewangan 		constraints->max_uV = pval;
448f446e6fSRajendra Nayak 
458f446e6fSRajendra Nayak 	/* Voltage change possible? */
4645fa2038SMark Brown 	if (constraints->min_uV != constraints->max_uV)
478f446e6fSRajendra Nayak 		constraints->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE;
4845fa2038SMark Brown 
4945fa2038SMark Brown 	/* Do we have a voltage range, if so try to apply it? */
5045fa2038SMark Brown 	if (constraints->min_uV && constraints->max_uV)
51ab62aa93SMark Brown 		constraints->apply_uV = true;
528f446e6fSRajendra Nayak 
531e050eabSSergei Shtylyov 	if (!of_property_read_u32(np, "regulator-microvolt-offset", &pval))
541e050eabSSergei Shtylyov 		constraints->uV_offset = pval;
551e050eabSSergei Shtylyov 	if (!of_property_read_u32(np, "regulator-min-microamp", &pval))
561e050eabSSergei Shtylyov 		constraints->min_uA = pval;
571e050eabSSergei Shtylyov 	if (!of_property_read_u32(np, "regulator-max-microamp", &pval))
581e050eabSSergei Shtylyov 		constraints->max_uA = pval;
598f446e6fSRajendra Nayak 
6036e4f839SStephen Boyd 	if (!of_property_read_u32(np, "regulator-input-current-limit-microamp",
6136e4f839SStephen Boyd 				  &pval))
6236e4f839SStephen Boyd 		constraints->ilim_uA = pval;
6336e4f839SStephen Boyd 
648f446e6fSRajendra Nayak 	/* Current change possible? */
658f446e6fSRajendra Nayak 	if (constraints->min_uA != constraints->max_uA)
668f446e6fSRajendra Nayak 		constraints->valid_ops_mask |= REGULATOR_CHANGE_CURRENT;
678f446e6fSRajendra Nayak 
681e050eabSSergei Shtylyov 	constraints->boot_on = of_property_read_bool(np, "regulator-boot-on");
691e050eabSSergei Shtylyov 	constraints->always_on = of_property_read_bool(np, "regulator-always-on");
701e050eabSSergei Shtylyov 	if (!constraints->always_on) /* status change should be possible. */
718f446e6fSRajendra Nayak 		constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS;
726f0b2c69SYadwinder Singh Brar 
7323c779b9SStephen Boyd 	constraints->pull_down = of_property_read_bool(np, "regulator-pull-down");
7423c779b9SStephen Boyd 
7593134c7bSKishon Vijay Abraham I 	if (of_property_read_bool(np, "regulator-allow-bypass"))
7693134c7bSKishon Vijay Abraham I 		constraints->valid_ops_mask |= REGULATOR_CHANGE_BYPASS;
7793134c7bSKishon Vijay Abraham I 
78b263d203SBjorn Andersson 	if (of_property_read_bool(np, "regulator-allow-set-load"))
79b263d203SBjorn Andersson 		constraints->valid_ops_mask |= REGULATOR_CHANGE_DRMS;
80b263d203SBjorn Andersson 
811e050eabSSergei Shtylyov 	ret = of_property_read_u32(np, "regulator-ramp-delay", &pval);
821e050eabSSergei Shtylyov 	if (!ret) {
831e050eabSSergei Shtylyov 		if (pval)
841e050eabSSergei Shtylyov 			constraints->ramp_delay = pval;
851653ccf4SYadwinder Singh Brar 		else
861653ccf4SYadwinder Singh Brar 			constraints->ramp_disable = true;
871653ccf4SYadwinder Singh Brar 	}
8800c877c6SLaxman Dewangan 
8900c877c6SLaxman Dewangan 	ret = of_property_read_u32(np, "regulator-enable-ramp-delay", &pval);
9000c877c6SLaxman Dewangan 	if (!ret)
9100c877c6SLaxman Dewangan 		constraints->enable_time = pval;
9240e20d68SChanwoo Choi 
9357f66b78SStephen Boyd 	constraints->soft_start = of_property_read_bool(np,
9457f66b78SStephen Boyd 					"regulator-soft-start");
95670666b9SLaxman Dewangan 	ret = of_property_read_u32(np, "regulator-active-discharge", &pval);
96670666b9SLaxman Dewangan 	if (!ret) {
97670666b9SLaxman Dewangan 		constraints->active_discharge =
98670666b9SLaxman Dewangan 				(pval) ? REGULATOR_ACTIVE_DISCHARGE_ENABLE :
99670666b9SLaxman Dewangan 					REGULATOR_ACTIVE_DISCHARGE_DISABLE;
100670666b9SLaxman Dewangan 	}
10157f66b78SStephen Boyd 
1025e5e3a42SJavier Martinez Canillas 	if (!of_property_read_u32(np, "regulator-initial-mode", &pval)) {
1035e5e3a42SJavier Martinez Canillas 		if (desc && desc->of_map_mode) {
1045e5e3a42SJavier Martinez Canillas 			ret = desc->of_map_mode(pval);
1055e5e3a42SJavier Martinez Canillas 			if (ret == -EINVAL)
1065e5e3a42SJavier Martinez Canillas 				pr_err("%s: invalid mode %u\n", np->name, pval);
1075e5e3a42SJavier Martinez Canillas 			else
1085e5e3a42SJavier Martinez Canillas 				constraints->initial_mode = ret;
1095e5e3a42SJavier Martinez Canillas 		} else {
1105e5e3a42SJavier Martinez Canillas 			pr_warn("%s: mapping for mode %d not defined\n",
1115e5e3a42SJavier Martinez Canillas 				np->name, pval);
1125e5e3a42SJavier Martinez Canillas 		}
1135e5e3a42SJavier Martinez Canillas 	}
1145e5e3a42SJavier Martinez Canillas 
11522a10bcaSStephen Boyd 	if (!of_property_read_u32(np, "regulator-system-load", &pval))
11622a10bcaSStephen Boyd 		constraints->system_load = pval;
11722a10bcaSStephen Boyd 
1183a003baeSStephen Boyd 	constraints->over_current_protection = of_property_read_bool(np,
1193a003baeSStephen Boyd 					"regulator-over-current-protection");
1203a003baeSStephen Boyd 
12140e20d68SChanwoo Choi 	for (i = 0; i < ARRAY_SIZE(regulator_states); i++) {
12240e20d68SChanwoo Choi 		switch (i) {
12340e20d68SChanwoo Choi 		case PM_SUSPEND_MEM:
12440e20d68SChanwoo Choi 			suspend_state = &constraints->state_mem;
12540e20d68SChanwoo Choi 			break;
12640e20d68SChanwoo Choi 		case PM_SUSPEND_MAX:
12740e20d68SChanwoo Choi 			suspend_state = &constraints->state_disk;
12840e20d68SChanwoo Choi 			break;
12940e20d68SChanwoo Choi 		case PM_SUSPEND_ON:
13040e20d68SChanwoo Choi 		case PM_SUSPEND_FREEZE:
13140e20d68SChanwoo Choi 		case PM_SUSPEND_STANDBY:
13240e20d68SChanwoo Choi 		default:
13340e20d68SChanwoo Choi 			continue;
1347cf225b9SKrzysztof Kozlowski 		}
13540e20d68SChanwoo Choi 
13640e20d68SChanwoo Choi 		suspend_np = of_get_child_by_name(np, regulator_states[i]);
13740e20d68SChanwoo Choi 		if (!suspend_np || !suspend_state)
13840e20d68SChanwoo Choi 			continue;
13940e20d68SChanwoo Choi 
1405e5e3a42SJavier Martinez Canillas 		if (!of_property_read_u32(suspend_np, "regulator-mode",
1415e5e3a42SJavier Martinez Canillas 					  &pval)) {
1425e5e3a42SJavier Martinez Canillas 			if (desc && desc->of_map_mode) {
1435e5e3a42SJavier Martinez Canillas 				ret = desc->of_map_mode(pval);
1445e5e3a42SJavier Martinez Canillas 				if (ret == -EINVAL)
1455e5e3a42SJavier Martinez Canillas 					pr_err("%s: invalid mode %u\n",
1465e5e3a42SJavier Martinez Canillas 					       np->name, pval);
1475e5e3a42SJavier Martinez Canillas 				else
1485e5e3a42SJavier Martinez Canillas 					suspend_state->mode = ret;
1495e5e3a42SJavier Martinez Canillas 			} else {
1505e5e3a42SJavier Martinez Canillas 				pr_warn("%s: mapping for mode %d not defined\n",
1515e5e3a42SJavier Martinez Canillas 					np->name, pval);
1525e5e3a42SJavier Martinez Canillas 			}
1535e5e3a42SJavier Martinez Canillas 		}
1545e5e3a42SJavier Martinez Canillas 
15540e20d68SChanwoo Choi 		if (of_property_read_bool(suspend_np,
15640e20d68SChanwoo Choi 					"regulator-on-in-suspend"))
15740e20d68SChanwoo Choi 			suspend_state->enabled = true;
15840e20d68SChanwoo Choi 		else if (of_property_read_bool(suspend_np,
15940e20d68SChanwoo Choi 					"regulator-off-in-suspend"))
16040e20d68SChanwoo Choi 			suspend_state->disabled = true;
16140e20d68SChanwoo Choi 
1628cbcaea8SDoug Anderson 		if (!of_property_read_u32(suspend_np,
1638cbcaea8SDoug Anderson 					"regulator-suspend-microvolt", &pval))
1648cbcaea8SDoug Anderson 			suspend_state->uV = pval;
1658cbcaea8SDoug Anderson 
1664eafec83SJavier Martinez Canillas 		of_node_put(suspend_np);
16740e20d68SChanwoo Choi 		suspend_state = NULL;
16840e20d68SChanwoo Choi 		suspend_np = NULL;
16940e20d68SChanwoo Choi 	}
1708f446e6fSRajendra Nayak }
1718f446e6fSRajendra Nayak 
1728f446e6fSRajendra Nayak /**
1738f446e6fSRajendra Nayak  * of_get_regulator_init_data - extract regulator_init_data structure info
1748f446e6fSRajendra Nayak  * @dev: device requesting for regulator_init_data
175072e78b1SJavier Martinez Canillas  * @node: regulator device node
176072e78b1SJavier Martinez Canillas  * @desc: regulator description
1778f446e6fSRajendra Nayak  *
1788f446e6fSRajendra Nayak  * Populates regulator_init_data structure by extracting data from device
1798f446e6fSRajendra Nayak  * tree node, returns a pointer to the populated struture or NULL if memory
1808f446e6fSRajendra Nayak  * alloc fails.
1818f446e6fSRajendra Nayak  */
182d9a861ccSShawn Guo struct regulator_init_data *of_get_regulator_init_data(struct device *dev,
183072e78b1SJavier Martinez Canillas 					  struct device_node *node,
184072e78b1SJavier Martinez Canillas 					  const struct regulator_desc *desc)
1858f446e6fSRajendra Nayak {
1868f446e6fSRajendra Nayak 	struct regulator_init_data *init_data;
1878f446e6fSRajendra Nayak 
188d9a861ccSShawn Guo 	if (!node)
1898f446e6fSRajendra Nayak 		return NULL;
1908f446e6fSRajendra Nayak 
1918f446e6fSRajendra Nayak 	init_data = devm_kzalloc(dev, sizeof(*init_data), GFP_KERNEL);
1928f446e6fSRajendra Nayak 	if (!init_data)
1938f446e6fSRajendra Nayak 		return NULL; /* Out of memory? */
1948f446e6fSRajendra Nayak 
1955e5e3a42SJavier Martinez Canillas 	of_get_regulation_constraints(node, &init_data, desc);
1968f446e6fSRajendra Nayak 	return init_data;
1978f446e6fSRajendra Nayak }
198e69af5e9SAxel Lin EXPORT_SYMBOL_GPL(of_get_regulator_init_data);
1991c8fa58fSThierry Reding 
20013b3fde8SCharles Keepax struct devm_of_regulator_matches {
20113b3fde8SCharles Keepax 	struct of_regulator_match *matches;
20213b3fde8SCharles Keepax 	unsigned int num_matches;
20313b3fde8SCharles Keepax };
20413b3fde8SCharles Keepax 
20513b3fde8SCharles Keepax static void devm_of_regulator_put_matches(struct device *dev, void *res)
20613b3fde8SCharles Keepax {
20713b3fde8SCharles Keepax 	struct devm_of_regulator_matches *devm_matches = res;
20813b3fde8SCharles Keepax 	int i;
20913b3fde8SCharles Keepax 
21013b3fde8SCharles Keepax 	for (i = 0; i < devm_matches->num_matches; i++)
21113b3fde8SCharles Keepax 		of_node_put(devm_matches->matches[i].of_node);
21213b3fde8SCharles Keepax }
21313b3fde8SCharles Keepax 
2141c8fa58fSThierry Reding /**
21513511defSStephen Warren  * of_regulator_match - extract multiple regulator init data from device tree.
2161c8fa58fSThierry Reding  * @dev: device requesting the data
2171c8fa58fSThierry Reding  * @node: parent device node of the regulators
2181c8fa58fSThierry Reding  * @matches: match table for the regulators
2191c8fa58fSThierry Reding  * @num_matches: number of entries in match table
2201c8fa58fSThierry Reding  *
22113511defSStephen Warren  * This function uses a match table specified by the regulator driver to
22213511defSStephen Warren  * parse regulator init data from the device tree. @node is expected to
22313511defSStephen Warren  * contain a set of child nodes, each providing the init data for one
22413511defSStephen Warren  * regulator. The data parsed from a child node will be matched to a regulator
22513511defSStephen Warren  * based on either the deprecated property regulator-compatible if present,
22613511defSStephen Warren  * or otherwise the child node's name. Note that the match table is modified
227bd0dda74SCharles Keepax  * in place and an additional of_node reference is taken for each matched
228bd0dda74SCharles Keepax  * regulator.
2291c8fa58fSThierry Reding  *
2301c8fa58fSThierry Reding  * Returns the number of matches found or a negative error code on failure.
2311c8fa58fSThierry Reding  */
2321c8fa58fSThierry Reding int of_regulator_match(struct device *dev, struct device_node *node,
2331c8fa58fSThierry Reding 		       struct of_regulator_match *matches,
2341c8fa58fSThierry Reding 		       unsigned int num_matches)
2351c8fa58fSThierry Reding {
2361c8fa58fSThierry Reding 	unsigned int count = 0;
2371c8fa58fSThierry Reding 	unsigned int i;
23813511defSStephen Warren 	const char *name;
2395260cd2bSLaxman Dewangan 	struct device_node *child;
24013b3fde8SCharles Keepax 	struct devm_of_regulator_matches *devm_matches;
2411c8fa58fSThierry Reding 
2421c8fa58fSThierry Reding 	if (!dev || !node)
2431c8fa58fSThierry Reding 		return -EINVAL;
2441c8fa58fSThierry Reding 
24513b3fde8SCharles Keepax 	devm_matches = devres_alloc(devm_of_regulator_put_matches,
24613b3fde8SCharles Keepax 				    sizeof(struct devm_of_regulator_matches),
24713b3fde8SCharles Keepax 				    GFP_KERNEL);
24813b3fde8SCharles Keepax 	if (!devm_matches)
24913b3fde8SCharles Keepax 		return -ENOMEM;
25013b3fde8SCharles Keepax 
25113b3fde8SCharles Keepax 	devm_matches->matches = matches;
25213b3fde8SCharles Keepax 	devm_matches->num_matches = num_matches;
25313b3fde8SCharles Keepax 
25413b3fde8SCharles Keepax 	devres_add(dev, devm_matches);
25513b3fde8SCharles Keepax 
256a2f95c36SStephen Warren 	for (i = 0; i < num_matches; i++) {
257a2f95c36SStephen Warren 		struct of_regulator_match *match = &matches[i];
258a2f95c36SStephen Warren 		match->init_data = NULL;
259a2f95c36SStephen Warren 		match->of_node = NULL;
260a2f95c36SStephen Warren 	}
261a2f95c36SStephen Warren 
2625260cd2bSLaxman Dewangan 	for_each_child_of_node(node, child) {
26313511defSStephen Warren 		name = of_get_property(child,
2645260cd2bSLaxman Dewangan 					"regulator-compatible", NULL);
26513511defSStephen Warren 		if (!name)
26613511defSStephen Warren 			name = child->name;
2671c8fa58fSThierry Reding 		for (i = 0; i < num_matches; i++) {
2681c8fa58fSThierry Reding 			struct of_regulator_match *match = &matches[i];
2695260cd2bSLaxman Dewangan 			if (match->of_node)
2701c8fa58fSThierry Reding 				continue;
2711c8fa58fSThierry Reding 
27213511defSStephen Warren 			if (strcmp(match->name, name))
2735260cd2bSLaxman Dewangan 				continue;
2745260cd2bSLaxman Dewangan 
2755260cd2bSLaxman Dewangan 			match->init_data =
27675d6b2faSJavier Martinez Canillas 				of_get_regulator_init_data(dev, child,
27775d6b2faSJavier Martinez Canillas 							   match->desc);
2781c8fa58fSThierry Reding 			if (!match->init_data) {
2795260cd2bSLaxman Dewangan 				dev_err(dev,
2805260cd2bSLaxman Dewangan 					"failed to parse DT for regulator %s\n",
2811c8fa58fSThierry Reding 					child->name);
2821c8fa58fSThierry Reding 				return -EINVAL;
2831c8fa58fSThierry Reding 			}
284bd0dda74SCharles Keepax 			match->of_node = of_node_get(child);
2851c8fa58fSThierry Reding 			count++;
2865260cd2bSLaxman Dewangan 			break;
2875260cd2bSLaxman Dewangan 		}
2881c8fa58fSThierry Reding 	}
2891c8fa58fSThierry Reding 
2901c8fa58fSThierry Reding 	return count;
2911c8fa58fSThierry Reding }
2921c8fa58fSThierry Reding EXPORT_SYMBOL_GPL(of_regulator_match);
293a0c7b164SMark Brown 
294a0c7b164SMark Brown struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
295a0c7b164SMark Brown 					    const struct regulator_desc *desc,
296bfa21a0dSKrzysztof Kozlowski 					    struct regulator_config *config,
297a0c7b164SMark Brown 					    struct device_node **node)
298a0c7b164SMark Brown {
299a0c7b164SMark Brown 	struct device_node *search, *child;
300a0c7b164SMark Brown 	struct regulator_init_data *init_data = NULL;
301a0c7b164SMark Brown 	const char *name;
302a0c7b164SMark Brown 
303a0c7b164SMark Brown 	if (!dev->of_node || !desc->of_match)
304a0c7b164SMark Brown 		return NULL;
305a0c7b164SMark Brown 
306a0c7b164SMark Brown 	if (desc->regulators_node)
307a0c7b164SMark Brown 		search = of_get_child_by_name(dev->of_node,
308a0c7b164SMark Brown 					      desc->regulators_node);
309a0c7b164SMark Brown 	else
310a0c7b164SMark Brown 		search = dev->of_node;
311a0c7b164SMark Brown 
312a0c7b164SMark Brown 	if (!search) {
3137de79a1dSMark Brown 		dev_dbg(dev, "Failed to find regulator container node '%s'\n",
3147de79a1dSMark Brown 			desc->regulators_node);
315a0c7b164SMark Brown 		return NULL;
316a0c7b164SMark Brown 	}
317a0c7b164SMark Brown 
318130daa3fSStephen Boyd 	for_each_available_child_of_node(search, child) {
319a0c7b164SMark Brown 		name = of_get_property(child, "regulator-compatible", NULL);
320a0c7b164SMark Brown 		if (!name)
321a0c7b164SMark Brown 			name = child->name;
322a0c7b164SMark Brown 
323a0c7b164SMark Brown 		if (strcmp(desc->of_match, name))
324a0c7b164SMark Brown 			continue;
325a0c7b164SMark Brown 
326072e78b1SJavier Martinez Canillas 		init_data = of_get_regulator_init_data(dev, child, desc);
327a0c7b164SMark Brown 		if (!init_data) {
328a0c7b164SMark Brown 			dev_err(dev,
329a0c7b164SMark Brown 				"failed to parse DT for regulator %s\n",
330a0c7b164SMark Brown 				child->name);
331a0c7b164SMark Brown 			break;
332a0c7b164SMark Brown 		}
333a0c7b164SMark Brown 
334bfa21a0dSKrzysztof Kozlowski 		if (desc->of_parse_cb) {
335bfa21a0dSKrzysztof Kozlowski 			if (desc->of_parse_cb(child, desc, config)) {
336bfa21a0dSKrzysztof Kozlowski 				dev_err(dev,
337bfa21a0dSKrzysztof Kozlowski 					"driver callback failed to parse DT for regulator %s\n",
338bfa21a0dSKrzysztof Kozlowski 					child->name);
339bfa21a0dSKrzysztof Kozlowski 				init_data = NULL;
340bfa21a0dSKrzysztof Kozlowski 				break;
341bfa21a0dSKrzysztof Kozlowski 			}
342bfa21a0dSKrzysztof Kozlowski 		}
343bfa21a0dSKrzysztof Kozlowski 
344a0c7b164SMark Brown 		of_node_get(child);
345a0c7b164SMark Brown 		*node = child;
346a0c7b164SMark Brown 		break;
347a0c7b164SMark Brown 	}
348a0c7b164SMark Brown 
349a0c7b164SMark Brown 	of_node_put(search);
350a0c7b164SMark Brown 
351a0c7b164SMark Brown 	return init_data;
352a0c7b164SMark Brown }
353