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