| 1 | // SPDX-License-Identifier: GPL-2.0 | 
|---|
| 2 | /* | 
|---|
| 3 | * drivers/base/power/common.c - Common device power management code. | 
|---|
| 4 | * | 
|---|
| 5 | * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp. | 
|---|
| 6 | */ | 
|---|
| 7 | #include <linux/kernel.h> | 
|---|
| 8 | #include <linux/device.h> | 
|---|
| 9 | #include <linux/export.h> | 
|---|
| 10 | #include <linux/slab.h> | 
|---|
| 11 | #include <linux/pm_clock.h> | 
|---|
| 12 | #include <linux/acpi.h> | 
|---|
| 13 | #include <linux/pm_domain.h> | 
|---|
| 14 | #include <linux/pm_opp.h> | 
|---|
| 15 |  | 
|---|
| 16 | #include "power.h" | 
|---|
| 17 |  | 
|---|
| 18 | /** | 
|---|
| 19 | * dev_pm_get_subsys_data - Create or refcount power.subsys_data for device. | 
|---|
| 20 | * @dev: Device to handle. | 
|---|
| 21 | * | 
|---|
| 22 | * If power.subsys_data is NULL, point it to a new object, otherwise increment | 
|---|
| 23 | * its reference counter.  Return 0 if new object has been created or refcount | 
|---|
| 24 | * increased, otherwise negative error code. | 
|---|
| 25 | */ | 
|---|
| 26 | int dev_pm_get_subsys_data(struct device *dev) | 
|---|
| 27 | { | 
|---|
| 28 | struct pm_subsys_data *psd; | 
|---|
| 29 |  | 
|---|
| 30 | psd = kzalloc(sizeof(*psd), GFP_KERNEL); | 
|---|
| 31 | if (!psd) | 
|---|
| 32 | return -ENOMEM; | 
|---|
| 33 |  | 
|---|
| 34 | spin_lock_irq(lock: &dev->power.lock); | 
|---|
| 35 |  | 
|---|
| 36 | if (dev->power.subsys_data) { | 
|---|
| 37 | dev->power.subsys_data->refcount++; | 
|---|
| 38 | } else { | 
|---|
| 39 | spin_lock_init(&psd->lock); | 
|---|
| 40 | psd->refcount = 1; | 
|---|
| 41 | dev->power.subsys_data = psd; | 
|---|
| 42 | pm_clk_init(dev); | 
|---|
| 43 | psd = NULL; | 
|---|
| 44 | } | 
|---|
| 45 |  | 
|---|
| 46 | spin_unlock_irq(lock: &dev->power.lock); | 
|---|
| 47 |  | 
|---|
| 48 | /* kfree() verifies that its argument is nonzero. */ | 
|---|
| 49 | kfree(objp: psd); | 
|---|
| 50 |  | 
|---|
| 51 | return 0; | 
|---|
| 52 | } | 
|---|
| 53 | EXPORT_SYMBOL_GPL(dev_pm_get_subsys_data); | 
|---|
| 54 |  | 
|---|
| 55 | /** | 
|---|
| 56 | * dev_pm_put_subsys_data - Drop reference to power.subsys_data. | 
|---|
| 57 | * @dev: Device to handle. | 
|---|
| 58 | * | 
|---|
| 59 | * If the reference counter of power.subsys_data is zero after dropping the | 
|---|
| 60 | * reference, power.subsys_data is removed. | 
|---|
| 61 | */ | 
|---|
| 62 | void dev_pm_put_subsys_data(struct device *dev) | 
|---|
| 63 | { | 
|---|
| 64 | struct pm_subsys_data *psd; | 
|---|
| 65 |  | 
|---|
| 66 | spin_lock_irq(lock: &dev->power.lock); | 
|---|
| 67 |  | 
|---|
| 68 | psd = dev_to_psd(dev); | 
|---|
| 69 | if (!psd) | 
|---|
| 70 | goto out; | 
|---|
| 71 |  | 
|---|
| 72 | if (--psd->refcount == 0) | 
|---|
| 73 | dev->power.subsys_data = NULL; | 
|---|
| 74 | else | 
|---|
| 75 | psd = NULL; | 
|---|
| 76 |  | 
|---|
| 77 | out: | 
|---|
| 78 | spin_unlock_irq(lock: &dev->power.lock); | 
|---|
| 79 | kfree(objp: psd); | 
|---|
| 80 | } | 
|---|
| 81 | EXPORT_SYMBOL_GPL(dev_pm_put_subsys_data); | 
|---|
| 82 |  | 
|---|
| 83 | /** | 
|---|
| 84 | * dev_pm_domain_attach - Attach a device to its PM domain. | 
|---|
| 85 | * @dev: Device to attach. | 
|---|
| 86 | * @flags: indicate whether we should power on/off the device on attach/detach | 
|---|
| 87 | * | 
|---|
| 88 | * The @dev may only be attached to a single PM domain. By iterating through | 
|---|
| 89 | * the available alternatives we try to find a valid PM domain for the device. | 
|---|
| 90 | * As attachment succeeds, the ->detach() callback in the struct dev_pm_domain | 
|---|
| 91 | * should be assigned by the corresponding attach function. | 
|---|
| 92 | * | 
|---|
| 93 | * This function should typically be invoked from subsystem level code during | 
|---|
| 94 | * the probe phase. Especially for those that holds devices which requires | 
|---|
| 95 | * power management through PM domains. | 
|---|
| 96 | * | 
|---|
| 97 | * Callers must ensure proper synchronization of this function with power | 
|---|
| 98 | * management callbacks. | 
|---|
| 99 | * | 
|---|
| 100 | * Returns 0 on successfully attached PM domain, or when it is found that the | 
|---|
| 101 | * device doesn't need a PM domain, else a negative error code. | 
|---|
| 102 | */ | 
|---|
| 103 | int dev_pm_domain_attach(struct device *dev, u32 flags) | 
|---|
| 104 | { | 
|---|
| 105 | int ret; | 
|---|
| 106 |  | 
|---|
| 107 | if (dev->pm_domain) | 
|---|
| 108 | return 0; | 
|---|
| 109 |  | 
|---|
| 110 | ret = acpi_dev_pm_attach(dev, power_on: !!(flags & PD_FLAG_ATTACH_POWER_ON)); | 
|---|
| 111 | if (!ret) | 
|---|
| 112 | ret = genpd_dev_pm_attach(dev); | 
|---|
| 113 |  | 
|---|
| 114 | if (dev->pm_domain) | 
|---|
| 115 | dev->power.detach_power_off = !!(flags & PD_FLAG_DETACH_POWER_OFF); | 
|---|
| 116 |  | 
|---|
| 117 | return ret < 0 ? ret : 0; | 
|---|
| 118 | } | 
|---|
| 119 | EXPORT_SYMBOL_GPL(dev_pm_domain_attach); | 
|---|
| 120 |  | 
|---|
| 121 | /** | 
|---|
| 122 | * dev_pm_domain_attach_by_id - Associate a device with one of its PM domains. | 
|---|
| 123 | * @dev: The device used to lookup the PM domain. | 
|---|
| 124 | * @index: The index of the PM domain. | 
|---|
| 125 | * | 
|---|
| 126 | * As @dev may only be attached to a single PM domain, the backend PM domain | 
|---|
| 127 | * provider creates a virtual device to attach instead. If attachment succeeds, | 
|---|
| 128 | * the ->detach() callback in the struct dev_pm_domain are assigned by the | 
|---|
| 129 | * corresponding backend attach function, as to deal with detaching of the | 
|---|
| 130 | * created virtual device. | 
|---|
| 131 | * | 
|---|
| 132 | * This function should typically be invoked by a driver during the probe phase, | 
|---|
| 133 | * in case its device requires power management through multiple PM domains. The | 
|---|
| 134 | * driver may benefit from using the received device, to configure device-links | 
|---|
| 135 | * towards its original device. Depending on the use-case and if needed, the | 
|---|
| 136 | * links may be dynamically changed by the driver, which allows it to control | 
|---|
| 137 | * the power to the PM domains independently from each other. | 
|---|
| 138 | * | 
|---|
| 139 | * Callers must ensure proper synchronization of this function with power | 
|---|
| 140 | * management callbacks. | 
|---|
| 141 | * | 
|---|
| 142 | * Returns the virtual created device when successfully attached to its PM | 
|---|
| 143 | * domain, NULL in case @dev don't need a PM domain, else an ERR_PTR(). | 
|---|
| 144 | * Note that, to detach the returned virtual device, the driver shall call | 
|---|
| 145 | * dev_pm_domain_detach() on it, typically during the remove phase. | 
|---|
| 146 | */ | 
|---|
| 147 | struct device *dev_pm_domain_attach_by_id(struct device *dev, | 
|---|
| 148 | unsigned int index) | 
|---|
| 149 | { | 
|---|
| 150 | if (dev->pm_domain) | 
|---|
| 151 | return ERR_PTR(error: -EEXIST); | 
|---|
| 152 |  | 
|---|
| 153 | return genpd_dev_pm_attach_by_id(dev, index); | 
|---|
| 154 | } | 
|---|
| 155 | EXPORT_SYMBOL_GPL(dev_pm_domain_attach_by_id); | 
|---|
| 156 |  | 
|---|
| 157 | /** | 
|---|
| 158 | * dev_pm_domain_attach_by_name - Associate a device with one of its PM domains. | 
|---|
| 159 | * @dev: The device used to lookup the PM domain. | 
|---|
| 160 | * @name: The name of the PM domain. | 
|---|
| 161 | * | 
|---|
| 162 | * For a detailed function description, see dev_pm_domain_attach_by_id(). | 
|---|
| 163 | */ | 
|---|
| 164 | struct device *dev_pm_domain_attach_by_name(struct device *dev, | 
|---|
| 165 | const char *name) | 
|---|
| 166 | { | 
|---|
| 167 | if (dev->pm_domain) | 
|---|
| 168 | return ERR_PTR(error: -EEXIST); | 
|---|
| 169 |  | 
|---|
| 170 | return genpd_dev_pm_attach_by_name(dev, name); | 
|---|
| 171 | } | 
|---|
| 172 | EXPORT_SYMBOL_GPL(dev_pm_domain_attach_by_name); | 
|---|
| 173 |  | 
|---|
| 174 | /** | 
|---|
| 175 | * dev_pm_domain_attach_list - Associate a device with its PM domains. | 
|---|
| 176 | * @dev: The device used to lookup the PM domains for. | 
|---|
| 177 | * @data: The data used for attaching to the PM domains. | 
|---|
| 178 | * @list: An out-parameter with an allocated list of attached PM domains. | 
|---|
| 179 | * | 
|---|
| 180 | * This function helps to attach a device to its multiple PM domains. The | 
|---|
| 181 | * caller, which is typically a driver's probe function, may provide a list of | 
|---|
| 182 | * names for the PM domains that we should try to attach the device to, but it | 
|---|
| 183 | * may also provide an empty list, in case the attach should be done for all of | 
|---|
| 184 | * the available PM domains. | 
|---|
| 185 | * | 
|---|
| 186 | * Callers must ensure proper synchronization of this function with power | 
|---|
| 187 | * management callbacks. | 
|---|
| 188 | * | 
|---|
| 189 | * Returns the number of attached PM domains or a negative error code in case of | 
|---|
| 190 | * a failure. Note that, to detach the list of PM domains, the driver shall call | 
|---|
| 191 | * dev_pm_domain_detach_list(), typically during the remove phase. | 
|---|
| 192 | */ | 
|---|
| 193 | int dev_pm_domain_attach_list(struct device *dev, | 
|---|
| 194 | const struct dev_pm_domain_attach_data *data, | 
|---|
| 195 | struct dev_pm_domain_list **list) | 
|---|
| 196 | { | 
|---|
| 197 | struct device_node *np = dev->of_node; | 
|---|
| 198 | struct dev_pm_domain_list *pds; | 
|---|
| 199 | struct device *pd_dev = NULL; | 
|---|
| 200 | int ret, i, num_pds = 0; | 
|---|
| 201 | bool by_id = true; | 
|---|
| 202 | size_t size; | 
|---|
| 203 | u32 pd_flags = data ? data->pd_flags : 0; | 
|---|
| 204 | u32 link_flags = pd_flags & PD_FLAG_NO_DEV_LINK ? 0 : | 
|---|
| 205 | DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME; | 
|---|
| 206 |  | 
|---|
| 207 | if (dev->pm_domain) | 
|---|
| 208 | return -EEXIST; | 
|---|
| 209 |  | 
|---|
| 210 | /* For now this is limited to OF based platforms. */ | 
|---|
| 211 | if (!np) | 
|---|
| 212 | return 0; | 
|---|
| 213 |  | 
|---|
| 214 | if (data && data->pd_names) { | 
|---|
| 215 | num_pds = data->num_pd_names; | 
|---|
| 216 | by_id = false; | 
|---|
| 217 | } else { | 
|---|
| 218 | num_pds = of_count_phandle_with_args(np, list_name: "power-domains", | 
|---|
| 219 | cells_name: "#power-domain-cells"); | 
|---|
| 220 | } | 
|---|
| 221 |  | 
|---|
| 222 | if (num_pds <= 0) | 
|---|
| 223 | return 0; | 
|---|
| 224 |  | 
|---|
| 225 | pds = kzalloc(sizeof(*pds), GFP_KERNEL); | 
|---|
| 226 | if (!pds) | 
|---|
| 227 | return -ENOMEM; | 
|---|
| 228 |  | 
|---|
| 229 | size = sizeof(*pds->pd_devs) + sizeof(*pds->pd_links) + | 
|---|
| 230 | sizeof(*pds->opp_tokens); | 
|---|
| 231 | pds->pd_devs = kcalloc(num_pds, size, GFP_KERNEL); | 
|---|
| 232 | if (!pds->pd_devs) { | 
|---|
| 233 | ret = -ENOMEM; | 
|---|
| 234 | goto free_pds; | 
|---|
| 235 | } | 
|---|
| 236 | pds->pd_links = (void *)(pds->pd_devs + num_pds); | 
|---|
| 237 | pds->opp_tokens = (void *)(pds->pd_links + num_pds); | 
|---|
| 238 |  | 
|---|
| 239 | if (link_flags && pd_flags & PD_FLAG_DEV_LINK_ON) | 
|---|
| 240 | link_flags |= DL_FLAG_RPM_ACTIVE; | 
|---|
| 241 |  | 
|---|
| 242 | for (i = 0; i < num_pds; i++) { | 
|---|
| 243 | if (by_id) | 
|---|
| 244 | pd_dev = dev_pm_domain_attach_by_id(dev, i); | 
|---|
| 245 | else | 
|---|
| 246 | pd_dev = dev_pm_domain_attach_by_name(dev, | 
|---|
| 247 | data->pd_names[i]); | 
|---|
| 248 | if (IS_ERR_OR_NULL(ptr: pd_dev)) { | 
|---|
| 249 | ret = pd_dev ? PTR_ERR(ptr: pd_dev) : -ENODEV; | 
|---|
| 250 | goto err_attach; | 
|---|
| 251 | } | 
|---|
| 252 |  | 
|---|
| 253 | if (pd_flags & PD_FLAG_REQUIRED_OPP) { | 
|---|
| 254 | struct dev_pm_opp_config config = { | 
|---|
| 255 | .required_dev = pd_dev, | 
|---|
| 256 | .required_dev_index = i, | 
|---|
| 257 | }; | 
|---|
| 258 |  | 
|---|
| 259 | ret = dev_pm_opp_set_config(dev, config: &config); | 
|---|
| 260 | if (ret < 0) | 
|---|
| 261 | goto err_link; | 
|---|
| 262 |  | 
|---|
| 263 | pds->opp_tokens[i] = ret; | 
|---|
| 264 | } | 
|---|
| 265 |  | 
|---|
| 266 | if (link_flags) { | 
|---|
| 267 | struct device_link *link; | 
|---|
| 268 |  | 
|---|
| 269 | link = device_link_add(consumer: dev, supplier: pd_dev, flags: link_flags); | 
|---|
| 270 | if (!link) { | 
|---|
| 271 | ret = -ENODEV; | 
|---|
| 272 | goto err_link; | 
|---|
| 273 | } | 
|---|
| 274 |  | 
|---|
| 275 | pds->pd_links[i] = link; | 
|---|
| 276 | } | 
|---|
| 277 |  | 
|---|
| 278 | pds->pd_devs[i] = pd_dev; | 
|---|
| 279 | } | 
|---|
| 280 |  | 
|---|
| 281 | pds->num_pds = num_pds; | 
|---|
| 282 | *list = pds; | 
|---|
| 283 | return num_pds; | 
|---|
| 284 |  | 
|---|
| 285 | err_link: | 
|---|
| 286 | dev_pm_opp_clear_config(token: pds->opp_tokens[i]); | 
|---|
| 287 | dev_pm_domain_detach(dev: pd_dev, power_off: true); | 
|---|
| 288 | err_attach: | 
|---|
| 289 | while (--i >= 0) { | 
|---|
| 290 | dev_pm_opp_clear_config(token: pds->opp_tokens[i]); | 
|---|
| 291 | if (pds->pd_links[i]) | 
|---|
| 292 | device_link_del(link: pds->pd_links[i]); | 
|---|
| 293 | dev_pm_domain_detach(dev: pds->pd_devs[i], power_off: true); | 
|---|
| 294 | } | 
|---|
| 295 | kfree(objp: pds->pd_devs); | 
|---|
| 296 | free_pds: | 
|---|
| 297 | kfree(objp: pds); | 
|---|
| 298 | return ret; | 
|---|
| 299 | } | 
|---|
| 300 | EXPORT_SYMBOL_GPL(dev_pm_domain_attach_list); | 
|---|
| 301 |  | 
|---|
| 302 | /** | 
|---|
| 303 | * devm_pm_domain_detach_list - devres-enabled version of dev_pm_domain_detach_list. | 
|---|
| 304 | * @_list: The list of PM domains to detach. | 
|---|
| 305 | * | 
|---|
| 306 | * This function reverse the actions from devm_pm_domain_attach_list(). | 
|---|
| 307 | * it will be invoked during the remove phase from drivers implicitly if driver | 
|---|
| 308 | * uses devm_pm_domain_attach_list() to attach the PM domains. | 
|---|
| 309 | */ | 
|---|
| 310 | static void devm_pm_domain_detach_list(void *_list) | 
|---|
| 311 | { | 
|---|
| 312 | struct dev_pm_domain_list *list = _list; | 
|---|
| 313 |  | 
|---|
| 314 | dev_pm_domain_detach_list(list); | 
|---|
| 315 | } | 
|---|
| 316 |  | 
|---|
| 317 | /** | 
|---|
| 318 | * devm_pm_domain_attach_list - devres-enabled version of dev_pm_domain_attach_list | 
|---|
| 319 | * @dev: The device used to lookup the PM domains for. | 
|---|
| 320 | * @data: The data used for attaching to the PM domains. | 
|---|
| 321 | * @list: An out-parameter with an allocated list of attached PM domains. | 
|---|
| 322 | * | 
|---|
| 323 | * NOTE: this will also handle calling devm_pm_domain_detach_list() for | 
|---|
| 324 | * you during remove phase. | 
|---|
| 325 | * | 
|---|
| 326 | * Returns the number of attached PM domains or a negative error code in case of | 
|---|
| 327 | * a failure. | 
|---|
| 328 | */ | 
|---|
| 329 | int devm_pm_domain_attach_list(struct device *dev, | 
|---|
| 330 | const struct dev_pm_domain_attach_data *data, | 
|---|
| 331 | struct dev_pm_domain_list **list) | 
|---|
| 332 | { | 
|---|
| 333 | int ret, num_pds; | 
|---|
| 334 |  | 
|---|
| 335 | num_pds = dev_pm_domain_attach_list(dev, data, list); | 
|---|
| 336 | if (num_pds <= 0) | 
|---|
| 337 | return num_pds; | 
|---|
| 338 |  | 
|---|
| 339 | ret = devm_add_action_or_reset(dev, devm_pm_domain_detach_list, *list); | 
|---|
| 340 | if (ret) | 
|---|
| 341 | return ret; | 
|---|
| 342 |  | 
|---|
| 343 | return num_pds; | 
|---|
| 344 | } | 
|---|
| 345 | EXPORT_SYMBOL_GPL(devm_pm_domain_attach_list); | 
|---|
| 346 |  | 
|---|
| 347 | /** | 
|---|
| 348 | * dev_pm_domain_detach - Detach a device from its PM domain. | 
|---|
| 349 | * @dev: Device to detach. | 
|---|
| 350 | * @power_off: Used to indicate whether we should power off the device. | 
|---|
| 351 | * | 
|---|
| 352 | * This functions will reverse the actions from dev_pm_domain_attach(), | 
|---|
| 353 | * dev_pm_domain_attach_by_id() and dev_pm_domain_attach_by_name(), thus it | 
|---|
| 354 | * detaches @dev from its PM domain.  Typically it should be invoked during the | 
|---|
| 355 | * remove phase, either from subsystem level code or from drivers. | 
|---|
| 356 | * | 
|---|
| 357 | * Callers must ensure proper synchronization of this function with power | 
|---|
| 358 | * management callbacks. | 
|---|
| 359 | */ | 
|---|
| 360 | void dev_pm_domain_detach(struct device *dev, bool power_off) | 
|---|
| 361 | { | 
|---|
| 362 | if (dev->pm_domain && dev->pm_domain->detach) | 
|---|
| 363 | dev->pm_domain->detach(dev, power_off); | 
|---|
| 364 | } | 
|---|
| 365 | EXPORT_SYMBOL_GPL(dev_pm_domain_detach); | 
|---|
| 366 |  | 
|---|
| 367 | /** | 
|---|
| 368 | * dev_pm_domain_detach_list - Detach a list of PM domains. | 
|---|
| 369 | * @list: The list of PM domains to detach. | 
|---|
| 370 | * | 
|---|
| 371 | * This function reverse the actions from dev_pm_domain_attach_list(). | 
|---|
| 372 | * Typically it should be invoked during the remove phase from drivers. | 
|---|
| 373 | * | 
|---|
| 374 | * Callers must ensure proper synchronization of this function with power | 
|---|
| 375 | * management callbacks. | 
|---|
| 376 | */ | 
|---|
| 377 | void dev_pm_domain_detach_list(struct dev_pm_domain_list *list) | 
|---|
| 378 | { | 
|---|
| 379 | int i; | 
|---|
| 380 |  | 
|---|
| 381 | if (!list) | 
|---|
| 382 | return; | 
|---|
| 383 |  | 
|---|
| 384 | for (i = 0; i < list->num_pds; i++) { | 
|---|
| 385 | dev_pm_opp_clear_config(token: list->opp_tokens[i]); | 
|---|
| 386 | if (list->pd_links[i]) | 
|---|
| 387 | device_link_del(link: list->pd_links[i]); | 
|---|
| 388 | dev_pm_domain_detach(list->pd_devs[i], true); | 
|---|
| 389 | } | 
|---|
| 390 |  | 
|---|
| 391 | kfree(objp: list->pd_devs); | 
|---|
| 392 | kfree(objp: list); | 
|---|
| 393 | } | 
|---|
| 394 | EXPORT_SYMBOL_GPL(dev_pm_domain_detach_list); | 
|---|
| 395 |  | 
|---|
| 396 | /** | 
|---|
| 397 | * dev_pm_domain_start - Start the device through its PM domain. | 
|---|
| 398 | * @dev: Device to start. | 
|---|
| 399 | * | 
|---|
| 400 | * This function should typically be called during probe by a subsystem/driver, | 
|---|
| 401 | * when it needs to start its device from the PM domain's perspective. Note | 
|---|
| 402 | * that, it's assumed that the PM domain is already powered on when this | 
|---|
| 403 | * function is called. | 
|---|
| 404 | * | 
|---|
| 405 | * Returns 0 on success and negative error values on failures. | 
|---|
| 406 | */ | 
|---|
| 407 | int dev_pm_domain_start(struct device *dev) | 
|---|
| 408 | { | 
|---|
| 409 | if (dev->pm_domain && dev->pm_domain->start) | 
|---|
| 410 | return dev->pm_domain->start(dev); | 
|---|
| 411 |  | 
|---|
| 412 | return 0; | 
|---|
| 413 | } | 
|---|
| 414 | EXPORT_SYMBOL_GPL(dev_pm_domain_start); | 
|---|
| 415 |  | 
|---|
| 416 | /** | 
|---|
| 417 | * dev_pm_domain_set - Set PM domain of a device. | 
|---|
| 418 | * @dev: Device whose PM domain is to be set. | 
|---|
| 419 | * @pd: PM domain to be set, or NULL. | 
|---|
| 420 | * | 
|---|
| 421 | * Sets the PM domain the device belongs to. The PM domain of a device needs | 
|---|
| 422 | * to be set before its probe finishes (it's bound to a driver). | 
|---|
| 423 | * | 
|---|
| 424 | * This function must be called with the device lock held. | 
|---|
| 425 | */ | 
|---|
| 426 | void dev_pm_domain_set(struct device *dev, struct dev_pm_domain *pd) | 
|---|
| 427 | { | 
|---|
| 428 | if (dev->pm_domain == pd) | 
|---|
| 429 | return; | 
|---|
| 430 |  | 
|---|
| 431 | WARN(pd && device_is_bound(dev), | 
|---|
| 432 | "PM domains can only be changed for unbound devices\n"); | 
|---|
| 433 | dev->pm_domain = pd; | 
|---|
| 434 | device_pm_check_callbacks(dev); | 
|---|
| 435 | } | 
|---|
| 436 | EXPORT_SYMBOL_GPL(dev_pm_domain_set); | 
|---|
| 437 |  | 
|---|
| 438 | /** | 
|---|
| 439 | * dev_pm_domain_set_performance_state - Request a new performance state. | 
|---|
| 440 | * @dev: The device to make the request for. | 
|---|
| 441 | * @state: Target performance state for the device. | 
|---|
| 442 | * | 
|---|
| 443 | * This function should be called when a new performance state needs to be | 
|---|
| 444 | * requested for a device that is attached to a PM domain. Note that, the | 
|---|
| 445 | * support for performance scaling for PM domains is optional. | 
|---|
| 446 | * | 
|---|
| 447 | * Returns 0 on success and when performance scaling isn't supported, negative | 
|---|
| 448 | * error code on failure. | 
|---|
| 449 | */ | 
|---|
| 450 | int dev_pm_domain_set_performance_state(struct device *dev, unsigned int state) | 
|---|
| 451 | { | 
|---|
| 452 | if (dev->pm_domain && dev->pm_domain->set_performance_state) | 
|---|
| 453 | return dev->pm_domain->set_performance_state(dev, state); | 
|---|
| 454 |  | 
|---|
| 455 | return 0; | 
|---|
| 456 | } | 
|---|
| 457 | EXPORT_SYMBOL_GPL(dev_pm_domain_set_performance_state); | 
|---|
| 458 |  | 
|---|