xref: /openbmc/linux/drivers/acpi/glue.c (revision eb9d0fe40e313c0a74115ef456a2e43a6c8da72f)
14e10d12aSDavid Shaohua Li /*
24e10d12aSDavid Shaohua Li  * Link physical devices with ACPI devices support
34e10d12aSDavid Shaohua Li  *
44e10d12aSDavid Shaohua Li  * Copyright (c) 2005 David Shaohua Li <shaohua.li@intel.com>
54e10d12aSDavid Shaohua Li  * Copyright (c) 2005 Intel Corp.
64e10d12aSDavid Shaohua Li  *
74e10d12aSDavid Shaohua Li  * This file is released under the GPLv2.
84e10d12aSDavid Shaohua Li  */
94e10d12aSDavid Shaohua Li #include <linux/init.h>
104e10d12aSDavid Shaohua Li #include <linux/list.h>
114e10d12aSDavid Shaohua Li #include <linux/device.h>
124e10d12aSDavid Shaohua Li #include <linux/rwsem.h>
134e10d12aSDavid Shaohua Li #include <linux/acpi.h>
144e10d12aSDavid Shaohua Li 
154e10d12aSDavid Shaohua Li #define ACPI_GLUE_DEBUG	0
164e10d12aSDavid Shaohua Li #if ACPI_GLUE_DEBUG
174e10d12aSDavid Shaohua Li #define DBG(x...) printk(PREFIX x)
184e10d12aSDavid Shaohua Li #else
194ebf83c8SDave Jones #define DBG(x...) do { } while(0)
204e10d12aSDavid Shaohua Li #endif
214e10d12aSDavid Shaohua Li static LIST_HEAD(bus_type_list);
224e10d12aSDavid Shaohua Li static DECLARE_RWSEM(bus_type_sem);
234e10d12aSDavid Shaohua Li 
244e10d12aSDavid Shaohua Li int register_acpi_bus_type(struct acpi_bus_type *type)
254e10d12aSDavid Shaohua Li {
264e10d12aSDavid Shaohua Li 	if (acpi_disabled)
274e10d12aSDavid Shaohua Li 		return -ENODEV;
284e10d12aSDavid Shaohua Li 	if (type && type->bus && type->find_device) {
294e10d12aSDavid Shaohua Li 		down_write(&bus_type_sem);
304e10d12aSDavid Shaohua Li 		list_add_tail(&type->list, &bus_type_list);
314e10d12aSDavid Shaohua Li 		up_write(&bus_type_sem);
324be44fcdSLen Brown 		printk(KERN_INFO PREFIX "bus type %s registered\n",
334be44fcdSLen Brown 		       type->bus->name);
344e10d12aSDavid Shaohua Li 		return 0;
354e10d12aSDavid Shaohua Li 	}
364e10d12aSDavid Shaohua Li 	return -ENODEV;
374e10d12aSDavid Shaohua Li }
384e10d12aSDavid Shaohua Li 
394e10d12aSDavid Shaohua Li int unregister_acpi_bus_type(struct acpi_bus_type *type)
404e10d12aSDavid Shaohua Li {
414e10d12aSDavid Shaohua Li 	if (acpi_disabled)
424e10d12aSDavid Shaohua Li 		return 0;
434e10d12aSDavid Shaohua Li 	if (type) {
444e10d12aSDavid Shaohua Li 		down_write(&bus_type_sem);
454e10d12aSDavid Shaohua Li 		list_del_init(&type->list);
464e10d12aSDavid Shaohua Li 		up_write(&bus_type_sem);
474be44fcdSLen Brown 		printk(KERN_INFO PREFIX "ACPI bus type %s unregistered\n",
484be44fcdSLen Brown 		       type->bus->name);
494e10d12aSDavid Shaohua Li 		return 0;
504e10d12aSDavid Shaohua Li 	}
514e10d12aSDavid Shaohua Li 	return -ENODEV;
524e10d12aSDavid Shaohua Li }
534e10d12aSDavid Shaohua Li 
544e10d12aSDavid Shaohua Li static struct acpi_bus_type *acpi_get_bus_type(struct bus_type *type)
554e10d12aSDavid Shaohua Li {
564e10d12aSDavid Shaohua Li 	struct acpi_bus_type *tmp, *ret = NULL;
574e10d12aSDavid Shaohua Li 
584e10d12aSDavid Shaohua Li 	down_read(&bus_type_sem);
594e10d12aSDavid Shaohua Li 	list_for_each_entry(tmp, &bus_type_list, list) {
604e10d12aSDavid Shaohua Li 		if (tmp->bus == type) {
614e10d12aSDavid Shaohua Li 			ret = tmp;
624e10d12aSDavid Shaohua Li 			break;
634e10d12aSDavid Shaohua Li 		}
644e10d12aSDavid Shaohua Li 	}
654e10d12aSDavid Shaohua Li 	up_read(&bus_type_sem);
664e10d12aSDavid Shaohua Li 	return ret;
674e10d12aSDavid Shaohua Li }
684e10d12aSDavid Shaohua Li 
694e10d12aSDavid Shaohua Li static int acpi_find_bridge_device(struct device *dev, acpi_handle * handle)
704e10d12aSDavid Shaohua Li {
714e10d12aSDavid Shaohua Li 	struct acpi_bus_type *tmp;
724e10d12aSDavid Shaohua Li 	int ret = -ENODEV;
734e10d12aSDavid Shaohua Li 
744e10d12aSDavid Shaohua Li 	down_read(&bus_type_sem);
754e10d12aSDavid Shaohua Li 	list_for_each_entry(tmp, &bus_type_list, list) {
764e10d12aSDavid Shaohua Li 		if (tmp->find_bridge && !tmp->find_bridge(dev, handle)) {
774e10d12aSDavid Shaohua Li 			ret = 0;
784e10d12aSDavid Shaohua Li 			break;
794e10d12aSDavid Shaohua Li 		}
804e10d12aSDavid Shaohua Li 	}
814e10d12aSDavid Shaohua Li 	up_read(&bus_type_sem);
824e10d12aSDavid Shaohua Li 	return ret;
834e10d12aSDavid Shaohua Li }
844e10d12aSDavid Shaohua Li 
854e10d12aSDavid Shaohua Li /* Get device's handler per its address under its parent */
864e10d12aSDavid Shaohua Li struct acpi_find_child {
874e10d12aSDavid Shaohua Li 	acpi_handle handle;
884e10d12aSDavid Shaohua Li 	acpi_integer address;
894e10d12aSDavid Shaohua Li };
904e10d12aSDavid Shaohua Li 
914e10d12aSDavid Shaohua Li static acpi_status
924e10d12aSDavid Shaohua Li do_acpi_find_child(acpi_handle handle, u32 lvl, void *context, void **rv)
934e10d12aSDavid Shaohua Li {
944e10d12aSDavid Shaohua Li 	acpi_status status;
954e10d12aSDavid Shaohua Li 	struct acpi_device_info *info;
964e10d12aSDavid Shaohua Li 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
9750dd0969SJan Engelhardt 	struct acpi_find_child *find = context;
984e10d12aSDavid Shaohua Li 
994e10d12aSDavid Shaohua Li 	status = acpi_get_object_info(handle, &buffer);
1004e10d12aSDavid Shaohua Li 	if (ACPI_SUCCESS(status)) {
1014e10d12aSDavid Shaohua Li 		info = buffer.pointer;
1024e10d12aSDavid Shaohua Li 		if (info->address == find->address)
1034e10d12aSDavid Shaohua Li 			find->handle = handle;
10402438d87SLen Brown 		kfree(buffer.pointer);
1054e10d12aSDavid Shaohua Li 	}
1064e10d12aSDavid Shaohua Li 	return AE_OK;
1074e10d12aSDavid Shaohua Li }
1084e10d12aSDavid Shaohua Li 
1094e10d12aSDavid Shaohua Li acpi_handle acpi_get_child(acpi_handle parent, acpi_integer address)
1104e10d12aSDavid Shaohua Li {
1114e10d12aSDavid Shaohua Li 	struct acpi_find_child find = { NULL, address };
1124e10d12aSDavid Shaohua Li 
1134e10d12aSDavid Shaohua Li 	if (!parent)
1144e10d12aSDavid Shaohua Li 		return NULL;
1154e10d12aSDavid Shaohua Li 	acpi_walk_namespace(ACPI_TYPE_DEVICE, parent,
1164e10d12aSDavid Shaohua Li 			    1, do_acpi_find_child, &find, NULL);
1174e10d12aSDavid Shaohua Li 	return find.handle;
1184e10d12aSDavid Shaohua Li }
1194e10d12aSDavid Shaohua Li 
1204e10d12aSDavid Shaohua Li EXPORT_SYMBOL(acpi_get_child);
1214e10d12aSDavid Shaohua Li 
1224e10d12aSDavid Shaohua Li /* Link ACPI devices with physical devices */
1234e10d12aSDavid Shaohua Li static void acpi_glue_data_handler(acpi_handle handle,
1244e10d12aSDavid Shaohua Li 				   u32 function, void *context)
1254e10d12aSDavid Shaohua Li {
1264e10d12aSDavid Shaohua Li 	/* we provide an empty handler */
1274e10d12aSDavid Shaohua Li }
1284e10d12aSDavid Shaohua Li 
1294e10d12aSDavid Shaohua Li /* Note: a success call will increase reference count by one */
1304e10d12aSDavid Shaohua Li struct device *acpi_get_physical_device(acpi_handle handle)
1314e10d12aSDavid Shaohua Li {
1324e10d12aSDavid Shaohua Li 	acpi_status status;
1334e10d12aSDavid Shaohua Li 	struct device *dev;
1344e10d12aSDavid Shaohua Li 
1354e10d12aSDavid Shaohua Li 	status = acpi_get_data(handle, acpi_glue_data_handler, (void **)&dev);
1364e10d12aSDavid Shaohua Li 	if (ACPI_SUCCESS(status))
1374e10d12aSDavid Shaohua Li 		return get_device(dev);
1384e10d12aSDavid Shaohua Li 	return NULL;
1394e10d12aSDavid Shaohua Li }
1404e10d12aSDavid Shaohua Li 
1414e10d12aSDavid Shaohua Li EXPORT_SYMBOL(acpi_get_physical_device);
1424e10d12aSDavid Shaohua Li 
1434e10d12aSDavid Shaohua Li static int acpi_bind_one(struct device *dev, acpi_handle handle)
1444e10d12aSDavid Shaohua Li {
1451071695fSDavid Brownell 	struct acpi_device *acpi_dev;
1464e10d12aSDavid Shaohua Li 	acpi_status status;
1474e10d12aSDavid Shaohua Li 
148465ae641SBenjamin Herrenschmidt 	if (dev->archdata.acpi_handle) {
1494e10d12aSDavid Shaohua Li 		printk(KERN_WARNING PREFIX
150465ae641SBenjamin Herrenschmidt 		       "Drivers changed 'acpi_handle' for %s\n", dev->bus_id);
1514e10d12aSDavid Shaohua Li 		return -EINVAL;
1524e10d12aSDavid Shaohua Li 	}
1534e10d12aSDavid Shaohua Li 	get_device(dev);
1544e10d12aSDavid Shaohua Li 	status = acpi_attach_data(handle, acpi_glue_data_handler, dev);
1554e10d12aSDavid Shaohua Li 	if (ACPI_FAILURE(status)) {
1564e10d12aSDavid Shaohua Li 		put_device(dev);
1574e10d12aSDavid Shaohua Li 		return -EINVAL;
1584e10d12aSDavid Shaohua Li 	}
159465ae641SBenjamin Herrenschmidt 	dev->archdata.acpi_handle = handle;
1604e10d12aSDavid Shaohua Li 
1611071695fSDavid Brownell 	status = acpi_bus_get_device(handle, &acpi_dev);
1621071695fSDavid Brownell 	if (!ACPI_FAILURE(status)) {
1631071695fSDavid Brownell 		int ret;
1641071695fSDavid Brownell 
1651071695fSDavid Brownell 		ret = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj,
1661071695fSDavid Brownell 				"firmware_node");
1671071695fSDavid Brownell 		ret = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
1681071695fSDavid Brownell 				"physical_node");
169*eb9d0fe4SRafael J. Wysocki 		if (acpi_dev->wakeup.flags.valid)
170*eb9d0fe4SRafael J. Wysocki 			device_set_wakeup_capable(dev, true);
1711071695fSDavid Brownell 	}
1721071695fSDavid Brownell 
1734e10d12aSDavid Shaohua Li 	return 0;
1744e10d12aSDavid Shaohua Li }
1754e10d12aSDavid Shaohua Li 
1764e10d12aSDavid Shaohua Li static int acpi_unbind_one(struct device *dev)
1774e10d12aSDavid Shaohua Li {
178465ae641SBenjamin Herrenschmidt 	if (!dev->archdata.acpi_handle)
1794e10d12aSDavid Shaohua Li 		return 0;
180465ae641SBenjamin Herrenschmidt 	if (dev == acpi_get_physical_device(dev->archdata.acpi_handle)) {
1811071695fSDavid Brownell 		struct acpi_device *acpi_dev;
1821071695fSDavid Brownell 
1834e10d12aSDavid Shaohua Li 		/* acpi_get_physical_device increase refcnt by one */
1844e10d12aSDavid Shaohua Li 		put_device(dev);
1851071695fSDavid Brownell 
1861071695fSDavid Brownell 		if (!acpi_bus_get_device(dev->archdata.acpi_handle,
1871071695fSDavid Brownell 					&acpi_dev)) {
1881071695fSDavid Brownell 			sysfs_remove_link(&dev->kobj, "firmware_node");
1891071695fSDavid Brownell 			sysfs_remove_link(&acpi_dev->dev.kobj, "physical_node");
1901071695fSDavid Brownell 		}
1911071695fSDavid Brownell 
192465ae641SBenjamin Herrenschmidt 		acpi_detach_data(dev->archdata.acpi_handle,
193465ae641SBenjamin Herrenschmidt 				 acpi_glue_data_handler);
194465ae641SBenjamin Herrenschmidt 		dev->archdata.acpi_handle = NULL;
1954e10d12aSDavid Shaohua Li 		/* acpi_bind_one increase refcnt by one */
1964e10d12aSDavid Shaohua Li 		put_device(dev);
1974e10d12aSDavid Shaohua Li 	} else {
1984e10d12aSDavid Shaohua Li 		printk(KERN_ERR PREFIX
199465ae641SBenjamin Herrenschmidt 		       "Oops, 'acpi_handle' corrupt for %s\n", dev->bus_id);
2004e10d12aSDavid Shaohua Li 	}
2014e10d12aSDavid Shaohua Li 	return 0;
2024e10d12aSDavid Shaohua Li }
2034e10d12aSDavid Shaohua Li 
2044e10d12aSDavid Shaohua Li static int acpi_platform_notify(struct device *dev)
2054e10d12aSDavid Shaohua Li {
2064e10d12aSDavid Shaohua Li 	struct acpi_bus_type *type;
2074e10d12aSDavid Shaohua Li 	acpi_handle handle;
2084e10d12aSDavid Shaohua Li 	int ret = -EINVAL;
2094e10d12aSDavid Shaohua Li 
2104e10d12aSDavid Shaohua Li 	if (!dev->bus || !dev->parent) {
2114e10d12aSDavid Shaohua Li 		/* bridge devices genernally haven't bus or parent */
2124e10d12aSDavid Shaohua Li 		ret = acpi_find_bridge_device(dev, &handle);
2134e10d12aSDavid Shaohua Li 		goto end;
2144e10d12aSDavid Shaohua Li 	}
2154e10d12aSDavid Shaohua Li 	type = acpi_get_bus_type(dev->bus);
2164e10d12aSDavid Shaohua Li 	if (!type) {
217ef7b06cdSDavid Shaohua Li 		DBG("No ACPI bus support for %s\n", dev->bus_id);
2184e10d12aSDavid Shaohua Li 		ret = -EINVAL;
2194e10d12aSDavid Shaohua Li 		goto end;
2204e10d12aSDavid Shaohua Li 	}
2214e10d12aSDavid Shaohua Li 	if ((ret = type->find_device(dev, &handle)) != 0)
222ef7b06cdSDavid Shaohua Li 		DBG("Can't get handler for %s\n", dev->bus_id);
2234e10d12aSDavid Shaohua Li       end:
2244e10d12aSDavid Shaohua Li 	if (!ret)
2254e10d12aSDavid Shaohua Li 		acpi_bind_one(dev, handle);
2264e10d12aSDavid Shaohua Li 
2274e10d12aSDavid Shaohua Li #if ACPI_GLUE_DEBUG
2284e10d12aSDavid Shaohua Li 	if (!ret) {
2294e10d12aSDavid Shaohua Li 		struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
2304e10d12aSDavid Shaohua Li 
231465ae641SBenjamin Herrenschmidt 		acpi_get_name(dev->archdata.acpi_handle,
232465ae641SBenjamin Herrenschmidt 			      ACPI_FULL_PATHNAME, &buffer);
2334e10d12aSDavid Shaohua Li 		DBG("Device %s -> %s\n", dev->bus_id, (char *)buffer.pointer);
23402438d87SLen Brown 		kfree(buffer.pointer);
2354e10d12aSDavid Shaohua Li 	} else
2364e10d12aSDavid Shaohua Li 		DBG("Device %s -> No ACPI support\n", dev->bus_id);
2374e10d12aSDavid Shaohua Li #endif
2384e10d12aSDavid Shaohua Li 
2394e10d12aSDavid Shaohua Li 	return ret;
2404e10d12aSDavid Shaohua Li }
2414e10d12aSDavid Shaohua Li 
2424e10d12aSDavid Shaohua Li static int acpi_platform_notify_remove(struct device *dev)
2434e10d12aSDavid Shaohua Li {
2444e10d12aSDavid Shaohua Li 	acpi_unbind_one(dev);
2454e10d12aSDavid Shaohua Li 	return 0;
2464e10d12aSDavid Shaohua Li }
2474e10d12aSDavid Shaohua Li 
2484e10d12aSDavid Shaohua Li static int __init init_acpi_device_notify(void)
2494e10d12aSDavid Shaohua Li {
2504e10d12aSDavid Shaohua Li 	if (acpi_disabled)
2514e10d12aSDavid Shaohua Li 		return 0;
2524e10d12aSDavid Shaohua Li 	if (platform_notify || platform_notify_remove) {
2534e10d12aSDavid Shaohua Li 		printk(KERN_ERR PREFIX "Can't use platform_notify\n");
2544e10d12aSDavid Shaohua Li 		return 0;
2554e10d12aSDavid Shaohua Li 	}
2564e10d12aSDavid Shaohua Li 	platform_notify = acpi_platform_notify;
2574e10d12aSDavid Shaohua Li 	platform_notify_remove = acpi_platform_notify_remove;
2584e10d12aSDavid Shaohua Li 	return 0;
2594e10d12aSDavid Shaohua Li }
2604e10d12aSDavid Shaohua Li 
2614e10d12aSDavid Shaohua Li arch_initcall(init_acpi_device_notify);
262a74388e2SDavid Brownell 
263a74388e2SDavid Brownell 
264a74388e2SDavid Brownell #if defined(CONFIG_RTC_DRV_CMOS) || defined(CONFIG_RTC_DRV_CMOS_MODULE)
265a74388e2SDavid Brownell 
266f5f72b46SDavid Brownell #ifdef CONFIG_PM
267f5f72b46SDavid Brownell static u32 rtc_handler(void *context)
268f5f72b46SDavid Brownell {
269f5f72b46SDavid Brownell 	acpi_clear_event(ACPI_EVENT_RTC);
270f5f72b46SDavid Brownell 	acpi_disable_event(ACPI_EVENT_RTC, 0);
271f5f72b46SDavid Brownell 	return ACPI_INTERRUPT_HANDLED;
272f5f72b46SDavid Brownell }
273f5f72b46SDavid Brownell 
274f5f72b46SDavid Brownell static inline void rtc_wake_setup(void)
275f5f72b46SDavid Brownell {
276f5f72b46SDavid Brownell 	acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL);
277f5f72b46SDavid Brownell }
278f5f72b46SDavid Brownell 
279f5f72b46SDavid Brownell static void rtc_wake_on(struct device *dev)
280f5f72b46SDavid Brownell {
281f5f72b46SDavid Brownell 	acpi_clear_event(ACPI_EVENT_RTC);
282f5f72b46SDavid Brownell 	acpi_enable_event(ACPI_EVENT_RTC, 0);
283f5f72b46SDavid Brownell }
284f5f72b46SDavid Brownell 
285f5f72b46SDavid Brownell static void rtc_wake_off(struct device *dev)
286f5f72b46SDavid Brownell {
287f5f72b46SDavid Brownell 	acpi_disable_event(ACPI_EVENT_RTC, 0);
288f5f72b46SDavid Brownell }
289f5f72b46SDavid Brownell #else
290f5f72b46SDavid Brownell #define rtc_wake_setup()	do{}while(0)
291f5f72b46SDavid Brownell #define rtc_wake_on		NULL
292f5f72b46SDavid Brownell #define rtc_wake_off		NULL
293f5f72b46SDavid Brownell #endif
294f5f72b46SDavid Brownell 
295a74388e2SDavid Brownell /* Every ACPI platform has a mc146818 compatible "cmos rtc".  Here we find
296a74388e2SDavid Brownell  * its device node and pass extra config data.  This helps its driver use
297a74388e2SDavid Brownell  * capabilities that the now-obsolete mc146818 didn't have, and informs it
298a74388e2SDavid Brownell  * that this board's RTC is wakeup-capable (per ACPI spec).
299a74388e2SDavid Brownell  */
300a74388e2SDavid Brownell #include <linux/mc146818rtc.h>
301a74388e2SDavid Brownell 
302a74388e2SDavid Brownell static struct cmos_rtc_board_info rtc_info;
303a74388e2SDavid Brownell 
304a74388e2SDavid Brownell 
305a74388e2SDavid Brownell /* PNP devices are registered in a subsys_initcall();
306a74388e2SDavid Brownell  * ACPI specifies the PNP IDs to use.
307a74388e2SDavid Brownell  */
308a74388e2SDavid Brownell #include <linux/pnp.h>
309a74388e2SDavid Brownell 
310a74388e2SDavid Brownell static int __init pnp_match(struct device *dev, void *data)
311a74388e2SDavid Brownell {
312a74388e2SDavid Brownell 	static const char *ids[] = { "PNP0b00", "PNP0b01", "PNP0b02", };
313a74388e2SDavid Brownell 	struct pnp_dev *pnp = to_pnp_dev(dev);
314a74388e2SDavid Brownell 	int i;
315a74388e2SDavid Brownell 
316a74388e2SDavid Brownell 	for (i = 0; i < ARRAY_SIZE(ids); i++) {
317a74388e2SDavid Brownell 		if (compare_pnp_id(pnp->id, ids[i]) != 0)
318a74388e2SDavid Brownell 			return 1;
319a74388e2SDavid Brownell 	}
320a74388e2SDavid Brownell 	return 0;
321a74388e2SDavid Brownell }
322a74388e2SDavid Brownell 
323a74388e2SDavid Brownell static struct device *__init get_rtc_dev(void)
324a74388e2SDavid Brownell {
325a74388e2SDavid Brownell 	return bus_find_device(&pnp_bus_type, NULL, NULL, pnp_match);
326a74388e2SDavid Brownell }
327a74388e2SDavid Brownell 
328a74388e2SDavid Brownell static int __init acpi_rtc_init(void)
329a74388e2SDavid Brownell {
330a74388e2SDavid Brownell 	struct device *dev = get_rtc_dev();
331a74388e2SDavid Brownell 
332a74388e2SDavid Brownell 	if (dev) {
333f5f72b46SDavid Brownell 		rtc_wake_setup();
334f5f72b46SDavid Brownell 		rtc_info.wake_on = rtc_wake_on;
335f5f72b46SDavid Brownell 		rtc_info.wake_off = rtc_wake_off;
336f5f72b46SDavid Brownell 
33719bfe37cSDavid Brownell 		/* workaround bug in some ACPI tables */
33819bfe37cSDavid Brownell 		if (acpi_gbl_FADT.month_alarm && !acpi_gbl_FADT.day_alarm) {
33919bfe37cSDavid Brownell 			DBG("bogus FADT month_alarm\n");
34019bfe37cSDavid Brownell 			acpi_gbl_FADT.month_alarm = 0;
34119bfe37cSDavid Brownell 		}
34219bfe37cSDavid Brownell 
343a74388e2SDavid Brownell 		rtc_info.rtc_day_alarm = acpi_gbl_FADT.day_alarm;
344a74388e2SDavid Brownell 		rtc_info.rtc_mon_alarm = acpi_gbl_FADT.month_alarm;
345a74388e2SDavid Brownell 		rtc_info.rtc_century = acpi_gbl_FADT.century;
346a74388e2SDavid Brownell 
347f5f72b46SDavid Brownell 		/* NOTE:  S4_RTC_WAKE is NOT currently useful to Linux */
348f5f72b46SDavid Brownell 		if (acpi_gbl_FADT.flags & ACPI_FADT_S4_RTC_WAKE)
34919bfe37cSDavid Brownell 			printk(PREFIX "RTC can wake from S4\n");
35019bfe37cSDavid Brownell 
351a74388e2SDavid Brownell 
352a74388e2SDavid Brownell 		dev->platform_data = &rtc_info;
353a74388e2SDavid Brownell 
354a74388e2SDavid Brownell 		/* RTC always wakes from S1/S2/S3, and often S4/STD */
355a74388e2SDavid Brownell 		device_init_wakeup(dev, 1);
356a74388e2SDavid Brownell 
357a74388e2SDavid Brownell 		put_device(dev);
358a74388e2SDavid Brownell 	} else
35919bfe37cSDavid Brownell 		DBG("RTC unavailable?\n");
360a74388e2SDavid Brownell 	return 0;
361a74388e2SDavid Brownell }
362a74388e2SDavid Brownell /* do this between RTC subsys_initcall() and rtc_cmos driver_initcall() */
363a74388e2SDavid Brownell fs_initcall(acpi_rtc_init);
364a74388e2SDavid Brownell 
365a74388e2SDavid Brownell #endif
366