linux/arch/sh/kernel/cpu/shmobile/pm_runtime.c
<<
>>
Prefs
   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
  20static DEFINE_SPINLOCK(hwblk_lock);
  21static LIST_HEAD(hwblk_idle_list);
  22static struct work_struct hwblk_work;
  23
  24extern struct hwblk_info *hwblk_info;
  25
  26static 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
  39static 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
  67static 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
  96static 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 */
 132void platform_pm_runtime_suspend_idle(void)
 133{
 134        queue_work(pm_wq, &hwblk_work);
 135}
 136
 137int 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
 180out:
 181        dev_dbg(dev, "platform_pm_runtime_suspend() [%d] returns %d\n",
 182                hwblk, ret);
 183
 184        return ret;
 185}
 186
 187int 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);
 225out:
 226        dev_dbg(dev, "platform_pm_runtime_resume() [%d] returns %d\n",
 227                hwblk, ret);
 228
 229        return ret;
 230}
 231
 232int 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);
 249out:
 250        dev_dbg(dev, "platform_pm_runtime_idle() [%d] done!\n", hwblk);
 251        return ret;
 252}
 253
 254static 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
 292static struct notifier_block platform_bus_notifier = {
 293        .notifier_call = platform_bus_notify
 294};
 295
 296static 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}
 303core_initcall(sh_pm_runtime_init);
 304