xref: /openbmc/linux/drivers/acpi/proc.c (revision 9090589d)
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 static int
34795b482a8SLen Brown acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset)
34895b482a8SLen Brown {
34995b482a8SLen Brown 	struct list_head *node, *next;
35095b482a8SLen Brown 
35195b482a8SLen Brown 	seq_printf(seq, "Device\tS-state\t  Status   Sysfs node\n");
35295b482a8SLen Brown 
3539090589dSShaohua Li 	mutex_lock(&acpi_device_lock);
35495b482a8SLen Brown 	list_for_each_safe(node, next, &acpi_wakeup_device_list) {
35595b482a8SLen Brown 		struct acpi_device *dev =
35695b482a8SLen Brown 		    container_of(node, struct acpi_device, wakeup_list);
35795b482a8SLen Brown 		struct device *ldev;
35895b482a8SLen Brown 
35995b482a8SLen Brown 		if (!dev->wakeup.flags.valid)
36095b482a8SLen Brown 			continue;
36195b482a8SLen Brown 
36295b482a8SLen Brown 		ldev = acpi_get_physical_device(dev->handle);
36395b482a8SLen Brown 		seq_printf(seq, "%s\t  S%d\t%c%-8s  ",
36495b482a8SLen Brown 			   dev->pnp.bus_id,
36595b482a8SLen Brown 			   (u32) dev->wakeup.sleep_state,
36695b482a8SLen Brown 			   dev->wakeup.flags.run_wake ? '*' : ' ',
36795b482a8SLen Brown 			   dev->wakeup.state.enabled ? "enabled" : "disabled");
36895b482a8SLen Brown 		if (ldev)
36995b482a8SLen Brown 			seq_printf(seq, "%s:%s",
37095b482a8SLen Brown 				   ldev->bus ? ldev->bus->name : "no-bus",
37195b482a8SLen Brown 				   dev_name(ldev));
37295b482a8SLen Brown 		seq_printf(seq, "\n");
37395b482a8SLen Brown 		put_device(ldev);
37495b482a8SLen Brown 
37595b482a8SLen Brown 	}
3769090589dSShaohua Li 	mutex_unlock(&acpi_device_lock);
37795b482a8SLen Brown 	return 0;
37895b482a8SLen Brown }
37995b482a8SLen Brown 
38095b482a8SLen Brown static void physical_device_enable_wakeup(struct acpi_device *adev)
38195b482a8SLen Brown {
38295b482a8SLen Brown 	struct device *dev = acpi_get_physical_device(adev->handle);
38395b482a8SLen Brown 
38495b482a8SLen Brown 	if (dev && device_can_wakeup(dev))
38595b482a8SLen Brown 		device_set_wakeup_enable(dev, adev->wakeup.state.enabled);
38695b482a8SLen Brown }
38795b482a8SLen Brown 
38895b482a8SLen Brown static ssize_t
38995b482a8SLen Brown acpi_system_write_wakeup_device(struct file *file,
39095b482a8SLen Brown 				const char __user * buffer,
39195b482a8SLen Brown 				size_t count, loff_t * ppos)
39295b482a8SLen Brown {
39395b482a8SLen Brown 	struct list_head *node, *next;
39495b482a8SLen Brown 	char strbuf[5];
39595b482a8SLen Brown 	char str[5] = "";
39695b482a8SLen Brown 	int len = count;
39795b482a8SLen Brown 	struct acpi_device *found_dev = NULL;
39895b482a8SLen Brown 
39995b482a8SLen Brown 	if (len > 4)
40095b482a8SLen Brown 		len = 4;
40195b482a8SLen Brown 
40295b482a8SLen Brown 	if (copy_from_user(strbuf, buffer, len))
40395b482a8SLen Brown 		return -EFAULT;
40495b482a8SLen Brown 	strbuf[len] = '\0';
40595b482a8SLen Brown 	sscanf(strbuf, "%s", str);
40695b482a8SLen Brown 
4079090589dSShaohua Li 	mutex_lock(&acpi_device_lock);
40895b482a8SLen Brown 	list_for_each_safe(node, next, &acpi_wakeup_device_list) {
40995b482a8SLen Brown 		struct acpi_device *dev =
41095b482a8SLen Brown 		    container_of(node, struct acpi_device, wakeup_list);
41195b482a8SLen Brown 		if (!dev->wakeup.flags.valid)
41295b482a8SLen Brown 			continue;
41395b482a8SLen Brown 
41495b482a8SLen Brown 		if (!strncmp(dev->pnp.bus_id, str, 4)) {
41595b482a8SLen Brown 			dev->wakeup.state.enabled =
41695b482a8SLen Brown 			    dev->wakeup.state.enabled ? 0 : 1;
41795b482a8SLen Brown 			found_dev = dev;
41895b482a8SLen Brown 			break;
41995b482a8SLen Brown 		}
42095b482a8SLen Brown 	}
42195b482a8SLen Brown 	if (found_dev) {
42295b482a8SLen Brown 		physical_device_enable_wakeup(found_dev);
42395b482a8SLen Brown 		list_for_each_safe(node, next, &acpi_wakeup_device_list) {
42495b482a8SLen Brown 			struct acpi_device *dev = container_of(node,
42595b482a8SLen Brown 							       struct
42695b482a8SLen Brown 							       acpi_device,
42795b482a8SLen Brown 							       wakeup_list);
42895b482a8SLen Brown 
42995b482a8SLen Brown 			if ((dev != found_dev) &&
43095b482a8SLen Brown 			    (dev->wakeup.gpe_number ==
43195b482a8SLen Brown 			     found_dev->wakeup.gpe_number)
43295b482a8SLen Brown 			    && (dev->wakeup.gpe_device ==
43395b482a8SLen Brown 				found_dev->wakeup.gpe_device)) {
43495b482a8SLen Brown 				printk(KERN_WARNING
43595b482a8SLen Brown 				       "ACPI: '%s' and '%s' have the same GPE, "
43695b482a8SLen Brown 				       "can't disable/enable one seperately\n",
43795b482a8SLen Brown 				       dev->pnp.bus_id, found_dev->pnp.bus_id);
43895b482a8SLen Brown 				dev->wakeup.state.enabled =
43995b482a8SLen Brown 				    found_dev->wakeup.state.enabled;
44095b482a8SLen Brown 				physical_device_enable_wakeup(dev);
44195b482a8SLen Brown 			}
44295b482a8SLen Brown 		}
44395b482a8SLen Brown 	}
4449090589dSShaohua Li 	mutex_unlock(&acpi_device_lock);
44595b482a8SLen Brown 	return count;
44695b482a8SLen Brown }
44795b482a8SLen Brown 
44895b482a8SLen Brown static int
44995b482a8SLen Brown acpi_system_wakeup_device_open_fs(struct inode *inode, struct file *file)
45095b482a8SLen Brown {
45195b482a8SLen Brown 	return single_open(file, acpi_system_wakeup_device_seq_show,
45295b482a8SLen Brown 			   PDE(inode)->data);
45395b482a8SLen Brown }
45495b482a8SLen Brown 
45595b482a8SLen Brown static const struct file_operations acpi_system_wakeup_device_fops = {
45695b482a8SLen Brown 	.owner = THIS_MODULE,
45795b482a8SLen Brown 	.open = acpi_system_wakeup_device_open_fs,
45895b482a8SLen Brown 	.read = seq_read,
45995b482a8SLen Brown 	.write = acpi_system_write_wakeup_device,
46095b482a8SLen Brown 	.llseek = seq_lseek,
46195b482a8SLen Brown 	.release = single_release,
46295b482a8SLen Brown };
46395b482a8SLen Brown 
46495b482a8SLen Brown #ifdef	CONFIG_ACPI_PROCFS
46595b482a8SLen Brown static const struct file_operations acpi_system_sleep_fops = {
46695b482a8SLen Brown 	.owner = THIS_MODULE,
46795b482a8SLen Brown 	.open = acpi_system_sleep_open_fs,
46895b482a8SLen Brown 	.read = seq_read,
46995b482a8SLen Brown 	.write = acpi_system_write_sleep,
47095b482a8SLen Brown 	.llseek = seq_lseek,
47195b482a8SLen Brown 	.release = single_release,
47295b482a8SLen Brown };
47395b482a8SLen Brown #endif				/* CONFIG_ACPI_PROCFS */
47495b482a8SLen Brown 
47595b482a8SLen Brown #ifdef	HAVE_ACPI_LEGACY_ALARM
47695b482a8SLen Brown static const struct file_operations acpi_system_alarm_fops = {
47795b482a8SLen Brown 	.owner = THIS_MODULE,
47895b482a8SLen Brown 	.open = acpi_system_alarm_open_fs,
47995b482a8SLen Brown 	.read = seq_read,
48095b482a8SLen Brown 	.write = acpi_system_write_alarm,
48195b482a8SLen Brown 	.llseek = seq_lseek,
48295b482a8SLen Brown 	.release = single_release,
48395b482a8SLen Brown };
48495b482a8SLen Brown 
48595b482a8SLen Brown static u32 rtc_handler(void *context)
48695b482a8SLen Brown {
48795b482a8SLen Brown 	acpi_clear_event(ACPI_EVENT_RTC);
48895b482a8SLen Brown 	acpi_disable_event(ACPI_EVENT_RTC, 0);
48995b482a8SLen Brown 
49095b482a8SLen Brown 	return ACPI_INTERRUPT_HANDLED;
49195b482a8SLen Brown }
49295b482a8SLen Brown #endif				/* HAVE_ACPI_LEGACY_ALARM */
49395b482a8SLen Brown 
4949cee43e0SBjorn Helgaas int __init acpi_sleep_proc_init(void)
49595b482a8SLen Brown {
49695b482a8SLen Brown #ifdef	CONFIG_ACPI_PROCFS
49795b482a8SLen Brown 	/* 'sleep' [R/W] */
49895b482a8SLen Brown 	proc_create("sleep", S_IFREG | S_IRUGO | S_IWUSR,
49995b482a8SLen Brown 		    acpi_root_dir, &acpi_system_sleep_fops);
50095b482a8SLen Brown #endif				/* CONFIG_ACPI_PROCFS */
50195b482a8SLen Brown 
50295b482a8SLen Brown #ifdef	HAVE_ACPI_LEGACY_ALARM
50395b482a8SLen Brown 	/* 'alarm' [R/W] */
50495b482a8SLen Brown 	proc_create("alarm", S_IFREG | S_IRUGO | S_IWUSR,
50595b482a8SLen Brown 		    acpi_root_dir, &acpi_system_alarm_fops);
50695b482a8SLen Brown 
50795b482a8SLen Brown 	acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL);
50895b482a8SLen Brown 	/*
50995b482a8SLen Brown 	 * Disable the RTC event after installing RTC handler.
51095b482a8SLen Brown 	 * Only when RTC alarm is set will it be enabled.
51195b482a8SLen Brown 	 */
51295b482a8SLen Brown 	acpi_clear_event(ACPI_EVENT_RTC);
51395b482a8SLen Brown 	acpi_disable_event(ACPI_EVENT_RTC, 0);
51495b482a8SLen Brown #endif				/* HAVE_ACPI_LEGACY_ALARM */
51595b482a8SLen Brown 
51695b482a8SLen Brown 	/* 'wakeup device' [R/W] */
51795b482a8SLen Brown 	proc_create("wakeup", S_IFREG | S_IRUGO | S_IWUSR,
51895b482a8SLen Brown 		    acpi_root_dir, &acpi_system_wakeup_device_fops);
51995b482a8SLen Brown 
52095b482a8SLen Brown 	return 0;
52195b482a8SLen Brown }
522