linux/arch/mips/alchemy/devboards/pm.c
<<
>>
Prefs
   1/*
   2 * Alchemy Development Board example suspend userspace interface.
   3 *
   4 * (c) 2008 Manuel Lauss <mano@roarinelk.homelinux.net>
   5 */
   6
   7#include <linux/init.h>
   8#include <linux/kobject.h>
   9#include <linux/suspend.h>
  10#include <linux/sysfs.h>
  11#include <asm/mach-au1x00/au1000.h>
  12#include <asm/mach-au1x00/gpio.h>
  13
  14/*
  15 * Generic suspend userspace interface for Alchemy development boards.
  16 * This code exports a few sysfs nodes under /sys/power/db1x/ which
  17 * can be used by userspace to en/disable all au1x-provided wakeup
  18 * sources and configure the timeout after which the the TOYMATCH2 irq
  19 * is to trigger a wakeup.
  20 */
  21
  22
  23static unsigned long db1x_pm_sleep_secs;
  24static unsigned long db1x_pm_wakemsk;
  25static unsigned long db1x_pm_last_wakesrc;
  26
  27static int db1x_pm_enter(suspend_state_t state)
  28{
  29        /* enable GPIO based wakeup */
  30        alchemy_gpio1_input_enable();
  31
  32        /* clear and setup wake cause and source */
  33        au_writel(0, SYS_WAKEMSK);
  34        au_sync();
  35        au_writel(0, SYS_WAKESRC);
  36        au_sync();
  37
  38        au_writel(db1x_pm_wakemsk, SYS_WAKEMSK);
  39        au_sync();
  40
  41        /* setup 1Hz-timer-based wakeup: wait for reg access */
  42        while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20)
  43                asm volatile ("nop");
  44
  45        au_writel(au_readl(SYS_TOYREAD) + db1x_pm_sleep_secs, SYS_TOYMATCH2);
  46        au_sync();
  47
  48        /* wait for value to really hit the register */
  49        while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20)
  50                asm volatile ("nop");
  51
  52        /* ...and now the sandman can come! */
  53        au_sleep();
  54
  55        return 0;
  56}
  57
  58static int db1x_pm_begin(suspend_state_t state)
  59{
  60        if (!db1x_pm_wakemsk) {
  61                printk(KERN_ERR "db1x: no wakeup source activated!\n");
  62                return -EINVAL;
  63        }
  64
  65        return 0;
  66}
  67
  68static void db1x_pm_end(void)
  69{
  70        /* read and store wakeup source, the clear the register. To
  71         * be able to clear it, WAKEMSK must be cleared first.
  72         */
  73        db1x_pm_last_wakesrc = au_readl(SYS_WAKESRC);
  74
  75        au_writel(0, SYS_WAKEMSK);
  76        au_writel(0, SYS_WAKESRC);
  77        au_sync();
  78
  79}
  80
  81static struct platform_suspend_ops db1x_pm_ops = {
  82        .valid          = suspend_valid_only_mem,
  83        .begin          = db1x_pm_begin,
  84        .enter          = db1x_pm_enter,
  85        .end            = db1x_pm_end,
  86};
  87
  88#define ATTRCMP(x) (0 == strcmp(attr->attr.name, #x))
  89
  90static ssize_t db1x_pmattr_show(struct kobject *kobj,
  91                                struct kobj_attribute *attr,
  92                                char *buf)
  93{
  94        int idx;
  95
  96        if (ATTRCMP(timer_timeout))
  97                return sprintf(buf, "%lu\n", db1x_pm_sleep_secs);
  98
  99        else if (ATTRCMP(timer))
 100                return sprintf(buf, "%u\n",
 101                                !!(db1x_pm_wakemsk & SYS_WAKEMSK_M2));
 102
 103        else if (ATTRCMP(wakesrc))
 104                return sprintf(buf, "%lu\n", db1x_pm_last_wakesrc);
 105
 106        else if (ATTRCMP(gpio0) || ATTRCMP(gpio1) || ATTRCMP(gpio2) ||
 107                 ATTRCMP(gpio3) || ATTRCMP(gpio4) || ATTRCMP(gpio5) ||
 108                 ATTRCMP(gpio6) || ATTRCMP(gpio7)) {
 109                idx = (attr->attr.name)[4] - '0';
 110                return sprintf(buf, "%d\n",
 111                        !!(db1x_pm_wakemsk & SYS_WAKEMSK_GPIO(idx)));
 112
 113        } else if (ATTRCMP(wakemsk)) {
 114                return sprintf(buf, "%08lx\n", db1x_pm_wakemsk);
 115        }
 116
 117        return -ENOENT;
 118}
 119
 120static ssize_t db1x_pmattr_store(struct kobject *kobj,
 121                                 struct kobj_attribute *attr,
 122                                 const char *instr,
 123                                 size_t bytes)
 124{
 125        unsigned long l;
 126        int tmp;
 127
 128        if (ATTRCMP(timer_timeout)) {
 129                tmp = strict_strtoul(instr, 0, &l);
 130                if (tmp)
 131                        return tmp;
 132
 133                db1x_pm_sleep_secs = l;
 134
 135        } else if (ATTRCMP(timer)) {
 136                if (instr[0] != '0')
 137                        db1x_pm_wakemsk |= SYS_WAKEMSK_M2;
 138                else
 139                        db1x_pm_wakemsk &= ~SYS_WAKEMSK_M2;
 140
 141        } else if (ATTRCMP(gpio0) || ATTRCMP(gpio1) || ATTRCMP(gpio2) ||
 142                   ATTRCMP(gpio3) || ATTRCMP(gpio4) || ATTRCMP(gpio5) ||
 143                   ATTRCMP(gpio6) || ATTRCMP(gpio7)) {
 144                tmp = (attr->attr.name)[4] - '0';
 145                if (instr[0] != '0') {
 146                        db1x_pm_wakemsk |= SYS_WAKEMSK_GPIO(tmp);
 147                } else {
 148                        db1x_pm_wakemsk &= ~SYS_WAKEMSK_GPIO(tmp);
 149                }
 150
 151        } else if (ATTRCMP(wakemsk)) {
 152                tmp = strict_strtoul(instr, 0, &l);
 153                if (tmp)
 154                        return tmp;
 155
 156                db1x_pm_wakemsk = l & 0x0000003f;
 157
 158        } else
 159                bytes = -ENOENT;
 160
 161        return bytes;
 162}
 163
 164#define ATTR(x)                                                 \
 165        static struct kobj_attribute x##_attribute =            \
 166                __ATTR(x, 0664, db1x_pmattr_show,               \
 167                                db1x_pmattr_store);
 168
 169ATTR(gpio0)             /* GPIO-based wakeup enable */
 170ATTR(gpio1)
 171ATTR(gpio2)
 172ATTR(gpio3)
 173ATTR(gpio4)
 174ATTR(gpio5)
 175ATTR(gpio6)
 176ATTR(gpio7)
 177ATTR(timer)             /* TOYMATCH2-based wakeup enable */
 178ATTR(timer_timeout)     /* timer-based wakeup timeout value, in seconds */
 179ATTR(wakesrc)           /* contents of SYS_WAKESRC after last wakeup */
 180ATTR(wakemsk)           /* direct access to SYS_WAKEMSK */
 181
 182#define ATTR_LIST(x)    & x ## _attribute.attr
 183static struct attribute *db1x_pmattrs[] = {
 184        ATTR_LIST(gpio0),
 185        ATTR_LIST(gpio1),
 186        ATTR_LIST(gpio2),
 187        ATTR_LIST(gpio3),
 188        ATTR_LIST(gpio4),
 189        ATTR_LIST(gpio5),
 190        ATTR_LIST(gpio6),
 191        ATTR_LIST(gpio7),
 192        ATTR_LIST(timer),
 193        ATTR_LIST(timer_timeout),
 194        ATTR_LIST(wakesrc),
 195        ATTR_LIST(wakemsk),
 196        NULL,           /* terminator */
 197};
 198
 199static struct attribute_group db1x_pmattr_group = {
 200        .name   = "db1x",
 201        .attrs  = db1x_pmattrs,
 202};
 203
 204/*
 205 * Initialize suspend interface
 206 */
 207static int __init pm_init(void)
 208{
 209        /* init TOY to tick at 1Hz if not already done. No need to wait
 210         * for confirmation since there's plenty of time from here to
 211         * the next suspend cycle.
 212         */
 213        if (au_readl(SYS_TOYTRIM) != 32767) {
 214                au_writel(32767, SYS_TOYTRIM);
 215                au_sync();
 216        }
 217
 218        db1x_pm_last_wakesrc = au_readl(SYS_WAKESRC);
 219
 220        au_writel(0, SYS_WAKESRC);
 221        au_sync();
 222        au_writel(0, SYS_WAKEMSK);
 223        au_sync();
 224
 225        suspend_set_ops(&db1x_pm_ops);
 226
 227        return sysfs_create_group(power_kobj, &db1x_pmattr_group);
 228}
 229
 230late_initcall(pm_init);
 231