xref: /openbmc/linux/drivers/acpi/proc.c (revision 05bce79e)
195b482a8SLen Brown #include <linux/proc_fs.h>
295b482a8SLen Brown #include <linux/seq_file.h>
3214f2c90SPaul Gortmaker #include <linux/export.h>
495b482a8SLen Brown #include <linux/suspend.h>
595b482a8SLen Brown #include <linux/bcd.h>
695b482a8SLen Brown #include <asm/uaccess.h>
795b482a8SLen Brown 
895b482a8SLen Brown #include <acpi/acpi_bus.h>
995b482a8SLen Brown #include <acpi/acpi_drivers.h>
1095b482a8SLen Brown 
1195b482a8SLen Brown #ifdef CONFIG_X86
1295b482a8SLen Brown #include <linux/mc146818rtc.h>
1395b482a8SLen Brown #endif
1495b482a8SLen Brown 
1595b482a8SLen Brown #include "sleep.h"
1695b482a8SLen Brown 
1795b482a8SLen Brown #define _COMPONENT		ACPI_SYSTEM_COMPONENT
1895b482a8SLen Brown 
1995b482a8SLen Brown /*
2095b482a8SLen Brown  * this file provides support for:
2195b482a8SLen Brown  * /proc/acpi/alarm
2295b482a8SLen Brown  * /proc/acpi/wakeup
2395b482a8SLen Brown  */
2495b482a8SLen Brown 
2595b482a8SLen Brown ACPI_MODULE_NAME("sleep")
2695b482a8SLen Brown 
2795b482a8SLen Brown #if defined(CONFIG_RTC_DRV_CMOS) || defined(CONFIG_RTC_DRV_CMOS_MODULE) || !defined(CONFIG_X86)
2895b482a8SLen Brown /* use /sys/class/rtc/rtcX/wakealarm instead; it's not ACPI-specific */
2995b482a8SLen Brown #else
3095b482a8SLen Brown #define	HAVE_ACPI_LEGACY_ALARM
3195b482a8SLen Brown #endif
3295b482a8SLen Brown 
3395b482a8SLen Brown #ifdef	HAVE_ACPI_LEGACY_ALARM
3495b482a8SLen Brown 
3538f64c77SLen Brown static u32 cmos_bcd_read(int offset, int rtc_control);
3638f64c77SLen Brown 
3795b482a8SLen Brown static int acpi_system_alarm_seq_show(struct seq_file *seq, void *offset)
3895b482a8SLen Brown {
3995b482a8SLen Brown 	u32 sec, min, hr;
4095b482a8SLen Brown 	u32 day, mo, yr, cent = 0;
4138f64c77SLen Brown 	u32 today = 0;
4295b482a8SLen Brown 	unsigned char rtc_control = 0;
4395b482a8SLen Brown 	unsigned long flags;
4495b482a8SLen Brown 
4595b482a8SLen Brown 	spin_lock_irqsave(&rtc_lock, flags);
4695b482a8SLen Brown 
4795b482a8SLen Brown 	rtc_control = CMOS_READ(RTC_CONTROL);
4838f64c77SLen Brown 	sec = cmos_bcd_read(RTC_SECONDS_ALARM, rtc_control);
4938f64c77SLen Brown 	min = cmos_bcd_read(RTC_MINUTES_ALARM, rtc_control);
5038f64c77SLen Brown 	hr = cmos_bcd_read(RTC_HOURS_ALARM, rtc_control);
5195b482a8SLen Brown 
5295b482a8SLen Brown 	/* If we ever get an FACP with proper values... */
5338f64c77SLen Brown 	if (acpi_gbl_FADT.day_alarm) {
5495b482a8SLen Brown 		/* ACPI spec: only low 6 its should be cared */
5595b482a8SLen Brown 		day = CMOS_READ(acpi_gbl_FADT.day_alarm) & 0x3F;
5638f64c77SLen Brown 		if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
5738f64c77SLen Brown 			day = bcd2bin(day);
5838f64c77SLen Brown 	} else
5938f64c77SLen Brown 		day = cmos_bcd_read(RTC_DAY_OF_MONTH, rtc_control);
6095b482a8SLen Brown 	if (acpi_gbl_FADT.month_alarm)
6138f64c77SLen Brown 		mo = cmos_bcd_read(acpi_gbl_FADT.month_alarm, rtc_control);
6238f64c77SLen Brown 	else {
6338f64c77SLen Brown 		mo = cmos_bcd_read(RTC_MONTH, rtc_control);
6438f64c77SLen Brown 		today = cmos_bcd_read(RTC_DAY_OF_MONTH, rtc_control);
6538f64c77SLen Brown 	}
6695b482a8SLen Brown 	if (acpi_gbl_FADT.century)
6738f64c77SLen Brown 		cent = cmos_bcd_read(acpi_gbl_FADT.century, rtc_control);
6895b482a8SLen Brown 
6938f64c77SLen Brown 	yr = cmos_bcd_read(RTC_YEAR, rtc_control);
7095b482a8SLen Brown 
7195b482a8SLen Brown 	spin_unlock_irqrestore(&rtc_lock, flags);
7295b482a8SLen Brown 
7395b482a8SLen Brown 	/* we're trusting the FADT (see above) */
7495b482a8SLen Brown 	if (!acpi_gbl_FADT.century)
7595b482a8SLen Brown 		/* If we're not trusting the FADT, we should at least make it
7695b482a8SLen Brown 		 * right for _this_ century... ehm, what is _this_ century?
7795b482a8SLen Brown 		 *
7895b482a8SLen Brown 		 * TBD:
7995b482a8SLen Brown 		 *  ASAP: find piece of code in the kernel, e.g. star tracker driver,
8095b482a8SLen Brown 		 *        which we can trust to determine the century correctly. Atom
8195b482a8SLen Brown 		 *        watch driver would be nice, too...
8295b482a8SLen Brown 		 *
8395b482a8SLen Brown 		 *  if that has not happened, change for first release in 2050:
8495b482a8SLen Brown 		 *        if (yr<50)
8595b482a8SLen Brown 		 *                yr += 2100;
8695b482a8SLen Brown 		 *        else
8795b482a8SLen Brown 		 *                yr += 2000;   // current line of code
8895b482a8SLen Brown 		 *
8995b482a8SLen Brown 		 *  if that has not happened either, please do on 2099/12/31:23:59:59
9095b482a8SLen Brown 		 *        s/2000/2100
9195b482a8SLen Brown 		 *
9295b482a8SLen Brown 		 */
9395b482a8SLen Brown 		yr += 2000;
9495b482a8SLen Brown 	else
9595b482a8SLen Brown 		yr += cent * 100;
9695b482a8SLen Brown 
9738f64c77SLen Brown 	/*
9838f64c77SLen Brown 	 * Show correct dates for alarms up to a month into the future.
9938f64c77SLen Brown 	 * This solves issues for nearly all situations with the common
10038f64c77SLen Brown 	 * 30-day alarm clocks in PC hardware.
10138f64c77SLen Brown 	 */
10238f64c77SLen Brown 	if (day < today) {
10338f64c77SLen Brown 		if (mo < 12) {
10438f64c77SLen Brown 			mo += 1;
10538f64c77SLen Brown 		} else {
10638f64c77SLen Brown 			mo = 1;
10738f64c77SLen Brown 			yr += 1;
10838f64c77SLen Brown 		}
10938f64c77SLen Brown 	}
11038f64c77SLen Brown 
11195b482a8SLen Brown 	seq_printf(seq, "%4.4u-", yr);
11295b482a8SLen Brown 	(mo > 12) ? seq_puts(seq, "**-") : seq_printf(seq, "%2.2u-", mo);
11395b482a8SLen Brown 	(day > 31) ? seq_puts(seq, "** ") : seq_printf(seq, "%2.2u ", day);
11495b482a8SLen Brown 	(hr > 23) ? seq_puts(seq, "**:") : seq_printf(seq, "%2.2u:", hr);
11595b482a8SLen Brown 	(min > 59) ? seq_puts(seq, "**:") : seq_printf(seq, "%2.2u:", min);
11695b482a8SLen Brown 	(sec > 59) ? seq_puts(seq, "**\n") : seq_printf(seq, "%2.2u\n", sec);
11795b482a8SLen Brown 
11895b482a8SLen Brown 	return 0;
11995b482a8SLen Brown }
12095b482a8SLen Brown 
12195b482a8SLen Brown static int acpi_system_alarm_open_fs(struct inode *inode, struct file *file)
12295b482a8SLen Brown {
12395b482a8SLen Brown 	return single_open(file, acpi_system_alarm_seq_show, PDE(inode)->data);
12495b482a8SLen Brown }
12595b482a8SLen Brown 
12695b482a8SLen Brown static int get_date_field(char **p, u32 * value)
12795b482a8SLen Brown {
12895b482a8SLen Brown 	char *next = NULL;
12995b482a8SLen Brown 	char *string_end = NULL;
13095b482a8SLen Brown 	int result = -EINVAL;
13195b482a8SLen Brown 
13295b482a8SLen Brown 	/*
13395b482a8SLen Brown 	 * Try to find delimeter, only to insert null.  The end of the
13495b482a8SLen Brown 	 * string won't have one, but is still valid.
13595b482a8SLen Brown 	 */
13695b482a8SLen Brown 	if (*p == NULL)
13795b482a8SLen Brown 		return result;
13895b482a8SLen Brown 
13995b482a8SLen Brown 	next = strpbrk(*p, "- :");
14095b482a8SLen Brown 	if (next)
14195b482a8SLen Brown 		*next++ = '\0';
14295b482a8SLen Brown 
14395b482a8SLen Brown 	*value = simple_strtoul(*p, &string_end, 10);
14495b482a8SLen Brown 
14595b482a8SLen Brown 	/* Signal success if we got a good digit */
14695b482a8SLen Brown 	if (string_end != *p)
14795b482a8SLen Brown 		result = 0;
14895b482a8SLen Brown 
14995b482a8SLen Brown 	if (next)
15095b482a8SLen Brown 		*p = next;
15195b482a8SLen Brown 	else
15295b482a8SLen Brown 		*p = NULL;
15395b482a8SLen Brown 
15495b482a8SLen Brown 	return result;
15595b482a8SLen Brown }
15695b482a8SLen Brown 
15795b482a8SLen Brown /* Read a possibly BCD register, always return binary */
15895b482a8SLen Brown static u32 cmos_bcd_read(int offset, int rtc_control)
15995b482a8SLen Brown {
16095b482a8SLen Brown 	u32 val = CMOS_READ(offset);
16195b482a8SLen Brown 	if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
16295b482a8SLen Brown 		val = bcd2bin(val);
16395b482a8SLen Brown 	return val;
16495b482a8SLen Brown }
16595b482a8SLen Brown 
16695b482a8SLen Brown /* Write binary value into possibly BCD register */
16795b482a8SLen Brown static void cmos_bcd_write(u32 val, int offset, int rtc_control)
16895b482a8SLen Brown {
16995b482a8SLen Brown 	if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
17095b482a8SLen Brown 		val = bin2bcd(val);
17195b482a8SLen Brown 	CMOS_WRITE(val, offset);
17295b482a8SLen Brown }
17395b482a8SLen Brown 
17495b482a8SLen Brown static ssize_t
17595b482a8SLen Brown acpi_system_write_alarm(struct file *file,
17695b482a8SLen Brown 			const char __user * buffer, size_t count, loff_t * ppos)
17795b482a8SLen Brown {
17895b482a8SLen Brown 	int result = 0;
17995b482a8SLen Brown 	char alarm_string[30] = { '\0' };
18095b482a8SLen Brown 	char *p = alarm_string;
18195b482a8SLen Brown 	u32 sec, min, hr, day, mo, yr;
18295b482a8SLen Brown 	int adjust = 0;
18395b482a8SLen Brown 	unsigned char rtc_control = 0;
18495b482a8SLen Brown 
18595b482a8SLen Brown 	if (count > sizeof(alarm_string) - 1)
18695b482a8SLen Brown 		return -EINVAL;
18795b482a8SLen Brown 
18895b482a8SLen Brown 	if (copy_from_user(alarm_string, buffer, count))
18995b482a8SLen Brown 		return -EFAULT;
19095b482a8SLen Brown 
19195b482a8SLen Brown 	alarm_string[count] = '\0';
19295b482a8SLen Brown 
19395b482a8SLen Brown 	/* check for time adjustment */
19495b482a8SLen Brown 	if (alarm_string[0] == '+') {
19595b482a8SLen Brown 		p++;
19695b482a8SLen Brown 		adjust = 1;
19795b482a8SLen Brown 	}
19895b482a8SLen Brown 
19995b482a8SLen Brown 	if ((result = get_date_field(&p, &yr)))
20095b482a8SLen Brown 		goto end;
20195b482a8SLen Brown 	if ((result = get_date_field(&p, &mo)))
20295b482a8SLen Brown 		goto end;
20395b482a8SLen Brown 	if ((result = get_date_field(&p, &day)))
20495b482a8SLen Brown 		goto end;
20595b482a8SLen Brown 	if ((result = get_date_field(&p, &hr)))
20695b482a8SLen Brown 		goto end;
20795b482a8SLen Brown 	if ((result = get_date_field(&p, &min)))
20895b482a8SLen Brown 		goto end;
20995b482a8SLen Brown 	if ((result = get_date_field(&p, &sec)))
21095b482a8SLen Brown 		goto end;
21195b482a8SLen Brown 
21295b482a8SLen Brown 	spin_lock_irq(&rtc_lock);
21395b482a8SLen Brown 
21495b482a8SLen Brown 	rtc_control = CMOS_READ(RTC_CONTROL);
21595b482a8SLen Brown 
21695b482a8SLen Brown 	if (adjust) {
21795b482a8SLen Brown 		yr += cmos_bcd_read(RTC_YEAR, rtc_control);
21895b482a8SLen Brown 		mo += cmos_bcd_read(RTC_MONTH, rtc_control);
21995b482a8SLen Brown 		day += cmos_bcd_read(RTC_DAY_OF_MONTH, rtc_control);
22095b482a8SLen Brown 		hr += cmos_bcd_read(RTC_HOURS, rtc_control);
22195b482a8SLen Brown 		min += cmos_bcd_read(RTC_MINUTES, rtc_control);
22295b482a8SLen Brown 		sec += cmos_bcd_read(RTC_SECONDS, rtc_control);
22395b482a8SLen Brown 	}
22495b482a8SLen Brown 
22595b482a8SLen Brown 	spin_unlock_irq(&rtc_lock);
22695b482a8SLen Brown 
22795b482a8SLen Brown 	if (sec > 59) {
22895b482a8SLen Brown 		min += sec/60;
22995b482a8SLen Brown 		sec = sec%60;
23095b482a8SLen Brown 	}
23195b482a8SLen Brown 	if (min > 59) {
23295b482a8SLen Brown 		hr += min/60;
23395b482a8SLen Brown 		min = min%60;
23495b482a8SLen Brown 	}
23595b482a8SLen Brown 	if (hr > 23) {
23695b482a8SLen Brown 		day += hr/24;
23795b482a8SLen Brown 		hr = hr%24;
23895b482a8SLen Brown 	}
23995b482a8SLen Brown 	if (day > 31) {
24095b482a8SLen Brown 		mo += day/32;
24195b482a8SLen Brown 		day = day%32;
24295b482a8SLen Brown 	}
24395b482a8SLen Brown 	if (mo > 12) {
24495b482a8SLen Brown 		yr += mo/13;
24595b482a8SLen Brown 		mo = mo%13;
24695b482a8SLen Brown 	}
24795b482a8SLen Brown 
24895b482a8SLen Brown 	spin_lock_irq(&rtc_lock);
24995b482a8SLen Brown 	/*
25095b482a8SLen Brown 	 * Disable alarm interrupt before setting alarm timer or else
25195b482a8SLen Brown 	 * when ACPI_EVENT_RTC is enabled, a spurious ACPI interrupt occurs
25295b482a8SLen Brown 	 */
25395b482a8SLen Brown 	rtc_control &= ~RTC_AIE;
25495b482a8SLen Brown 	CMOS_WRITE(rtc_control, RTC_CONTROL);
25595b482a8SLen Brown 	CMOS_READ(RTC_INTR_FLAGS);
25695b482a8SLen Brown 
25795b482a8SLen Brown 	/* write the fields the rtc knows about */
25895b482a8SLen Brown 	cmos_bcd_write(hr, RTC_HOURS_ALARM, rtc_control);
25995b482a8SLen Brown 	cmos_bcd_write(min, RTC_MINUTES_ALARM, rtc_control);
26095b482a8SLen Brown 	cmos_bcd_write(sec, RTC_SECONDS_ALARM, rtc_control);
26195b482a8SLen Brown 
26295b482a8SLen Brown 	/*
26395b482a8SLen Brown 	 * If the system supports an enhanced alarm it will have non-zero
26495b482a8SLen Brown 	 * offsets into the CMOS RAM here -- which for some reason are pointing
26595b482a8SLen Brown 	 * to the RTC area of memory.
26695b482a8SLen Brown 	 */
26795b482a8SLen Brown 	if (acpi_gbl_FADT.day_alarm)
26895b482a8SLen Brown 		cmos_bcd_write(day, acpi_gbl_FADT.day_alarm, rtc_control);
26995b482a8SLen Brown 	if (acpi_gbl_FADT.month_alarm)
27095b482a8SLen Brown 		cmos_bcd_write(mo, acpi_gbl_FADT.month_alarm, rtc_control);
27195b482a8SLen Brown 	if (acpi_gbl_FADT.century) {
27295b482a8SLen Brown 		if (adjust)
27395b482a8SLen Brown 			yr += cmos_bcd_read(acpi_gbl_FADT.century, rtc_control) * 100;
27495b482a8SLen Brown 		cmos_bcd_write(yr / 100, acpi_gbl_FADT.century, rtc_control);
27595b482a8SLen Brown 	}
27695b482a8SLen Brown 	/* enable the rtc alarm interrupt */
27795b482a8SLen Brown 	rtc_control |= RTC_AIE;
27895b482a8SLen Brown 	CMOS_WRITE(rtc_control, RTC_CONTROL);
27995b482a8SLen Brown 	CMOS_READ(RTC_INTR_FLAGS);
28095b482a8SLen Brown 
28195b482a8SLen Brown 	spin_unlock_irq(&rtc_lock);
28295b482a8SLen Brown 
28395b482a8SLen Brown 	acpi_clear_event(ACPI_EVENT_RTC);
28495b482a8SLen Brown 	acpi_enable_event(ACPI_EVENT_RTC, 0);
28595b482a8SLen Brown 
28695b482a8SLen Brown 	*ppos += count;
28795b482a8SLen Brown 
28895b482a8SLen Brown 	result = 0;
28995b482a8SLen Brown       end:
29095b482a8SLen Brown 	return result ? result : count;
29195b482a8SLen Brown }
29295b482a8SLen Brown #endif				/* HAVE_ACPI_LEGACY_ALARM */
29395b482a8SLen Brown 
29495b482a8SLen Brown static int
29595b482a8SLen Brown acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset)
29695b482a8SLen Brown {
29795b482a8SLen Brown 	struct list_head *node, *next;
29895b482a8SLen Brown 
29995b482a8SLen Brown 	seq_printf(seq, "Device\tS-state\t  Status   Sysfs node\n");
30095b482a8SLen Brown 
3019090589dSShaohua Li 	mutex_lock(&acpi_device_lock);
30295b482a8SLen Brown 	list_for_each_safe(node, next, &acpi_wakeup_device_list) {
30395b482a8SLen Brown 		struct acpi_device *dev =
30495b482a8SLen Brown 		    container_of(node, struct acpi_device, wakeup_list);
3051033f904SLan Tianyu 		struct acpi_device_physical_node *entry;
30695b482a8SLen Brown 
30795b482a8SLen Brown 		if (!dev->wakeup.flags.valid)
30895b482a8SLen Brown 			continue;
30995b482a8SLen Brown 
3101033f904SLan Tianyu 		seq_printf(seq, "%s\t  S%d\t",
31195b482a8SLen Brown 			   dev->pnp.bus_id,
3121033f904SLan Tianyu 			   (u32) dev->wakeup.sleep_state);
31395b482a8SLen Brown 
3141033f904SLan Tianyu 		if (!dev->physical_node_count)
3151033f904SLan Tianyu 			seq_printf(seq, "%c%-8s\n",
3161033f904SLan Tianyu 				dev->wakeup.flags.run_wake ?
3171033f904SLan Tianyu 				'*' : ' ', "disabled");
3181033f904SLan Tianyu 		else {
3191033f904SLan Tianyu 			struct device *ldev;
3201033f904SLan Tianyu 			list_for_each_entry(entry, &dev->physical_node_list,
3211033f904SLan Tianyu 					node) {
3221033f904SLan Tianyu 				ldev = get_device(entry->dev);
3231033f904SLan Tianyu 				if (!ldev)
3241033f904SLan Tianyu 					continue;
3251033f904SLan Tianyu 
3261033f904SLan Tianyu 				if (&entry->node !=
3271033f904SLan Tianyu 						dev->physical_node_list.next)
3281033f904SLan Tianyu 					seq_printf(seq, "\t\t");
3291033f904SLan Tianyu 
3301033f904SLan Tianyu 				seq_printf(seq, "%c%-8s  %s:%s\n",
3311033f904SLan Tianyu 					dev->wakeup.flags.run_wake ? '*' : ' ',
3321033f904SLan Tianyu 					(device_may_wakeup(&dev->dev) ||
3331033f904SLan Tianyu 					(ldev && device_may_wakeup(ldev))) ?
3341033f904SLan Tianyu 					"enabled" : "disabled",
3351033f904SLan Tianyu 					ldev->bus ? ldev->bus->name :
3361033f904SLan Tianyu 					"no-bus", dev_name(ldev));
3371033f904SLan Tianyu 				put_device(ldev);
3381033f904SLan Tianyu 			}
3391033f904SLan Tianyu 		}
34095b482a8SLen Brown 	}
3419090589dSShaohua Li 	mutex_unlock(&acpi_device_lock);
34295b482a8SLen Brown 	return 0;
34395b482a8SLen Brown }
34495b482a8SLen Brown 
34595b482a8SLen Brown static void physical_device_enable_wakeup(struct acpi_device *adev)
34695b482a8SLen Brown {
3471033f904SLan Tianyu 	struct acpi_device_physical_node *entry;
34895b482a8SLen Brown 
3491033f904SLan Tianyu 	list_for_each_entry(entry,
3501033f904SLan Tianyu 		&adev->physical_node_list, node)
3511033f904SLan Tianyu 		if (entry->dev && device_can_wakeup(entry->dev)) {
3521033f904SLan Tianyu 			bool enable = !device_may_wakeup(entry->dev);
3531033f904SLan Tianyu 			device_set_wakeup_enable(entry->dev, enable);
354f2b56bc8SRafael J. Wysocki 		}
35595b482a8SLen Brown }
35695b482a8SLen Brown 
35795b482a8SLen Brown static ssize_t
35895b482a8SLen Brown acpi_system_write_wakeup_device(struct file *file,
35995b482a8SLen Brown 				const char __user * buffer,
36095b482a8SLen Brown 				size_t count, loff_t * ppos)
36195b482a8SLen Brown {
36295b482a8SLen Brown 	struct list_head *node, *next;
36395b482a8SLen Brown 	char strbuf[5];
36495b482a8SLen Brown 	char str[5] = "";
36595b482a8SLen Brown 
36605bce79eSCyril Roelandt 	if (count > 4)
36705bce79eSCyril Roelandt 		count = 4;
36895b482a8SLen Brown 
36905bce79eSCyril Roelandt 	if (copy_from_user(strbuf, buffer, count))
37095b482a8SLen Brown 		return -EFAULT;
37105bce79eSCyril Roelandt 	strbuf[count] = '\0';
37295b482a8SLen Brown 	sscanf(strbuf, "%s", str);
37395b482a8SLen Brown 
3749090589dSShaohua Li 	mutex_lock(&acpi_device_lock);
37595b482a8SLen Brown 	list_for_each_safe(node, next, &acpi_wakeup_device_list) {
37695b482a8SLen Brown 		struct acpi_device *dev =
37795b482a8SLen Brown 		    container_of(node, struct acpi_device, wakeup_list);
37895b482a8SLen Brown 		if (!dev->wakeup.flags.valid)
37995b482a8SLen Brown 			continue;
38095b482a8SLen Brown 
38195b482a8SLen Brown 		if (!strncmp(dev->pnp.bus_id, str, 4)) {
382f2b56bc8SRafael J. Wysocki 			if (device_can_wakeup(&dev->dev)) {
383f2b56bc8SRafael J. Wysocki 				bool enable = !device_may_wakeup(&dev->dev);
384f2b56bc8SRafael J. Wysocki 				device_set_wakeup_enable(&dev->dev, enable);
385f2b56bc8SRafael J. Wysocki 			} else {
38695b482a8SLen Brown 				physical_device_enable_wakeup(dev);
387f2b56bc8SRafael J. Wysocki 			}
388b014f4f1SRafael J. Wysocki 			break;
38995b482a8SLen Brown 		}
39095b482a8SLen Brown 	}
3919090589dSShaohua Li 	mutex_unlock(&acpi_device_lock);
39295b482a8SLen Brown 	return count;
39395b482a8SLen Brown }
39495b482a8SLen Brown 
39595b482a8SLen Brown static int
39695b482a8SLen Brown acpi_system_wakeup_device_open_fs(struct inode *inode, struct file *file)
39795b482a8SLen Brown {
39895b482a8SLen Brown 	return single_open(file, acpi_system_wakeup_device_seq_show,
39995b482a8SLen Brown 			   PDE(inode)->data);
40095b482a8SLen Brown }
40195b482a8SLen Brown 
40295b482a8SLen Brown static const struct file_operations acpi_system_wakeup_device_fops = {
40395b482a8SLen Brown 	.owner = THIS_MODULE,
40495b482a8SLen Brown 	.open = acpi_system_wakeup_device_open_fs,
40595b482a8SLen Brown 	.read = seq_read,
40695b482a8SLen Brown 	.write = acpi_system_write_wakeup_device,
40795b482a8SLen Brown 	.llseek = seq_lseek,
40895b482a8SLen Brown 	.release = single_release,
40995b482a8SLen Brown };
41095b482a8SLen Brown 
41195b482a8SLen Brown #ifdef	HAVE_ACPI_LEGACY_ALARM
41295b482a8SLen Brown static const struct file_operations acpi_system_alarm_fops = {
41395b482a8SLen Brown 	.owner = THIS_MODULE,
41495b482a8SLen Brown 	.open = acpi_system_alarm_open_fs,
41595b482a8SLen Brown 	.read = seq_read,
41695b482a8SLen Brown 	.write = acpi_system_write_alarm,
41795b482a8SLen Brown 	.llseek = seq_lseek,
41895b482a8SLen Brown 	.release = single_release,
41995b482a8SLen Brown };
42095b482a8SLen Brown 
42195b482a8SLen Brown static u32 rtc_handler(void *context)
42295b482a8SLen Brown {
42395b482a8SLen Brown 	acpi_clear_event(ACPI_EVENT_RTC);
42495b482a8SLen Brown 	acpi_disable_event(ACPI_EVENT_RTC, 0);
42595b482a8SLen Brown 
42695b482a8SLen Brown 	return ACPI_INTERRUPT_HANDLED;
42795b482a8SLen Brown }
42895b482a8SLen Brown #endif				/* HAVE_ACPI_LEGACY_ALARM */
42995b482a8SLen Brown 
4309cee43e0SBjorn Helgaas int __init acpi_sleep_proc_init(void)
43195b482a8SLen Brown {
43295b482a8SLen Brown #ifdef	HAVE_ACPI_LEGACY_ALARM
43395b482a8SLen Brown 	/* 'alarm' [R/W] */
43495b482a8SLen Brown 	proc_create("alarm", S_IFREG | S_IRUGO | S_IWUSR,
43595b482a8SLen Brown 		    acpi_root_dir, &acpi_system_alarm_fops);
43695b482a8SLen Brown 
43795b482a8SLen Brown 	acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL);
43895b482a8SLen Brown 	/*
43995b482a8SLen Brown 	 * Disable the RTC event after installing RTC handler.
44095b482a8SLen Brown 	 * Only when RTC alarm is set will it be enabled.
44195b482a8SLen Brown 	 */
44295b482a8SLen Brown 	acpi_clear_event(ACPI_EVENT_RTC);
44395b482a8SLen Brown 	acpi_disable_event(ACPI_EVENT_RTC, 0);
44495b482a8SLen Brown #endif				/* HAVE_ACPI_LEGACY_ALARM */
44595b482a8SLen Brown 
44695b482a8SLen Brown 	/* 'wakeup device' [R/W] */
44795b482a8SLen Brown 	proc_create("wakeup", S_IFREG | S_IRUGO | S_IWUSR,
44895b482a8SLen Brown 		    acpi_root_dir, &acpi_system_wakeup_device_fops);
44995b482a8SLen Brown 
45095b482a8SLen Brown 	return 0;
45195b482a8SLen Brown }
452