diff options
| author | Rhyland Klein <rklein@nvidia.com> | 2013-04-01 17:45:55 -0400 |
|---|---|---|
| committer | Anton Vorontsov <anton@enomsg.org> | 2013-04-16 21:51:16 -0400 |
| commit | f6e0b081fb300a4601b064346963cf6bb163f437 (patch) | |
| tree | 0ac887a0a5e1802213619ada446125e8fee6e7c3 | |
| parent | 5e0848c6026ab98f47e0e179f5c76875cd509d58 (diff) | |
power_supply: Populate supplied_from hierarchy from the device tree
With this patch the power_supply_core will try to populate supplied_from
hierarchy from the device tree.
Signed-off-by: Rhyland Klein <rklein@nvidia.com>
Signed-off-by: Anton Vorontsov <anton@enomsg.org>
| -rw-r--r-- | drivers/power/power_supply_core.c | 140 | ||||
| -rw-r--r-- | include/linux/power_supply.h | 3 |
2 files changed, 143 insertions, 0 deletions
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index d843cc9df030..1c517c34e4be 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c | |||
| @@ -88,6 +88,139 @@ void power_supply_changed(struct power_supply *psy) | |||
| 88 | } | 88 | } |
| 89 | EXPORT_SYMBOL_GPL(power_supply_changed); | 89 | EXPORT_SYMBOL_GPL(power_supply_changed); |
| 90 | 90 | ||
| 91 | #ifdef CONFIG_OF | ||
| 92 | #include <linux/of.h> | ||
| 93 | |||
| 94 | static int __power_supply_populate_supplied_from(struct device *dev, | ||
| 95 | void *data) | ||
| 96 | { | ||
| 97 | struct power_supply *psy = (struct power_supply *)data; | ||
| 98 | struct power_supply *epsy = dev_get_drvdata(dev); | ||
| 99 | struct device_node *np; | ||
| 100 | int i = 0; | ||
| 101 | |||
| 102 | do { | ||
| 103 | np = of_parse_phandle(psy->of_node, "power-supplies", i++); | ||
| 104 | if (!np) | ||
| 105 | continue; | ||
| 106 | |||
| 107 | if (np == epsy->of_node) { | ||
| 108 | dev_info(psy->dev, "%s: Found supply : %s\n", | ||
| 109 | psy->name, epsy->name); | ||
| 110 | psy->supplied_from[i-1] = (char *)epsy->name; | ||
| 111 | psy->num_supplies++; | ||
| 112 | break; | ||
| 113 | } | ||
| 114 | } while (np); | ||
| 115 | |||
| 116 | return 0; | ||
| 117 | } | ||
| 118 | |||
| 119 | static int power_supply_populate_supplied_from(struct power_supply *psy) | ||
| 120 | { | ||
| 121 | int error; | ||
| 122 | |||
| 123 | error = class_for_each_device(power_supply_class, NULL, psy, | ||
| 124 | __power_supply_populate_supplied_from); | ||
| 125 | |||
| 126 | dev_dbg(psy->dev, "%s %d\n", __func__, error); | ||
| 127 | |||
| 128 | return error; | ||
| 129 | } | ||
| 130 | |||
| 131 | static int __power_supply_find_supply_from_node(struct device *dev, | ||
| 132 | void *data) | ||
| 133 | { | ||
| 134 | struct device_node *np = (struct device_node *)data; | ||
| 135 | struct power_supply *epsy = dev_get_drvdata(dev); | ||
| 136 | |||
| 137 | /* return error breaks out of class_for_each_device loop */ | ||
| 138 | if (epsy->of_node == np) | ||
| 139 | return -EINVAL; | ||
| 140 | |||
| 141 | return 0; | ||
| 142 | } | ||
| 143 | |||
| 144 | static int power_supply_find_supply_from_node(struct device_node *supply_node) | ||
| 145 | { | ||
| 146 | int error; | ||
| 147 | struct device *dev; | ||
| 148 | struct class_dev_iter iter; | ||
| 149 | |||
| 150 | /* | ||
| 151 | * Use iterator to see if any other device is registered. | ||
| 152 | * This is required since class_for_each_device returns 0 | ||
| 153 | * if there are no devices registered. | ||
| 154 | */ | ||
| 155 | class_dev_iter_init(&iter, power_supply_class, NULL, NULL); | ||
| 156 | dev = class_dev_iter_next(&iter); | ||
| 157 | |||
| 158 | if (!dev) | ||
| 159 | return -EPROBE_DEFER; | ||
| 160 | |||
| 161 | /* | ||
| 162 | * We have to treat the return value as inverted, because if | ||
| 163 | * we return error on not found, then it won't continue looking. | ||
| 164 | * So we trick it by returning error on success to stop looking | ||
| 165 | * once the matching device is found. | ||
| 166 | */ | ||
| 167 | error = class_for_each_device(power_supply_class, NULL, supply_node, | ||
| 168 | __power_supply_find_supply_from_node); | ||
| 169 | |||
| 170 | return error ? 0 : -EPROBE_DEFER; | ||
| 171 | } | ||
| 172 | |||
| 173 | static int power_supply_check_supplies(struct power_supply *psy) | ||
| 174 | { | ||
| 175 | struct device_node *np; | ||
| 176 | int cnt = 0; | ||
| 177 | |||
| 178 | /* If there is already a list honor it */ | ||
| 179 | if (psy->supplied_from && psy->num_supplies > 0) | ||
| 180 | return 0; | ||
| 181 | |||
| 182 | /* No device node found, nothing to do */ | ||
| 183 | if (!psy->of_node) | ||
| 184 | return 0; | ||
| 185 | |||
| 186 | do { | ||
| 187 | int ret; | ||
| 188 | |||
| 189 | np = of_parse_phandle(psy->of_node, "power-supplies", cnt++); | ||
| 190 | if (!np) | ||
| 191 | continue; | ||
| 192 | |||
| 193 | ret = power_supply_find_supply_from_node(np); | ||
| 194 | if (ret) { | ||
| 195 | dev_dbg(psy->dev, "Failed to find supply, defer!\n"); | ||
| 196 | return -EPROBE_DEFER; | ||
| 197 | } | ||
| 198 | } while (np); | ||
| 199 | |||
| 200 | /* All supplies found, allocate char ** array for filling */ | ||
| 201 | psy->supplied_from = devm_kzalloc(psy->dev, sizeof(psy->supplied_from), | ||
| 202 | GFP_KERNEL); | ||
| 203 | if (!psy->supplied_from) { | ||
| 204 | dev_err(psy->dev, "Couldn't allocate memory for supply list\n"); | ||
| 205 | return -ENOMEM; | ||
| 206 | } | ||
| 207 | |||
| 208 | *psy->supplied_from = devm_kzalloc(psy->dev, sizeof(char *) * cnt, | ||
| 209 | GFP_KERNEL); | ||
| 210 | if (!*psy->supplied_from) { | ||
| 211 | dev_err(psy->dev, "Couldn't allocate memory for supply list\n"); | ||
| 212 | return -ENOMEM; | ||
| 213 | } | ||
| 214 | |||
| 215 | return power_supply_populate_supplied_from(psy); | ||
| 216 | } | ||
| 217 | #else | ||
| 218 | static inline int power_supply_check_supplies(struct power_supply *psy) | ||
| 219 | { | ||
| 220 | return 0; | ||
| 221 | } | ||
| 222 | #endif | ||
| 223 | |||
| 91 | static int __power_supply_am_i_supplied(struct device *dev, void *data) | 224 | static int __power_supply_am_i_supplied(struct device *dev, void *data) |
| 92 | { | 225 | { |
| 93 | union power_supply_propval ret = {0,}; | 226 | union power_supply_propval ret = {0,}; |
| @@ -357,6 +490,12 @@ int power_supply_register(struct device *parent, struct power_supply *psy) | |||
| 357 | 490 | ||
| 358 | INIT_WORK(&psy->changed_work, power_supply_changed_work); | 491 | INIT_WORK(&psy->changed_work, power_supply_changed_work); |
| 359 | 492 | ||
| 493 | rc = power_supply_check_supplies(psy); | ||
| 494 | if (rc) { | ||
| 495 | dev_info(dev, "Not all required supplies found, defer probe\n"); | ||
| 496 | goto check_supplies_failed; | ||
| 497 | } | ||
| 498 | |||
| 360 | rc = kobject_set_name(&dev->kobj, "%s", psy->name); | 499 | rc = kobject_set_name(&dev->kobj, "%s", psy->name); |
| 361 | if (rc) | 500 | if (rc) |
| 362 | goto kobject_set_name_failed; | 501 | goto kobject_set_name_failed; |
| @@ -389,6 +528,7 @@ register_thermal_failed: | |||
| 389 | device_del(dev); | 528 | device_del(dev); |
| 390 | kobject_set_name_failed: | 529 | kobject_set_name_failed: |
| 391 | device_add_failed: | 530 | device_add_failed: |
| 531 | check_supplies_failed: | ||
| 392 | put_device(dev); | 532 | put_device(dev); |
| 393 | success: | 533 | success: |
| 394 | return rc; | 534 | return rc; |
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index c1cbd5e4e484..3828cefb4f65 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h | |||
| @@ -173,6 +173,9 @@ struct power_supply { | |||
| 173 | 173 | ||
| 174 | char **supplied_from; | 174 | char **supplied_from; |
| 175 | size_t num_supplies; | 175 | size_t num_supplies; |
| 176 | #ifdef CONFIG_OF | ||
| 177 | struct device_node *of_node; | ||
| 178 | #endif | ||
| 176 | 179 | ||
| 177 | int (*get_property)(struct power_supply *psy, | 180 | int (*get_property)(struct power_supply *psy, |
| 178 | enum power_supply_property psp, | 181 | enum power_supply_property psp, |
