From 4a7164023959040e687e51663dee67cff4d2b770 Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Fri, 29 Jul 2005 15:51:36 -0400 Subject: [PATCH 01/18] [ACPI] Fix the regression with c1_default_handler on some systems where C-states come from FADT. Thanks to Kevin Radloff for identifying the issue and isolating it to exact line of code that is causing the issue. Signed-off-by: Venkatesh Pallipadi Signed-off-by: Len Brown --- drivers/acpi/processor_idle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 8f038cd29477..aea745f4a8b0 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -842,7 +842,7 @@ static int acpi_processor_get_power_info ( result = acpi_processor_get_power_info_cst(pr); if ((result) || (acpi_processor_power_verify(pr) < 2)) { result = acpi_processor_get_power_info_fadt(pr); - if (result) + if ((result) || (acpi_processor_power_verify(pr) < 2)) result = acpi_processor_get_power_info_default_c1(pr); } From 0b6b2f08c24a65535cb18893ca27516389c5fc0f Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Fri, 29 Jul 2005 16:00:13 -0400 Subject: [PATCH 02/18] [ACPI] Fix memset arguments in acpi processor_idle.c http://bugzilla.kernel.org/show_bug.cgi?id=4954 Signed-off-by: Venkatesh Pallipadi Signed-off-by: Len Brown --- drivers/acpi/processor_idle.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index aea745f4a8b0..42b34f5df98b 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -549,7 +549,8 @@ static int acpi_processor_get_power_info_default_c1 (struct acpi_processor *pr) ACPI_FUNCTION_TRACE("acpi_processor_get_power_info_default_c1"); for (i = 0; i < ACPI_PROCESSOR_MAX_POWER; i++) - memset(pr->power.states, 0, sizeof(struct acpi_processor_cx)); + memset(&(pr->power.states[i]), 0, + sizeof(struct acpi_processor_cx)); /* if info is obtained from pblk/fadt, type equals state */ pr->power.states[ACPI_STATE_C1].type = ACPI_STATE_C1; @@ -580,7 +581,8 @@ static int acpi_processor_get_power_info_cst (struct acpi_processor *pr) pr->power.count = 0; for (i = 0; i < ACPI_PROCESSOR_MAX_POWER; i++) - memset(pr->power.states, 0, sizeof(struct acpi_processor_cx)); + memset(&(pr->power.states[i]), 0, + sizeof(struct acpi_processor_cx)); status = acpi_evaluate_object(pr->handle, "_CST", NULL, &buffer); if (ACPI_FAILURE(status)) { From 335f16be5d917334f56ec9ef7ecf983476ac0563 Mon Sep 17 00:00:00 2001 From: David Shaohua Li Date: Wed, 22 Jun 2005 18:37:00 -0400 Subject: [PATCH 03/18] [ACPI] address boot-freeze with updated DMI blacklist for c-states http://bugzilla.kernel.org/show_bug.cgi?id=4763 Signed-off-by: David Shaohua Li Signed-off-by: Len Brown --- drivers/acpi/processor_idle.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 42b34f5df98b..3702725db97a 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -81,30 +81,33 @@ module_param(bm_history, uint, 0644); * * To skip this limit, boot/load with a large max_cstate limit. */ -static int no_c2c3(struct dmi_system_id *id) +static int set_max_cstate(struct dmi_system_id *id) { if (max_cstate > ACPI_PROCESSOR_MAX_POWER) return 0; - printk(KERN_NOTICE PREFIX "%s detected - C2,C3 disabled." + printk(KERN_NOTICE PREFIX "%s detected - %s disabled." " Override with \"processor.max_cstate=%d\"\n", id->ident, + ((int)id->driver_data == 1)? "C2,C3":"C3", ACPI_PROCESSOR_MAX_POWER + 1); - max_cstate = 1; + max_cstate = (int)id->driver_data; return 0; } - - static struct dmi_system_id __initdata processor_power_dmi_table[] = { - { no_c2c3, "IBM ThinkPad R40e", { + { set_max_cstate, "IBM ThinkPad R40e", { DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), - DMI_MATCH(DMI_BIOS_VERSION,"1SET60WW") }}, - { no_c2c3, "Medion 41700", { + DMI_MATCH(DMI_BIOS_VERSION,"1SET60WW") }, (void*)1}, + { set_max_cstate, "Medion 41700", { DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"), - DMI_MATCH(DMI_BIOS_VERSION,"R01-A1J") }}, + DMI_MATCH(DMI_BIOS_VERSION,"R01-A1J") }, (void*)1}, + { set_max_cstate, "Clevo 5600D", { + DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"), + DMI_MATCH(DMI_BIOS_VERSION,"SHE845M0.86C.0013.D.0302131307") }, + (void*)2}, {}, }; From 45bea1555f5bf0cd5871b208b4b02d188f106861 Mon Sep 17 00:00:00 2001 From: Luming Yu Date: Sat, 23 Jul 2005 04:08:00 -0400 Subject: [PATCH 04/18] [ACPI] Add "ec_polling" boot option EC burst mode benefits many machines, some of them significantly. However, our current implementation fails on some machines such as Rafael's Asus L5D. This patch restores the alternative EC polling code, which can be enabled at boot time via "ec_polling" http://bugzilla.kernel.org/show_bug.cgi?id=4665 Signed-off-by: Luming Yu Signed-off-by: Len Brown --- drivers/acpi/ec.c | 891 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 721 insertions(+), 170 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index fca4140a50a9..2dadb7f63269 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -59,76 +59,185 @@ ACPI_MODULE_NAME ("acpi_ec") #define ACPI_EC_DELAY 50 /* Wait 50ms max. during EC ops */ #define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ +#define ACPI_EC_UDELAY 100 /* Poll @ 100us increments */ +#define ACPI_EC_UDELAY_COUNT 1000 /* Wait 10ms max. during EC ops */ + #define ACPI_EC_COMMAND_READ 0x80 #define ACPI_EC_COMMAND_WRITE 0x81 #define ACPI_EC_BURST_ENABLE 0x82 #define ACPI_EC_BURST_DISABLE 0x83 #define ACPI_EC_COMMAND_QUERY 0x84 -static int acpi_ec_add (struct acpi_device *device); +#define EC_POLLING 0xFF +#define EC_BURST 0x00 + + static int acpi_ec_remove (struct acpi_device *device, int type); static int acpi_ec_start (struct acpi_device *device); static int acpi_ec_stop (struct acpi_device *device, int type); +static int acpi_ec_burst_add ( struct acpi_device *device); static struct acpi_driver acpi_ec_driver = { .name = ACPI_EC_DRIVER_NAME, .class = ACPI_EC_CLASS, .ids = ACPI_EC_HID, .ops = { - .add = acpi_ec_add, + .add = acpi_ec_burst_add, .remove = acpi_ec_remove, .start = acpi_ec_start, .stop = acpi_ec_stop, }, }; +union acpi_ec { + struct { + u32 mode; + acpi_handle handle; + unsigned long uid; + unsigned long gpe_bit; + struct acpi_generic_address status_addr; + struct acpi_generic_address command_addr; + struct acpi_generic_address data_addr; + unsigned long global_lock; + } common; -struct acpi_ec { - acpi_handle handle; - unsigned long uid; - unsigned long gpe_bit; - struct acpi_generic_address status_addr; - struct acpi_generic_address command_addr; - struct acpi_generic_address data_addr; - unsigned long global_lock; - unsigned int expect_event; - atomic_t leaving_burst; /* 0 : No, 1 : Yes, 2: abort*/ - atomic_t pending_gpe; - struct semaphore sem; - wait_queue_head_t wait; + struct { + u32 mode; + acpi_handle handle; + unsigned long uid; + unsigned long gpe_bit; + struct acpi_generic_address status_addr; + struct acpi_generic_address command_addr; + struct acpi_generic_address data_addr; + unsigned long global_lock; + unsigned int expect_event; + atomic_t leaving_burst; /* 0 : No, 1 : Yes, 2: abort*/ + atomic_t pending_gpe; + struct semaphore sem; + wait_queue_head_t wait; + }burst; + + struct { + u32 mode; + acpi_handle handle; + unsigned long uid; + unsigned long gpe_bit; + struct acpi_generic_address status_addr; + struct acpi_generic_address command_addr; + struct acpi_generic_address data_addr; + unsigned long global_lock; + spinlock_t lock; + }polling; }; +static int acpi_ec_polling_wait ( union acpi_ec *ec, u8 event); +static int acpi_ec_burst_wait(union acpi_ec *ec, unsigned int event); +static int acpi_ec_polling_read ( union acpi_ec *ec, u8 address, u32 *data); +static int acpi_ec_burst_read( union acpi_ec *ec, u8 address, u32 *data); +static int acpi_ec_polling_write ( union acpi_ec *ec, u8 address, u8 data); +static int acpi_ec_burst_write ( union acpi_ec *ec, u8 address, u8 data); +static int acpi_ec_polling_query ( union acpi_ec *ec, u32 *data); +static int acpi_ec_burst_query ( union acpi_ec *ec, u32 *data); +static void acpi_ec_gpe_polling_query ( void *ec_cxt); +static void acpi_ec_gpe_burst_query ( void *ec_cxt); +static u32 acpi_ec_gpe_polling_handler ( void *data); +static u32 acpi_ec_gpe_burst_handler ( void *data); +static acpi_status __init +acpi_fake_ecdt_polling_callback ( + acpi_handle handle, + u32 Level, + void *context, + void **retval); + +static acpi_status __init +acpi_fake_ecdt_burst_callback ( + acpi_handle handle, + u32 Level, + void *context, + void **retval); + +static int __init +acpi_ec_polling_get_real_ecdt(void); +static int __init +acpi_ec_burst_get_real_ecdt(void); /* If we find an EC via the ECDT, we need to keep a ptr to its context */ -static struct acpi_ec *ec_ecdt; +static union acpi_ec *ec_ecdt; /* External interfaces use first EC only, so remember */ static struct acpi_device *first_ec; +static int acpi_ec_polling_mode; /* -------------------------------------------------------------------------- Transaction Management -------------------------------------------------------------------------- */ -static inline u32 acpi_ec_read_status(struct acpi_ec *ec) +static inline u32 acpi_ec_read_status(union acpi_ec *ec) { u32 status = 0; - acpi_hw_low_level_read(8, &status, &ec->status_addr); + acpi_hw_low_level_read(8, &status, &ec->common.status_addr); return status; } -static int acpi_ec_wait(struct acpi_ec *ec, unsigned int event) +static int +acpi_ec_wait ( + union acpi_ec *ec, + u8 event) +{ + if (acpi_ec_polling_mode) + return acpi_ec_polling_wait (ec, event); + else + return acpi_ec_burst_wait (ec, event); +} + +static int +acpi_ec_polling_wait ( + union acpi_ec *ec, + u8 event) +{ + u32 acpi_ec_status = 0; + u32 i = ACPI_EC_UDELAY_COUNT; + + if (!ec) + return -EINVAL; + + /* Poll the EC status register waiting for the event to occur. */ + switch (event) { + case ACPI_EC_EVENT_OBF: + do { + acpi_hw_low_level_read(8, &acpi_ec_status, &ec->common.status_addr); + if (acpi_ec_status & ACPI_EC_FLAG_OBF) + return 0; + udelay(ACPI_EC_UDELAY); + } while (--i>0); + break; + case ACPI_EC_EVENT_IBE: + do { + acpi_hw_low_level_read(8, &acpi_ec_status, &ec->common.status_addr); + if (!(acpi_ec_status & ACPI_EC_FLAG_IBF)) + return 0; + udelay(ACPI_EC_UDELAY); + } while (--i>0); + break; + default: + return -EINVAL; + } + + return -ETIME; +} +static int acpi_ec_burst_wait(union acpi_ec *ec, unsigned int event) { int result = 0; ACPI_FUNCTION_TRACE("acpi_ec_wait"); - ec->expect_event = event; + ec->burst.expect_event = event; smp_mb(); - result = wait_event_interruptible_timeout(ec->wait, - !ec->expect_event, + result = wait_event_interruptible_timeout(ec->burst.wait, + !ec->burst.expect_event, msecs_to_jiffies(ACPI_EC_DELAY)); - ec->expect_event = 0; + ec->burst.expect_event = 0; smp_mb(); if (result < 0){ @@ -160,7 +269,7 @@ static int acpi_ec_wait(struct acpi_ec *ec, unsigned int event) static int acpi_ec_enter_burst_mode ( - struct acpi_ec *ec) + union acpi_ec *ec) { u32 tmp = 0; int status = 0; @@ -170,43 +279,43 @@ acpi_ec_enter_burst_mode ( status = acpi_ec_read_status(ec); if (status != -EINVAL && !(status & ACPI_EC_FLAG_BURST)){ - acpi_hw_low_level_write(8, ACPI_EC_BURST_ENABLE, &ec->command_addr); + acpi_hw_low_level_write(8, ACPI_EC_BURST_ENABLE, &ec->common.command_addr); status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); if (status){ - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); return_VALUE(-EINVAL); } - acpi_hw_low_level_read(8, &tmp, &ec->data_addr); - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); + acpi_hw_low_level_read(8, &tmp, &ec->common.data_addr); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); if(tmp != 0x90 ) {/* Burst ACK byte*/ return_VALUE(-EINVAL); } } - atomic_set(&ec->leaving_burst , 0); + atomic_set(&ec->burst.leaving_burst , 0); return_VALUE(0); } static int acpi_ec_leave_burst_mode ( - struct acpi_ec *ec) + union acpi_ec *ec) { int status =0; ACPI_FUNCTION_TRACE("acpi_ec_leave_burst_mode"); - atomic_set(&ec->leaving_burst , 1); + atomic_set(&ec->burst.leaving_burst , 1); status = acpi_ec_read_status(ec); if (status != -EINVAL && (status & ACPI_EC_FLAG_BURST)){ - acpi_hw_low_level_write(8, ACPI_EC_BURST_DISABLE, &ec->command_addr); + acpi_hw_low_level_write(8, ACPI_EC_BURST_DISABLE, &ec->common.command_addr); status = acpi_ec_wait(ec, ACPI_EC_FLAG_IBF); if (status){ - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); ACPI_DEBUG_PRINT((ACPI_DB_ERROR,"------->wait fail\n")); return_VALUE(-EINVAL); } - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); status = acpi_ec_read_status(ec); } @@ -215,7 +324,131 @@ acpi_ec_leave_burst_mode ( static int acpi_ec_read ( - struct acpi_ec *ec, + union acpi_ec *ec, + u8 address, + u32 *data) +{ + if (acpi_ec_polling_mode) + return acpi_ec_polling_read(ec, address, data); + else + return acpi_ec_burst_read(ec, address, data); +} +static int +acpi_ec_write ( + union acpi_ec *ec, + u8 address, + u8 data) +{ + if (acpi_ec_polling_mode) + return acpi_ec_polling_write(ec, address, data); + else + return acpi_ec_burst_write(ec, address, data); +} +static int +acpi_ec_polling_read ( + union acpi_ec *ec, + u8 address, + u32 *data) +{ + acpi_status status = AE_OK; + int result = 0; + unsigned long flags = 0; + u32 glk = 0; + + ACPI_FUNCTION_TRACE("acpi_ec_read"); + + if (!ec || !data) + return_VALUE(-EINVAL); + + *data = 0; + + if (ec->common.global_lock) { + status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + } + + spin_lock_irqsave(&ec->polling.lock, flags); + + acpi_hw_low_level_write(8, ACPI_EC_COMMAND_READ, &ec->common.command_addr); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); + if (result) + goto end; + + acpi_hw_low_level_write(8, address, &ec->common.data_addr); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); + if (result) + goto end; + + acpi_hw_low_level_read(8, data, &ec->common.data_addr); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Read [%02x] from address [%02x]\n", + *data, address)); + +end: + spin_unlock_irqrestore(&ec->polling.lock, flags); + + if (ec->common.global_lock) + acpi_release_global_lock(glk); + + return_VALUE(result); +} + + +static int +acpi_ec_polling_write ( + union acpi_ec *ec, + u8 address, + u8 data) +{ + int result = 0; + acpi_status status = AE_OK; + unsigned long flags = 0; + u32 glk = 0; + + ACPI_FUNCTION_TRACE("acpi_ec_write"); + + if (!ec) + return_VALUE(-EINVAL); + + if (ec->common.global_lock) { + status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + } + + spin_lock_irqsave(&ec->polling.lock, flags); + + acpi_hw_low_level_write(8, ACPI_EC_COMMAND_WRITE, &ec->common.command_addr); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); + if (result) + goto end; + + acpi_hw_low_level_write(8, address, &ec->common.data_addr); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); + if (result) + goto end; + + acpi_hw_low_level_write(8, data, &ec->common.data_addr); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); + if (result) + goto end; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Wrote [%02x] to address [%02x]\n", + data, address)); + +end: + spin_unlock_irqrestore(&ec->polling.lock, flags); + + if (ec->common.global_lock) + acpi_release_global_lock(glk); + + return_VALUE(result); +} + +static int +acpi_ec_burst_read ( + union acpi_ec *ec, u8 address, u32 *data) { @@ -230,51 +463,51 @@ acpi_ec_read ( retry: *data = 0; - if (ec->global_lock) { + if (ec->common.global_lock) { status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); if (ACPI_FAILURE(status)) return_VALUE(-ENODEV); } WARN_ON(in_interrupt()); - down(&ec->sem); + down(&ec->burst.sem); if(acpi_ec_enter_burst_mode(ec)) goto end; - acpi_hw_low_level_write(8, ACPI_EC_COMMAND_READ, &ec->command_addr); + acpi_hw_low_level_write(8, ACPI_EC_COMMAND_READ, &ec->common.command_addr); status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); if (status) { goto end; } - acpi_hw_low_level_write(8, address, &ec->data_addr); + acpi_hw_low_level_write(8, address, &ec->common.data_addr); status= acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); if (status){ - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); goto end; } - acpi_hw_low_level_read(8, data, &ec->data_addr); - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); + acpi_hw_low_level_read(8, data, &ec->common.data_addr); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Read [%02x] from address [%02x]\n", *data, address)); end: acpi_ec_leave_burst_mode(ec); - up(&ec->sem); + up(&ec->burst.sem); - if (ec->global_lock) + if (ec->common.global_lock) acpi_release_global_lock(glk); - if(atomic_read(&ec->leaving_burst) == 2){ + if(atomic_read(&ec->burst.leaving_burst) == 2){ ACPI_DEBUG_PRINT((ACPI_DB_INFO,"aborted, retry ...\n")); - while(atomic_read(&ec->pending_gpe)){ + while(atomic_read(&ec->burst.pending_gpe)){ msleep(1); } - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); goto retry; } @@ -283,8 +516,8 @@ acpi_ec_read ( static int -acpi_ec_write ( - struct acpi_ec *ec, +acpi_ec_burst_write ( + union acpi_ec *ec, u8 address, u8 data) { @@ -297,14 +530,14 @@ acpi_ec_write ( if (!ec) return_VALUE(-EINVAL); retry: - if (ec->global_lock) { + if (ec->common.global_lock) { status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); if (ACPI_FAILURE(status)) return_VALUE(-ENODEV); } WARN_ON(in_interrupt()); - down(&ec->sem); + down(&ec->burst.sem); if(acpi_ec_enter_burst_mode(ec)) goto end; @@ -312,33 +545,33 @@ acpi_ec_write ( status = acpi_ec_read_status(ec); if (status != -EINVAL && !(status & ACPI_EC_FLAG_BURST)){ - acpi_hw_low_level_write(8, ACPI_EC_BURST_ENABLE, &ec->command_addr); + acpi_hw_low_level_write(8, ACPI_EC_BURST_ENABLE, &ec->common.command_addr); status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); if (status) goto end; - acpi_hw_low_level_read(8, &tmp, &ec->data_addr); + acpi_hw_low_level_read(8, &tmp, &ec->common.data_addr); if(tmp != 0x90 ) /* Burst ACK byte*/ goto end; } /*Now we are in burst mode*/ - acpi_hw_low_level_write(8, ACPI_EC_COMMAND_WRITE, &ec->command_addr); + acpi_hw_low_level_write(8, ACPI_EC_COMMAND_WRITE, &ec->common.command_addr); status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); if (status){ goto end; } - acpi_hw_low_level_write(8, address, &ec->data_addr); + acpi_hw_low_level_write(8, address, &ec->common.data_addr); status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); if (status){ - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); goto end; } - acpi_hw_low_level_write(8, data, &ec->data_addr); + acpi_hw_low_level_write(8, data, &ec->common.data_addr); status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); if (status) goto end; @@ -347,17 +580,17 @@ acpi_ec_write ( end: acpi_ec_leave_burst_mode(ec); - up(&ec->sem); + up(&ec->burst.sem); - if (ec->global_lock) + if (ec->common.global_lock) acpi_release_global_lock(glk); - if(atomic_read(&ec->leaving_burst) == 2){ + if(atomic_read(&ec->burst.leaving_burst) == 2){ ACPI_DEBUG_PRINT((ACPI_DB_INFO,"aborted, retry ...\n")); - while(atomic_read(&ec->pending_gpe)){ + while(atomic_read(&ec->burst.pending_gpe)){ msleep(1); } - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); goto retry; } @@ -370,7 +603,7 @@ acpi_ec_write ( int ec_read(u8 addr, u8 *val) { - struct acpi_ec *ec; + union acpi_ec *ec; int err; u32 temp_data; @@ -393,7 +626,7 @@ EXPORT_SYMBOL(ec_read); int ec_write(u8 addr, u8 val) { - struct acpi_ec *ec; + union acpi_ec *ec; int err; if (!first_ec) @@ -407,10 +640,66 @@ ec_write(u8 addr, u8 val) } EXPORT_SYMBOL(ec_write); - static int acpi_ec_query ( - struct acpi_ec *ec, + union acpi_ec *ec, + u32 *data) +{ + if (acpi_ec_polling_mode) + return acpi_ec_polling_query(ec, data); + else + return acpi_ec_burst_query(ec, data); +} +static int +acpi_ec_polling_query ( + union acpi_ec *ec, + u32 *data) +{ + int result = 0; + acpi_status status = AE_OK; + unsigned long flags = 0; + u32 glk = 0; + + ACPI_FUNCTION_TRACE("acpi_ec_query"); + + if (!ec || !data) + return_VALUE(-EINVAL); + + *data = 0; + + if (ec->common.global_lock) { + status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + } + + /* + * Query the EC to find out which _Qxx method we need to evaluate. + * Note that successful completion of the query causes the ACPI_EC_SCI + * bit to be cleared (and thus clearing the interrupt source). + */ + spin_lock_irqsave(&ec->polling.lock, flags); + + acpi_hw_low_level_write(8, ACPI_EC_COMMAND_QUERY, &ec->common.command_addr); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); + if (result) + goto end; + + acpi_hw_low_level_read(8, data, &ec->common.data_addr); + if (!*data) + result = -ENODATA; + +end: + spin_unlock_irqrestore(&ec->polling.lock, flags); + + if (ec->common.global_lock) + acpi_release_global_lock(glk); + + return_VALUE(result); +} +static int +acpi_ec_burst_query ( + union acpi_ec *ec, u32 *data) { int status = 0; @@ -422,13 +711,13 @@ acpi_ec_query ( return_VALUE(-EINVAL); *data = 0; - if (ec->global_lock) { + if (ec->common.global_lock) { status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); if (ACPI_FAILURE(status)) return_VALUE(-ENODEV); } - down(&ec->sem); + down(&ec->burst.sem); if(acpi_ec_enter_burst_mode(ec)) goto end; /* @@ -436,28 +725,28 @@ acpi_ec_query ( * Note that successful completion of the query causes the ACPI_EC_SCI * bit to be cleared (and thus clearing the interrupt source). */ - acpi_hw_low_level_write(8, ACPI_EC_COMMAND_QUERY, &ec->command_addr); + acpi_hw_low_level_write(8, ACPI_EC_COMMAND_QUERY, &ec->common.command_addr); status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); if (status){ - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); goto end; } - acpi_hw_low_level_read(8, data, &ec->data_addr); - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); + acpi_hw_low_level_read(8, data, &ec->common.data_addr); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); if (!*data) status = -ENODATA; end: acpi_ec_leave_burst_mode(ec); - up(&ec->sem); + up(&ec->burst.sem); - if (ec->global_lock) + if (ec->common.global_lock) acpi_release_global_lock(glk); - if(atomic_read(&ec->leaving_burst) == 2){ + if(atomic_read(&ec->burst.leaving_burst) == 2){ ACPI_DEBUG_PRINT((ACPI_DB_INFO,"aborted, retry ...\n")); - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); status = -ENODATA; } return_VALUE(status); @@ -468,7 +757,7 @@ acpi_ec_query ( Event Management -------------------------------------------------------------------------- */ -struct acpi_ec_query_data { +union acpi_ec_query_data { acpi_handle handle; u8 data; }; @@ -477,7 +766,59 @@ static void acpi_ec_gpe_query ( void *ec_cxt) { - struct acpi_ec *ec = (struct acpi_ec *) ec_cxt; + if (acpi_ec_polling_mode) + acpi_ec_gpe_polling_query(ec_cxt); + else + acpi_ec_gpe_burst_query(ec_cxt); +} + +static void +acpi_ec_gpe_polling_query ( + void *ec_cxt) +{ + union acpi_ec *ec = (union acpi_ec *) ec_cxt; + u32 value = 0; + unsigned long flags = 0; + static char object_name[5] = {'_','Q','0','0','\0'}; + const char hex[] = {'0','1','2','3','4','5','6','7', + '8','9','A','B','C','D','E','F'}; + + ACPI_FUNCTION_TRACE("acpi_ec_gpe_query"); + + if (!ec_cxt) + goto end; + + spin_lock_irqsave(&ec->polling.lock, flags); + acpi_hw_low_level_read(8, &value, &ec->common.command_addr); + spin_unlock_irqrestore(&ec->polling.lock, flags); + + /* TBD: Implement asynch events! + * NOTE: All we care about are EC-SCI's. Other EC events are + * handled via polling (yuck!). This is because some systems + * treat EC-SCIs as level (versus EDGE!) triggered, preventing + * a purely interrupt-driven approach (grumble, grumble). + */ + if (!(value & ACPI_EC_FLAG_SCI)) + goto end; + + if (acpi_ec_query(ec, &value)) + goto end; + + object_name[2] = hex[((value >> 4) & 0x0F)]; + object_name[3] = hex[(value & 0x0F)]; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s\n", object_name)); + + acpi_evaluate_object(ec->common.handle, object_name, NULL, NULL); + +end: + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); +} +static void +acpi_ec_gpe_burst_query ( + void *ec_cxt) +{ + union acpi_ec *ec = (union acpi_ec *) ec_cxt; u32 value; int result = -ENODATA; static char object_name[5] = {'_','Q','0','0','\0'}; @@ -497,58 +838,87 @@ acpi_ec_gpe_query ( ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s\n", object_name)); - acpi_evaluate_object(ec->handle, object_name, NULL, NULL); + acpi_evaluate_object(ec->common.handle, object_name, NULL, NULL); end: - atomic_dec(&ec->pending_gpe); + atomic_dec(&ec->burst.pending_gpe); return; } static u32 acpi_ec_gpe_handler ( void *data) +{ + if (acpi_ec_polling_mode) + return acpi_ec_gpe_polling_handler(data); + else + return acpi_ec_gpe_burst_handler(data); +} +static u32 +acpi_ec_gpe_polling_handler ( + void *data) { acpi_status status = AE_OK; - u32 value; - struct acpi_ec *ec = (struct acpi_ec *) data; + union acpi_ec *ec = (union acpi_ec *) data; if (!ec) return ACPI_INTERRUPT_NOT_HANDLED; - acpi_disable_gpe(NULL, ec->gpe_bit, ACPI_ISR); + acpi_disable_gpe(NULL, ec->common.gpe_bit, ACPI_ISR); + + status = acpi_os_queue_for_execution(OSD_PRIORITY_GPE, + acpi_ec_gpe_query, ec); + + if (status == AE_OK) + return ACPI_INTERRUPT_HANDLED; + else + return ACPI_INTERRUPT_NOT_HANDLED; +} +static u32 +acpi_ec_gpe_burst_handler ( + void *data) +{ + acpi_status status = AE_OK; + u32 value; + union acpi_ec *ec = (union acpi_ec *) data; + + if (!ec) + return ACPI_INTERRUPT_NOT_HANDLED; + + acpi_disable_gpe(NULL, ec->common.gpe_bit, ACPI_ISR); value = acpi_ec_read_status(ec); if((value & ACPI_EC_FLAG_IBF) && !(value & ACPI_EC_FLAG_BURST) && - (atomic_read(&ec->leaving_burst) == 0)) { + (atomic_read(&ec->burst.leaving_burst) == 0)) { /* * the embedded controller disables * burst mode for any reason other * than the burst disable command * to process critical event. */ - atomic_set(&ec->leaving_burst , 2); /* block current pending transaction + atomic_set(&ec->burst.leaving_burst , 2); /* block current pending transaction and retry */ - wake_up(&ec->wait); + wake_up(&ec->burst.wait); }else { - if ((ec->expect_event == ACPI_EC_EVENT_OBF && + if ((ec->burst.expect_event == ACPI_EC_EVENT_OBF && (value & ACPI_EC_FLAG_OBF)) || - (ec->expect_event == ACPI_EC_EVENT_IBE && + (ec->burst.expect_event == ACPI_EC_EVENT_IBE && !(value & ACPI_EC_FLAG_IBF))) { - ec->expect_event = 0; - wake_up(&ec->wait); + ec->burst.expect_event = 0; + wake_up(&ec->burst.wait); return ACPI_INTERRUPT_HANDLED; } } if (value & ACPI_EC_FLAG_SCI){ - atomic_add(1, &ec->pending_gpe) ; + atomic_add(1, &ec->burst.pending_gpe) ; status = acpi_os_queue_for_execution(OSD_PRIORITY_GPE, acpi_ec_gpe_query, ec); return status == AE_OK ? ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED; } - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_ISR); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_ISR); return status == AE_OK ? ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED; } @@ -585,7 +955,7 @@ acpi_ec_space_handler ( void *region_context) { int result = 0; - struct acpi_ec *ec = NULL; + union acpi_ec *ec = NULL; u64 temp = *value; acpi_integer f_v = 0; int i = 0; @@ -600,7 +970,7 @@ acpi_ec_space_handler ( return_VALUE(AE_BAD_PARAMETER); } - ec = (struct acpi_ec *) handler_context; + ec = (union acpi_ec *) handler_context; next_byte: switch (function) { @@ -661,7 +1031,7 @@ static struct proc_dir_entry *acpi_ec_dir; static int acpi_ec_read_info (struct seq_file *seq, void *offset) { - struct acpi_ec *ec = (struct acpi_ec *) seq->private; + union acpi_ec *ec = (union acpi_ec *) seq->private; ACPI_FUNCTION_TRACE("acpi_ec_read_info"); @@ -669,12 +1039,12 @@ acpi_ec_read_info (struct seq_file *seq, void *offset) goto end; seq_printf(seq, "gpe bit: 0x%02x\n", - (u32) ec->gpe_bit); + (u32) ec->common.gpe_bit); seq_printf(seq, "ports: 0x%02x, 0x%02x\n", - (u32) ec->status_addr.address, (u32) ec->data_addr.address); + (u32) ec->common.status_addr.address, (u32) ec->common.data_addr.address); seq_printf(seq, "use global lock: %s\n", - ec->global_lock?"yes":"no"); - acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); + ec->common.global_lock?"yes":"no"); + acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR); end: return_VALUE(0); @@ -697,7 +1067,7 @@ static int acpi_ec_add_fs ( struct acpi_device *device) { - struct proc_dir_entry *entry; + struct proc_dir_entry *entry = NULL; ACPI_FUNCTION_TRACE("acpi_ec_add_fs"); @@ -744,13 +1114,14 @@ acpi_ec_remove_fs ( Driver Interface -------------------------------------------------------------------------- */ + static int -acpi_ec_add ( +acpi_ec_polling_add ( struct acpi_device *device) { - int result; - acpi_status status; - struct acpi_ec *ec; + int result = 0; + acpi_status status = AE_OK; + union acpi_ec *ec = NULL; unsigned long uid; ACPI_FUNCTION_TRACE("acpi_ec_add"); @@ -758,39 +1129,36 @@ acpi_ec_add ( if (!device) return_VALUE(-EINVAL); - ec = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL); + ec = kmalloc(sizeof(union acpi_ec), GFP_KERNEL); if (!ec) return_VALUE(-ENOMEM); - memset(ec, 0, sizeof(struct acpi_ec)); + memset(ec, 0, sizeof(union acpi_ec)); - ec->handle = device->handle; - ec->uid = -1; - atomic_set(&ec->pending_gpe, 0); - atomic_set(&ec->leaving_burst , 1); - init_MUTEX(&ec->sem); - init_waitqueue_head(&ec->wait); + ec->common.handle = device->handle; + ec->common.uid = -1; + spin_lock_init(&ec->polling.lock); strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_EC_CLASS); acpi_driver_data(device) = ec; /* Use the global lock for all EC transactions? */ - acpi_evaluate_integer(ec->handle, "_GLK", NULL, &ec->global_lock); + acpi_evaluate_integer(ec->common.handle, "_GLK", NULL, &ec->common.global_lock); /* If our UID matches the UID for the ECDT-enumerated EC, we now have the *real* EC info, so kill the makeshift one.*/ - acpi_evaluate_integer(ec->handle, "_UID", NULL, &uid); - if (ec_ecdt && ec_ecdt->uid == uid) { + acpi_evaluate_integer(ec->common.handle, "_UID", NULL, &uid); + if (ec_ecdt && ec_ecdt->common.uid == uid) { acpi_remove_address_space_handler(ACPI_ROOT_OBJECT, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler); - - acpi_remove_gpe_handler(NULL, ec_ecdt->gpe_bit, &acpi_ec_gpe_handler); + + acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit, &acpi_ec_gpe_handler); kfree(ec_ecdt); } /* Get GPE bit assignment (EC events). */ /* TODO: Add support for _GPE returning a package */ - status = acpi_evaluate_integer(ec->handle, "_GPE", NULL, &ec->gpe_bit); + status = acpi_evaluate_integer(ec->common.handle, "_GPE", NULL, &ec->common.gpe_bit); if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error obtaining GPE bit assignment\n")); @@ -804,7 +1172,78 @@ acpi_ec_add ( printk(KERN_INFO PREFIX "%s [%s] (gpe %d)\n", acpi_device_name(device), acpi_device_bid(device), - (u32) ec->gpe_bit); + (u32) ec->common.gpe_bit); + + if (!first_ec) + first_ec = device; + +end: + if (result) + kfree(ec); + + return_VALUE(result); +} +static int +acpi_ec_burst_add ( + struct acpi_device *device) +{ + int result = 0; + acpi_status status = AE_OK; + union acpi_ec *ec = NULL; + unsigned long uid; + + ACPI_FUNCTION_TRACE("acpi_ec_add"); + + if (!device) + return_VALUE(-EINVAL); + + ec = kmalloc(sizeof(union acpi_ec), GFP_KERNEL); + if (!ec) + return_VALUE(-ENOMEM); + memset(ec, 0, sizeof(union acpi_ec)); + + ec->common.handle = device->handle; + ec->common.uid = -1; + atomic_set(&ec->burst.pending_gpe, 0); + atomic_set(&ec->burst.leaving_burst , 1); + init_MUTEX(&ec->burst.sem); + init_waitqueue_head(&ec->burst.wait); + strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_EC_CLASS); + acpi_driver_data(device) = ec; + + /* Use the global lock for all EC transactions? */ + acpi_evaluate_integer(ec->common.handle, "_GLK", NULL, &ec->common.global_lock); + + /* If our UID matches the UID for the ECDT-enumerated EC, + we now have the *real* EC info, so kill the makeshift one.*/ + acpi_evaluate_integer(ec->common.handle, "_UID", NULL, &uid); + if (ec_ecdt && ec_ecdt->common.uid == uid) { + acpi_remove_address_space_handler(ACPI_ROOT_OBJECT, + ACPI_ADR_SPACE_EC, &acpi_ec_space_handler); + + acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit, &acpi_ec_gpe_handler); + + kfree(ec_ecdt); + } + + /* Get GPE bit assignment (EC events). */ + /* TODO: Add support for _GPE returning a package */ + status = acpi_evaluate_integer(ec->common.handle, "_GPE", NULL, &ec->common.gpe_bit); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error obtaining GPE bit assignment\n")); + result = -ENODEV; + goto end; + } + + result = acpi_ec_add_fs(device); + if (result) + goto end; + + printk(KERN_INFO PREFIX "%s [%s] (gpe %d)\n", + acpi_device_name(device), acpi_device_bid(device), + (u32) ec->common.gpe_bit); if (!first_ec) first_ec = device; @@ -822,7 +1261,7 @@ acpi_ec_remove ( struct acpi_device *device, int type) { - struct acpi_ec *ec; + union acpi_ec *ec = NULL; ACPI_FUNCTION_TRACE("acpi_ec_remove"); @@ -844,7 +1283,7 @@ acpi_ec_io_ports ( struct acpi_resource *resource, void *context) { - struct acpi_ec *ec = (struct acpi_ec *) context; + union acpi_ec *ec = (union acpi_ec *) context; struct acpi_generic_address *addr; if (resource->id != ACPI_RSTYPE_IO) { @@ -856,10 +1295,10 @@ acpi_ec_io_ports ( * the second address region returned is the status/command * port. */ - if (ec->data_addr.register_bit_width == 0) { - addr = &ec->data_addr; - } else if (ec->command_addr.register_bit_width == 0) { - addr = &ec->command_addr; + if (ec->common.data_addr.register_bit_width == 0) { + addr = &ec->common.data_addr; + } else if (ec->common.command_addr.register_bit_width == 0) { + addr = &ec->common.command_addr; } else { return AE_CTRL_TERMINATE; } @@ -877,8 +1316,8 @@ static int acpi_ec_start ( struct acpi_device *device) { - acpi_status status; - struct acpi_ec *ec; + acpi_status status = AE_OK; + union acpi_ec *ec = NULL; ACPI_FUNCTION_TRACE("acpi_ec_start"); @@ -893,35 +1332,36 @@ acpi_ec_start ( /* * Get I/O port addresses. Convert to GAS format. */ - status = acpi_walk_resources(ec->handle, METHOD_NAME__CRS, + status = acpi_walk_resources(ec->common.handle, METHOD_NAME__CRS, acpi_ec_io_ports, ec); - if (ACPI_FAILURE(status) || ec->command_addr.register_bit_width == 0) { + if (ACPI_FAILURE(status) || ec->common.command_addr.register_bit_width == 0) { ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error getting I/O port addresses")); return_VALUE(-ENODEV); } - ec->status_addr = ec->command_addr; + ec->common.status_addr = ec->common.command_addr; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02x, ports=0x%2x,0x%2x\n", - (u32) ec->gpe_bit, (u32) ec->command_addr.address, - (u32) ec->data_addr.address)); + (u32) ec->common.gpe_bit, (u32) ec->common.command_addr.address, + (u32) ec->common.data_addr.address)); + /* * Install GPE handler */ - status = acpi_install_gpe_handler(NULL, ec->gpe_bit, + status = acpi_install_gpe_handler(NULL, ec->common.gpe_bit, ACPI_GPE_EDGE_TRIGGERED, &acpi_ec_gpe_handler, ec); if (ACPI_FAILURE(status)) { return_VALUE(-ENODEV); } - acpi_set_gpe_type (NULL, ec->gpe_bit, ACPI_GPE_TYPE_RUNTIME); - acpi_enable_gpe (NULL, ec->gpe_bit, ACPI_NOT_ISR); + acpi_set_gpe_type (NULL, ec->common.gpe_bit, ACPI_GPE_TYPE_RUNTIME); + acpi_enable_gpe (NULL, ec->common.gpe_bit, ACPI_NOT_ISR); - status = acpi_install_address_space_handler (ec->handle, + status = acpi_install_address_space_handler (ec->common.handle, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler, &acpi_ec_space_setup, ec); if (ACPI_FAILURE(status)) { - acpi_remove_gpe_handler(NULL, ec->gpe_bit, &acpi_ec_gpe_handler); + acpi_remove_gpe_handler(NULL, ec->common.gpe_bit, &acpi_ec_gpe_handler); return_VALUE(-ENODEV); } @@ -934,8 +1374,8 @@ acpi_ec_stop ( struct acpi_device *device, int type) { - acpi_status status; - struct acpi_ec *ec; + acpi_status status = AE_OK; + union acpi_ec *ec = NULL; ACPI_FUNCTION_TRACE("acpi_ec_stop"); @@ -944,12 +1384,12 @@ acpi_ec_stop ( ec = acpi_driver_data(device); - status = acpi_remove_address_space_handler(ec->handle, + status = acpi_remove_address_space_handler(ec->common.handle, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler); if (ACPI_FAILURE(status)) return_VALUE(-ENODEV); - status = acpi_remove_gpe_handler(NULL, ec->gpe_bit, &acpi_ec_gpe_handler); + status = acpi_remove_gpe_handler(NULL, ec->common.gpe_bit, &acpi_ec_gpe_handler); if (ACPI_FAILURE(status)) return_VALUE(-ENODEV); @@ -963,26 +1403,76 @@ acpi_fake_ecdt_callback ( void *context, void **retval) { + + if (acpi_ec_polling_mode) + return acpi_fake_ecdt_polling_callback(handle, + Level, context, retval); + else + return acpi_fake_ecdt_burst_callback(handle, + Level, context, retval); +} + +static acpi_status __init +acpi_fake_ecdt_polling_callback ( + acpi_handle handle, + u32 Level, + void *context, + void **retval) +{ acpi_status status; status = acpi_walk_resources(handle, METHOD_NAME__CRS, acpi_ec_io_ports, ec_ecdt); if (ACPI_FAILURE(status)) return status; - ec_ecdt->status_addr = ec_ecdt->command_addr; + ec_ecdt->common.status_addr = ec_ecdt->common.command_addr; - ec_ecdt->uid = -1; - acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->uid); + ec_ecdt->common.uid = -1; + acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->common.uid); - status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->gpe_bit); + status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->common.gpe_bit); if (ACPI_FAILURE(status)) return status; - ec_ecdt->global_lock = TRUE; - ec_ecdt->handle = handle; + spin_lock_init(&ec_ecdt->polling.lock); + ec_ecdt->common.global_lock = TRUE; + ec_ecdt->common.handle = handle; printk(KERN_INFO PREFIX "GPE=0x%02x, ports=0x%2x, 0x%2x\n", - (u32) ec_ecdt->gpe_bit, (u32) ec_ecdt->command_addr.address, - (u32) ec_ecdt->data_addr.address); + (u32) ec_ecdt->common.gpe_bit, (u32) ec_ecdt->common.command_addr.address, + (u32) ec_ecdt->common.data_addr.address); + + return AE_CTRL_TERMINATE; +} + +static acpi_status __init +acpi_fake_ecdt_burst_callback ( + acpi_handle handle, + u32 Level, + void *context, + void **retval) +{ + acpi_status status; + + init_MUTEX(&ec_ecdt->burst.sem); + init_waitqueue_head(&ec_ecdt->burst.wait); + status = acpi_walk_resources(handle, METHOD_NAME__CRS, + acpi_ec_io_ports, ec_ecdt); + if (ACPI_FAILURE(status)) + return status; + ec_ecdt->common.status_addr = ec_ecdt->common.command_addr; + + ec_ecdt->common.uid = -1; + acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->common.uid); + + status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->common.gpe_bit); + if (ACPI_FAILURE(status)) + return status; + ec_ecdt->common.global_lock = TRUE; + ec_ecdt->common.handle = handle; + + printk(KERN_INFO PREFIX "GPE=0x%02x, ports=0x%2x, 0x%2x\n", + (u32) ec_ecdt->common.gpe_bit, (u32) ec_ecdt->common.command_addr.address, + (u32) ec_ecdt->common.data_addr.address); return AE_CTRL_TERMINATE; } @@ -1005,12 +1495,12 @@ acpi_ec_fake_ecdt(void) printk(KERN_INFO PREFIX "Try to make an fake ECDT\n"); - ec_ecdt = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL); + ec_ecdt = kmalloc(sizeof(union acpi_ec), GFP_KERNEL); if (!ec_ecdt) { ret = -ENOMEM; goto error; } - memset(ec_ecdt, 0, sizeof(struct acpi_ec)); + memset(ec_ecdt, 0, sizeof(union acpi_ec)); status = acpi_get_devices (ACPI_EC_HID, acpi_fake_ecdt_callback, @@ -1030,6 +1520,60 @@ acpi_ec_fake_ecdt(void) static int __init acpi_ec_get_real_ecdt(void) +{ + if (acpi_ec_polling_mode) + return acpi_ec_polling_get_real_ecdt(); + else + return acpi_ec_burst_get_real_ecdt(); +} + +static int __init +acpi_ec_polling_get_real_ecdt(void) +{ + acpi_status status; + struct acpi_table_ecdt *ecdt_ptr; + + status = acpi_get_firmware_table("ECDT", 1, ACPI_LOGICAL_ADDRESSING, + (struct acpi_table_header **) &ecdt_ptr); + if (ACPI_FAILURE(status)) + return -ENODEV; + + printk(KERN_INFO PREFIX "Found ECDT\n"); + + /* + * Generate a temporary ec context to use until the namespace is scanned + */ + ec_ecdt = kmalloc(sizeof(union acpi_ec), GFP_KERNEL); + if (!ec_ecdt) + return -ENOMEM; + memset(ec_ecdt, 0, sizeof(union acpi_ec)); + + ec_ecdt->common.command_addr = ecdt_ptr->ec_control; + ec_ecdt->common.status_addr = ecdt_ptr->ec_control; + ec_ecdt->common.data_addr = ecdt_ptr->ec_data; + ec_ecdt->common.gpe_bit = ecdt_ptr->gpe_bit; + spin_lock_init(&ec_ecdt->polling.lock); + /* use the GL just to be safe */ + ec_ecdt->common.global_lock = TRUE; + ec_ecdt->common.uid = ecdt_ptr->uid; + + status = acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->common.handle); + if (ACPI_FAILURE(status)) { + goto error; + } + + return 0; +error: + printk(KERN_ERR PREFIX "Could not use ECDT\n"); + kfree(ec_ecdt); + ec_ecdt = NULL; + + return -ENODEV; +} + + +static int __init +acpi_ec_burst_get_real_ecdt(void) { acpi_status status; struct acpi_table_ecdt *ecdt_ptr; @@ -1044,22 +1588,22 @@ acpi_ec_get_real_ecdt(void) /* * Generate a temporary ec context to use until the namespace is scanned */ - ec_ecdt = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL); + ec_ecdt = kmalloc(sizeof(union acpi_ec), GFP_KERNEL); if (!ec_ecdt) return -ENOMEM; - memset(ec_ecdt, 0, sizeof(struct acpi_ec)); + memset(ec_ecdt, 0, sizeof(union acpi_ec)); - init_MUTEX(&ec_ecdt->sem); - init_waitqueue_head(&ec_ecdt->wait); - ec_ecdt->command_addr = ecdt_ptr->ec_control; - ec_ecdt->status_addr = ecdt_ptr->ec_control; - ec_ecdt->data_addr = ecdt_ptr->ec_data; - ec_ecdt->gpe_bit = ecdt_ptr->gpe_bit; + init_MUTEX(&ec_ecdt->burst.sem); + init_waitqueue_head(&ec_ecdt->burst.wait); + ec_ecdt->common.command_addr = ecdt_ptr->ec_control; + ec_ecdt->common.status_addr = ecdt_ptr->ec_control; + ec_ecdt->common.data_addr = ecdt_ptr->ec_data; + ec_ecdt->common.gpe_bit = ecdt_ptr->gpe_bit; /* use the GL just to be safe */ - ec_ecdt->global_lock = TRUE; - ec_ecdt->uid = ecdt_ptr->uid; + ec_ecdt->common.global_lock = TRUE; + ec_ecdt->common.uid = ecdt_ptr->uid; - status = acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->handle); + status = acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->common.handle); if (ACPI_FAILURE(status)) { goto error; } @@ -1092,20 +1636,20 @@ acpi_ec_ecdt_probe (void) /* * Install GPE handler */ - status = acpi_install_gpe_handler(NULL, ec_ecdt->gpe_bit, + status = acpi_install_gpe_handler(NULL, ec_ecdt->common.gpe_bit, ACPI_GPE_EDGE_TRIGGERED, &acpi_ec_gpe_handler, ec_ecdt); if (ACPI_FAILURE(status)) { goto error; } - acpi_set_gpe_type (NULL, ec_ecdt->gpe_bit, ACPI_GPE_TYPE_RUNTIME); - acpi_enable_gpe (NULL, ec_ecdt->gpe_bit, ACPI_NOT_ISR); + acpi_set_gpe_type (NULL, ec_ecdt->common.gpe_bit, ACPI_GPE_TYPE_RUNTIME); + acpi_enable_gpe (NULL, ec_ecdt->common.gpe_bit, ACPI_NOT_ISR); status = acpi_install_address_space_handler (ACPI_ROOT_OBJECT, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler, &acpi_ec_space_setup, ec_ecdt); if (ACPI_FAILURE(status)) { - acpi_remove_gpe_handler(NULL, ec_ecdt->gpe_bit, + acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit, &acpi_ec_gpe_handler); goto error; } @@ -1123,7 +1667,7 @@ acpi_ec_ecdt_probe (void) static int __init acpi_ec_init (void) { - int result; + int result = 0; ACPI_FUNCTION_TRACE("acpi_ec_init"); @@ -1167,3 +1711,10 @@ static int __init acpi_fake_ecdt_setup(char *str) return 0; } __setup("acpi_fake_ecdt", acpi_fake_ecdt_setup); +static int __init acpi_ec_set_polling_mode(char *str) +{ + acpi_ec_polling_mode = EC_POLLING; + acpi_ec_driver.ops.add = acpi_ec_polling_add; + return 0; +} +__setup("ec_polling", acpi_ec_set_polling_mode); From 4b31e77455b868b43e665edceb111c9a330c8e0f Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Wed, 18 May 2005 13:49:00 -0400 Subject: [PATCH 05/18] [ACPI] Always set P-state on initialization Otherwise a platform that supports ACPI based cpufreq and boots up at lowest possible speed could stay there forever. This because the governor may request max speed, but the code doesn't update if there is no change in speed, and it assumed the initial state of max speed. http://bugzilla.kernel.org/show_bug.cgi?id=4634 Signed-off-by: Dominik Brodowski Signed-off-by: Venkatesh Pallipadi Signed-off-by: Len Brown --- arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c index 963e17aa205d..60a9e54dd20e 100644 --- a/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c +++ b/arch/i386/kernel/cpu/cpufreq/acpi-cpufreq.c @@ -442,6 +442,13 @@ acpi_cpufreq_cpu_init ( (u32) data->acpi_data.states[i].transition_latency); cpufreq_frequency_table_get_attr(data->freq_table, policy->cpu); + + /* + * the first call to ->target() should result in us actually + * writing something to the appropriate registers. + */ + data->resume = 1; + return (result); err_freqfree: From 90158b83204842c0108d744326868d91cc9c4dfd Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 24 Jul 2005 14:22:00 -0400 Subject: [PATCH 06/18] [ACPI] fix resume issues on Asus L5D http://bugzilla.kernel.org/show_bug.cgi?id=4416 Signed-off-by: Rafael J. Wysocki Signed-off-by: Len Brown --- drivers/net/sk98lin/skge.c | 63 +++++++++++++++++++++++++++++++++++ drivers/pcmcia/yenta_socket.c | 9 +++++ sound/pci/intel8x0.c | 6 +++- 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/drivers/net/sk98lin/skge.c b/drivers/net/sk98lin/skge.c index 05b827f79f54..7bfaef9305b2 100644 --- a/drivers/net/sk98lin/skge.c +++ b/drivers/net/sk98lin/skge.c @@ -5134,6 +5134,67 @@ static void __devexit skge_remove_one(struct pci_dev *pdev) kfree(pAC); } +#ifdef CONFIG_PM +static int skge_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + DEV_NET *pNet = netdev_priv(dev); + SK_AC *pAC = pNet->pAC; + struct net_device *otherdev = pAC->dev[1]; + + if (pNet->Up) { + pAC->WasIfUp[0] = SK_TRUE; + DoPrintInterfaceChange = SK_FALSE; + SkDrvDeInitAdapter(pAC, 0); /* performs SkGeClose */ + } + if (otherdev != dev) { + pNet = netdev_priv(otherdev); + if (pNet->Up) { + pAC->WasIfUp[1] = SK_TRUE; + DoPrintInterfaceChange = SK_FALSE; + SkDrvDeInitAdapter(pAC, 1); /* performs SkGeClose */ + } + } + + pci_save_state(pdev); + pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); + if (pAC->AllocFlag & SK_ALLOC_IRQ) { + free_irq(dev->irq, dev); + } + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +static int skge_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + DEV_NET *pNet = netdev_priv(dev); + SK_AC *pAC = pNet->pAC; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + pci_enable_device(pdev); + pci_set_master(pdev); + if (pAC->GIni.GIMacsFound == 2) + request_irq(dev->irq, SkGeIsr, SA_SHIRQ, pAC->Name, dev); + else + request_irq(dev->irq, SkGeIsrOnePort, SA_SHIRQ, pAC->Name, dev); + + if (pAC->WasIfUp[0] == SK_TRUE) { + DoPrintInterfaceChange = SK_FALSE; + SkDrvInitAdapter(pAC, 0); /* first device */ + } + if (pAC->dev[1] != dev && pAC->WasIfUp[1] == SK_TRUE) { + DoPrintInterfaceChange = SK_FALSE; + SkDrvInitAdapter(pAC, 1); /* first device */ + } + + return 0; +} +#endif + static struct pci_device_id skge_pci_tbl[] = { { PCI_VENDOR_ID_3COM, 0x1700, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_3COM, 0x80eb, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, @@ -5159,6 +5220,8 @@ static struct pci_driver skge_driver = { .id_table = skge_pci_tbl, .probe = skge_probe_one, .remove = __devexit_p(skge_remove_one), + .suspend = skge_suspend, + .resume = skge_resume, }; static int __init skge_init(void) diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index caf7159a54be..a8a9d954bcf4 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -1034,6 +1034,8 @@ static int yenta_dev_suspend (struct pci_dev *dev, pm_message_t state) pci_read_config_dword(dev, 17*4, &socket->saved_state[1]); pci_disable_device(dev); + free_irq(dev->irq, socket); + /* * Some laptops (IBM T22) do not like us putting the Cardbus * bridge into D3. At a guess, some other laptop will @@ -1059,6 +1061,13 @@ static int yenta_dev_resume (struct pci_dev *dev) pci_enable_device(dev); pci_set_master(dev); + if (socket->cb_irq) + if (request_irq(socket->cb_irq, yenta_interrupt, + SA_SHIRQ, "yenta", socket)) { + printk(KERN_WARNING "Yenta: request_irq() failed on resume!\n"); + socket->cb_irq = 0; + } + if (socket->type && socket->type->restore_state) socket->type->restore_state(socket); } diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 8b33b12fa5dc..2a7e63b5757f 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -2367,6 +2367,8 @@ static int intel8x0_suspend(snd_card_t *card, pm_message_t state) for (i = 0; i < 3; i++) if (chip->ac97[i]) snd_ac97_suspend(chip->ac97[i]); + if (chip->irq >= 0) + free_irq(chip->irq, (void *)chip); pci_disable_device(chip->pci); return 0; } @@ -2378,7 +2380,9 @@ static int intel8x0_resume(snd_card_t *card) pci_enable_device(chip->pci); pci_set_master(chip->pci); - snd_intel8x0_chip_init(chip, 0); + request_irq(chip->irq, snd_intel8x0_interrupt, SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip); + synchronize_irq(chip->irq); + snd_intel8x0_chip_init(chip, 1); /* refill nocache */ if (chip->fix_nocache) From 68ac767686fd72f37a25bb4895fb4ab0080ba755 Mon Sep 17 00:00:00 2001 From: Venkatesh Pallipadi Date: Mon, 25 Apr 2005 14:38:00 -0400 Subject: [PATCH 07/18] [ACPI] delete boot-time printk()s from processor_idle.c http://bugzilla.kernel.org/show_bug.cgi?id=4401 Signed-off-by: Venkatesh Pallipadi Signed-off-by: Len Brown --- drivers/acpi/processor_idle.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 3702725db97a..fd5458947851 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -768,7 +768,6 @@ static void acpi_processor_power_verify_c3( } if (pr->flags.bm_check) { - printk("Disabling BM access before entering C3\n"); /* bus mastering control is necessary */ if (!pr->flags.bm_control) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, @@ -776,7 +775,6 @@ static void acpi_processor_power_verify_c3( return_VOID; } } else { - printk("Invalidating cache before entering C3\n"); /* * WBINVD should be set in fadt, for C3 state to be * supported on when bm_check is not required. From 87bec66b9691522414862dd8d41e430b063735ef Mon Sep 17 00:00:00 2001 From: David Shaohua Li Date: Wed, 27 Jul 2005 23:02:00 -0400 Subject: [PATCH 08/18] [ACPI] suspend/resume ACPI PCI Interrupt Links Add reference count and disable ACPI PCI Interrupt Link when no device still uses it. Warn when drivers have not released Link at suspend time. http://bugzilla.kernel.org/show_bug.cgi?id=3469 Signed-off-by: David Shaohua Li Signed-off-by: Len Brown --- arch/i386/pci/acpi.c | 1 + arch/i386/pci/common.c | 6 +++ arch/i386/pci/irq.c | 1 + arch/i386/pci/pci.h | 1 + drivers/acpi/pci_irq.c | 85 ++++++++++++++++++++--------- drivers/acpi/pci_link.c | 103 ++++++++++++++++++++++++++++++------ include/acpi/acpi_drivers.h | 3 +- include/linux/acpi.h | 4 -- 8 files changed, 157 insertions(+), 47 deletions(-) diff --git a/arch/i386/pci/acpi.c b/arch/i386/pci/acpi.c index 2db65ec45dc3..42913f43feb0 100644 --- a/arch/i386/pci/acpi.c +++ b/arch/i386/pci/acpi.c @@ -30,6 +30,7 @@ static int __init pci_acpi_init(void) acpi_irq_penalty_init(); pcibios_scanned++; pcibios_enable_irq = acpi_pci_irq_enable; + pcibios_disable_irq = acpi_pci_irq_disable; if (pci_routeirq) { /* diff --git a/arch/i386/pci/common.c b/arch/i386/pci/common.c index 720975e1af50..751e49bda180 100644 --- a/arch/i386/pci/common.c +++ b/arch/i386/pci/common.c @@ -249,3 +249,9 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) return pcibios_enable_irq(dev); } + +void pcibios_disable_device (struct pci_dev *dev) +{ + if (pcibios_disable_irq) + pcibios_disable_irq(dev); +} diff --git a/arch/i386/pci/irq.c b/arch/i386/pci/irq.c index d21b3a2dc978..66e4149ef189 100644 --- a/arch/i386/pci/irq.c +++ b/arch/i386/pci/irq.c @@ -56,6 +56,7 @@ struct irq_router_handler { }; int (*pcibios_enable_irq)(struct pci_dev *dev) = NULL; +void (*pcibios_disable_irq)(struct pci_dev *dev) = NULL; /* * Search 0xf0000 -- 0xfffff for the PCI IRQ Routing Table. diff --git a/arch/i386/pci/pci.h b/arch/i386/pci/pci.h index a8fc80ca69f3..dc442dfcab9e 100644 --- a/arch/i386/pci/pci.h +++ b/arch/i386/pci/pci.h @@ -72,3 +72,4 @@ extern int pcibios_scanned; extern spinlock_t pci_config_lock; extern int (*pcibios_enable_irq)(struct pci_dev *dev); +extern void (*pcibios_disable_irq)(struct pci_dev *dev); diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 8093f2e00321..c536ccfc5413 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -269,7 +269,51 @@ acpi_pci_irq_del_prt (int segment, int bus) /* -------------------------------------------------------------------------- PCI Interrupt Routing Support -------------------------------------------------------------------------- */ +typedef int (*irq_lookup_func)(struct acpi_prt_entry *, int *, int *, char **); +static int +acpi_pci_allocate_irq(struct acpi_prt_entry *entry, + int *edge_level, + int *active_high_low, + char **link) +{ + int irq; + + ACPI_FUNCTION_TRACE("acpi_pci_allocate_irq"); + + if (entry->link.handle) { + irq = acpi_pci_link_allocate_irq(entry->link.handle, + entry->link.index, edge_level, active_high_low, link); + if (irq < 0) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid IRQ link routing entry\n")); + return_VALUE(-1); + } + } else { + irq = entry->link.index; + *edge_level = ACPI_LEVEL_SENSITIVE; + *active_high_low = ACPI_ACTIVE_LOW; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found IRQ %d\n", irq)); + return_VALUE(irq); +} + +static int +acpi_pci_free_irq(struct acpi_prt_entry *entry, + int *edge_level, + int *active_high_low, + char **link) +{ + int irq; + + ACPI_FUNCTION_TRACE("acpi_pci_free_irq"); + if (entry->link.handle) { + irq = acpi_pci_link_free_irq(entry->link.handle); + } else { + irq = entry->link.index; + } + return_VALUE(irq); +} /* * acpi_pci_irq_lookup * success: return IRQ >= 0 @@ -282,12 +326,13 @@ acpi_pci_irq_lookup ( int pin, int *edge_level, int *active_high_low, - char **link) + char **link, + irq_lookup_func func) { struct acpi_prt_entry *entry = NULL; int segment = pci_domain_nr(bus); int bus_nr = bus->number; - int irq; + int ret; ACPI_FUNCTION_TRACE("acpi_pci_irq_lookup"); @@ -301,22 +346,8 @@ acpi_pci_irq_lookup ( return_VALUE(-1); } - if (entry->link.handle) { - irq = acpi_pci_link_get_irq(entry->link.handle, - entry->link.index, edge_level, active_high_low, link); - if (irq < 0) { - ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid IRQ link routing entry\n")); - return_VALUE(-1); - } - } else { - irq = entry->link.index; - *edge_level = ACPI_LEVEL_SENSITIVE; - *active_high_low = ACPI_ACTIVE_LOW; - } - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found IRQ %d\n", irq)); - - return_VALUE(irq); + ret = func(entry, edge_level, active_high_low, link); + return_VALUE(ret); } /* @@ -330,7 +361,8 @@ acpi_pci_irq_derive ( int pin, int *edge_level, int *active_high_low, - char **link) + char **link, + irq_lookup_func func) { struct pci_dev *bridge = dev; int irq = -1; @@ -363,7 +395,7 @@ acpi_pci_irq_derive ( } irq = acpi_pci_irq_lookup(bridge->bus, PCI_SLOT(bridge->devfn), - pin, edge_level, active_high_low, link); + pin, edge_level, active_high_low, link, func); } if (irq < 0) { @@ -415,7 +447,7 @@ acpi_pci_irq_enable ( * values override any BIOS-assigned IRQs set during boot. */ irq = acpi_pci_irq_lookup(dev->bus, PCI_SLOT(dev->devfn), pin, - &edge_level, &active_high_low, &link); + &edge_level, &active_high_low, &link, acpi_pci_allocate_irq); /* * If no PRT entry was found, we'll try to derive an IRQ from the @@ -423,7 +455,7 @@ acpi_pci_irq_enable ( */ if (irq < 0) irq = acpi_pci_irq_derive(dev, pin, &edge_level, - &active_high_low, &link); + &active_high_low, &link, acpi_pci_allocate_irq); /* * No IRQ known to the ACPI subsystem - maybe the BIOS / @@ -461,7 +493,9 @@ acpi_pci_irq_enable ( EXPORT_SYMBOL(acpi_pci_irq_enable); -#ifdef CONFIG_ACPI_DEALLOCATE_IRQ +/* FIXME: implement x86/x86_64 version */ +void __attribute__((weak)) acpi_unregister_gsi(u32 i) {} + void acpi_pci_irq_disable ( struct pci_dev *dev) @@ -488,14 +522,14 @@ acpi_pci_irq_disable ( * First we check the PCI IRQ routing table (PRT) for an IRQ. */ gsi = acpi_pci_irq_lookup(dev->bus, PCI_SLOT(dev->devfn), pin, - &edge_level, &active_high_low, NULL); + &edge_level, &active_high_low, NULL, acpi_pci_free_irq); /* * If no PRT entry was found, we'll try to derive an IRQ from the * device's parent bridge. */ if (gsi < 0) gsi = acpi_pci_irq_derive(dev, pin, - &edge_level, &active_high_low, NULL); + &edge_level, &active_high_low, NULL, acpi_pci_free_irq); if (gsi < 0) return_VOID; @@ -511,4 +545,3 @@ acpi_pci_irq_disable ( return_VOID; } -#endif /* CONFIG_ACPI_DEALLOCATE_IRQ */ diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 6ad0e77df9b3..6a29610edc11 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -68,6 +68,10 @@ static struct acpi_driver acpi_pci_link_driver = { }, }; +/* + * If a link is initialized, we never change its active and initialized + * later even the link is disable. Instead, we just repick the active irq + */ struct acpi_pci_link_irq { u8 active; /* Current IRQ */ u8 edge_level; /* All IRQs */ @@ -76,8 +80,7 @@ struct acpi_pci_link_irq { u8 possible_count; u8 possible[ACPI_PCI_LINK_MAX_POSSIBLE]; u8 initialized:1; - u8 suspend_resume:1; - u8 reserved:6; + u8 reserved:7; }; struct acpi_pci_link { @@ -85,12 +88,14 @@ struct acpi_pci_link { struct acpi_device *device; acpi_handle handle; struct acpi_pci_link_irq irq; + int refcnt; }; static struct { int count; struct list_head entries; } acpi_link; +DECLARE_MUTEX(acpi_link_lock); /* -------------------------------------------------------------------------- @@ -532,12 +537,12 @@ static int acpi_pci_link_allocate( ACPI_FUNCTION_TRACE("acpi_pci_link_allocate"); - if (link->irq.suspend_resume) { - acpi_pci_link_set(link, link->irq.active); - link->irq.suspend_resume = 0; - } - if (link->irq.initialized) + if (link->irq.initialized) { + if (link->refcnt == 0) + /* This means the link is disabled but initialized */ + acpi_pci_link_set(link, link->irq.active); return_VALUE(0); + } /* * search for active IRQ in list of possible IRQs. @@ -596,13 +601,13 @@ static int acpi_pci_link_allocate( } /* - * acpi_pci_link_get_irq + * acpi_pci_link_allocate_irq * success: return IRQ >= 0 * failure: return -1 */ int -acpi_pci_link_get_irq ( +acpi_pci_link_allocate_irq ( acpi_handle handle, int index, int *edge_level, @@ -613,7 +618,7 @@ acpi_pci_link_get_irq ( struct acpi_device *device = NULL; struct acpi_pci_link *link = NULL; - ACPI_FUNCTION_TRACE("acpi_pci_link_get_irq"); + ACPI_FUNCTION_TRACE("acpi_pci_link_allocate_irq"); result = acpi_bus_get_device(handle, &device); if (result) { @@ -633,21 +638,70 @@ acpi_pci_link_get_irq ( return_VALUE(-1); } - if (acpi_pci_link_allocate(link)) + down(&acpi_link_lock); + if (acpi_pci_link_allocate(link)) { + up(&acpi_link_lock); return_VALUE(-1); + } if (!link->irq.active) { + up(&acpi_link_lock); ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link active IRQ is 0!\n")); return_VALUE(-1); } + link->refcnt ++; + up(&acpi_link_lock); if (edge_level) *edge_level = link->irq.edge_level; if (active_high_low) *active_high_low = link->irq.active_high_low; if (name) *name = acpi_device_bid(link->device); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Link %s is referenced\n", acpi_device_bid(link->device))); return_VALUE(link->irq.active); } +/* + * We don't change link's irq information here. After it is reenabled, we + * continue use the info + */ +int +acpi_pci_link_free_irq(acpi_handle handle) +{ + struct acpi_device *device = NULL; + struct acpi_pci_link *link = NULL; + acpi_status result; + ACPI_FUNCTION_TRACE("acpi_pci_link_free_irq"); + + result = acpi_bus_get_device(handle, &device); + if (result) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link device\n")); + return_VALUE(-1); + } + + link = (struct acpi_pci_link *) acpi_driver_data(device); + if (!link) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n")); + return_VALUE(-1); + } + + down(&acpi_link_lock); + if (!link->irq.initialized) { + up(&acpi_link_lock); + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link isn't initialized\n")); + return_VALUE(-1); + } + + link->refcnt --; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Link %s is dereferenced\n", acpi_device_bid(link->device))); + + if (link->refcnt == 0) { + acpi_ut_evaluate_object(link->handle, "_DIS", 0, NULL); + } + up(&acpi_link_lock); + return_VALUE(link->irq.active); +} /* -------------------------------------------------------------------------- Driver Interface -------------------------------------------------------------------------- */ @@ -677,6 +731,7 @@ acpi_pci_link_add ( strcpy(acpi_device_class(device), ACPI_PCI_LINK_CLASS); acpi_driver_data(device) = link; + down(&acpi_link_lock); result = acpi_pci_link_get_possible(link); if (result) goto end; @@ -712,6 +767,7 @@ acpi_pci_link_add ( end: /* disable all links -- to be activated on use */ acpi_ut_evaluate_object(link->handle, "_DIS", 0, NULL); + up(&acpi_link_lock); if (result) kfree(link); @@ -726,19 +782,32 @@ irqrouter_suspend( { struct list_head *node = NULL; struct acpi_pci_link *link = NULL; + int ret = 0; ACPI_FUNCTION_TRACE("irqrouter_suspend"); list_for_each(node, &acpi_link.entries) { link = list_entry(node, struct acpi_pci_link, node); if (!link) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n")); + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Invalid link context\n")); continue; } - if (link->irq.active && link->irq.initialized) - link->irq.suspend_resume = 1; + if (link->irq.initialized && link->refcnt != 0 + /* We ignore legacy IDE device irq */ + && link->irq.active != 14 && link->irq.active !=15) { + printk(KERN_WARNING PREFIX + "%d drivers with interrupt %d neglected to call" + " pci_disable_device at .suspend\n", + link->refcnt, + link->irq.active); + printk(KERN_WARNING PREFIX + "Fix the driver, or rmmod before suspend\n"); + link->refcnt = 0; + ret = -EINVAL; + } } - return_VALUE(0); + return_VALUE(ret); } @@ -756,8 +825,9 @@ acpi_pci_link_remove ( link = (struct acpi_pci_link *) acpi_driver_data(device); - /* TBD: Acquire/release lock */ + down(&acpi_link_lock); list_del(&link->node); + up(&acpi_link_lock); kfree(link); @@ -849,6 +919,7 @@ int __init acpi_irq_balance_set(char *str) __setup("acpi_irq_balance", acpi_irq_balance_set); +/* FIXME: we will remove this interface after all drivers call pci_disable_device */ static struct sysdev_class irqrouter_sysdev_class = { set_kset_name("irqrouter"), .suspend = irqrouter_suspend, diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index e00d9289201b..13f092977c0c 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -56,8 +56,9 @@ /* ACPI PCI Interrupt Link (pci_link.c) */ int acpi_irq_penalty_init (void); -int acpi_pci_link_get_irq (acpi_handle handle, int index, int *edge_level, +int acpi_pci_link_allocate_irq (acpi_handle handle, int index, int *edge_level, int *active_high_low, char **name); +int acpi_pci_link_free_irq(acpi_handle handle); /* ACPI PCI Interrupt Routing (pci_irq.c) */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 9c14959bcfa0..ca0cd240cee0 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -440,9 +440,7 @@ int acpi_gsi_to_irq (u32 gsi, unsigned int *irq); * If this matches the last registration, any IRQ resources for gsi * are freed. */ -#ifdef CONFIG_ACPI_DEALLOCATE_IRQ void acpi_unregister_gsi (u32 gsi); -#endif #ifdef CONFIG_ACPI_PCI @@ -467,9 +465,7 @@ struct pci_dev; int acpi_pci_irq_enable (struct pci_dev *dev); void acpi_penalize_isa_irq(int irq, int active); -#ifdef CONFIG_ACPI_DEALLOCATE_IRQ void acpi_pci_irq_disable (struct pci_dev *dev); -#endif struct acpi_pci_driver { struct acpi_pci_driver *next; From 141d751e264c16db554f6681d84056ebde0e8078 Mon Sep 17 00:00:00 2001 From: Robert Love Date: Fri, 29 Jul 2005 22:59:16 -0700 Subject: [PATCH 09/18] [PATCH] ppc32: inotify syscalls Add inotify system call stubs to PPC32. Signed-off-by: Robert Love Acked-by: Paul Mackerras Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ppc/kernel/misc.S | 3 +++ include/asm-ppc/unistd.h | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index 191a8def3bdb..c2fb85b49a11 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -1451,3 +1451,6 @@ _GLOBAL(sys_call_table) .long sys_waitid .long sys_ioprio_set .long sys_ioprio_get + .long sys_inotify_init /* 275 */ + .long sys_inotify_add_watch + .long sys_inotify_rm_watch diff --git a/include/asm-ppc/unistd.h b/include/asm-ppc/unistd.h index a7894e0fbbb1..3173ab3d2eb9 100644 --- a/include/asm-ppc/unistd.h +++ b/include/asm-ppc/unistd.h @@ -279,8 +279,11 @@ #define __NR_waitid 272 #define __NR_ioprio_set 273 #define __NR_ioprio_get 274 +#define __NR_inotify_init 275 +#define __NR_inotify_add_watch 276 +#define __NR_inotify_rm_watch 277 -#define __NR_syscalls 275 +#define __NR_syscalls 278 #define __NR(n) #n From 5fa918b451f625870cd4275ca908b2392ee86a51 Mon Sep 17 00:00:00 2001 From: Robert Love Date: Fri, 29 Jul 2005 22:59:17 -0700 Subject: [PATCH 10/18] [PATCH] ppc64: inotify syscalls inotify system call support for PPC64 [ I don't think we need sys32 compatibility versions--and if we do, I failed in life. ] Signed-off-by: Robert Love Acked-by: Paul Mackerras Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ppc64/kernel/misc.S | 6 ++++++ include/asm-ppc64/unistd.h | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/arch/ppc64/kernel/misc.S b/arch/ppc64/kernel/misc.S index 59f4f9973818..a05b50b738e9 100644 --- a/arch/ppc64/kernel/misc.S +++ b/arch/ppc64/kernel/misc.S @@ -1129,6 +1129,9 @@ _GLOBAL(sys_call_table32) .llong .compat_sys_waitid .llong .sys32_ioprio_set .llong .sys32_ioprio_get + .llong .sys_inotify_init /* 275 */ + .llong .sys_inotify_add_watch + .llong .sys_inotify_rm_watch .balign 8 _GLOBAL(sys_call_table) @@ -1407,3 +1410,6 @@ _GLOBAL(sys_call_table) .llong .sys_waitid .llong .sys_ioprio_set .llong .sys_ioprio_get + .llong .sys_inotify_init /* 275 */ + .llong .sys_inotify_add_watch + .llong .sys_inotify_rm_watch diff --git a/include/asm-ppc64/unistd.h b/include/asm-ppc64/unistd.h index 4a94acf6bfed..977bc980c1af 100644 --- a/include/asm-ppc64/unistd.h +++ b/include/asm-ppc64/unistd.h @@ -285,8 +285,11 @@ #define __NR_waitid 272 #define __NR_ioprio_set 273 #define __NR_ioprio_get 274 +#define __NR_inotify_init 275 +#define __NR_inotify_add_watch 276 +#define __NR_inotify_rm_watch 277 -#define __NR_syscalls 275 +#define __NR_syscalls 278 #ifdef __KERNEL__ #define NR_syscalls __NR_syscalls #endif From e310fd43256b3cf4d37f6447b8f7413ca744657a Mon Sep 17 00:00:00 2001 From: "Martin J. Bligh" Date: Fri, 29 Jul 2005 22:59:18 -0700 Subject: [PATCH 11/18] [PATCH] Fix NUMA node sizing in nr_free_zone_pages We are iterating over all nodes in nr_free_zone_pages(). Because the fallback zonelists contain all nodes in the system, and we walk all the zonelists, we're counting memory multiple times (once for each node). This caused us to make a size estimate of 32GB for an 8GB AMD64 box, which makes all the dirty ratio calculations, etc incorrect. There's still a further bug to fix from e820 holes causing overestimation as well, but this fix is separate, and good as is, and fixes one class of problems. Problem found by Badari, and tested by Ram Pai - thanks! Signed-off-by: Martin J. Bligh Signed-off-by: Matt Dobson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- mm/page_alloc.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 42bccfb8464d..8d088371196a 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1061,20 +1061,19 @@ unsigned int nr_free_pages_pgdat(pg_data_t *pgdat) static unsigned int nr_free_zone_pages(int offset) { - pg_data_t *pgdat; + /* Just pick one node, since fallback list is circular */ + pg_data_t *pgdat = NODE_DATA(numa_node_id()); unsigned int sum = 0; - for_each_pgdat(pgdat) { - struct zonelist *zonelist = pgdat->node_zonelists + offset; - struct zone **zonep = zonelist->zones; - struct zone *zone; + struct zonelist *zonelist = pgdat->node_zonelists + offset; + struct zone **zonep = zonelist->zones; + struct zone *zone; - for (zone = *zonep++; zone; zone = *zonep++) { - unsigned long size = zone->present_pages; - unsigned long high = zone->pages_high; - if (size > high) - sum += size - high; - } + for (zone = *zonep++; zone; zone = *zonep++) { + unsigned long size = zone->present_pages; + unsigned long high = zone->pages_high; + if (size > high) + sum += size - high; } return sum; From 5ce17b18e16177dd6409dabd38df5c2c9b58fc2d Mon Sep 17 00:00:00 2001 From: Eugene Surovegin Date: Fri, 29 Jul 2005 22:59:19 -0700 Subject: [PATCH 12/18] [PATCH] ppc32: fix 44x early serial debug for configurations with more than 512M of RAM Fix 44x early serial debugging for big RAM configurations (more than 512M). We cannot use default OpenBIOS virtual mapping, because it interferes with pinned TLB entry. While we are at it, move early UART mapping to TLB slot 0, so it can survive longer during boot process (slot 1 is used by the first ioremap call, effectively killing UART mapping if it occupies this slot). Also, change UART TLB entry size to 4K (256M is too much for a bunch of registers :). Squash some warnings on the way. Tested on Ebony and Ocotea with 1G of RAM. Thanks to Scott Coulter for diagnosing this problem. Signed-off-by: Eugene Surovegin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/ppc/kernel/head_44x.S | 12 ++++++------ arch/ppc/platforms/4xx/ebony.c | 6 +++++- arch/ppc/platforms/4xx/ebony.h | 13 +++++++++++-- arch/ppc/platforms/4xx/ocotea.c | 4 ++++ arch/ppc/platforms/4xx/ocotea.h | 13 +++++++++++-- 5 files changed, 37 insertions(+), 11 deletions(-) diff --git a/arch/ppc/kernel/head_44x.S b/arch/ppc/kernel/head_44x.S index 6c7ae6052464..72ee8f33bde4 100644 --- a/arch/ppc/kernel/head_44x.S +++ b/arch/ppc/kernel/head_44x.S @@ -179,14 +179,14 @@ skpinv: addi r4,r4,1 /* Increment */ 4: #ifdef CONFIG_SERIAL_TEXT_DEBUG /* - * Add temporary UART mapping for early debug. This - * mapping must be identical to that used by the early - * bootloader code since the same asm/serial.h parameters - * are used for polled operation. + * Add temporary UART mapping for early debug. + * We can map UART registers wherever we want as long as they don't + * interfere with other system mappings (e.g. with pinned entries). + * For an example of how we handle this - see ocotea.h. --ebs */ /* pageid fields */ lis r3,UART0_IO_BASE@h - ori r3,r3,PPC44x_TLB_VALID | PPC44x_TLB_256M + ori r3,r3,PPC44x_TLB_VALID | PPC44x_TLB_4K /* xlat fields */ lis r4,UART0_PHYS_IO_BASE@h /* RPN depends on SoC */ @@ -196,7 +196,7 @@ skpinv: addi r4,r4,1 /* Increment */ li r5,0 ori r5,r5,(PPC44x_TLB_SW | PPC44x_TLB_SR | PPC44x_TLB_I | PPC44x_TLB_G) - li r0,1 /* TLB slot 1 */ + li r0,0 /* TLB slot 0 */ tlbwe r3,r0,PPC44x_TLB_PAGEID /* Load the pageid fields */ tlbwe r4,r0,PPC44x_TLB_XLAT /* Load the translation fields */ diff --git a/arch/ppc/platforms/4xx/ebony.c b/arch/ppc/platforms/4xx/ebony.c index cd11734ef7c5..509e69a095f0 100644 --- a/arch/ppc/platforms/4xx/ebony.c +++ b/arch/ppc/platforms/4xx/ebony.c @@ -7,7 +7,7 @@ * Copyright 2002-2005 MontaVista Software Inc. * * Eugene Surovegin or - * Copyright (c) 2003, 2004 Zultys Technologies + * Copyright (c) 2003-2005 Zultys Technologies * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -50,6 +50,7 @@ #include #include #include +#include #include #include @@ -248,6 +249,9 @@ ebony_early_serial_map(void) #if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) /* Configure debug serial access */ gen550_init(0, &port); + + /* Purge TLB entry added in head_44x.S for early serial access */ + _tlbie(UART0_IO_BASE); #endif port.membase = ioremap64(PPC440GP_UART1_ADDR, 8); diff --git a/arch/ppc/platforms/4xx/ebony.h b/arch/ppc/platforms/4xx/ebony.h index 47c391c9174d..d08faa46a0ae 100644 --- a/arch/ppc/platforms/4xx/ebony.h +++ b/arch/ppc/platforms/4xx/ebony.h @@ -56,9 +56,18 @@ * Serial port defines */ -/* OpenBIOS defined UART mappings, used before early_serial_setup */ +#if defined(__BOOTER__) +/* OpenBIOS defined UART mappings, used by bootloader shim */ #define UART0_IO_BASE 0xE0000200 #define UART1_IO_BASE 0xE0000300 +#else +/* head_44x.S created UART mapping, used before early_serial_setup. + * We cannot use default OpenBIOS UART mappings because they + * don't work for configurations with more than 512M RAM. --ebs + */ +#define UART0_IO_BASE 0xF0000200 +#define UART1_IO_BASE 0xF0000300 +#endif /* external Epson SG-615P */ #define BASE_BAUD 691200 @@ -66,7 +75,7 @@ #define STD_UART_OP(num) \ { 0, BASE_BAUD, 0, UART##num##_INT, \ (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ - iomem_base: UART##num##_IO_BASE, \ + iomem_base: (void*)UART##num##_IO_BASE, \ io_type: SERIAL_IO_MEM}, #define SERIAL_PORT_DFNS \ diff --git a/arch/ppc/platforms/4xx/ocotea.c b/arch/ppc/platforms/4xx/ocotea.c index 5f82a6bc7046..8fc34a344769 100644 --- a/arch/ppc/platforms/4xx/ocotea.c +++ b/arch/ppc/platforms/4xx/ocotea.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include @@ -266,6 +267,9 @@ ocotea_early_serial_map(void) #if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) /* Configure debug serial access */ gen550_init(0, &port); + + /* Purge TLB entry added in head_44x.S for early serial access */ + _tlbie(UART0_IO_BASE); #endif port.membase = ioremap64(PPC440GX_UART1_ADDR, 8); diff --git a/arch/ppc/platforms/4xx/ocotea.h b/arch/ppc/platforms/4xx/ocotea.h index 202dc8251190..33251153ac5f 100644 --- a/arch/ppc/platforms/4xx/ocotea.h +++ b/arch/ppc/platforms/4xx/ocotea.h @@ -55,15 +55,24 @@ */ #define RS_TABLE_SIZE 2 -/* OpenBIOS defined UART mappings, used before early_serial_setup */ +#if defined(__BOOTER__) +/* OpenBIOS defined UART mappings, used by bootloader shim */ #define UART0_IO_BASE 0xE0000200 #define UART1_IO_BASE 0xE0000300 +#else +/* head_44x.S created UART mapping, used before early_serial_setup. + * We cannot use default OpenBIOS UART mappings because they + * don't work for configurations with more than 512M RAM. --ebs + */ +#define UART0_IO_BASE 0xF0000200 +#define UART1_IO_BASE 0xF0000300 +#endif #define BASE_BAUD 11059200/16 #define STD_UART_OP(num) \ { 0, BASE_BAUD, 0, UART##num##_INT, \ (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ - iomem_base: UART##num##_IO_BASE, \ + iomem_base: (void*)UART##num##_IO_BASE, \ io_type: SERIAL_IO_MEM}, #define SERIAL_PORT_DFNS \ From 92ed0223aefa795d1873427e25599cb70b2148ee Mon Sep 17 00:00:00 2001 From: Dave Peterson Date: Fri, 29 Jul 2005 22:59:20 -0700 Subject: [PATCH 13/18] [PATCH] x86_64: fix bug in csum_partial_copy_generic() I was observing reproducible crashes on the "movw %bx,(%rsi)" instruction below while a process in a recvfrom() system call was copying packet data to user space. The patch below fixes the exception table and causes the crash to no longer reproduce. Please apply. Acked-by: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86_64/lib/csum-copy.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86_64/lib/csum-copy.S b/arch/x86_64/lib/csum-copy.S index 01808ec37836..72fd55ee896e 100644 --- a/arch/x86_64/lib/csum-copy.S +++ b/arch/x86_64/lib/csum-copy.S @@ -188,8 +188,8 @@ csum_partial_copy_generic: source movw (%rdi),%bx adcl %ebx,%eax - dest decl %ecx + dest movw %bx,(%rsi) leaq 2(%rdi),%rdi leaq 2(%rsi),%rsi From d7a465b4740806e4ee34061ad455595b2fa9fad5 Mon Sep 17 00:00:00 2001 From: "Antonino A. Daplas" Date: Fri, 29 Jul 2005 22:59:21 -0700 Subject: [PATCH 14/18] [PATCH] vesafb: Document mtrr boot option usage Document mtrr boot option usage to Documentation/fb/vesafb.txt. Signed-off-by: Antonino Daplas Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/fb/vesafb.txt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Documentation/fb/vesafb.txt b/Documentation/fb/vesafb.txt index 814e2f56a6ad..62db6758d1c1 100644 --- a/Documentation/fb/vesafb.txt +++ b/Documentation/fb/vesafb.txt @@ -144,7 +144,21 @@ vgapal Use the standard vga registers for palette changes. This is the default. pmipal Use the protected mode interface for palette changes. -mtrr setup memory type range registers for the vesafb framebuffer. +mtrr:n setup memory type range registers for the vesafb framebuffer + where n: + 0 - disabled (equivalent to nomtrr) + 1 - uncachable + 2 - write-back + 3 - write-combining (default) + 4 - write-through + + If you see the following in dmesg, choose the type that matches the + old one. In this example, use "mtrr:2". +... +mtrr: type mismatch for e0000000,8000000 old: write-back new: write-combining +... + +nomtrr disable mtrr vremap:n remap 'n' MiB of video RAM. If 0 or not specified, remap memory From c70f5d6610c601ea2ae4ae4e49f66c80801e895f Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 30 Jul 2005 10:22:49 -0700 Subject: [PATCH 15/18] [PATCH] revert bogus softirq changes This snuck in with an x86_64 change. Thanks to Richard Purdie for spotting it. Cc: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/softirq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/softirq.c b/kernel/softirq.c index 31007d6542cc..b4ab6af1dea8 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -86,7 +86,7 @@ asmlinkage void __do_softirq(void) /* Reset the pending bitmask before enabling irqs */ local_softirq_pending() = 0; - //local_irq_enable(); + local_irq_enable(); h = softirq_vec; @@ -99,7 +99,7 @@ asmlinkage void __do_softirq(void) pending >>= 1; } while (pending); - //local_irq_disable(); + local_irq_disable(); pending = local_softirq_pending(); if (pending && --max_restart) From 6a1caa21d66bcb9ba9892435a0a54fc32cd9eeab Mon Sep 17 00:00:00 2001 From: "Natalie.Protasevich@unisys.com" Date: Sat, 30 Jul 2005 11:25:32 -0700 Subject: [PATCH 16/18] [PATCH] x86_64: avoid wasting IRQs patch update The patch adds boundary check for the MAX_GSI_NUM. Same as the update for i386, the patch addresses a problem with ACPI SCI IRQ. The patch corrects the code such that SCI IRQ is skipped and duplicate entry is avoided. The VIA chipset uses 4-bit IRQ register for internal interrupt routing, and therefore cannot handle IRQ numbers assigned to its devices. The patch corrects this problem by allowing PCI IRQs below 16. Signed-off-by: Natalie Protasevich Acked-by: Andi Kleen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86_64/kernel/mpparse.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/arch/x86_64/kernel/mpparse.c b/arch/x86_64/kernel/mpparse.c index 08abf9f5b159..79c362d03e2e 100644 --- a/arch/x86_64/kernel/mpparse.c +++ b/arch/x86_64/kernel/mpparse.c @@ -970,8 +970,21 @@ int mp_register_gsi(u32 gsi, int edge_level, int active_high_low) * due to unused I/O APIC pins. */ int irq = gsi; - gsi = pci_irq++; - gsi_to_irq[irq] = gsi; + if (gsi < MAX_GSI_NUM) { + if (gsi > 15) + gsi = pci_irq++; +#ifdef CONFIG_ACPI_BUS + /* + * Don't assign IRQ used by ACPI SCI + */ + if (gsi == acpi_fadt.sci_int) + gsi = pci_irq++; +#endif + gsi_to_irq[irq] = gsi; + } else { + printk(KERN_ERR "GSI %u is too high\n", gsi); + return gsi; + } } io_apic_set_pci_routing(ioapic, ioapic_pin, gsi, From 035a4a4f8976bdf12aab992c630d3a6cfba90ea8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 30 Jul 2005 13:12:18 -0700 Subject: [PATCH 17/18] [PATCH] sk98lin: basic suspend/resume support fixes An early version of the sk98lin patch was merged via Len's tree. But there were subsequent updates as a result of review from Jeff. THis fixes things up. Signed-off-by: Rafael J. Wysocki Cc: Jeff Garzik Cc: "Brown, Len" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/net/sk98lin/skge.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/drivers/net/sk98lin/skge.c b/drivers/net/sk98lin/skge.c index 074521a36341..49bd8c7c3f7d 100644 --- a/drivers/net/sk98lin/skge.c +++ b/drivers/net/sk98lin/skge.c @@ -5141,17 +5141,18 @@ static int skge_suspend(struct pci_dev *pdev, pm_message_t state) SK_AC *pAC = pNet->pAC; struct net_device *otherdev = pAC->dev[1]; - if (pNet->Up) { - pAC->WasIfUp[0] = SK_TRUE; + if (netif_running(dev)) { + netif_carrier_off(dev); DoPrintInterfaceChange = SK_FALSE; SkDrvDeInitAdapter(pAC, 0); /* performs SkGeClose */ + netif_device_detach(dev); } if (otherdev != dev) { - pNet = netdev_priv(otherdev); - if (pNet->Up) { - pAC->WasIfUp[1] = SK_TRUE; + if (netif_running(otherdev)) { + netif_carrier_off(otherdev); DoPrintInterfaceChange = SK_FALSE; SkDrvDeInitAdapter(pAC, 1); /* performs SkGeClose */ + netif_device_detach(otherdev); } } @@ -5171,23 +5172,36 @@ static int skge_resume(struct pci_dev *pdev) struct net_device *dev = pci_get_drvdata(pdev); DEV_NET *pNet = netdev_priv(dev); SK_AC *pAC = pNet->pAC; + struct net_device *otherdev = pAC->dev[1]; + int ret; pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); pci_enable_device(pdev); pci_set_master(pdev); if (pAC->GIni.GIMacsFound == 2) - request_irq(dev->irq, SkGeIsr, SA_SHIRQ, pAC->Name, dev); + ret = request_irq(dev->irq, SkGeIsr, SA_SHIRQ, pAC->Name, dev); else - request_irq(dev->irq, SkGeIsrOnePort, SA_SHIRQ, pAC->Name, dev); + ret = request_irq(dev->irq, SkGeIsrOnePort, SA_SHIRQ, pAC->Name, dev); + if (ret) { + printk(KERN_WARNING "sk98lin: unable to acquire IRQ %d\n", dev->irq); + pAC->AllocFlag &= ~SK_ALLOC_IRQ; + dev->irq = 0; + pci_disable_device(pdev); + return -EBUSY; + } - if (pAC->WasIfUp[0] == SK_TRUE) { + netif_device_attach(dev); + if (netif_running(dev)) { DoPrintInterfaceChange = SK_FALSE; SkDrvInitAdapter(pAC, 0); /* first device */ - } - if (pAC->dev[1] != dev && pAC->WasIfUp[1] == SK_TRUE) { - DoPrintInterfaceChange = SK_FALSE; - SkDrvInitAdapter(pAC, 1); /* first device */ + } + if (otherdev != dev) { + netif_device_attach(otherdev); + if (netif_running(otherdev)) { + DoPrintInterfaceChange = SK_FALSE; + SkDrvInitAdapter(pAC, 1); /* second device */ + } } return 0; From 889371f61fd5bb914d0331268f12432590cf7e85 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 30 Jul 2005 13:41:56 -0700 Subject: [PATCH 18/18] Revert "yenta free_irq on suspend" ACPI is wrong. Devices should not release their IRQ's on suspend and re-aquire them on resume. ACPI should just re-init the IRQ controller instead of breaking most drivers very subtly. Breakage reported by Hugh Dickins Undo: d8c4b4195c7d664baf296818bf756775149232d3 Signed-off-by: Linus Torvalds --- drivers/pcmcia/yenta_socket.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c index 744e469a9eda..6837491f021c 100644 --- a/drivers/pcmcia/yenta_socket.c +++ b/drivers/pcmcia/yenta_socket.c @@ -1107,8 +1107,6 @@ static int yenta_dev_suspend (struct pci_dev *dev, pm_message_t state) pci_read_config_dword(dev, 17*4, &socket->saved_state[1]); pci_disable_device(dev); - free_irq(dev->irq, socket); - /* * Some laptops (IBM T22) do not like us putting the Cardbus * bridge into D3. At a guess, some other laptop will @@ -1134,13 +1132,6 @@ static int yenta_dev_resume (struct pci_dev *dev) pci_enable_device(dev); pci_set_master(dev); - if (socket->cb_irq) - if (request_irq(socket->cb_irq, yenta_interrupt, - SA_SHIRQ, "yenta", socket)) { - printk(KERN_WARNING "Yenta: request_irq() failed on resume!\n"); - socket->cb_irq = 0; - } - if (socket->type && socket->type->restore_state) socket->type->restore_state(socket); }