1 // SPDX-License-Identifier: GPL-2.0 1 2 /* 3 * Alchemy Development Board example suspend u 4 * 5 * (c) 2008 Manuel Lauss <mano@roarinelk.homel 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 Alc 18 * This code exports a few sysfs nodes under / 19 * can be used by userspace to en/disable all 20 * sources and configure the timeout after whi 21 * is to trigger a wakeup. 22 */ 23 24 25 static unsigned long db1x_pm_sleep_secs; 26 static unsigned long db1x_pm_wakemsk; 27 static unsigned long db1x_pm_last_wakesrc; 28 29 static 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) >= 37 j = (hasint) ? BCSR_MASKSET : BCSR_SYS 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 sour 49 alchemy_wrsys(0, AU1000_SYS_WAKEMSK); 50 alchemy_wrsys(0, AU1000_SYS_WAKESRC); 51 52 alchemy_wrsys(db1x_pm_wakemsk, AU1000_ 53 54 /* setup 1Hz-timer-based wakeup: wait 55 while (alchemy_rdsys(AU1000_SYS_CNTRCT 56 asm volatile ("nop"); 57 58 alchemy_wrsys(alchemy_rdsys(AU1000_SYS 59 AU1000_SYS_TOYMATCH2); 60 61 /* wait for value to really hit the re 62 while (alchemy_rdsys(AU1000_SYS_CNTRCT 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 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, 0xfff 77 bcsr_write(BCSR_INTSTAT, 0xfff 78 bcsr_write(BCSR_INTSET, bcsrs[ 79 bcsr_write(BCSR_MASKSET, bcsrs 80 } 81 82 /* light up hexleds */ 83 bcsr_write(BCSR_HEXCLEAR, 0); 84 85 return 0; 86 } 87 88 static int db1x_pm_begin(suspend_state_t state 89 { 90 if (!db1x_pm_wakemsk) { 91 printk(KERN_ERR "db1x: no wake 92 return -EINVAL; 93 } 94 95 return 0; 96 } 97 98 static void db1x_pm_end(void) 99 { 100 /* read and store wakeup source, the c 101 * be able to clear it, WAKEMSK must b 102 */ 103 db1x_pm_last_wakesrc = alchemy_rdsys(A 104 105 alchemy_wrsys(0, AU1000_SYS_WAKEMSK); 106 alchemy_wrsys(0, AU1000_SYS_WAKESRC); 107 } 108 109 static const struct platform_suspend_ops db1x_ 110 .valid = suspend_valid_only_m 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.nam 117 118 static ssize_t db1x_pmattr_show(struct kobject 119 struct kobj_at 120 char *buf) 121 { 122 int idx; 123 124 if (ATTRCMP(timer_timeout)) 125 return sprintf(buf, "%lu\n", d 126 127 else if (ATTRCMP(timer)) 128 return sprintf(buf, "%u\n", 129 !!(db1x_pm_wak 130 131 else if (ATTRCMP(wakesrc)) 132 return sprintf(buf, "%lu\n", d 133 134 else if (ATTRCMP(gpio0) || ATTRCMP(gpi 135 ATTRCMP(gpio3) || ATTRCMP(gpi 136 ATTRCMP(gpio6) || ATTRCMP(gpi 137 idx = (attr->attr.name)[4] - ' 138 return sprintf(buf, "%d\n", 139 !!(db1x_pm_wakemsk & S 140 141 } else if (ATTRCMP(wakemsk)) { 142 return sprintf(buf, "%08lx\n", 143 } 144 145 return -ENOENT; 146 } 147 148 static ssize_t db1x_pmattr_store(struct kobjec 149 struct kobj_a 150 const char *i 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] != '') 165 db1x_pm_wakemsk |= SYS 166 else 167 db1x_pm_wakemsk &= ~SY 168 169 } else if (ATTRCMP(gpio0) || ATTRCMP(g 170 ATTRCMP(gpio3) || ATTRCMP(g 171 ATTRCMP(gpio6) || ATTRCMP(g 172 tmp = (attr->attr.name)[4] - ' 173 if (instr[0] != '') { 174 db1x_pm_wakemsk |= SYS 175 } else { 176 db1x_pm_wakemsk &= ~SY 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 & 0x000000 185 186 } else 187 bytes = -ENOENT; 188 189 return bytes; 190 } 191 192 #define ATTR(x) 193 static struct kobj_attribute x##_attri 194 __ATTR(x, 0664, db1x_pmattr_sh 195 db1x_pmattr_st 196 197 ATTR(gpio0) /* GPIO-based wakeup e 198 ATTR(gpio1) 199 ATTR(gpio2) 200 ATTR(gpio3) 201 ATTR(gpio4) 202 ATTR(gpio5) 203 ATTR(gpio6) 204 ATTR(gpio7) 205 ATTR(timer) /* TOYMATCH2-based wak 206 ATTR(timer_timeout) /* timer-based wakeup 207 ATTR(wakesrc) /* contents of SYS_WAK 208 ATTR(wakemsk) /* direct access to SY 209 210 #define ATTR_LIST(x) & x ## _attribute.attr 211 static 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 227 static struct attribute_group db1x_pmattr_grou 228 .name = "db1x", 229 .attrs = db1x_pmattrs, 230 }; 231 232 /* 233 * Initialize suspend interface 234 */ 235 static int __init pm_init(void) 236 { 237 /* init TOY to tick at 1Hz if not alre 238 * for confirmation since there's plen 239 * the next suspend cycle. 240 */ 241 if (alchemy_rdsys(AU1000_SYS_TOYTRIM) 242 alchemy_wrsys(32767, AU1000_SY 243 244 db1x_pm_last_wakesrc = alchemy_rdsys(A 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, 252 } 253 254 late_initcall(pm_init); 255
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.