xref: /openbmc/linux/drivers/base/power/common.c (revision ef27bed1870dbd5fd363ff5ec51eebd5a695e277)
1*ef27bed1SRafael J. Wysocki /*
2*ef27bed1SRafael J. Wysocki  * drivers/base/power/common.c - Common device power management code.
3*ef27bed1SRafael J. Wysocki  *
4*ef27bed1SRafael J. Wysocki  * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
5*ef27bed1SRafael J. Wysocki  *
6*ef27bed1SRafael J. Wysocki  * This file is released under the GPLv2.
7*ef27bed1SRafael J. Wysocki  */
8*ef27bed1SRafael J. Wysocki 
9*ef27bed1SRafael J. Wysocki #include <linux/init.h>
10*ef27bed1SRafael J. Wysocki #include <linux/kernel.h>
11*ef27bed1SRafael J. Wysocki #include <linux/module.h>
12*ef27bed1SRafael J. Wysocki #include <linux/slab.h>
13*ef27bed1SRafael J. Wysocki #include <linux/device.h>
14*ef27bed1SRafael J. Wysocki #include <linux/pm_runtime.h>
15*ef27bed1SRafael J. Wysocki 
16*ef27bed1SRafael J. Wysocki /**
17*ef27bed1SRafael J. Wysocki  * dev_pm_get_subsys_data - Create or refcount power.subsys_data for device.
18*ef27bed1SRafael J. Wysocki  * @dev: Device to handle.
19*ef27bed1SRafael J. Wysocki  *
20*ef27bed1SRafael J. Wysocki  * If power.subsys_data is NULL, point it to a new object, otherwise increment
21*ef27bed1SRafael J. Wysocki  * its reference counter.  Return 1 if a new object has been created, otherwise
22*ef27bed1SRafael J. Wysocki  * return 0 or error code.
23*ef27bed1SRafael J. Wysocki  */
24*ef27bed1SRafael J. Wysocki int dev_pm_get_subsys_data(struct device *dev)
25*ef27bed1SRafael J. Wysocki {
26*ef27bed1SRafael J. Wysocki 	struct pm_subsys_data *psd;
27*ef27bed1SRafael J. Wysocki 	int ret = 0;
28*ef27bed1SRafael J. Wysocki 
29*ef27bed1SRafael J. Wysocki 	psd = kzalloc(sizeof(*psd), GFP_KERNEL);
30*ef27bed1SRafael J. Wysocki 	if (!psd)
31*ef27bed1SRafael J. Wysocki 		return -ENOMEM;
32*ef27bed1SRafael J. Wysocki 
33*ef27bed1SRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
34*ef27bed1SRafael J. Wysocki 
35*ef27bed1SRafael J. Wysocki 	if (dev->power.subsys_data) {
36*ef27bed1SRafael J. Wysocki 		dev->power.subsys_data->refcount++;
37*ef27bed1SRafael J. Wysocki 	} else {
38*ef27bed1SRafael J. Wysocki 		spin_lock_init(&psd->lock);
39*ef27bed1SRafael J. Wysocki 		psd->refcount = 1;
40*ef27bed1SRafael J. Wysocki 		dev->power.subsys_data = psd;
41*ef27bed1SRafael J. Wysocki 		pm_clk_init(dev);
42*ef27bed1SRafael J. Wysocki 		psd = NULL;
43*ef27bed1SRafael J. Wysocki 		ret = 1;
44*ef27bed1SRafael J. Wysocki 	}
45*ef27bed1SRafael J. Wysocki 
46*ef27bed1SRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
47*ef27bed1SRafael J. Wysocki 
48*ef27bed1SRafael J. Wysocki 	/* kfree() verifies that its argument is nonzero. */
49*ef27bed1SRafael J. Wysocki 	kfree(psd);
50*ef27bed1SRafael J. Wysocki 
51*ef27bed1SRafael J. Wysocki 	return ret;
52*ef27bed1SRafael J. Wysocki }
53*ef27bed1SRafael J. Wysocki EXPORT_SYMBOL_GPL(dev_pm_get_subsys_data);
54*ef27bed1SRafael J. Wysocki 
55*ef27bed1SRafael J. Wysocki /**
56*ef27bed1SRafael J. Wysocki  * dev_pm_put_subsys_data - Drop reference to power.subsys_data.
57*ef27bed1SRafael J. Wysocki  * @dev: Device to handle.
58*ef27bed1SRafael J. Wysocki  *
59*ef27bed1SRafael J. Wysocki  * If the reference counter of power.subsys_data is zero after dropping the
60*ef27bed1SRafael J. Wysocki  * reference, power.subsys_data is removed.  Return 1 if that happens or 0
61*ef27bed1SRafael J. Wysocki  * otherwise.
62*ef27bed1SRafael J. Wysocki  */
63*ef27bed1SRafael J. Wysocki int dev_pm_put_subsys_data(struct device *dev)
64*ef27bed1SRafael J. Wysocki {
65*ef27bed1SRafael J. Wysocki 	struct pm_subsys_data *psd;
66*ef27bed1SRafael J. Wysocki 	int ret = 0;
67*ef27bed1SRafael J. Wysocki 
68*ef27bed1SRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
69*ef27bed1SRafael J. Wysocki 
70*ef27bed1SRafael J. Wysocki 	psd = dev_to_psd(dev);
71*ef27bed1SRafael J. Wysocki 	if (!psd) {
72*ef27bed1SRafael J. Wysocki 		ret = -EINVAL;
73*ef27bed1SRafael J. Wysocki 		goto out;
74*ef27bed1SRafael J. Wysocki 	}
75*ef27bed1SRafael J. Wysocki 
76*ef27bed1SRafael J. Wysocki 	if (--psd->refcount == 0) {
77*ef27bed1SRafael J. Wysocki 		dev->power.subsys_data = NULL;
78*ef27bed1SRafael J. Wysocki 		kfree(psd);
79*ef27bed1SRafael J. Wysocki 		ret = 1;
80*ef27bed1SRafael J. Wysocki 	}
81*ef27bed1SRafael J. Wysocki 
82*ef27bed1SRafael J. Wysocki  out:
83*ef27bed1SRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
84*ef27bed1SRafael J. Wysocki 
85*ef27bed1SRafael J. Wysocki 	return ret;
86*ef27bed1SRafael J. Wysocki }
87*ef27bed1SRafael J. Wysocki EXPORT_SYMBOL_GPL(dev_pm_put_subsys_data);
88