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 [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html