[RFC] Early ACPI init

From: Alex Williamson <alex.williamson_at_hp.com>
Date: 2005-01-26 09:14:07
   We have a bit of an ordering problem today with ACPI initialization.
Platforms are being described entirely in ACPI namespace, but ACPI isn't
initialized until fairly late in boot.  For example, on hp ia64 systems
the only programmatic way to identify and locate the chipset components
is in ACPI namespace.  However, we'd really like to know for certain if
a hardware iommu is present in the system before the memory zones are
initialized.  The (non-)solution today is to guess based on a few
tidbits of data.

   Long, long ago in the 2.4 ia64 patches we used to have early ACPI
initialization to get the PCI routing table info.  The last trace of it
seemed to show up in the 2.4.17 ia64 patches.  Initializing ACPI twice
during a bootup certainly wasn't pretty, but the need for the
functionality still exists.

   Here's a pass at forward porting some of that old early ACPI
initialization to current 2.6 trees.  It's really not as bad as I
thought it might be.  I'd appreciate feedback from the ACPI gurus on
whether the boot time functions I'm providing are necessary and
sufficient.  I know that the stall routine is particularly ugly and the
non-ia64 version is pathetic.  That's hopefully just a place holder for
a better implementation.  I'd prefer not to use architecture specific
code there, but I don't know of any utility that provides a delay this
early in boot.  Would this functionality be useful for anyone else?  I'd
appreciate feedback.  BTW, a good portion of the changes in osl.c are
simply from moving functions around for better ordering.  Thanks,

	Alex

-- 
Alex Williamson                             HP Linux & Open Source Lab

 arch/ia64/Kconfig                        |    5 
 arch/ia64/hp/common/sba_iommu.c          |   49 +++
 drivers/acpi/events/evxfevnt.c           |    2 
 drivers/acpi/osl.c                       |  454 ++++++++++++++++++++++++-------
 include/acpi/acpiosxf.h                  |   11 
 include/asm-ia64/machvec_hpzx1_swiotlb.h |    4 
 6 files changed, 422 insertions(+), 103 deletions(-)

===== arch/ia64/Kconfig 1.84 vs edited =====
--- 1.84/arch/ia64/Kconfig	2005-01-18 13:06:17 -07:00
+++ edited/arch/ia64/Kconfig	2005-01-25 10:48:41 -07:00
@@ -360,6 +360,11 @@
 	depends on !IA64_HP_SIM
 	default y
 
+config ACPI_EARLY_BOOT
+	bool
+	depends on IA64_GENERIC
+	default y
+
 if !IA64_HP_SIM
 
 source "drivers/acpi/Kconfig"
===== arch/ia64/hp/common/sba_iommu.c 1.50 vs edited =====
--- 1.50/arch/ia64/hp/common/sba_iommu.c	2005-01-11 17:10:37 -07:00
+++ edited/arch/ia64/hp/common/sba_iommu.c	2005-01-25 11:19:44 -07:00
@@ -2052,6 +2052,21 @@
 
 subsys_initcall(sba_init); /* must be initialized after ACPI etc., but before any drivers... */
 
+#if defined(CONFIG_ACPI_EARLY_BOOT) && defined(CONFIG_IA64_GENERIC)
+acpi_status __init
+find_sba_callback(
+	acpi_handle	handle,
+	u32		level,
+	void		*context,
+	void		**return_value)
+{
+	/* Indicate SBA Found */
+	*(int *)context = 1;
+
+	return AE_CTRL_TERMINATE;
+}
+#endif
+
 extern void dig_setup(char**);
 /*
  * MAX_DMA_ADDRESS needs to be setup prior to paging_init to do any good,
@@ -2060,7 +2075,39 @@
 void __init
 sba_setup(char **cmdline_p)
 {
-	MAX_DMA_ADDRESS = ~0UL;
+#if defined(CONFIG_ACPI_EARLY_BOOT) && defined(CONFIG_IA64_GENERIC)
+	acpi_status	status;
+	int		found = 0;
+
+	status = acpi_bt_init();
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_WARNING "%s(): acpi_bt_init() failed: %d\n",
+		       __FUNCTION__, status);
+		found = 1; /* fudge, old behavior */
+		goto normal_setup;
+	}
+
+	acpi_get_devices("HWP0001", find_sba_callback, &found, NULL);
+	if (!found)
+		acpi_get_devices("HWP0004", find_sba_callback, &found, NULL);
+
+	status = acpi_bt_terminate();
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_WARNING "%s(): acpi_bt_terminate() failed: %d\n",
+		       __FUNCTION__, status);
+	}
+
+normal_setup:
+	if (!found) {
+		printk(KERN_INFO PFX "No SBA IOMMU in namespace, "
+		                     "using DIG machvec\n");
+		machvec_init("dig");
+	}
+#endif /* CONFIG_ACPI_EARLY_BOOT && CONFIG_IA64_GENERIC */
+
+	if (ia64_platform_is("hpzx1"))
+		MAX_DMA_ADDRESS = ~0UL;
+
 	dig_setup(cmdline_p);
 }
 
===== drivers/acpi/osl.c 1.64 vs edited =====
--- 1.64/drivers/acpi/osl.c	2004-12-23 06:09:11 -07:00
+++ edited/drivers/acpi/osl.c	2005-01-25 10:41:58 -07:00
@@ -25,6 +25,7 @@
  *
  */
 
+#include <linux/bootmem.h>
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -101,19 +102,6 @@
 	return AE_OK;
 }
 
-acpi_status
-acpi_os_terminate(void)
-{
-	if (acpi_irq_handler) {
-		acpi_os_remove_interrupt_handler(acpi_irq_irq,
-						 acpi_irq_handler);
-	}
-
-	destroy_workqueue(kacpid_wq);
-
-	return AE_OK;
-}
-
 void
 acpi_os_printf(const char *fmt,...)
 {
@@ -142,19 +130,371 @@
 #endif
 }
 
+/*
+ * Standard runtime OS interfaces
+ */
+static void *
+acpi_os_allocate_rt(acpi_size size)
+{
+	return kmalloc(size, GFP_KERNEL);
+}
+
+static void
+acpi_os_free_rt(void *ptr)
+{
+	kfree(ptr);
+}
+
+static void
+acpi_os_stall_rt(u32 us)
+{
+	while (us) {
+		u32 delay = 1000;
+
+		if (delay > us)
+			delay = us;
+		udelay(delay);
+		touch_nmi_watchdog();
+		us -= delay;
+	}
+}
+
+static void
+acpi_os_execute_deferred (
+	void *context)
+{
+	struct acpi_os_dpc	*dpc = NULL;
+
+	ACPI_FUNCTION_TRACE ("os_execute_deferred");
+
+	dpc = (struct acpi_os_dpc *) context;
+	if (!dpc) {
+		ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid (NULL) context.\n"));
+		return_VOID;
+	}
+
+	dpc->function(dpc->context);
+
+	kfree(dpc);
+
+	return_VOID;
+}
+
+static acpi_status
+acpi_os_queue_for_execution_rt(
+	u32			priority,
+	acpi_osd_exec_callback	function,
+	void			*context)
+{
+	acpi_status 		status = AE_OK;
+	struct acpi_os_dpc	*dpc;
+	struct work_struct	*task;
+
+	ACPI_FUNCTION_TRACE ("os_queue_for_execution");
+
+	ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Scheduling function [%p(%p)] for deferred execution.\n", function, context));
+
+	if (!function)
+		return_ACPI_STATUS (AE_BAD_PARAMETER);
+
+	/*
+	 * Allocate/initialize DPC structure.  Note that this memory will be
+	 * freed by the callee.  The kernel handles the tq_struct list  in a
+	 * way that allows us to also free its memory inside the callee.
+	 * Because we may want to schedule several tasks with different
+	 * parameters we can't use the approach some kernel code uses of
+	 * having a static tq_struct.
+	 * We can save time and code by allocating the DPC and tq_structs
+	 * from the same memory.
+	 */
+
+	dpc = kmalloc(sizeof(struct acpi_os_dpc)+sizeof(struct work_struct), GFP_ATOMIC);
+	if (!dpc)
+		return_ACPI_STATUS (AE_NO_MEMORY);
+
+	dpc->function = function;
+	dpc->context = context;
+
+	task = (void *)(dpc+1);
+	INIT_WORK(task, acpi_os_execute_deferred, (void*)dpc);
+
+	if (!queue_work(kacpid_wq, task)) {
+		ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Call to queue_work() failed.\n"));
+		kfree(dpc);
+		status = AE_ERROR;
+	}
+
+	return_ACPI_STATUS (status);
+}
+
+static acpi_status
+acpi_os_terminate_rt(void)
+{
+	if (acpi_irq_handler) {
+		acpi_os_remove_interrupt_handler(acpi_irq_irq,
+						 acpi_irq_handler);
+	}
+
+	destroy_workqueue(kacpid_wq);
+
+	return AE_OK;
+}
+
+struct acpi_osd {
+	void		*(*allocate)(acpi_size size);
+	void		(*free)(void *ptr);
+	acpi_status	(*queue_for_execution)(u32 priority,
+			                       acpi_osd_exec_callback function,
+			                       void *context);
+	void		(*stall)(u32 us);
+	acpi_status	(*terminate)(void);
+};
+
+static struct acpi_osd acpi_osd_rt = {
+	acpi_os_allocate_rt,
+	acpi_os_free_rt,
+	acpi_os_queue_for_execution_rt,
+	acpi_os_stall_rt,
+	acpi_os_terminate_rt
+};
+
+static struct acpi_osd *acpi_osd = &acpi_osd_rt;
+
 void *
 acpi_os_allocate(acpi_size size)
 {
-	return kmalloc(size, GFP_KERNEL);
+	return acpi_osd->allocate(size);
 }
 
 void
 acpi_os_free(void *ptr)
 {
-	kfree(ptr);
+	acpi_osd->free(ptr);
 }
 EXPORT_SYMBOL(acpi_os_free);
 
+void
+acpi_os_stall(u32 us)
+{
+	acpi_osd->stall(us);
+}
+EXPORT_SYMBOL(acpi_os_stall);
+
+acpi_status
+acpi_os_queue_for_execution(
+	u32			priority,
+	acpi_osd_exec_callback	function,
+	void			*context)
+{
+	return acpi_osd->queue_for_execution(priority, function, context);
+}
+EXPORT_SYMBOL(acpi_os_queue_for_execution);
+
+acpi_status
+acpi_os_terminate(void)
+{
+	return acpi_osd->terminate();
+}
+
+#ifdef CONFIG_ACPI_EARLY_BOOT
+/*
+ * Boot time OS interfaces
+ */
+static void * __init
+acpi_os_allocate_bt(acpi_size size)
+{
+	void	*ptr;
+
+	size += sizeof(unsigned long);
+	ptr = alloc_bootmem(size);
+
+	if (ptr) {
+		*((unsigned long *)ptr) = (unsigned long)size;
+		ptr += sizeof(unsigned long);
+	}
+
+	return ptr;
+}
+
+static void __init
+acpi_os_free_bt(void *ptr)
+{
+	unsigned long	size;
+
+	ptr -= sizeof(size);
+	size = *((unsigned long *)ptr);
+
+	free_bootmem(__pa((unsigned long)ptr), (u32)size);
+}
+
+static void __init
+acpi_os_fallback_stall_bt(u32 us)
+{
+	volatile unsigned long i, j;
+
+	/* FIXME: This really sucks */
+	for (i = 0 ; i < (us * 100000) ; i++)
+		j = i;
+}
+
+#ifdef CONFIG_IA64
+#include <asm/sal.h>
+#include <asm/pal.h>
+
+static void __init
+acpi_os_stall_bt(u32 us)
+{
+	unsigned long cycles, start = ia64_get_itc();
+	static unsigned long itc_freq = 0;
+
+	if (!itc_freq) {
+		unsigned long platform_base_freq;
+		struct pal_freq_ratio itc_ratio, proc_ratio;
+		long status, platform_base_drift;
+
+		status = ia64_sal_freq_base(SAL_FREQ_BASE_PLATFORM,
+		                            &platform_base_freq,
+					    &platform_base_drift);
+		if (status != 0)
+			return acpi_os_fallback_stall_bt(us);
+
+		status = ia64_pal_freq_ratios(&proc_ratio, NULL, &itc_ratio);
+		if (status != 0)
+			return acpi_os_fallback_stall_bt(us);
+
+		if (!itc_ratio.den)
+			itc_ratio.den = 1; /* avoid division by zero */
+
+		itc_freq = (platform_base_freq*itc_ratio.num)/itc_ratio.den;
+
+		itc_freq /= 1000; /* freq in us */
+	}
+
+	cycles = us * itc_freq;
+
+	while (ia64_get_itc() - start < cycles)
+		cpu_relax();
+}
+#else
+static void __init
+acpi_os_stall_bt(u32 us)
+{
+	acpi_os_fallback_stall_bt(us);
+}
+#endif
+
+static acpi_status __init
+acpi_os_queue_for_execution_bt(
+	u32			priority,
+	acpi_osd_exec_callback	function,
+	void			*context)
+{
+	/* run callback immediately */
+	(*function)(context);
+	return AE_OK;
+}
+
+static acpi_status __init
+acpi_os_terminate_bt(void)
+{
+	return AE_OK;
+}
+
+static struct acpi_osd acpi_osd_bt __initdata = {
+	acpi_os_allocate_bt,
+	acpi_os_free_bt,
+	acpi_os_queue_for_execution_bt,
+	acpi_os_stall_bt,
+	acpi_os_terminate_bt
+};
+
+#define ACPI_BT_PHASE_BOOTTIME  0x00
+#define ACPI_BT_PHASE_RUNTIME   0x01
+
+static void __init
+acpi_os_bind_osd(int acpi_phase)
+{
+	switch (acpi_phase) {
+		case ACPI_BT_PHASE_BOOTTIME:
+			acpi_osd = &acpi_osd_bt;
+			break;
+		default:
+			acpi_osd = &acpi_osd_rt;
+			break;
+	}
+}
+
+static int	acpi_bt_initialized __initdata = 0;
+
+#define ACPI_BT_INITIALIZED()   (acpi_bt_initialized > 0)
+
+acpi_status __init
+acpi_bt_init(void)
+{
+	acpi_status	status;
+
+	if (ACPI_BT_INITIALIZED())
+		return AE_OK;
+
+	acpi_os_bind_osd(ACPI_BT_PHASE_BOOTTIME);
+
+	status = acpi_initialize_subsystem();
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_ERR PREFIX "Unable to initialize the boot time "
+		                       "ACPI Interpreter\n");
+		return status;
+	}
+
+	status = acpi_load_tables();
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_ERR PREFIX "Unable to load the System Description "
+		                       "Tables\n");
+		return status;
+	}
+
+	status = acpi_enable_subsystem(ACPI_NO_HANDLER_INIT);
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_ERR PREFIX "Unable to enable ACPI subsystem\n");
+		return status;
+	}
+
+	status = acpi_initialize_objects(ACPI_FULL_INITIALIZATION);
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_ERR PREFIX "Unable to initialize ACPI objects\n");
+		return status;
+	}
+
+	acpi_bt_initialized++;
+
+	return AE_OK;
+}
+
+acpi_status __init
+acpi_bt_terminate(void)
+{
+	acpi_status	status;
+
+	if (!ACPI_BT_INITIALIZED())
+		return AE_OK;
+
+	status = acpi_disable();
+	if (ACPI_FAILURE(status)) {
+		/* fall thru... */
+	}
+
+	status = acpi_terminate();
+	if (ACPI_FAILURE(status)) {
+		printk(KERN_ERR PREFIX "Unable to terminate boot time ACPI\n");
+		/* fall thru... */
+	}
+
+	acpi_os_bind_osd(ACPI_BT_PHASE_RUNTIME);
+	acpi_bt_initialized--;
+
+	return status;
+}
+#endif /* CONFIG_ACPI_EARLY_BOOT */
+
 acpi_status
 acpi_os_get_root_pointer(u32 flags, struct acpi_pointer *addr)
 {
@@ -322,21 +662,6 @@
 }
 EXPORT_SYMBOL(acpi_os_sleep);
 
-void
-acpi_os_stall(u32 us)
-{
-	while (us) {
-		u32 delay = 1000;
-
-		if (delay > us)
-			delay = us;
-		udelay(delay);
-		touch_nmi_watchdog();
-		us -= delay;
-	}
-}
-EXPORT_SYMBOL(acpi_os_stall);
-
 /*
  * Support ACPI 3.0 AML Timer operand
  * Returns 64-bit free-running, monotonically increasing timer
@@ -658,75 +983,6 @@
 }
 
 #endif /*CONFIG_ACPI_PCI*/
-
-static void
-acpi_os_execute_deferred (
-	void *context)
-{
-	struct acpi_os_dpc	*dpc = NULL;
-
-	ACPI_FUNCTION_TRACE ("os_execute_deferred");
-
-	dpc = (struct acpi_os_dpc *) context;
-	if (!dpc) {
-		ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid (NULL) context.\n"));
-		return_VOID;
-	}
-
-	dpc->function(dpc->context);
-
-	kfree(dpc);
-
-	return_VOID;
-}
-
-acpi_status
-acpi_os_queue_for_execution(
-	u32			priority,
-	acpi_osd_exec_callback	function,
-	void			*context)
-{
-	acpi_status 		status = AE_OK;
-	struct acpi_os_dpc	*dpc;
-	struct work_struct	*task;
-
-	ACPI_FUNCTION_TRACE ("os_queue_for_execution");
-
-	ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Scheduling function [%p(%p)] for deferred execution.\n", function, context));
-
-	if (!function)
-		return_ACPI_STATUS (AE_BAD_PARAMETER);
-
-	/*
-	 * Allocate/initialize DPC structure.  Note that this memory will be
-	 * freed by the callee.  The kernel handles the tq_struct list  in a
-	 * way that allows us to also free its memory inside the callee.
-	 * Because we may want to schedule several tasks with different
-	 * parameters we can't use the approach some kernel code uses of
-	 * having a static tq_struct.
-	 * We can save time and code by allocating the DPC and tq_structs
-	 * from the same memory.
-	 */
-
-	dpc = kmalloc(sizeof(struct acpi_os_dpc)+sizeof(struct work_struct), GFP_ATOMIC);
-	if (!dpc)
-		return_ACPI_STATUS (AE_NO_MEMORY);
-
-	dpc->function = function;
-	dpc->context = context;
-
-	task = (void *)(dpc+1);
-	INIT_WORK(task, acpi_os_execute_deferred, (void*)dpc);
-
-	if (!queue_work(kacpid_wq, task)) {
-		ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Call to queue_work() failed.\n"));
-		kfree(dpc);
-		status = AE_ERROR;
-	}
-
-	return_ACPI_STATUS (status);
-}
-EXPORT_SYMBOL(acpi_os_queue_for_execution);
 
 void
 acpi_os_wait_events_complete(
===== drivers/acpi/events/evxfevnt.c 1.25 vs edited =====
--- 1.25/drivers/acpi/events/evxfevnt.c	2004-12-05 22:09:54 -07:00
+++ edited/drivers/acpi/events/evxfevnt.c	2005-01-24 16:49:11 -07:00
@@ -127,7 +127,7 @@
 	if (acpi_hw_get_mode() == ACPI_SYS_MODE_LEGACY) {
 		ACPI_DEBUG_PRINT ((ACPI_DB_INIT, "System is already in legacy (non-ACPI) mode\n"));
 	}
-	else {
+	else if (acpi_gbl_FADT->smi_cmd) {
 		/* Transition to LEGACY mode */
 
 		status = acpi_hw_set_mode (ACPI_SYS_MODE_LEGACY);
===== include/acpi/acpiosxf.h 1.38 vs edited =====
--- 1.38/include/acpi/acpiosxf.h	2004-11-11 23:29:47 -07:00
+++ edited/include/acpi/acpiosxf.h	2005-01-25 08:57:03 -07:00
@@ -385,5 +385,16 @@
 	u32                             line_number,
 	char                            *message);
 
+#ifdef CONFIG_ACPI_EARLY_BOOT
+
+acpi_status
+acpi_bt_init (
+	void);
+
+acpi_status
+acpi_bt_terminate (
+	void);
+
+#endif /* CONFIG_ACPI_EARLY_BOOT */
 
 #endif /* __ACPIOSXF_H__ */
===== include/asm-ia64/machvec_hpzx1_swiotlb.h 1.1 vs edited =====
--- 1.1/include/asm-ia64/machvec_hpzx1_swiotlb.h	2005-01-12 10:10:35 -07:00
+++ edited/include/asm-ia64/machvec_hpzx1_swiotlb.h	2005-01-25 10:44:28 -07:00
@@ -1,7 +1,7 @@
 #ifndef _ASM_IA64_MACHVEC_HPZX1_SWIOTLB_h
 #define _ASM_IA64_MACHVEC_HPZX1_SWIOTLB_h
 
-extern ia64_mv_setup_t				dig_setup;
+extern ia64_mv_setup_t				sba_setup;
 extern ia64_mv_dma_init				hwsw_init;
 extern ia64_mv_dma_alloc_coherent		hwsw_alloc_coherent;
 extern ia64_mv_dma_free_coherent		hwsw_free_coherent;
@@ -25,7 +25,7 @@
  */
 #define platform_name				"hpzx1_swiotlb"
 
-#define platform_setup				dig_setup
+#define platform_setup				sba_setup
 #define platform_dma_init			hwsw_init
 #define platform_dma_alloc_coherent		hwsw_alloc_coherent
 #define platform_dma_free_coherent		hwsw_free_coherent


-
To unsubscribe from this list: send the line "unsubscribe linux-ia64" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Received on Tue Jan 25 17:34:36 2005

This archive was generated by hypermail 2.1.8 : 2005-08-02 09:20:35 EST