Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Alchemy Development Board example suspend userspace interface.
0004  *
0005  * (c) 2008 Manuel Lauss <mano@roarinelk.homelinux.net>
0006  */
0007 
0008 #include <linux/init.h>
0009 #include <linux/kobject.h>
0010 #include <linux/suspend.h>
0011 #include <linux/sysfs.h>
0012 #include <asm/mach-au1x00/au1000.h>
0013 #include <asm/mach-au1x00/gpio-au1000.h>
0014 #include <asm/mach-db1x00/bcsr.h>
0015 
0016 /*
0017  * Generic suspend userspace interface for Alchemy development boards.
0018  * This code exports a few sysfs nodes under /sys/power/db1x/ which
0019  * can be used by userspace to en/disable all au1x-provided wakeup
0020  * sources and configure the timeout after which the TOYMATCH2 irq
0021  * is to trigger a wakeup.
0022  */
0023 
0024 
0025 static unsigned long db1x_pm_sleep_secs;
0026 static unsigned long db1x_pm_wakemsk;
0027 static unsigned long db1x_pm_last_wakesrc;
0028 
0029 static int db1x_pm_enter(suspend_state_t state)
0030 {
0031     unsigned short bcsrs[16];
0032     int i, j, hasint;
0033 
0034     /* save CPLD regs */
0035     hasint = bcsr_read(BCSR_WHOAMI);
0036     hasint = BCSR_WHOAMI_BOARD(hasint) >= BCSR_WHOAMI_DB1200;
0037     j = (hasint) ? BCSR_MASKSET : BCSR_SYSTEM;
0038 
0039     for (i = BCSR_STATUS; i <= j; i++)
0040         bcsrs[i] = bcsr_read(i);
0041 
0042     /* shut off hexleds */
0043     bcsr_write(BCSR_HEXCLEAR, 3);
0044 
0045     /* enable GPIO based wakeup */
0046     alchemy_gpio1_input_enable();
0047 
0048     /* clear and setup wake cause and source */
0049     alchemy_wrsys(0, AU1000_SYS_WAKEMSK);
0050     alchemy_wrsys(0, AU1000_SYS_WAKESRC);
0051 
0052     alchemy_wrsys(db1x_pm_wakemsk, AU1000_SYS_WAKEMSK);
0053 
0054     /* setup 1Hz-timer-based wakeup: wait for reg access */
0055     while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_M20)
0056         asm volatile ("nop");
0057 
0058     alchemy_wrsys(alchemy_rdsys(AU1000_SYS_TOYREAD) + db1x_pm_sleep_secs,
0059               AU1000_SYS_TOYMATCH2);
0060 
0061     /* wait for value to really hit the register */
0062     while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_M20)
0063         asm volatile ("nop");
0064 
0065     /* ...and now the sandman can come! */
0066     au_sleep();
0067 
0068 
0069     /* restore CPLD regs */
0070     for (i = BCSR_STATUS; i <= BCSR_SYSTEM; i++)
0071         bcsr_write(i, bcsrs[i]);
0072 
0073     /* restore CPLD int registers */
0074     if (hasint) {
0075         bcsr_write(BCSR_INTCLR, 0xffff);
0076         bcsr_write(BCSR_MASKCLR, 0xffff);
0077         bcsr_write(BCSR_INTSTAT, 0xffff);
0078         bcsr_write(BCSR_INTSET, bcsrs[BCSR_INTSET]);
0079         bcsr_write(BCSR_MASKSET, bcsrs[BCSR_MASKSET]);
0080     }
0081 
0082     /* light up hexleds */
0083     bcsr_write(BCSR_HEXCLEAR, 0);
0084 
0085     return 0;
0086 }
0087 
0088 static int db1x_pm_begin(suspend_state_t state)
0089 {
0090     if (!db1x_pm_wakemsk) {
0091         printk(KERN_ERR "db1x: no wakeup source activated!\n");
0092         return -EINVAL;
0093     }
0094 
0095     return 0;
0096 }
0097 
0098 static void db1x_pm_end(void)
0099 {
0100     /* read and store wakeup source, the clear the register. To
0101      * be able to clear it, WAKEMSK must be cleared first.
0102      */
0103     db1x_pm_last_wakesrc = alchemy_rdsys(AU1000_SYS_WAKESRC);
0104 
0105     alchemy_wrsys(0, AU1000_SYS_WAKEMSK);
0106     alchemy_wrsys(0, AU1000_SYS_WAKESRC);
0107 }
0108 
0109 static const struct platform_suspend_ops db1x_pm_ops = {
0110     .valid      = suspend_valid_only_mem,
0111     .begin      = db1x_pm_begin,
0112     .enter      = db1x_pm_enter,
0113     .end        = db1x_pm_end,
0114 };
0115 
0116 #define ATTRCMP(x) (0 == strcmp(attr->attr.name, #x))
0117 
0118 static ssize_t db1x_pmattr_show(struct kobject *kobj,
0119                 struct kobj_attribute *attr,
0120                 char *buf)
0121 {
0122     int idx;
0123 
0124     if (ATTRCMP(timer_timeout))
0125         return sprintf(buf, "%lu\n", db1x_pm_sleep_secs);
0126 
0127     else if (ATTRCMP(timer))
0128         return sprintf(buf, "%u\n",
0129                 !!(db1x_pm_wakemsk & SYS_WAKEMSK_M2));
0130 
0131     else if (ATTRCMP(wakesrc))
0132         return sprintf(buf, "%lu\n", db1x_pm_last_wakesrc);
0133 
0134     else if (ATTRCMP(gpio0) || ATTRCMP(gpio1) || ATTRCMP(gpio2) ||
0135          ATTRCMP(gpio3) || ATTRCMP(gpio4) || ATTRCMP(gpio5) ||
0136          ATTRCMP(gpio6) || ATTRCMP(gpio7)) {
0137         idx = (attr->attr.name)[4] - '0';
0138         return sprintf(buf, "%d\n",
0139             !!(db1x_pm_wakemsk & SYS_WAKEMSK_GPIO(idx)));
0140 
0141     } else if (ATTRCMP(wakemsk)) {
0142         return sprintf(buf, "%08lx\n", db1x_pm_wakemsk);
0143     }
0144 
0145     return -ENOENT;
0146 }
0147 
0148 static ssize_t db1x_pmattr_store(struct kobject *kobj,
0149                  struct kobj_attribute *attr,
0150                  const char *instr,
0151                  size_t bytes)
0152 {
0153     unsigned long l;
0154     int tmp;
0155 
0156     if (ATTRCMP(timer_timeout)) {
0157         tmp = kstrtoul(instr, 0, &l);
0158         if (tmp)
0159             return tmp;
0160 
0161         db1x_pm_sleep_secs = l;
0162 
0163     } else if (ATTRCMP(timer)) {
0164         if (instr[0] != '0')
0165             db1x_pm_wakemsk |= SYS_WAKEMSK_M2;
0166         else
0167             db1x_pm_wakemsk &= ~SYS_WAKEMSK_M2;
0168 
0169     } else if (ATTRCMP(gpio0) || ATTRCMP(gpio1) || ATTRCMP(gpio2) ||
0170            ATTRCMP(gpio3) || ATTRCMP(gpio4) || ATTRCMP(gpio5) ||
0171            ATTRCMP(gpio6) || ATTRCMP(gpio7)) {
0172         tmp = (attr->attr.name)[4] - '0';
0173         if (instr[0] != '0') {
0174             db1x_pm_wakemsk |= SYS_WAKEMSK_GPIO(tmp);
0175         } else {
0176             db1x_pm_wakemsk &= ~SYS_WAKEMSK_GPIO(tmp);
0177         }
0178 
0179     } else if (ATTRCMP(wakemsk)) {
0180         tmp = kstrtoul(instr, 0, &l);
0181         if (tmp)
0182             return tmp;
0183 
0184         db1x_pm_wakemsk = l & 0x0000003f;
0185 
0186     } else
0187         bytes = -ENOENT;
0188 
0189     return bytes;
0190 }
0191 
0192 #define ATTR(x)                         \
0193     static struct kobj_attribute x##_attribute =        \
0194         __ATTR(x, 0664, db1x_pmattr_show,       \
0195                 db1x_pmattr_store);
0196 
0197 ATTR(gpio0)     /* GPIO-based wakeup enable */
0198 ATTR(gpio1)
0199 ATTR(gpio2)
0200 ATTR(gpio3)
0201 ATTR(gpio4)
0202 ATTR(gpio5)
0203 ATTR(gpio6)
0204 ATTR(gpio7)
0205 ATTR(timer)     /* TOYMATCH2-based wakeup enable */
0206 ATTR(timer_timeout) /* timer-based wakeup timeout value, in seconds */
0207 ATTR(wakesrc)       /* contents of SYS_WAKESRC after last wakeup */
0208 ATTR(wakemsk)       /* direct access to SYS_WAKEMSK */
0209 
0210 #define ATTR_LIST(x)    & x ## _attribute.attr
0211 static struct attribute *db1x_pmattrs[] = {
0212     ATTR_LIST(gpio0),
0213     ATTR_LIST(gpio1),
0214     ATTR_LIST(gpio2),
0215     ATTR_LIST(gpio3),
0216     ATTR_LIST(gpio4),
0217     ATTR_LIST(gpio5),
0218     ATTR_LIST(gpio6),
0219     ATTR_LIST(gpio7),
0220     ATTR_LIST(timer),
0221     ATTR_LIST(timer_timeout),
0222     ATTR_LIST(wakesrc),
0223     ATTR_LIST(wakemsk),
0224     NULL,       /* terminator */
0225 };
0226 
0227 static struct attribute_group db1x_pmattr_group = {
0228     .name   = "db1x",
0229     .attrs  = db1x_pmattrs,
0230 };
0231 
0232 /*
0233  * Initialize suspend interface
0234  */
0235 static int __init pm_init(void)
0236 {
0237     /* init TOY to tick at 1Hz if not already done. No need to wait
0238      * for confirmation since there's plenty of time from here to
0239      * the next suspend cycle.
0240      */
0241     if (alchemy_rdsys(AU1000_SYS_TOYTRIM) != 32767)
0242         alchemy_wrsys(32767, AU1000_SYS_TOYTRIM);
0243 
0244     db1x_pm_last_wakesrc = alchemy_rdsys(AU1000_SYS_WAKESRC);
0245 
0246     alchemy_wrsys(0, AU1000_SYS_WAKESRC);
0247     alchemy_wrsys(0, AU1000_SYS_WAKEMSK);
0248 
0249     suspend_set_ops(&db1x_pm_ops);
0250 
0251     return sysfs_create_group(power_kobj, &db1x_pmattr_group);
0252 }
0253 
0254 late_initcall(pm_init);