xref: /openbmc/linux/drivers/base/power/common.c (revision d5e1670afe0c886d6dd92afb7a1f085f88294dc8)
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>
1151990e82SPaul 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 
28ef27bed1SRafael J. Wysocki 	psd = kzalloc(sizeof(*psd), GFP_KERNEL);
29ef27bed1SRafael J. Wysocki 	if (!psd)
30ef27bed1SRafael J. Wysocki 		return -ENOMEM;
31ef27bed1SRafael J. Wysocki 
32ef27bed1SRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
33ef27bed1SRafael J. Wysocki 
34ef27bed1SRafael J. Wysocki 	if (dev->power.subsys_data) {
35ef27bed1SRafael J. Wysocki 		dev->power.subsys_data->refcount++;
36ef27bed1SRafael J. Wysocki 	} else {
37ef27bed1SRafael J. Wysocki 		spin_lock_init(&psd->lock);
38ef27bed1SRafael J. Wysocki 		psd->refcount = 1;
39ef27bed1SRafael J. Wysocki 		dev->power.subsys_data = psd;
40ef27bed1SRafael J. Wysocki 		pm_clk_init(dev);
41ef27bed1SRafael J. Wysocki 		psd = NULL;
42ef27bed1SRafael J. Wysocki 	}
43ef27bed1SRafael J. Wysocki 
44ef27bed1SRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
45ef27bed1SRafael J. Wysocki 
46ef27bed1SRafael J. Wysocki 	/* kfree() verifies that its argument is nonzero. */
47ef27bed1SRafael J. Wysocki 	kfree(psd);
48ef27bed1SRafael J. Wysocki 
4977254950SRafael J. Wysocki 	return 0;
50ef27bed1SRafael J. Wysocki }
51ef27bed1SRafael J. Wysocki EXPORT_SYMBOL_GPL(dev_pm_get_subsys_data);
52ef27bed1SRafael J. Wysocki 
53ef27bed1SRafael J. Wysocki /**
54ef27bed1SRafael J. Wysocki  * dev_pm_put_subsys_data - Drop reference to power.subsys_data.
55ef27bed1SRafael J. Wysocki  * @dev: Device to handle.
56ef27bed1SRafael J. Wysocki  *
57ef27bed1SRafael J. Wysocki  * If the reference counter of power.subsys_data is zero after dropping the
58ef27bed1SRafael J. Wysocki  * reference, power.subsys_data is removed.  Return 1 if that happens or 0
59ef27bed1SRafael J. Wysocki  * otherwise.
60ef27bed1SRafael J. Wysocki  */
61ef27bed1SRafael J. Wysocki int dev_pm_put_subsys_data(struct device *dev)
62ef27bed1SRafael J. Wysocki {
63ef27bed1SRafael J. Wysocki 	struct pm_subsys_data *psd;
64*d5e1670aSShuah Khan 	int ret = 1;
65ef27bed1SRafael J. Wysocki 
66ef27bed1SRafael J. Wysocki 	spin_lock_irq(&dev->power.lock);
67ef27bed1SRafael J. Wysocki 
68ef27bed1SRafael J. Wysocki 	psd = dev_to_psd(dev);
69*d5e1670aSShuah Khan 	if (!psd)
70ef27bed1SRafael J. Wysocki 		goto out;
71ef27bed1SRafael J. Wysocki 
72ef27bed1SRafael J. Wysocki 	if (--psd->refcount == 0) {
73ef27bed1SRafael J. Wysocki 		dev->power.subsys_data = NULL;
74*d5e1670aSShuah Khan 	} else {
75*d5e1670aSShuah Khan 		psd = NULL;
76*d5e1670aSShuah Khan 		ret = 0;
77ef27bed1SRafael J. Wysocki 	}
78ef27bed1SRafael J. Wysocki 
79ef27bed1SRafael J. Wysocki  out:
80ef27bed1SRafael J. Wysocki 	spin_unlock_irq(&dev->power.lock);
81*d5e1670aSShuah Khan 	kfree(psd);
82ef27bed1SRafael J. Wysocki 
83ef27bed1SRafael J. Wysocki 	return ret;
84ef27bed1SRafael J. Wysocki }
85ef27bed1SRafael J. Wysocki EXPORT_SYMBOL_GPL(dev_pm_put_subsys_data);
86