xref: /openbmc/linux/drivers/base/power/common.c (revision 1e95e3b2da424db68d0a465273f1901a990c6277)
1ef27bed1SRafael J. Wysocki /*
2ef27bed1SRafael J. Wysocki  * drivers/base/power/common.c - Common device power management code.
3ef27bed1SRafael J. Wysocki  *
4ef27bed1SRafael J. Wysocki  * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
5ef27bed1SRafael J. Wysocki  *
6ef27bed1SRafael J. Wysocki  * This file is released under the GPLv2.
7ef27bed1SRafael J. Wysocki  */
8ef27bed1SRafael J. Wysocki 
9ef27bed1SRafael J. Wysocki #include <linux/kernel.h>
1051990e82SPaul Gortmaker #include <linux/device.h>
11aaf19544SPaul Gortmaker #include <linux/export.h>
12ef27bed1SRafael J. Wysocki #include <linux/slab.h>
13b5e8d269SRafael J. Wysocki #include <linux/pm_clock.h>
1446420dd7SUlf Hansson #include <linux/acpi.h>
1546420dd7SUlf Hansson #include <linux/pm_domain.h>
16ef27bed1SRafael J. Wysocki 
17ef27bed1SRafael J. Wysocki /**
18ef27bed1SRafael J. Wysocki  * dev_pm_get_subsys_data - Create or refcount power.subsys_data for device.
19ef27bed1SRafael J. Wysocki  * @dev: Device to handle.
20ef27bed1SRafael J. Wysocki  *
21ef27bed1SRafael J. Wysocki  * If power.subsys_data is NULL, point it to a new object, otherwise increment
22766bb53cSUlf Hansson  * its reference counter.  Return 0 if new object has been created or refcount
23766bb53cSUlf Hansson  * increased, otherwise negative error code.
24ef27bed1SRafael J. Wysocki  */
25ef27bed1SRafael J. Wysocki int dev_pm_get_subsys_data(struct device *dev)
26ef27bed1SRafael J. Wysocki {
27ef27bed1SRafael J. Wysocki 	struct pm_subsys_data *psd;
28ef27bed1SRafael J. Wysocki 
29ef27bed1SRafael J. Wysocki 	psd = kzalloc(sizeof(*psd), GFP_KERNEL);
30ef27bed1SRafael J. Wysocki 	if (!psd)
31ef27bed1SRafael J. Wysocki 		return -ENOMEM;
32ef27bed1SRafael J. Wysocki 
33ef27bed1SRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
34ef27bed1SRafael J. Wysocki 
35ef27bed1SRafael J. Wysocki 	if (dev->power.subsys_data) {
36ef27bed1SRafael J. Wysocki 		dev->power.subsys_data->refcount++;
37ef27bed1SRafael J. Wysocki 	} else {
38ef27bed1SRafael J. Wysocki 		spin_lock_init(&psd->lock);
39ef27bed1SRafael J. Wysocki 		psd->refcount = 1;
40ef27bed1SRafael J. Wysocki 		dev->power.subsys_data = psd;
41ef27bed1SRafael J. Wysocki 		pm_clk_init(dev);
42ef27bed1SRafael J. Wysocki 		psd = NULL;
43ef27bed1SRafael J. Wysocki 	}
44ef27bed1SRafael J. Wysocki 
45ef27bed1SRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
46ef27bed1SRafael J. Wysocki 
47ef27bed1SRafael J. Wysocki 	/* kfree() verifies that its argument is nonzero. */
48ef27bed1SRafael J. Wysocki 	kfree(psd);
49ef27bed1SRafael J. Wysocki 
5077254950SRafael J. Wysocki 	return 0;
51ef27bed1SRafael J. Wysocki }
52ef27bed1SRafael J. Wysocki EXPORT_SYMBOL_GPL(dev_pm_get_subsys_data);
53ef27bed1SRafael J. Wysocki 
54ef27bed1SRafael J. Wysocki /**
55ef27bed1SRafael J. Wysocki  * dev_pm_put_subsys_data - Drop reference to power.subsys_data.
56ef27bed1SRafael J. Wysocki  * @dev: Device to handle.
57ef27bed1SRafael J. Wysocki  *
58ef27bed1SRafael J. Wysocki  * If the reference counter of power.subsys_data is zero after dropping the
59*1e95e3b2SUlf Hansson  * reference, power.subsys_data is removed.
60ef27bed1SRafael J. Wysocki  */
61*1e95e3b2SUlf Hansson void dev_pm_put_subsys_data(struct device *dev)
62ef27bed1SRafael J. Wysocki {
63ef27bed1SRafael J. Wysocki 	struct pm_subsys_data *psd;
64ef27bed1SRafael J. Wysocki 
65ef27bed1SRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
66ef27bed1SRafael J. Wysocki 
67ef27bed1SRafael J. Wysocki 	psd = dev_to_psd(dev);
68d5e1670aSShuah Khan 	if (!psd)
69ef27bed1SRafael J. Wysocki 		goto out;
70ef27bed1SRafael J. Wysocki 
71*1e95e3b2SUlf Hansson 	if (--psd->refcount == 0)
72ef27bed1SRafael J. Wysocki 		dev->power.subsys_data = NULL;
73*1e95e3b2SUlf Hansson 	else
74d5e1670aSShuah Khan 		psd = NULL;
75ef27bed1SRafael J. Wysocki 
76ef27bed1SRafael J. Wysocki  out:
77ef27bed1SRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
78d5e1670aSShuah Khan 	kfree(psd);
79ef27bed1SRafael J. Wysocki }
80ef27bed1SRafael J. Wysocki EXPORT_SYMBOL_GPL(dev_pm_put_subsys_data);
8146420dd7SUlf Hansson 
8246420dd7SUlf Hansson /**
8346420dd7SUlf Hansson  * dev_pm_domain_attach - Attach a device to its PM domain.
8446420dd7SUlf Hansson  * @dev: Device to attach.
8546420dd7SUlf Hansson  * @power_on: Used to indicate whether we should power on the device.
8646420dd7SUlf Hansson  *
8746420dd7SUlf Hansson  * The @dev may only be attached to a single PM domain. By iterating through
8846420dd7SUlf Hansson  * the available alternatives we try to find a valid PM domain for the device.
8946420dd7SUlf Hansson  * As attachment succeeds, the ->detach() callback in the struct dev_pm_domain
9046420dd7SUlf Hansson  * should be assigned by the corresponding attach function.
9146420dd7SUlf Hansson  *
9246420dd7SUlf Hansson  * This function should typically be invoked from subsystem level code during
9346420dd7SUlf Hansson  * the probe phase. Especially for those that holds devices which requires
9446420dd7SUlf Hansson  * power management through PM domains.
9546420dd7SUlf Hansson  *
9646420dd7SUlf Hansson  * Callers must ensure proper synchronization of this function with power
9746420dd7SUlf Hansson  * management callbacks.
9846420dd7SUlf Hansson  *
9946420dd7SUlf Hansson  * Returns 0 on successfully attached PM domain or negative error code.
10046420dd7SUlf Hansson  */
10146420dd7SUlf Hansson int dev_pm_domain_attach(struct device *dev, bool power_on)
10246420dd7SUlf Hansson {
10346420dd7SUlf Hansson 	int ret;
10446420dd7SUlf Hansson 
10546420dd7SUlf Hansson 	ret = acpi_dev_pm_attach(dev, power_on);
10646420dd7SUlf Hansson 	if (ret)
10746420dd7SUlf Hansson 		ret = genpd_dev_pm_attach(dev);
10846420dd7SUlf Hansson 
10946420dd7SUlf Hansson 	return ret;
11046420dd7SUlf Hansson }
11146420dd7SUlf Hansson EXPORT_SYMBOL_GPL(dev_pm_domain_attach);
11246420dd7SUlf Hansson 
11346420dd7SUlf Hansson /**
11446420dd7SUlf Hansson  * dev_pm_domain_detach - Detach a device from its PM domain.
11546420dd7SUlf Hansson  * @dev: Device to attach.
11646420dd7SUlf Hansson  * @power_off: Used to indicate whether we should power off the device.
11746420dd7SUlf Hansson  *
11846420dd7SUlf Hansson  * This functions will reverse the actions from dev_pm_domain_attach() and thus
11946420dd7SUlf Hansson  * try to detach the @dev from its PM domain. Typically it should be invoked
12046420dd7SUlf Hansson  * from subsystem level code during the remove phase.
12146420dd7SUlf Hansson  *
12246420dd7SUlf Hansson  * Callers must ensure proper synchronization of this function with power
12346420dd7SUlf Hansson  * management callbacks.
12446420dd7SUlf Hansson  */
12546420dd7SUlf Hansson void dev_pm_domain_detach(struct device *dev, bool power_off)
12646420dd7SUlf Hansson {
12746420dd7SUlf Hansson 	if (dev->pm_domain && dev->pm_domain->detach)
12846420dd7SUlf Hansson 		dev->pm_domain->detach(dev, power_off);
12946420dd7SUlf Hansson }
13046420dd7SUlf Hansson EXPORT_SYMBOL_GPL(dev_pm_domain_detach);
131