195b482a8SLen Brown #include <linux/proc_fs.h> 295b482a8SLen Brown #include <linux/seq_file.h> 395b482a8SLen Brown #include <linux/suspend.h> 495b482a8SLen Brown #include <linux/bcd.h> 595b482a8SLen Brown #include <asm/uaccess.h> 695b482a8SLen Brown 795b482a8SLen Brown #include <acpi/acpi_bus.h> 895b482a8SLen Brown #include <acpi/acpi_drivers.h> 995b482a8SLen Brown 1095b482a8SLen Brown #ifdef CONFIG_X86 1195b482a8SLen Brown #include <linux/mc146818rtc.h> 1295b482a8SLen Brown #endif 1395b482a8SLen Brown 1495b482a8SLen Brown #include "sleep.h" 1595b482a8SLen Brown 1695b482a8SLen Brown #define _COMPONENT ACPI_SYSTEM_COMPONENT 1795b482a8SLen Brown 1895b482a8SLen Brown /* 1995b482a8SLen Brown * this file provides support for: 2095b482a8SLen Brown * /proc/acpi/sleep 2195b482a8SLen Brown * /proc/acpi/alarm 2295b482a8SLen Brown * /proc/acpi/wakeup 2395b482a8SLen Brown */ 2495b482a8SLen Brown 2595b482a8SLen Brown ACPI_MODULE_NAME("sleep") 2695b482a8SLen Brown #ifdef CONFIG_ACPI_PROCFS 2795b482a8SLen Brown static int acpi_system_sleep_seq_show(struct seq_file *seq, void *offset) 2895b482a8SLen Brown { 2995b482a8SLen Brown int i; 3095b482a8SLen Brown 3195b482a8SLen Brown for (i = 0; i <= ACPI_STATE_S5; i++) { 3295b482a8SLen Brown if (sleep_states[i]) { 3395b482a8SLen Brown seq_printf(seq, "S%d ", i); 3495b482a8SLen Brown } 3595b482a8SLen Brown } 3695b482a8SLen Brown 3795b482a8SLen Brown seq_puts(seq, "\n"); 3895b482a8SLen Brown 3995b482a8SLen Brown return 0; 4095b482a8SLen Brown } 4195b482a8SLen Brown 4295b482a8SLen Brown static int acpi_system_sleep_open_fs(struct inode *inode, struct file *file) 4395b482a8SLen Brown { 4495b482a8SLen Brown return single_open(file, acpi_system_sleep_seq_show, PDE(inode)->data); 4595b482a8SLen Brown } 4695b482a8SLen Brown 4795b482a8SLen Brown static ssize_t 4895b482a8SLen Brown acpi_system_write_sleep(struct file *file, 4995b482a8SLen Brown const char __user * buffer, size_t count, loff_t * ppos) 5095b482a8SLen Brown { 5195b482a8SLen Brown char str[12]; 5295b482a8SLen Brown u32 state = 0; 5395b482a8SLen Brown int error = 0; 5495b482a8SLen Brown 5595b482a8SLen Brown if (count > sizeof(str) - 1) 5695b482a8SLen Brown goto Done; 5795b482a8SLen Brown memset(str, 0, sizeof(str)); 5895b482a8SLen Brown if (copy_from_user(str, buffer, count)) 5995b482a8SLen Brown return -EFAULT; 6095b482a8SLen Brown 6195b482a8SLen Brown /* Check for S4 bios request */ 6295b482a8SLen Brown if (!strcmp(str, "4b")) { 6395b482a8SLen Brown error = acpi_suspend(4); 6495b482a8SLen Brown goto Done; 6595b482a8SLen Brown } 6695b482a8SLen Brown state = simple_strtoul(str, NULL, 0); 6795b482a8SLen Brown #ifdef CONFIG_HIBERNATION 6895b482a8SLen Brown if (state == 4) { 6995b482a8SLen Brown error = hibernate(); 7095b482a8SLen Brown goto Done; 7195b482a8SLen Brown } 7295b482a8SLen Brown #endif 7395b482a8SLen Brown error = acpi_suspend(state); 7495b482a8SLen Brown Done: 7595b482a8SLen Brown return error ? error : count; 7695b482a8SLen Brown } 7795b482a8SLen Brown #endif /* CONFIG_ACPI_PROCFS */ 7895b482a8SLen Brown 7995b482a8SLen Brown #if defined(CONFIG_RTC_DRV_CMOS) || defined(CONFIG_RTC_DRV_CMOS_MODULE) || !defined(CONFIG_X86) 8095b482a8SLen Brown /* use /sys/class/rtc/rtcX/wakealarm instead; it's not ACPI-specific */ 8195b482a8SLen Brown #else 8295b482a8SLen Brown #define HAVE_ACPI_LEGACY_ALARM 8395b482a8SLen Brown #endif 8495b482a8SLen Brown 8595b482a8SLen Brown #ifdef HAVE_ACPI_LEGACY_ALARM 8695b482a8SLen Brown 8738f64c77SLen Brown static u32 cmos_bcd_read(int offset, int rtc_control); 8838f64c77SLen Brown 8995b482a8SLen Brown static int acpi_system_alarm_seq_show(struct seq_file *seq, void *offset) 9095b482a8SLen Brown { 9195b482a8SLen Brown u32 sec, min, hr; 9295b482a8SLen Brown u32 day, mo, yr, cent = 0; 9338f64c77SLen Brown u32 today = 0; 9495b482a8SLen Brown unsigned char rtc_control = 0; 9595b482a8SLen Brown unsigned long flags; 9695b482a8SLen Brown 9795b482a8SLen Brown spin_lock_irqsave(&rtc_lock, flags); 9895b482a8SLen Brown 9995b482a8SLen Brown rtc_control = CMOS_READ(RTC_CONTROL); 10038f64c77SLen Brown sec = cmos_bcd_read(RTC_SECONDS_ALARM, rtc_control); 10138f64c77SLen Brown min = cmos_bcd_read(RTC_MINUTES_ALARM, rtc_control); 10238f64c77SLen Brown hr = cmos_bcd_read(RTC_HOURS_ALARM, rtc_control); 10395b482a8SLen Brown 10495b482a8SLen Brown /* If we ever get an FACP with proper values... */ 10538f64c77SLen Brown if (acpi_gbl_FADT.day_alarm) { 10695b482a8SLen Brown /* ACPI spec: only low 6 its should be cared */ 10795b482a8SLen Brown day = CMOS_READ(acpi_gbl_FADT.day_alarm) & 0x3F; 10838f64c77SLen Brown if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) 10938f64c77SLen Brown day = bcd2bin(day); 11038f64c77SLen Brown } else 11138f64c77SLen Brown day = cmos_bcd_read(RTC_DAY_OF_MONTH, rtc_control); 11295b482a8SLen Brown if (acpi_gbl_FADT.month_alarm) 11338f64c77SLen Brown mo = cmos_bcd_read(acpi_gbl_FADT.month_alarm, rtc_control); 11438f64c77SLen Brown else { 11538f64c77SLen Brown mo = cmos_bcd_read(RTC_MONTH, rtc_control); 11638f64c77SLen Brown today = cmos_bcd_read(RTC_DAY_OF_MONTH, rtc_control); 11738f64c77SLen Brown } 11895b482a8SLen Brown if (acpi_gbl_FADT.century) 11938f64c77SLen Brown cent = cmos_bcd_read(acpi_gbl_FADT.century, rtc_control); 12095b482a8SLen Brown 12138f64c77SLen Brown yr = cmos_bcd_read(RTC_YEAR, rtc_control); 12295b482a8SLen Brown 12395b482a8SLen Brown spin_unlock_irqrestore(&rtc_lock, flags); 12495b482a8SLen Brown 12595b482a8SLen Brown /* we're trusting the FADT (see above) */ 12695b482a8SLen Brown if (!acpi_gbl_FADT.century) 12795b482a8SLen Brown /* If we're not trusting the FADT, we should at least make it 12895b482a8SLen Brown * right for _this_ century... ehm, what is _this_ century? 12995b482a8SLen Brown * 13095b482a8SLen Brown * TBD: 13195b482a8SLen Brown * ASAP: find piece of code in the kernel, e.g. star tracker driver, 13295b482a8SLen Brown * which we can trust to determine the century correctly. Atom 13395b482a8SLen Brown * watch driver would be nice, too... 13495b482a8SLen Brown * 13595b482a8SLen Brown * if that has not happened, change for first release in 2050: 13695b482a8SLen Brown * if (yr<50) 13795b482a8SLen Brown * yr += 2100; 13895b482a8SLen Brown * else 13995b482a8SLen Brown * yr += 2000; // current line of code 14095b482a8SLen Brown * 14195b482a8SLen Brown * if that has not happened either, please do on 2099/12/31:23:59:59 14295b482a8SLen Brown * s/2000/2100 14395b482a8SLen Brown * 14495b482a8SLen Brown */ 14595b482a8SLen Brown yr += 2000; 14695b482a8SLen Brown else 14795b482a8SLen Brown yr += cent * 100; 14895b482a8SLen Brown 14938f64c77SLen Brown /* 15038f64c77SLen Brown * Show correct dates for alarms up to a month into the future. 15138f64c77SLen Brown * This solves issues for nearly all situations with the common 15238f64c77SLen Brown * 30-day alarm clocks in PC hardware. 15338f64c77SLen Brown */ 15438f64c77SLen Brown if (day < today) { 15538f64c77SLen Brown if (mo < 12) { 15638f64c77SLen Brown mo += 1; 15738f64c77SLen Brown } else { 15838f64c77SLen Brown mo = 1; 15938f64c77SLen Brown yr += 1; 16038f64c77SLen Brown } 16138f64c77SLen Brown } 16238f64c77SLen Brown 16395b482a8SLen Brown seq_printf(seq, "%4.4u-", yr); 16495b482a8SLen Brown (mo > 12) ? seq_puts(seq, "**-") : seq_printf(seq, "%2.2u-", mo); 16595b482a8SLen Brown (day > 31) ? seq_puts(seq, "** ") : seq_printf(seq, "%2.2u ", day); 16695b482a8SLen Brown (hr > 23) ? seq_puts(seq, "**:") : seq_printf(seq, "%2.2u:", hr); 16795b482a8SLen Brown (min > 59) ? seq_puts(seq, "**:") : seq_printf(seq, "%2.2u:", min); 16895b482a8SLen Brown (sec > 59) ? seq_puts(seq, "**\n") : seq_printf(seq, "%2.2u\n", sec); 16995b482a8SLen Brown 17095b482a8SLen Brown return 0; 17195b482a8SLen Brown } 17295b482a8SLen Brown 17395b482a8SLen Brown static int acpi_system_alarm_open_fs(struct inode *inode, struct file *file) 17495b482a8SLen Brown { 17595b482a8SLen Brown return single_open(file, acpi_system_alarm_seq_show, PDE(inode)->data); 17695b482a8SLen Brown } 17795b482a8SLen Brown 17895b482a8SLen Brown static int get_date_field(char **p, u32 * value) 17995b482a8SLen Brown { 18095b482a8SLen Brown char *next = NULL; 18195b482a8SLen Brown char *string_end = NULL; 18295b482a8SLen Brown int result = -EINVAL; 18395b482a8SLen Brown 18495b482a8SLen Brown /* 18595b482a8SLen Brown * Try to find delimeter, only to insert null. The end of the 18695b482a8SLen Brown * string won't have one, but is still valid. 18795b482a8SLen Brown */ 18895b482a8SLen Brown if (*p == NULL) 18995b482a8SLen Brown return result; 19095b482a8SLen Brown 19195b482a8SLen Brown next = strpbrk(*p, "- :"); 19295b482a8SLen Brown if (next) 19395b482a8SLen Brown *next++ = '\0'; 19495b482a8SLen Brown 19595b482a8SLen Brown *value = simple_strtoul(*p, &string_end, 10); 19695b482a8SLen Brown 19795b482a8SLen Brown /* Signal success if we got a good digit */ 19895b482a8SLen Brown if (string_end != *p) 19995b482a8SLen Brown result = 0; 20095b482a8SLen Brown 20195b482a8SLen Brown if (next) 20295b482a8SLen Brown *p = next; 20395b482a8SLen Brown else 20495b482a8SLen Brown *p = NULL; 20595b482a8SLen Brown 20695b482a8SLen Brown return result; 20795b482a8SLen Brown } 20895b482a8SLen Brown 20995b482a8SLen Brown /* Read a possibly BCD register, always return binary */ 21095b482a8SLen Brown static u32 cmos_bcd_read(int offset, int rtc_control) 21195b482a8SLen Brown { 21295b482a8SLen Brown u32 val = CMOS_READ(offset); 21395b482a8SLen Brown if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) 21495b482a8SLen Brown val = bcd2bin(val); 21595b482a8SLen Brown return val; 21695b482a8SLen Brown } 21795b482a8SLen Brown 21895b482a8SLen Brown /* Write binary value into possibly BCD register */ 21995b482a8SLen Brown static void cmos_bcd_write(u32 val, int offset, int rtc_control) 22095b482a8SLen Brown { 22195b482a8SLen Brown if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) 22295b482a8SLen Brown val = bin2bcd(val); 22395b482a8SLen Brown CMOS_WRITE(val, offset); 22495b482a8SLen Brown } 22595b482a8SLen Brown 22695b482a8SLen Brown static ssize_t 22795b482a8SLen Brown acpi_system_write_alarm(struct file *file, 22895b482a8SLen Brown const char __user * buffer, size_t count, loff_t * ppos) 22995b482a8SLen Brown { 23095b482a8SLen Brown int result = 0; 23195b482a8SLen Brown char alarm_string[30] = { '\0' }; 23295b482a8SLen Brown char *p = alarm_string; 23395b482a8SLen Brown u32 sec, min, hr, day, mo, yr; 23495b482a8SLen Brown int adjust = 0; 23595b482a8SLen Brown unsigned char rtc_control = 0; 23695b482a8SLen Brown 23795b482a8SLen Brown if (count > sizeof(alarm_string) - 1) 23895b482a8SLen Brown return -EINVAL; 23995b482a8SLen Brown 24095b482a8SLen Brown if (copy_from_user(alarm_string, buffer, count)) 24195b482a8SLen Brown return -EFAULT; 24295b482a8SLen Brown 24395b482a8SLen Brown alarm_string[count] = '\0'; 24495b482a8SLen Brown 24595b482a8SLen Brown /* check for time adjustment */ 24695b482a8SLen Brown if (alarm_string[0] == '+') { 24795b482a8SLen Brown p++; 24895b482a8SLen Brown adjust = 1; 24995b482a8SLen Brown } 25095b482a8SLen Brown 25195b482a8SLen Brown if ((result = get_date_field(&p, &yr))) 25295b482a8SLen Brown goto end; 25395b482a8SLen Brown if ((result = get_date_field(&p, &mo))) 25495b482a8SLen Brown goto end; 25595b482a8SLen Brown if ((result = get_date_field(&p, &day))) 25695b482a8SLen Brown goto end; 25795b482a8SLen Brown if ((result = get_date_field(&p, &hr))) 25895b482a8SLen Brown goto end; 25995b482a8SLen Brown if ((result = get_date_field(&p, &min))) 26095b482a8SLen Brown goto end; 26195b482a8SLen Brown if ((result = get_date_field(&p, &sec))) 26295b482a8SLen Brown goto end; 26395b482a8SLen Brown 26495b482a8SLen Brown spin_lock_irq(&rtc_lock); 26595b482a8SLen Brown 26695b482a8SLen Brown rtc_control = CMOS_READ(RTC_CONTROL); 26795b482a8SLen Brown 26895b482a8SLen Brown if (adjust) { 26995b482a8SLen Brown yr += cmos_bcd_read(RTC_YEAR, rtc_control); 27095b482a8SLen Brown mo += cmos_bcd_read(RTC_MONTH, rtc_control); 27195b482a8SLen Brown day += cmos_bcd_read(RTC_DAY_OF_MONTH, rtc_control); 27295b482a8SLen Brown hr += cmos_bcd_read(RTC_HOURS, rtc_control); 27395b482a8SLen Brown min += cmos_bcd_read(RTC_MINUTES, rtc_control); 27495b482a8SLen Brown sec += cmos_bcd_read(RTC_SECONDS, rtc_control); 27595b482a8SLen Brown } 27695b482a8SLen Brown 27795b482a8SLen Brown spin_unlock_irq(&rtc_lock); 27895b482a8SLen Brown 27995b482a8SLen Brown if (sec > 59) { 28095b482a8SLen Brown min += sec/60; 28195b482a8SLen Brown sec = sec%60; 28295b482a8SLen Brown } 28395b482a8SLen Brown if (min > 59) { 28495b482a8SLen Brown hr += min/60; 28595b482a8SLen Brown min = min%60; 28695b482a8SLen Brown } 28795b482a8SLen Brown if (hr > 23) { 28895b482a8SLen Brown day += hr/24; 28995b482a8SLen Brown hr = hr%24; 29095b482a8SLen Brown } 29195b482a8SLen Brown if (day > 31) { 29295b482a8SLen Brown mo += day/32; 29395b482a8SLen Brown day = day%32; 29495b482a8SLen Brown } 29595b482a8SLen Brown if (mo > 12) { 29695b482a8SLen Brown yr += mo/13; 29795b482a8SLen Brown mo = mo%13; 29895b482a8SLen Brown } 29995b482a8SLen Brown 30095b482a8SLen Brown spin_lock_irq(&rtc_lock); 30195b482a8SLen Brown /* 30295b482a8SLen Brown * Disable alarm interrupt before setting alarm timer or else 30395b482a8SLen Brown * when ACPI_EVENT_RTC is enabled, a spurious ACPI interrupt occurs 30495b482a8SLen Brown */ 30595b482a8SLen Brown rtc_control &= ~RTC_AIE; 30695b482a8SLen Brown CMOS_WRITE(rtc_control, RTC_CONTROL); 30795b482a8SLen Brown CMOS_READ(RTC_INTR_FLAGS); 30895b482a8SLen Brown 30995b482a8SLen Brown /* write the fields the rtc knows about */ 31095b482a8SLen Brown cmos_bcd_write(hr, RTC_HOURS_ALARM, rtc_control); 31195b482a8SLen Brown cmos_bcd_write(min, RTC_MINUTES_ALARM, rtc_control); 31295b482a8SLen Brown cmos_bcd_write(sec, RTC_SECONDS_ALARM, rtc_control); 31395b482a8SLen Brown 31495b482a8SLen Brown /* 31595b482a8SLen Brown * If the system supports an enhanced alarm it will have non-zero 31695b482a8SLen Brown * offsets into the CMOS RAM here -- which for some reason are pointing 31795b482a8SLen Brown * to the RTC area of memory. 31895b482a8SLen Brown */ 31995b482a8SLen Brown if (acpi_gbl_FADT.day_alarm) 32095b482a8SLen Brown cmos_bcd_write(day, acpi_gbl_FADT.day_alarm, rtc_control); 32195b482a8SLen Brown if (acpi_gbl_FADT.month_alarm) 32295b482a8SLen Brown cmos_bcd_write(mo, acpi_gbl_FADT.month_alarm, rtc_control); 32395b482a8SLen Brown if (acpi_gbl_FADT.century) { 32495b482a8SLen Brown if (adjust) 32595b482a8SLen Brown yr += cmos_bcd_read(acpi_gbl_FADT.century, rtc_control) * 100; 32695b482a8SLen Brown cmos_bcd_write(yr / 100, acpi_gbl_FADT.century, rtc_control); 32795b482a8SLen Brown } 32895b482a8SLen Brown /* enable the rtc alarm interrupt */ 32995b482a8SLen Brown rtc_control |= RTC_AIE; 33095b482a8SLen Brown CMOS_WRITE(rtc_control, RTC_CONTROL); 33195b482a8SLen Brown CMOS_READ(RTC_INTR_FLAGS); 33295b482a8SLen Brown 33395b482a8SLen Brown spin_unlock_irq(&rtc_lock); 33495b482a8SLen Brown 33595b482a8SLen Brown acpi_clear_event(ACPI_EVENT_RTC); 33695b482a8SLen Brown acpi_enable_event(ACPI_EVENT_RTC, 0); 33795b482a8SLen Brown 33895b482a8SLen Brown *ppos += count; 33995b482a8SLen Brown 34095b482a8SLen Brown result = 0; 34195b482a8SLen Brown end: 34295b482a8SLen Brown return result ? result : count; 34395b482a8SLen Brown } 34495b482a8SLen Brown #endif /* HAVE_ACPI_LEGACY_ALARM */ 34595b482a8SLen Brown 34695b482a8SLen Brown extern struct list_head acpi_wakeup_device_list; 34795b482a8SLen Brown extern spinlock_t acpi_device_lock; 34895b482a8SLen Brown 34995b482a8SLen Brown static int 35095b482a8SLen Brown acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset) 35195b482a8SLen Brown { 35295b482a8SLen Brown struct list_head *node, *next; 35395b482a8SLen Brown 35495b482a8SLen Brown seq_printf(seq, "Device\tS-state\t Status Sysfs node\n"); 35595b482a8SLen Brown 35695b482a8SLen Brown spin_lock(&acpi_device_lock); 35795b482a8SLen Brown list_for_each_safe(node, next, &acpi_wakeup_device_list) { 35895b482a8SLen Brown struct acpi_device *dev = 35995b482a8SLen Brown container_of(node, struct acpi_device, wakeup_list); 36095b482a8SLen Brown struct device *ldev; 36195b482a8SLen Brown 36295b482a8SLen Brown if (!dev->wakeup.flags.valid) 36395b482a8SLen Brown continue; 36495b482a8SLen Brown spin_unlock(&acpi_device_lock); 36595b482a8SLen Brown 36695b482a8SLen Brown ldev = acpi_get_physical_device(dev->handle); 36795b482a8SLen Brown seq_printf(seq, "%s\t S%d\t%c%-8s ", 36895b482a8SLen Brown dev->pnp.bus_id, 36995b482a8SLen Brown (u32) dev->wakeup.sleep_state, 37095b482a8SLen Brown dev->wakeup.flags.run_wake ? '*' : ' ', 37195b482a8SLen Brown dev->wakeup.state.enabled ? "enabled" : "disabled"); 37295b482a8SLen Brown if (ldev) 37395b482a8SLen Brown seq_printf(seq, "%s:%s", 37495b482a8SLen Brown ldev->bus ? ldev->bus->name : "no-bus", 37595b482a8SLen Brown dev_name(ldev)); 37695b482a8SLen Brown seq_printf(seq, "\n"); 37795b482a8SLen Brown put_device(ldev); 37895b482a8SLen Brown 37995b482a8SLen Brown spin_lock(&acpi_device_lock); 38095b482a8SLen Brown } 38195b482a8SLen Brown spin_unlock(&acpi_device_lock); 38295b482a8SLen Brown return 0; 38395b482a8SLen Brown } 38495b482a8SLen Brown 38595b482a8SLen Brown static void physical_device_enable_wakeup(struct acpi_device *adev) 38695b482a8SLen Brown { 38795b482a8SLen Brown struct device *dev = acpi_get_physical_device(adev->handle); 38895b482a8SLen Brown 38995b482a8SLen Brown if (dev && device_can_wakeup(dev)) 39095b482a8SLen Brown device_set_wakeup_enable(dev, adev->wakeup.state.enabled); 39195b482a8SLen Brown } 39295b482a8SLen Brown 39395b482a8SLen Brown static ssize_t 39495b482a8SLen Brown acpi_system_write_wakeup_device(struct file *file, 39595b482a8SLen Brown const char __user * buffer, 39695b482a8SLen Brown size_t count, loff_t * ppos) 39795b482a8SLen Brown { 39895b482a8SLen Brown struct list_head *node, *next; 39995b482a8SLen Brown char strbuf[5]; 40095b482a8SLen Brown char str[5] = ""; 40195b482a8SLen Brown int len = count; 40295b482a8SLen Brown struct acpi_device *found_dev = NULL; 40395b482a8SLen Brown 40495b482a8SLen Brown if (len > 4) 40595b482a8SLen Brown len = 4; 40695b482a8SLen Brown 40795b482a8SLen Brown if (copy_from_user(strbuf, buffer, len)) 40895b482a8SLen Brown return -EFAULT; 40995b482a8SLen Brown strbuf[len] = '\0'; 41095b482a8SLen Brown sscanf(strbuf, "%s", str); 41195b482a8SLen Brown 41295b482a8SLen Brown spin_lock(&acpi_device_lock); 41395b482a8SLen Brown list_for_each_safe(node, next, &acpi_wakeup_device_list) { 41495b482a8SLen Brown struct acpi_device *dev = 41595b482a8SLen Brown container_of(node, struct acpi_device, wakeup_list); 41695b482a8SLen Brown if (!dev->wakeup.flags.valid) 41795b482a8SLen Brown continue; 41895b482a8SLen Brown 41995b482a8SLen Brown if (!strncmp(dev->pnp.bus_id, str, 4)) { 42095b482a8SLen Brown dev->wakeup.state.enabled = 42195b482a8SLen Brown dev->wakeup.state.enabled ? 0 : 1; 42295b482a8SLen Brown found_dev = dev; 42395b482a8SLen Brown break; 42495b482a8SLen Brown } 42595b482a8SLen Brown } 42695b482a8SLen Brown if (found_dev) { 42795b482a8SLen Brown physical_device_enable_wakeup(found_dev); 42895b482a8SLen Brown list_for_each_safe(node, next, &acpi_wakeup_device_list) { 42995b482a8SLen Brown struct acpi_device *dev = container_of(node, 43095b482a8SLen Brown struct 43195b482a8SLen Brown acpi_device, 43295b482a8SLen Brown wakeup_list); 43395b482a8SLen Brown 43495b482a8SLen Brown if ((dev != found_dev) && 43595b482a8SLen Brown (dev->wakeup.gpe_number == 43695b482a8SLen Brown found_dev->wakeup.gpe_number) 43795b482a8SLen Brown && (dev->wakeup.gpe_device == 43895b482a8SLen Brown found_dev->wakeup.gpe_device)) { 43995b482a8SLen Brown printk(KERN_WARNING 44095b482a8SLen Brown "ACPI: '%s' and '%s' have the same GPE, " 44195b482a8SLen Brown "can't disable/enable one seperately\n", 44295b482a8SLen Brown dev->pnp.bus_id, found_dev->pnp.bus_id); 44395b482a8SLen Brown dev->wakeup.state.enabled = 44495b482a8SLen Brown found_dev->wakeup.state.enabled; 44595b482a8SLen Brown physical_device_enable_wakeup(dev); 44695b482a8SLen Brown } 44795b482a8SLen Brown } 44895b482a8SLen Brown } 44995b482a8SLen Brown spin_unlock(&acpi_device_lock); 45095b482a8SLen Brown return count; 45195b482a8SLen Brown } 45295b482a8SLen Brown 45395b482a8SLen Brown static int 45495b482a8SLen Brown acpi_system_wakeup_device_open_fs(struct inode *inode, struct file *file) 45595b482a8SLen Brown { 45695b482a8SLen Brown return single_open(file, acpi_system_wakeup_device_seq_show, 45795b482a8SLen Brown PDE(inode)->data); 45895b482a8SLen Brown } 45995b482a8SLen Brown 46095b482a8SLen Brown static const struct file_operations acpi_system_wakeup_device_fops = { 46195b482a8SLen Brown .owner = THIS_MODULE, 46295b482a8SLen Brown .open = acpi_system_wakeup_device_open_fs, 46395b482a8SLen Brown .read = seq_read, 46495b482a8SLen Brown .write = acpi_system_write_wakeup_device, 46595b482a8SLen Brown .llseek = seq_lseek, 46695b482a8SLen Brown .release = single_release, 46795b482a8SLen Brown }; 46895b482a8SLen Brown 46995b482a8SLen Brown #ifdef CONFIG_ACPI_PROCFS 47095b482a8SLen Brown static const struct file_operations acpi_system_sleep_fops = { 47195b482a8SLen Brown .owner = THIS_MODULE, 47295b482a8SLen Brown .open = acpi_system_sleep_open_fs, 47395b482a8SLen Brown .read = seq_read, 47495b482a8SLen Brown .write = acpi_system_write_sleep, 47595b482a8SLen Brown .llseek = seq_lseek, 47695b482a8SLen Brown .release = single_release, 47795b482a8SLen Brown }; 47895b482a8SLen Brown #endif /* CONFIG_ACPI_PROCFS */ 47995b482a8SLen Brown 48095b482a8SLen Brown #ifdef HAVE_ACPI_LEGACY_ALARM 48195b482a8SLen Brown static const struct file_operations acpi_system_alarm_fops = { 48295b482a8SLen Brown .owner = THIS_MODULE, 48395b482a8SLen Brown .open = acpi_system_alarm_open_fs, 48495b482a8SLen Brown .read = seq_read, 48595b482a8SLen Brown .write = acpi_system_write_alarm, 48695b482a8SLen Brown .llseek = seq_lseek, 48795b482a8SLen Brown .release = single_release, 48895b482a8SLen Brown }; 48995b482a8SLen Brown 49095b482a8SLen Brown static u32 rtc_handler(void *context) 49195b482a8SLen Brown { 49295b482a8SLen Brown acpi_clear_event(ACPI_EVENT_RTC); 49395b482a8SLen Brown acpi_disable_event(ACPI_EVENT_RTC, 0); 49495b482a8SLen Brown 49595b482a8SLen Brown return ACPI_INTERRUPT_HANDLED; 49695b482a8SLen Brown } 49795b482a8SLen Brown #endif /* HAVE_ACPI_LEGACY_ALARM */ 49895b482a8SLen Brown 49995b482a8SLen Brown static int __init acpi_sleep_proc_init(void) 50095b482a8SLen Brown { 50195b482a8SLen Brown if (acpi_disabled) 50295b482a8SLen Brown return 0; 50395b482a8SLen Brown 50495b482a8SLen Brown #ifdef CONFIG_ACPI_PROCFS 50595b482a8SLen Brown /* 'sleep' [R/W] */ 50695b482a8SLen Brown proc_create("sleep", S_IFREG | S_IRUGO | S_IWUSR, 50795b482a8SLen Brown acpi_root_dir, &acpi_system_sleep_fops); 50895b482a8SLen Brown #endif /* CONFIG_ACPI_PROCFS */ 50995b482a8SLen Brown 51095b482a8SLen Brown #ifdef HAVE_ACPI_LEGACY_ALARM 51195b482a8SLen Brown /* 'alarm' [R/W] */ 51295b482a8SLen Brown proc_create("alarm", S_IFREG | S_IRUGO | S_IWUSR, 51395b482a8SLen Brown acpi_root_dir, &acpi_system_alarm_fops); 51495b482a8SLen Brown 51595b482a8SLen Brown acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL); 51695b482a8SLen Brown /* 51795b482a8SLen Brown * Disable the RTC event after installing RTC handler. 51895b482a8SLen Brown * Only when RTC alarm is set will it be enabled. 51995b482a8SLen Brown */ 52095b482a8SLen Brown acpi_clear_event(ACPI_EVENT_RTC); 52195b482a8SLen Brown acpi_disable_event(ACPI_EVENT_RTC, 0); 52295b482a8SLen Brown #endif /* HAVE_ACPI_LEGACY_ALARM */ 52395b482a8SLen Brown 52495b482a8SLen Brown /* 'wakeup device' [R/W] */ 52595b482a8SLen Brown proc_create("wakeup", S_IFREG | S_IRUGO | S_IWUSR, 52695b482a8SLen Brown acpi_root_dir, &acpi_system_wakeup_device_fops); 52795b482a8SLen Brown 52895b482a8SLen Brown return 0; 52995b482a8SLen Brown } 53095b482a8SLen Brown 53195b482a8SLen Brown late_initcall(acpi_sleep_proc_init); 532