Path: blob/master/arch/mips/alchemy/devboards/pm.c
10818 views
/*1* Alchemy Development Board example suspend userspace interface.2*3* (c) 2008 Manuel Lauss <[email protected]>4*/56#include <linux/init.h>7#include <linux/kobject.h>8#include <linux/suspend.h>9#include <linux/sysfs.h>10#include <asm/mach-au1x00/au1000.h>11#include <asm/mach-au1x00/gpio.h>12#include <asm/mach-db1x00/bcsr.h>1314/*15* Generic suspend userspace interface for Alchemy development boards.16* This code exports a few sysfs nodes under /sys/power/db1x/ which17* can be used by userspace to en/disable all au1x-provided wakeup18* sources and configure the timeout after which the the TOYMATCH2 irq19* is to trigger a wakeup.20*/212223static unsigned long db1x_pm_sleep_secs;24static unsigned long db1x_pm_wakemsk;25static unsigned long db1x_pm_last_wakesrc;2627static int db1x_pm_enter(suspend_state_t state)28{29unsigned short bcsrs[16];30int i, j, hasint;3132/* save CPLD regs */33hasint = bcsr_read(BCSR_WHOAMI);34hasint = BCSR_WHOAMI_BOARD(hasint) >= BCSR_WHOAMI_DB1200;35j = (hasint) ? BCSR_MASKSET : BCSR_SYSTEM;3637for (i = BCSR_STATUS; i <= j; i++)38bcsrs[i] = bcsr_read(i);3940/* shut off hexleds */41bcsr_write(BCSR_HEXCLEAR, 3);4243/* enable GPIO based wakeup */44alchemy_gpio1_input_enable();4546/* clear and setup wake cause and source */47au_writel(0, SYS_WAKEMSK);48au_sync();49au_writel(0, SYS_WAKESRC);50au_sync();5152au_writel(db1x_pm_wakemsk, SYS_WAKEMSK);53au_sync();5455/* setup 1Hz-timer-based wakeup: wait for reg access */56while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20)57asm volatile ("nop");5859au_writel(au_readl(SYS_TOYREAD) + db1x_pm_sleep_secs, SYS_TOYMATCH2);60au_sync();6162/* wait for value to really hit the register */63while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20)64asm volatile ("nop");6566/* ...and now the sandman can come! */67au_sleep();686970/* restore CPLD regs */71for (i = BCSR_STATUS; i <= BCSR_SYSTEM; i++)72bcsr_write(i, bcsrs[i]);7374/* restore CPLD int registers */75if (hasint) {76bcsr_write(BCSR_INTCLR, 0xffff);77bcsr_write(BCSR_MASKCLR, 0xffff);78bcsr_write(BCSR_INTSTAT, 0xffff);79bcsr_write(BCSR_INTSET, bcsrs[BCSR_INTSET]);80bcsr_write(BCSR_MASKSET, bcsrs[BCSR_MASKSET]);81}8283/* light up hexleds */84bcsr_write(BCSR_HEXCLEAR, 0);8586return 0;87}8889static int db1x_pm_begin(suspend_state_t state)90{91if (!db1x_pm_wakemsk) {92printk(KERN_ERR "db1x: no wakeup source activated!\n");93return -EINVAL;94}9596return 0;97}9899static void db1x_pm_end(void)100{101/* read and store wakeup source, the clear the register. To102* be able to clear it, WAKEMSK must be cleared first.103*/104db1x_pm_last_wakesrc = au_readl(SYS_WAKESRC);105106au_writel(0, SYS_WAKEMSK);107au_writel(0, SYS_WAKESRC);108au_sync();109110}111112static const struct platform_suspend_ops db1x_pm_ops = {113.valid = suspend_valid_only_mem,114.begin = db1x_pm_begin,115.enter = db1x_pm_enter,116.end = db1x_pm_end,117};118119#define ATTRCMP(x) (0 == strcmp(attr->attr.name, #x))120121static ssize_t db1x_pmattr_show(struct kobject *kobj,122struct kobj_attribute *attr,123char *buf)124{125int idx;126127if (ATTRCMP(timer_timeout))128return sprintf(buf, "%lu\n", db1x_pm_sleep_secs);129130else if (ATTRCMP(timer))131return sprintf(buf, "%u\n",132!!(db1x_pm_wakemsk & SYS_WAKEMSK_M2));133134else if (ATTRCMP(wakesrc))135return sprintf(buf, "%lu\n", db1x_pm_last_wakesrc);136137else if (ATTRCMP(gpio0) || ATTRCMP(gpio1) || ATTRCMP(gpio2) ||138ATTRCMP(gpio3) || ATTRCMP(gpio4) || ATTRCMP(gpio5) ||139ATTRCMP(gpio6) || ATTRCMP(gpio7)) {140idx = (attr->attr.name)[4] - '0';141return sprintf(buf, "%d\n",142!!(db1x_pm_wakemsk & SYS_WAKEMSK_GPIO(idx)));143144} else if (ATTRCMP(wakemsk)) {145return sprintf(buf, "%08lx\n", db1x_pm_wakemsk);146}147148return -ENOENT;149}150151static ssize_t db1x_pmattr_store(struct kobject *kobj,152struct kobj_attribute *attr,153const char *instr,154size_t bytes)155{156unsigned long l;157int tmp;158159if (ATTRCMP(timer_timeout)) {160tmp = strict_strtoul(instr, 0, &l);161if (tmp)162return tmp;163164db1x_pm_sleep_secs = l;165166} else if (ATTRCMP(timer)) {167if (instr[0] != '0')168db1x_pm_wakemsk |= SYS_WAKEMSK_M2;169else170db1x_pm_wakemsk &= ~SYS_WAKEMSK_M2;171172} else if (ATTRCMP(gpio0) || ATTRCMP(gpio1) || ATTRCMP(gpio2) ||173ATTRCMP(gpio3) || ATTRCMP(gpio4) || ATTRCMP(gpio5) ||174ATTRCMP(gpio6) || ATTRCMP(gpio7)) {175tmp = (attr->attr.name)[4] - '0';176if (instr[0] != '0') {177db1x_pm_wakemsk |= SYS_WAKEMSK_GPIO(tmp);178} else {179db1x_pm_wakemsk &= ~SYS_WAKEMSK_GPIO(tmp);180}181182} else if (ATTRCMP(wakemsk)) {183tmp = strict_strtoul(instr, 0, &l);184if (tmp)185return tmp;186187db1x_pm_wakemsk = l & 0x0000003f;188189} else190bytes = -ENOENT;191192return bytes;193}194195#define ATTR(x) \196static struct kobj_attribute x##_attribute = \197__ATTR(x, 0664, db1x_pmattr_show, \198db1x_pmattr_store);199200ATTR(gpio0) /* GPIO-based wakeup enable */201ATTR(gpio1)202ATTR(gpio2)203ATTR(gpio3)204ATTR(gpio4)205ATTR(gpio5)206ATTR(gpio6)207ATTR(gpio7)208ATTR(timer) /* TOYMATCH2-based wakeup enable */209ATTR(timer_timeout) /* timer-based wakeup timeout value, in seconds */210ATTR(wakesrc) /* contents of SYS_WAKESRC after last wakeup */211ATTR(wakemsk) /* direct access to SYS_WAKEMSK */212213#define ATTR_LIST(x) & x ## _attribute.attr214static struct attribute *db1x_pmattrs[] = {215ATTR_LIST(gpio0),216ATTR_LIST(gpio1),217ATTR_LIST(gpio2),218ATTR_LIST(gpio3),219ATTR_LIST(gpio4),220ATTR_LIST(gpio5),221ATTR_LIST(gpio6),222ATTR_LIST(gpio7),223ATTR_LIST(timer),224ATTR_LIST(timer_timeout),225ATTR_LIST(wakesrc),226ATTR_LIST(wakemsk),227NULL, /* terminator */228};229230static struct attribute_group db1x_pmattr_group = {231.name = "db1x",232.attrs = db1x_pmattrs,233};234235/*236* Initialize suspend interface237*/238static int __init pm_init(void)239{240/* init TOY to tick at 1Hz if not already done. No need to wait241* for confirmation since there's plenty of time from here to242* the next suspend cycle.243*/244if (au_readl(SYS_TOYTRIM) != 32767) {245au_writel(32767, SYS_TOYTRIM);246au_sync();247}248249db1x_pm_last_wakesrc = au_readl(SYS_WAKESRC);250251au_writel(0, SYS_WAKESRC);252au_sync();253au_writel(0, SYS_WAKEMSK);254au_sync();255256suspend_set_ops(&db1x_pm_ops);257258return sysfs_create_group(power_kobj, &db1x_pmattr_group);259}260261late_initcall(pm_init);262263264