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