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