~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/sh/kernel/cpu/shmobile/pm_runtime.c

Version: ~ [ linux-5.10-rc5 ] ~ [ linux-5.9.10 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.79 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.159 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.208 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.245 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.245 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.85 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * arch/sh/kernel/cpu/shmobile/pm_runtime.c
  3  *
  4  * Runtime PM support code for SuperH Mobile
  5  *
  6  *  Copyright (C) 2009 Magnus Damm
  7  *
  8  * This file is subject to the terms and conditions of the GNU General Public
  9  * License.  See the file "COPYING" in the main directory of this archive
 10  * for more details.
 11  */
 12 #include <linux/init.h>
 13 #include <linux/kernel.h>
 14 #include <linux/io.h>
 15 #include <linux/pm_runtime.h>
 16 #include <linux/platform_device.h>
 17 #include <linux/mutex.h>
 18 #include <asm/hwblk.h>
 19 
 20 static DEFINE_SPINLOCK(hwblk_lock);
 21 static LIST_HEAD(hwblk_idle_list);
 22 static struct work_struct hwblk_work;
 23 
 24 extern struct hwblk_info *hwblk_info;
 25 
 26 static void platform_pm_runtime_not_idle(struct platform_device *pdev)
 27 {
 28         unsigned long flags;
 29 
 30         /* remove device from idle list */
 31         spin_lock_irqsave(&hwblk_lock, flags);
 32         if (test_bit(PDEV_ARCHDATA_FLAG_IDLE, &pdev->archdata.flags)) {
 33                 list_del(&pdev->archdata.entry);
 34                 __clear_bit(PDEV_ARCHDATA_FLAG_IDLE, &pdev->archdata.flags);
 35         }
 36         spin_unlock_irqrestore(&hwblk_lock, flags);
 37 }
 38 
 39 static int __platform_pm_runtime_resume(struct platform_device *pdev)
 40 {
 41         struct device *d = &pdev->dev;
 42         struct pdev_archdata *ad = &pdev->archdata;
 43         int hwblk = ad->hwblk_id;
 44         int ret = -ENOSYS;
 45 
 46         dev_dbg(d, "__platform_pm_runtime_resume() [%d]\n", hwblk);
 47 
 48         if (d->driver && d->driver->pm && d->driver->pm->runtime_resume) {
 49                 hwblk_enable(hwblk_info, hwblk);
 50                 ret = 0;
 51 
 52                 if (test_bit(PDEV_ARCHDATA_FLAG_SUSP, &ad->flags)) {
 53                         ret = d->driver->pm->runtime_resume(d);
 54                         if (!ret)
 55                                 clear_bit(PDEV_ARCHDATA_FLAG_SUSP, &ad->flags);
 56                         else
 57                                 hwblk_disable(hwblk_info, hwblk);
 58                 }
 59         }
 60 
 61         dev_dbg(d, "__platform_pm_runtime_resume() [%d] - returns %d\n",
 62                 hwblk, ret);
 63 
 64         return ret;
 65 }
 66 
 67 static int __platform_pm_runtime_suspend(struct platform_device *pdev)
 68 {
 69         struct device *d = &pdev->dev;
 70         struct pdev_archdata *ad = &pdev->archdata;
 71         int hwblk = ad->hwblk_id;
 72         int ret = -ENOSYS;
 73 
 74         dev_dbg(d, "__platform_pm_runtime_suspend() [%d]\n", hwblk);
 75 
 76         if (d->driver && d->driver->pm && d->driver->pm->runtime_suspend) {
 77                 BUG_ON(!test_bit(PDEV_ARCHDATA_FLAG_IDLE, &ad->flags));
 78 
 79                 hwblk_enable(hwblk_info, hwblk);
 80                 ret = d->driver->pm->runtime_suspend(d);
 81                 hwblk_disable(hwblk_info, hwblk);
 82 
 83                 if (!ret) {
 84                         set_bit(PDEV_ARCHDATA_FLAG_SUSP, &ad->flags);
 85                         platform_pm_runtime_not_idle(pdev);
 86                         hwblk_cnt_dec(hwblk_info, hwblk, HWBLK_CNT_IDLE);
 87                 }
 88         }
 89 
 90         dev_dbg(d, "__platform_pm_runtime_suspend() [%d] - returns %d\n",
 91                 hwblk, ret);
 92 
 93         return ret;
 94 }
 95 
 96 static void platform_pm_runtime_work(struct work_struct *work)
 97 {
 98         struct platform_device *pdev;
 99         unsigned long flags;
100         int ret;
101 
102         /* go through the idle list and suspend one device at a time */
103         do {
104                 spin_lock_irqsave(&hwblk_lock, flags);
105                 if (list_empty(&hwblk_idle_list))
106                         pdev = NULL;
107                 else
108                         pdev = list_first_entry(&hwblk_idle_list,
109                                                 struct platform_device,
110                                                 archdata.entry);
111                 spin_unlock_irqrestore(&hwblk_lock, flags);
112 
113                 if (pdev) {
114                         mutex_lock(&pdev->archdata.mutex);
115                         ret = __platform_pm_runtime_suspend(pdev);
116 
117                         /* at this point the platform device may be:
118                          * suspended: ret = 0, FLAG_SUSP set, clock stopped
119                          * failed: ret < 0, FLAG_IDLE set, clock stopped
120                          */
121                         mutex_unlock(&pdev->archdata.mutex);
122                 } else {
123                         ret = -ENODEV;
124                 }
125         } while (!ret);
126 }
127 
128 /* this function gets called from cpuidle context when all devices in the
129  * main power domain are unused but some are counted as idle, ie the hwblk
130  * counter values are (HWBLK_CNT_USAGE == 0) && (HWBLK_CNT_IDLE != 0)
131  */
132 void platform_pm_runtime_suspend_idle(void)
133 {
134         queue_work(pm_wq, &hwblk_work);
135 }
136 
137 int platform_pm_runtime_suspend(struct device *dev)
138 {
139         struct platform_device *pdev = to_platform_device(dev);
140         struct pdev_archdata *ad = &pdev->archdata;
141         unsigned long flags;
142         int hwblk = ad->hwblk_id;
143         int ret = 0;
144 
145         dev_dbg(dev, "platform_pm_runtime_suspend() [%d]\n", hwblk);
146 
147         /* ignore off-chip platform devices */
148         if (!hwblk)
149                 goto out;
150 
151         /* interrupt context not allowed */
152         might_sleep();
153 
154         /* catch misconfigured drivers not starting with resume */
155         if (test_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags)) {
156                 ret = -EINVAL;
157                 goto out;
158         }
159 
160         /* serialize */
161         mutex_lock(&ad->mutex);
162 
163         /* disable clock */
164         hwblk_disable(hwblk_info, hwblk);
165 
166         /* put device on idle list */
167         spin_lock_irqsave(&hwblk_lock, flags);
168         list_add_tail(&pdev->archdata.entry, &hwblk_idle_list);
169         __set_bit(PDEV_ARCHDATA_FLAG_IDLE, &pdev->archdata.flags);
170         spin_unlock_irqrestore(&hwblk_lock, flags);
171 
172         /* increase idle count */
173         hwblk_cnt_inc(hwblk_info, hwblk, HWBLK_CNT_IDLE);
174 
175         /* at this point the platform device is:
176          * idle: ret = 0, FLAG_IDLE set, clock stopped
177          */
178         mutex_unlock(&ad->mutex);
179 
180 out:
181         dev_dbg(dev, "platform_pm_runtime_suspend() [%d] returns %d\n",
182                 hwblk, ret);
183 
184         return ret;
185 }
186 
187 int platform_pm_runtime_resume(struct device *dev)
188 {
189         struct platform_device *pdev = to_platform_device(dev);
190         struct pdev_archdata *ad = &pdev->archdata;
191         int hwblk = ad->hwblk_id;
192         int ret = 0;
193 
194         dev_dbg(dev, "platform_pm_runtime_resume() [%d]\n", hwblk);
195 
196         /* ignore off-chip platform devices */
197         if (!hwblk)
198                 goto out;
199 
200         /* interrupt context not allowed */
201         might_sleep();
202 
203         /* serialize */
204         mutex_lock(&ad->mutex);
205 
206         /* make sure device is removed from idle list */
207         platform_pm_runtime_not_idle(pdev);
208 
209         /* decrease idle count */
210         if (!test_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags) &&
211             !test_bit(PDEV_ARCHDATA_FLAG_SUSP, &pdev->archdata.flags))
212                 hwblk_cnt_dec(hwblk_info, hwblk, HWBLK_CNT_IDLE);
213 
214         /* resume the device if needed */
215         ret = __platform_pm_runtime_resume(pdev);
216 
217         /* the driver has been initialized now, so clear the init flag */
218         clear_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags);
219 
220         /* at this point the platform device may be:
221          * resumed: ret = 0, flags = 0, clock started
222          * failed: ret < 0, FLAG_SUSP set, clock stopped
223          */
224         mutex_unlock(&ad->mutex);
225 out:
226         dev_dbg(dev, "platform_pm_runtime_resume() [%d] returns %d\n",
227                 hwblk, ret);
228 
229         return ret;
230 }
231 
232 int platform_pm_runtime_idle(struct device *dev)
233 {
234         struct platform_device *pdev = to_platform_device(dev);
235         int hwblk = pdev->archdata.hwblk_id;
236         int ret = 0;
237 
238         dev_dbg(dev, "platform_pm_runtime_idle() [%d]\n", hwblk);
239 
240         /* ignore off-chip platform devices */
241         if (!hwblk)
242                 goto out;
243 
244         /* interrupt context not allowed, use pm_runtime_put()! */
245         might_sleep();
246 
247         /* suspend synchronously to disable clocks immediately */
248         ret = pm_runtime_suspend(dev);
249 out:
250         dev_dbg(dev, "platform_pm_runtime_idle() [%d] done!\n", hwblk);
251         return ret;
252 }
253 
254 static int platform_bus_notify(struct notifier_block *nb,
255                                unsigned long action, void *data)
256 {
257         struct device *dev = data;
258         struct platform_device *pdev = to_platform_device(dev);
259         int hwblk = pdev->archdata.hwblk_id;
260 
261         /* ignore off-chip platform devices */
262         if (!hwblk)
263                 return 0;
264 
265         switch (action) {
266         case BUS_NOTIFY_ADD_DEVICE:
267                 INIT_LIST_HEAD(&pdev->archdata.entry);
268                 mutex_init(&pdev->archdata.mutex);
269                 /* platform devices without drivers should be disabled */
270                 hwblk_enable(hwblk_info, hwblk);
271                 hwblk_disable(hwblk_info, hwblk);
272                 /* make sure driver re-inits itself once */
273                 __set_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags);
274                 break;
275         /* TODO: add BUS_NOTIFY_BIND_DRIVER and increase idle count */
276         case BUS_NOTIFY_BOUND_DRIVER:
277                 /* keep track of number of devices in use per hwblk */
278                 hwblk_cnt_inc(hwblk_info, hwblk, HWBLK_CNT_DEVICES);
279                 break;
280         case BUS_NOTIFY_UNBOUND_DRIVER:
281                 /* keep track of number of devices in use per hwblk */
282                 hwblk_cnt_dec(hwblk_info, hwblk, HWBLK_CNT_DEVICES);
283                 /* make sure driver re-inits itself once */
284                 __set_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags);
285                 break;
286         case BUS_NOTIFY_DEL_DEVICE:
287                 break;
288         }
289         return 0;
290 }
291 
292 static struct notifier_block platform_bus_notifier = {
293         .notifier_call = platform_bus_notify
294 };
295 
296 static int __init sh_pm_runtime_init(void)
297 {
298         INIT_WORK(&hwblk_work, platform_pm_runtime_work);
299 
300         bus_register_notifier(&platform_bus_type, &platform_bus_notifier);
301         return 0;
302 }
303 core_initcall(sh_pm_runtime_init);
304 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp