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? */
468f446e6fSRajendra Nayak 	if (constraints->min_uV != constraints->max_uV)
478f446e6fSRajendra Nayak 		constraints->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE;
48ab62aa93SMark Brown 	/* Only one voltage?  Then make sure it's set. */
49a34785f1SLaxman Dewangan 	if (constraints->min_uV && constraints->max_uV &&
50a34785f1SLaxman Dewangan 	    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");
9557f66b78SStephen Boyd 
965e5e3a42SJavier Martinez Canillas 	if (!of_property_read_u32(np, "regulator-initial-mode", &pval)) {
975e5e3a42SJavier Martinez Canillas 		if (desc && desc->of_map_mode) {
985e5e3a42SJavier Martinez Canillas 			ret = desc->of_map_mode(pval);
995e5e3a42SJavier Martinez Canillas 			if (ret == -EINVAL)
1005e5e3a42SJavier Martinez Canillas 				pr_err("%s: invalid mode %u\n", np->name, pval);
1015e5e3a42SJavier Martinez Canillas 			else
1025e5e3a42SJavier Martinez Canillas 				constraints->initial_mode = ret;
1035e5e3a42SJavier Martinez Canillas 		} else {
1045e5e3a42SJavier Martinez Canillas 			pr_warn("%s: mapping for mode %d not defined\n",
1055e5e3a42SJavier Martinez Canillas 				np->name, pval);
1065e5e3a42SJavier Martinez Canillas 		}
1075e5e3a42SJavier Martinez Canillas 	}
1085e5e3a42SJavier Martinez Canillas 
10922a10bcaSStephen Boyd 	if (!of_property_read_u32(np, "regulator-system-load", &pval))
11022a10bcaSStephen Boyd 		constraints->system_load = pval;
11122a10bcaSStephen Boyd 
1123a003baeSStephen Boyd 	constraints->over_current_protection = of_property_read_bool(np,
1133a003baeSStephen Boyd 					"regulator-over-current-protection");
1143a003baeSStephen Boyd 
11540e20d68SChanwoo Choi 	for (i = 0; i < ARRAY_SIZE(regulator_states); i++) {
11640e20d68SChanwoo Choi 		switch (i) {
11740e20d68SChanwoo Choi 		case PM_SUSPEND_MEM:
11840e20d68SChanwoo Choi 			suspend_state = &constraints->state_mem;
11940e20d68SChanwoo Choi 			break;
12040e20d68SChanwoo Choi 		case PM_SUSPEND_MAX:
12140e20d68SChanwoo Choi 			suspend_state = &constraints->state_disk;
12240e20d68SChanwoo Choi 			break;
12340e20d68SChanwoo Choi 		case PM_SUSPEND_ON:
12440e20d68SChanwoo Choi 		case PM_SUSPEND_FREEZE:
12540e20d68SChanwoo Choi 		case PM_SUSPEND_STANDBY:
12640e20d68SChanwoo Choi 		default:
12740e20d68SChanwoo Choi 			continue;
1287cf225b9SKrzysztof Kozlowski 		}
12940e20d68SChanwoo Choi 
13040e20d68SChanwoo Choi 		suspend_np = of_get_child_by_name(np, regulator_states[i]);
13140e20d68SChanwoo Choi 		if (!suspend_np || !suspend_state)
13240e20d68SChanwoo Choi 			continue;
13340e20d68SChanwoo Choi 
1345e5e3a42SJavier Martinez Canillas 		if (!of_property_read_u32(suspend_np, "regulator-mode",
1355e5e3a42SJavier Martinez Canillas 					  &pval)) {
1365e5e3a42SJavier Martinez Canillas 			if (desc && desc->of_map_mode) {
1375e5e3a42SJavier Martinez Canillas 				ret = desc->of_map_mode(pval);
1385e5e3a42SJavier Martinez Canillas 				if (ret == -EINVAL)
1395e5e3a42SJavier Martinez Canillas 					pr_err("%s: invalid mode %u\n",
1405e5e3a42SJavier Martinez Canillas 					       np->name, pval);
1415e5e3a42SJavier Martinez Canillas 				else
1425e5e3a42SJavier Martinez Canillas 					suspend_state->mode = ret;
1435e5e3a42SJavier Martinez Canillas 			} else {
1445e5e3a42SJavier Martinez Canillas 				pr_warn("%s: mapping for mode %d not defined\n",
1455e5e3a42SJavier Martinez Canillas 					np->name, pval);
1465e5e3a42SJavier Martinez Canillas 			}
1475e5e3a42SJavier Martinez Canillas 		}
1485e5e3a42SJavier Martinez Canillas 
14940e20d68SChanwoo Choi 		if (of_property_read_bool(suspend_np,
15040e20d68SChanwoo Choi 					"regulator-on-in-suspend"))
15140e20d68SChanwoo Choi 			suspend_state->enabled = true;
15240e20d68SChanwoo Choi 		else if (of_property_read_bool(suspend_np,
15340e20d68SChanwoo Choi 					"regulator-off-in-suspend"))
15440e20d68SChanwoo Choi 			suspend_state->disabled = true;
15540e20d68SChanwoo Choi 
1568cbcaea8SDoug Anderson 		if (!of_property_read_u32(suspend_np,
1578cbcaea8SDoug Anderson 					"regulator-suspend-microvolt", &pval))
1588cbcaea8SDoug Anderson 			suspend_state->uV = pval;
1598cbcaea8SDoug Anderson 
1604eafec83SJavier Martinez Canillas 		of_node_put(suspend_np);
16140e20d68SChanwoo Choi 		suspend_state = NULL;
16240e20d68SChanwoo Choi 		suspend_np = NULL;
16340e20d68SChanwoo Choi 	}
1648f446e6fSRajendra Nayak }
1658f446e6fSRajendra Nayak 
1668f446e6fSRajendra Nayak /**
1678f446e6fSRajendra Nayak  * of_get_regulator_init_data - extract regulator_init_data structure info
1688f446e6fSRajendra Nayak  * @dev: device requesting for regulator_init_data
169072e78b1SJavier Martinez Canillas  * @node: regulator device node
170072e78b1SJavier Martinez Canillas  * @desc: regulator description
1718f446e6fSRajendra Nayak  *
1728f446e6fSRajendra Nayak  * Populates regulator_init_data structure by extracting data from device
1738f446e6fSRajendra Nayak  * tree node, returns a pointer to the populated struture or NULL if memory
1748f446e6fSRajendra Nayak  * alloc fails.
1758f446e6fSRajendra Nayak  */
176d9a861ccSShawn Guo struct regulator_init_data *of_get_regulator_init_data(struct device *dev,
177072e78b1SJavier Martinez Canillas 					  struct device_node *node,
178072e78b1SJavier Martinez Canillas 					  const struct regulator_desc *desc)
1798f446e6fSRajendra Nayak {
1808f446e6fSRajendra Nayak 	struct regulator_init_data *init_data;
1818f446e6fSRajendra Nayak 
182d9a861ccSShawn Guo 	if (!node)
1838f446e6fSRajendra Nayak 		return NULL;
1848f446e6fSRajendra Nayak 
1858f446e6fSRajendra Nayak 	init_data = devm_kzalloc(dev, sizeof(*init_data), GFP_KERNEL);
1868f446e6fSRajendra Nayak 	if (!init_data)
1878f446e6fSRajendra Nayak 		return NULL; /* Out of memory? */
1888f446e6fSRajendra Nayak 
1895e5e3a42SJavier Martinez Canillas 	of_get_regulation_constraints(node, &init_data, desc);
1908f446e6fSRajendra Nayak 	return init_data;
1918f446e6fSRajendra Nayak }
192e69af5e9SAxel Lin EXPORT_SYMBOL_GPL(of_get_regulator_init_data);
1931c8fa58fSThierry Reding 
19413b3fde8SCharles Keepax struct devm_of_regulator_matches {
19513b3fde8SCharles Keepax 	struct of_regulator_match *matches;
19613b3fde8SCharles Keepax 	unsigned int num_matches;
19713b3fde8SCharles Keepax };
19813b3fde8SCharles Keepax 
19913b3fde8SCharles Keepax static void devm_of_regulator_put_matches(struct device *dev, void *res)
20013b3fde8SCharles Keepax {
20113b3fde8SCharles Keepax 	struct devm_of_regulator_matches *devm_matches = res;
20213b3fde8SCharles Keepax 	int i;
20313b3fde8SCharles Keepax 
20413b3fde8SCharles Keepax 	for (i = 0; i < devm_matches->num_matches; i++)
20513b3fde8SCharles Keepax 		of_node_put(devm_matches->matches[i].of_node);
20613b3fde8SCharles Keepax }
20713b3fde8SCharles Keepax 
2081c8fa58fSThierry Reding /**
20913511defSStephen Warren  * of_regulator_match - extract multiple regulator init data from device tree.
2101c8fa58fSThierry Reding  * @dev: device requesting the data
2111c8fa58fSThierry Reding  * @node: parent device node of the regulators
2121c8fa58fSThierry Reding  * @matches: match table for the regulators
2131c8fa58fSThierry Reding  * @num_matches: number of entries in match table
2141c8fa58fSThierry Reding  *
21513511defSStephen Warren  * This function uses a match table specified by the regulator driver to
21613511defSStephen Warren  * parse regulator init data from the device tree. @node is expected to
21713511defSStephen Warren  * contain a set of child nodes, each providing the init data for one
21813511defSStephen Warren  * regulator. The data parsed from a child node will be matched to a regulator
21913511defSStephen Warren  * based on either the deprecated property regulator-compatible if present,
22013511defSStephen Warren  * or otherwise the child node's name. Note that the match table is modified
221bd0dda74SCharles Keepax  * in place and an additional of_node reference is taken for each matched
222bd0dda74SCharles Keepax  * regulator.
2231c8fa58fSThierry Reding  *
2241c8fa58fSThierry Reding  * Returns the number of matches found or a negative error code on failure.
2251c8fa58fSThierry Reding  */
2261c8fa58fSThierry Reding int of_regulator_match(struct device *dev, struct device_node *node,
2271c8fa58fSThierry Reding 		       struct of_regulator_match *matches,
2281c8fa58fSThierry Reding 		       unsigned int num_matches)
2291c8fa58fSThierry Reding {
2301c8fa58fSThierry Reding 	unsigned int count = 0;
2311c8fa58fSThierry Reding 	unsigned int i;
23213511defSStephen Warren 	const char *name;
2335260cd2bSLaxman Dewangan 	struct device_node *child;
23413b3fde8SCharles Keepax 	struct devm_of_regulator_matches *devm_matches;
2351c8fa58fSThierry Reding 
2361c8fa58fSThierry Reding 	if (!dev || !node)
2371c8fa58fSThierry Reding 		return -EINVAL;
2381c8fa58fSThierry Reding 
23913b3fde8SCharles Keepax 	devm_matches = devres_alloc(devm_of_regulator_put_matches,
24013b3fde8SCharles Keepax 				    sizeof(struct devm_of_regulator_matches),
24113b3fde8SCharles Keepax 				    GFP_KERNEL);
24213b3fde8SCharles Keepax 	if (!devm_matches)
24313b3fde8SCharles Keepax 		return -ENOMEM;
24413b3fde8SCharles Keepax 
24513b3fde8SCharles Keepax 	devm_matches->matches = matches;
24613b3fde8SCharles Keepax 	devm_matches->num_matches = num_matches;
24713b3fde8SCharles Keepax 
24813b3fde8SCharles Keepax 	devres_add(dev, devm_matches);
24913b3fde8SCharles Keepax 
250a2f95c36SStephen Warren 	for (i = 0; i < num_matches; i++) {
251a2f95c36SStephen Warren 		struct of_regulator_match *match = &matches[i];
252a2f95c36SStephen Warren 		match->init_data = NULL;
253a2f95c36SStephen Warren 		match->of_node = NULL;
254a2f95c36SStephen Warren 	}
255a2f95c36SStephen Warren 
2565260cd2bSLaxman Dewangan 	for_each_child_of_node(node, child) {
25713511defSStephen Warren 		name = of_get_property(child,
2585260cd2bSLaxman Dewangan 					"regulator-compatible", NULL);
25913511defSStephen Warren 		if (!name)
26013511defSStephen Warren 			name = child->name;
2611c8fa58fSThierry Reding 		for (i = 0; i < num_matches; i++) {
2621c8fa58fSThierry Reding 			struct of_regulator_match *match = &matches[i];
2635260cd2bSLaxman Dewangan 			if (match->of_node)
2641c8fa58fSThierry Reding 				continue;
2651c8fa58fSThierry Reding 
26613511defSStephen Warren 			if (strcmp(match->name, name))
2675260cd2bSLaxman Dewangan 				continue;
2685260cd2bSLaxman Dewangan 
2695260cd2bSLaxman Dewangan 			match->init_data =
27075d6b2faSJavier Martinez Canillas 				of_get_regulator_init_data(dev, child,
27175d6b2faSJavier Martinez Canillas 							   match->desc);
2721c8fa58fSThierry Reding 			if (!match->init_data) {
2735260cd2bSLaxman Dewangan 				dev_err(dev,
2745260cd2bSLaxman Dewangan 					"failed to parse DT for regulator %s\n",
2751c8fa58fSThierry Reding 					child->name);
2761c8fa58fSThierry Reding 				return -EINVAL;
2771c8fa58fSThierry Reding 			}
278bd0dda74SCharles Keepax 			match->of_node = of_node_get(child);
2791c8fa58fSThierry Reding 			count++;
2805260cd2bSLaxman Dewangan 			break;
2815260cd2bSLaxman Dewangan 		}
2821c8fa58fSThierry Reding 	}
2831c8fa58fSThierry Reding 
2841c8fa58fSThierry Reding 	return count;
2851c8fa58fSThierry Reding }
2861c8fa58fSThierry Reding EXPORT_SYMBOL_GPL(of_regulator_match);
287a0c7b164SMark Brown 
288a0c7b164SMark Brown struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
289a0c7b164SMark Brown 					    const struct regulator_desc *desc,
290bfa21a0dSKrzysztof Kozlowski 					    struct regulator_config *config,
291a0c7b164SMark Brown 					    struct device_node **node)
292a0c7b164SMark Brown {
293a0c7b164SMark Brown 	struct device_node *search, *child;
294a0c7b164SMark Brown 	struct regulator_init_data *init_data = NULL;
295a0c7b164SMark Brown 	const char *name;
296a0c7b164SMark Brown 
297a0c7b164SMark Brown 	if (!dev->of_node || !desc->of_match)
298a0c7b164SMark Brown 		return NULL;
299a0c7b164SMark Brown 
300a0c7b164SMark Brown 	if (desc->regulators_node)
301a0c7b164SMark Brown 		search = of_get_child_by_name(dev->of_node,
302a0c7b164SMark Brown 					      desc->regulators_node);
303a0c7b164SMark Brown 	else
304a0c7b164SMark Brown 		search = dev->of_node;
305a0c7b164SMark Brown 
306a0c7b164SMark Brown 	if (!search) {
3077de79a1dSMark Brown 		dev_dbg(dev, "Failed to find regulator container node '%s'\n",
3087de79a1dSMark Brown 			desc->regulators_node);
309a0c7b164SMark Brown 		return NULL;
310a0c7b164SMark Brown 	}
311a0c7b164SMark Brown 
312130daa3fSStephen Boyd 	for_each_available_child_of_node(search, child) {
313a0c7b164SMark Brown 		name = of_get_property(child, "regulator-compatible", NULL);
314a0c7b164SMark Brown 		if (!name)
315a0c7b164SMark Brown 			name = child->name;
316a0c7b164SMark Brown 
317a0c7b164SMark Brown 		if (strcmp(desc->of_match, name))
318a0c7b164SMark Brown 			continue;
319a0c7b164SMark Brown 
320072e78b1SJavier Martinez Canillas 		init_data = of_get_regulator_init_data(dev, child, desc);
321a0c7b164SMark Brown 		if (!init_data) {
322a0c7b164SMark Brown 			dev_err(dev,
323a0c7b164SMark Brown 				"failed to parse DT for regulator %s\n",
324a0c7b164SMark Brown 				child->name);
325a0c7b164SMark Brown 			break;
326a0c7b164SMark Brown 		}
327a0c7b164SMark Brown 
328bfa21a0dSKrzysztof Kozlowski 		if (desc->of_parse_cb) {
329bfa21a0dSKrzysztof Kozlowski 			if (desc->of_parse_cb(child, desc, config)) {
330bfa21a0dSKrzysztof Kozlowski 				dev_err(dev,
331bfa21a0dSKrzysztof Kozlowski 					"driver callback failed to parse DT for regulator %s\n",
332bfa21a0dSKrzysztof Kozlowski 					child->name);
333bfa21a0dSKrzysztof Kozlowski 				init_data = NULL;
334bfa21a0dSKrzysztof Kozlowski 				break;
335bfa21a0dSKrzysztof Kozlowski 			}
336bfa21a0dSKrzysztof Kozlowski 		}
337bfa21a0dSKrzysztof Kozlowski 
338a0c7b164SMark Brown 		of_node_get(child);
339a0c7b164SMark Brown 		*node = child;
340a0c7b164SMark Brown 		break;
341a0c7b164SMark Brown 	}
342a0c7b164SMark Brown 
343a0c7b164SMark Brown 	of_node_put(search);
344a0c7b164SMark Brown 
345a0c7b164SMark Brown 	return init_data;
346a0c7b164SMark Brown }
347