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