xref: /openbmc/linux/drivers/base/power/common.c (revision 51990e825431089747f8896244b5c17d3a6423f1)
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/init.h>
10ef27bed1SRafael J. Wysocki #include <linux/kernel.h>
11*51990e82SPaul Gortmaker #include <linux/device.h>
12aaf19544SPaul Gortmaker #include <linux/export.h>
13ef27bed1SRafael J. Wysocki #include <linux/slab.h>
14b5e8d269SRafael J. Wysocki #include <linux/pm_clock.h>
15ef27bed1SRafael J. Wysocki 
16ef27bed1SRafael J. Wysocki /**
17ef27bed1SRafael J. Wysocki  * dev_pm_get_subsys_data - Create or refcount power.subsys_data for device.
18ef27bed1SRafael J. Wysocki  * @dev: Device to handle.
19ef27bed1SRafael J. Wysocki  *
20ef27bed1SRafael J. Wysocki  * If power.subsys_data is NULL, point it to a new object, otherwise increment
21ef27bed1SRafael J. Wysocki  * its reference counter.  Return 1 if a new object has been created, otherwise
22ef27bed1SRafael J. Wysocki  * return 0 or error code.
23ef27bed1SRafael J. Wysocki  */
24ef27bed1SRafael J. Wysocki int dev_pm_get_subsys_data(struct device *dev)
25ef27bed1SRafael J. Wysocki {
26ef27bed1SRafael J. Wysocki 	struct pm_subsys_data *psd;
27ef27bed1SRafael J. Wysocki 	int ret = 0;
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 		ret = 1;
44ef27bed1SRafael J. Wysocki 	}
45ef27bed1SRafael J. Wysocki 
46ef27bed1SRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
47ef27bed1SRafael J. Wysocki 
48ef27bed1SRafael J. Wysocki 	/* kfree() verifies that its argument is nonzero. */
49ef27bed1SRafael J. Wysocki 	kfree(psd);
50ef27bed1SRafael J. Wysocki 
51ef27bed1SRafael J. Wysocki 	return ret;
52ef27bed1SRafael J. Wysocki }
53ef27bed1SRafael J. Wysocki EXPORT_SYMBOL_GPL(dev_pm_get_subsys_data);
54ef27bed1SRafael J. Wysocki 
55ef27bed1SRafael J. Wysocki /**
56ef27bed1SRafael J. Wysocki  * dev_pm_put_subsys_data - Drop reference to power.subsys_data.
57ef27bed1SRafael J. Wysocki  * @dev: Device to handle.
58ef27bed1SRafael J. Wysocki  *
59ef27bed1SRafael J. Wysocki  * If the reference counter of power.subsys_data is zero after dropping the
60ef27bed1SRafael J. Wysocki  * reference, power.subsys_data is removed.  Return 1 if that happens or 0
61ef27bed1SRafael J. Wysocki  * otherwise.
62ef27bed1SRafael J. Wysocki  */
63ef27bed1SRafael J. Wysocki int dev_pm_put_subsys_data(struct device *dev)
64ef27bed1SRafael J. Wysocki {
65ef27bed1SRafael J. Wysocki 	struct pm_subsys_data *psd;
66ef27bed1SRafael J. Wysocki 	int ret = 0;
67ef27bed1SRafael J. Wysocki 
68ef27bed1SRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
69ef27bed1SRafael J. Wysocki 
70ef27bed1SRafael J. Wysocki 	psd = dev_to_psd(dev);
71ef27bed1SRafael J. Wysocki 	if (!psd) {
72ef27bed1SRafael J. Wysocki 		ret = -EINVAL;
73ef27bed1SRafael J. Wysocki 		goto out;
74ef27bed1SRafael J. Wysocki 	}
75ef27bed1SRafael J. Wysocki 
76ef27bed1SRafael J. Wysocki 	if (--psd->refcount == 0) {
77ef27bed1SRafael J. Wysocki 		dev->power.subsys_data = NULL;
78ef27bed1SRafael J. Wysocki 		kfree(psd);
79ef27bed1SRafael J. Wysocki 		ret = 1;
80ef27bed1SRafael J. Wysocki 	}
81ef27bed1SRafael J. Wysocki 
82ef27bed1SRafael J. Wysocki  out:
83ef27bed1SRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
84ef27bed1SRafael J. Wysocki 
85ef27bed1SRafael J. Wysocki 	return ret;
86ef27bed1SRafael J. Wysocki }
87ef27bed1SRafael J. Wysocki EXPORT_SYMBOL_GPL(dev_pm_put_subsys_data);
88