Restore udelay in poll mode
PATCH 1/6 Restore udelay in poll mode
Allow EC to serve keyboard even then we polling it.
---
drivers/acpi/ec.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
drivers/acpi/ec.c +2 -0
@@ -73,6 +73,7 @@ enum ec_event {#define ACPI_EC_DELAY 500/* Wait 500ms max. during EC ops */ #define ACPI_EC_UDELAY_GLK 1000/* Wait 1ms max. to get global lock */ +#define ACPI_EC_UDELAY 100/* Wait 100us before polling EC again */ enum {EC_FLAGS_WAIT_GPE =0 ,/* Don't check status until GPE arrives */
@@ -227,6 +228,7 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll)while (time_before (jiffies, delay)) {if (acpi_ec_check_status (ec, event)) goto end; +udelay (ACPI_EC_UDELAY ); } } pr_err(PREFIX "acpi_ec_wait timeout,"
PATCH 2/6 ACPI: EC: Add poll timer
If we can not use interrupt mode of EC for some reason, start polling EC for events periodically.
---
drivers/acpi/ec.c | 43 +++++++++++++++++++++++++++++++++++++++---- 1 files changed, 39 insertions(+), 4 deletions(-)
drivers/acpi/ec.c +39 -4
@@ -84,6 +84,7 @@ enum {EC_FLAGS_NO_WDATA_GPE ,/* Don't expect WDATA GPE event */ EC_FLAGS_WDATA ,/* Data is being written */ EC_FLAGS_NO_OBF1_GPE ,/* Don't expect GPE before read */ + EC_FLAGS_RESCHEDULE_POLL /* Re-schedule poll */ };static int acpi_ec_remove (struct acpi_device *device,int type);
@@ -130,6 +131,7 @@ static struct acpi_ec {struct mutex lock;wait_queue_head_t wait;struct list_head list; +struct delayed_work work;u8 handlers_installed; } *boot_ec, *first_ec;
@@ -178,6 +180,20 @@ static inline int acpi_ec_check_status(struct acpi_ec *ec, enum ec_event event)return 0 ; } +static void ec_schedule_ec_poll (struct acpi_ec *ec) +{ +if (test_bit (EC_FLAGS_RESCHEDULE_POLL , &ec->flags)) +schedule_delayed_work (&ec->work, +msecs_to_jiffies (ACPI_EC_DELAY )); +} + +static void ec_switch_to_poll_mode (struct acpi_ec *ec) +{ +clear_bit (EC_FLAGS_GPE_MODE , &ec->flags); +acpi_disable_gpe (NULL , ec->gpe,ACPI_NOT_ISR ); +set_bit (EC_FLAGS_RESCHEDULE_POLL , &ec->flags); +} +static int acpi_ec_wait (struct acpi_ec *ec,enum ec_event event,int force_poll) {int ret =0 ;
@@ -218,7 +234,8 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll)if (printk_ratelimit ())pr_info (PREFIX "missing confirmations, " "switch off interrupt mode.\n" ); -clear_bit (EC_FLAGS_GPE_MODE , &ec->flags); +ec_switch_to_poll_mode (ec); +ec_schedule_ec_poll (ec); } goto end; }
@@ -529,28 +546,37 @@ static u32 acpi_ec_gpe_handler(void *data) {acpi_status status =AE_OK ;struct acpi_ec *ec = data; +u8 state =acpi_ec_read_status (ec);pr_debug (PREFIX "~~~> interrupt\n" );clear_bit (EC_FLAGS_WAIT_GPE , &ec->flags);if (test_bit (EC_FLAGS_GPE_MODE , &ec->flags))wake_up (&ec->wait); -if (acpi_ec_read_status (ec) &ACPI_EC_FLAG_SCI ) { +if (state &ACPI_EC_FLAG_SCI ) {if (!test_and_set_bit (EC_FLAGS_QUERY_PENDING , &ec->flags)) status =acpi_os_execute (OSL_EC_BURST_HANDLER , acpi_ec_gpe_query, ec); - }else if (unlikely (!test_bit (EC_FLAGS_GPE_MODE , &ec->flags))) { + }else if (!test_bit (EC_FLAGS_GPE_MODE , &ec->flags) && +in_interrupt ()) {/* this is non-query, must be confirmation */ if (printk_ratelimit ())pr_info (PREFIX "non-query interrupt received," " switching to interrupt mode\n" );set_bit (EC_FLAGS_GPE_MODE , &ec->flags); +clear_bit (EC_FLAGS_RESCHEDULE_POLL , &ec->flags); } - +ec_schedule_ec_poll (ec);return ACPI_SUCCESS (status) ?ACPI_INTERRUPT_HANDLED :ACPI_INTERRUPT_NOT_HANDLED ; } +static void do_ec_poll (struct work_struct *work) +{ + struct acpi_ec *ec = container_of(work, struct acpi_ec, work.work); + (void )acpi_ec_gpe_handler (ec); +} +/* -------------------------------------------------------------------------- Address Space Management -------------------------------------------------------------------------- */
@@ -711,6 +737,7 @@ static struct acpi_ec *make_acpi_ec(void)mutex_init (&ec->lock);init_waitqueue_head (&ec->wait);INIT_LIST_HEAD (&ec->list); +INIT_DELAYED_WORK_DEFERRABLE (&ec->work, do_ec_poll);return ec; }
@@ -752,8 +779,15 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval)return AE_CTRL_TERMINATE ; } +static void ec_poll_stop (struct acpi_ec *ec) +{ +clear_bit (EC_FLAGS_RESCHEDULE_POLL , &ec->flags); +cancel_delayed_work (&ec->work); +} +static void ec_remove_handlers (struct acpi_ec *ec) { +ec_poll_stop (ec);if (ACPI_FAILURE (acpi_remove_address_space_handler (ec->handle,ACPI_ADR_SPACE_EC , &acpi_ec_space_handler)))pr_err (PREFIX "failed to remove space handler\n" );
@@ -899,6 +933,7 @@ static int acpi_ec_start(struct acpi_device *device)/* EC is fully operational, allow queries */ clear_bit (EC_FLAGS_QUERY_PENDING , &ec->flags); +ec_schedule_ec_poll (ec);return ret; }
PATCH 3/6 Improve debug output
In reply to Alexey Starikovskiy---
drivers/acpi/ec.c | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-)
drivers/acpi/ec.c +4 -4
@@ -248,9 +248,9 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll)udelay (ACPI_EC_UDELAY ); } } -pr_err (PREFIX "acpi_ec_wait timeout," -" status = %d, expect_event = %d\n" , -acpi_ec_read_status (ec), event); +pr_err (PREFIX "acpi_ec_wait timeout, status = 0x%2.2x, event = %s\n" , +acpi_ec_read_status (ec), + (event ==ACPI_EC_EVENT_OBF_1 ) ?"\"b0=1\"" :"\"b1=0\"" ); ret = -ETIME ; end:clear_bit (EC_FLAGS_ADDRESS , &ec->flags);
@@ -264,8 +264,8 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, {int result =0 ;set_bit (EC_FLAGS_WAIT_GPE , &ec->flags); -acpi_ec_write_cmd (ec, command);pr_debug (PREFIX "transaction start\n" ); +acpi_ec_write_cmd (ec, command);for (; wdata_len >0 ; --wdata_len) { result =acpi_ec_wait (ec,ACPI_EC_EVENT_IBF_0 , force_poll);if (result) {
PATCH 4/6 Drop support for broken controllers
In reply to Alexey StarikovskiyPut all broken controllers into fully functional poll mode
---
drivers/acpi/ec.c | 61 ++++++++++------------------------------------------- 1 files changed, 12 insertions(+), 49 deletions(-)
drivers/acpi/ec.c +12 -49
@@ -79,11 +79,7 @@ enum {EC_FLAGS_WAIT_GPE =0 ,/* Don't check status until GPE arrives */ EC_FLAGS_QUERY_PENDING ,/* Query is pending */ EC_FLAGS_GPE_MODE ,/* Expect GPE to be sent for status change */ -EC_FLAGS_NO_ADDRESS_GPE ,/* Expect GPE only for non-address event */ -EC_FLAGS_ADDRESS ,/* Address is being written */ -EC_FLAGS_NO_WDATA_GPE ,/* Don't expect WDATA GPE event */ -EC_FLAGS_WDATA ,/* Data is being written */ -EC_FLAGS_NO_OBF1_GPE ,/* Don't expect GPE before read */ +EC_FLAGS_NO_GPE ,/* Don't use GPE mode */ EC_FLAGS_RESCHEDULE_POLL /* Re-schedule poll */ };
@@ -189,6 +185,7 @@ static void ec_schedule_ec_poll(struct acpi_ec *ec)static void ec_switch_to_poll_mode (struct acpi_ec *ec) { +set_bit (EC_FLAGS_NO_GPE , &ec->flags);clear_bit (EC_FLAGS_GPE_MODE , &ec->flags);acpi_disable_gpe (NULL , ec->gpe,ACPI_NOT_ISR );set_bit (EC_FLAGS_RESCHEDULE_POLL , &ec->flags);
@@ -196,65 +193,34 @@ static void ec_switch_to_poll_mode(struct acpi_ec *ec)static int acpi_ec_wait (struct acpi_ec *ec,enum ec_event event,int force_poll) { -int ret =0 ; - -if (unlikely (event ==ACPI_EC_EVENT_OBF_1 && -test_bit (EC_FLAGS_NO_OBF1_GPE , &ec->flags))) - force_poll =1 ; -if (unlikely (test_bit (EC_FLAGS_ADDRESS , &ec->flags) && -test_bit (EC_FLAGS_NO_ADDRESS_GPE , &ec->flags))) - force_poll =1 ; -if (unlikely (test_bit (EC_FLAGS_WDATA , &ec->flags) && -test_bit (EC_FLAGS_NO_WDATA_GPE , &ec->flags))) - force_poll =1 ;if (likely (test_bit (EC_FLAGS_GPE_MODE , &ec->flags)) &&likely (!force_poll)) {if (wait_event_timeout (ec->wait,acpi_ec_check_status (ec, event),msecs_to_jiffies (ACPI_EC_DELAY ))) - goto end; +return 0 ;clear_bit (EC_FLAGS_WAIT_GPE , &ec->flags);if (acpi_ec_check_status (ec, event)) { -if (event ==ACPI_EC_EVENT_OBF_1 ) { -/* miss OBF_1 GPE, don't expect it */ -pr_info (PREFIX "missing OBF confirmation, " -"don't expect it any longer.\n" ); -set_bit (EC_FLAGS_NO_OBF1_GPE , &ec->flags); - }else if (test_bit (EC_FLAGS_ADDRESS , &ec->flags)) { -/* miss address GPE, don't expect it anymore */ -pr_info (PREFIX "missing address confirmation, " -"don't expect it any longer.\n" ); -set_bit (EC_FLAGS_NO_ADDRESS_GPE , &ec->flags); - }else if (test_bit (EC_FLAGS_WDATA , &ec->flags)) { -/* miss write data GPE, don't expect it */ -pr_info (PREFIX "missing write data confirmation, " -"don't expect it any longer.\n" ); -set_bit (EC_FLAGS_NO_WDATA_GPE , &ec->flags); - }else { -/* missing GPEs, switch back to poll mode */ -if (printk_ratelimit ()) -pr_info (PREFIX "missing confirmations, " +/* missing GPEs, switch back to poll mode */ +if (printk_ratelimit ()) +pr_info (PREFIX "missing confirmations, " "switch off interrupt mode.\n" ); -ec_switch_to_poll_mode (ec); -ec_schedule_ec_poll (ec); - } - goto end; +ec_switch_to_poll_mode (ec); +ec_schedule_ec_poll (ec); +return 0 ; } }else {unsigned long delay = jiffies +msecs_to_jiffies (ACPI_EC_DELAY );clear_bit (EC_FLAGS_WAIT_GPE , &ec->flags);while (time_before (jiffies, delay)) {if (acpi_ec_check_status (ec, event)) - goto end; +return 0 ;udelay (ACPI_EC_UDELAY ); } }pr_err (PREFIX "acpi_ec_wait timeout, status = 0x%2.2x, event = %s\n" ,acpi_ec_read_status (ec), (event ==ACPI_EC_EVENT_OBF_1 ) ?"\"b0=1\"" :"\"b1=0\"" ); - ret = -ETIME ; - end: -clear_bit (EC_FLAGS_ADDRESS , &ec->flags); -return ret; +return -ETIME ; } static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command,
@@ -273,15 +239,11 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, "write_cmd timeout, command = %d\n", command); goto end; } -/* mark the address byte written to EC */ -if (rdata_len + wdata_len >1 ) -set_bit (EC_FLAGS_ADDRESS , &ec->flags);set_bit (EC_FLAGS_WAIT_GPE , &ec->flags);acpi_ec_write_data (ec, *(wdata++)); }if (!rdata_len) { -set_bit (EC_FLAGS_WDATA , &ec->flags); result =acpi_ec_wait (ec,ACPI_EC_EVENT_IBF_0 , force_poll);if (result) { pr_err(PREFIX
@@ -558,6 +520,7 @@ static u32 acpi_ec_gpe_handler(void *data) status =acpi_os_execute (OSL_EC_BURST_HANDLER , acpi_ec_gpe_query, ec); }else if (!test_bit (EC_FLAGS_GPE_MODE , &ec->flags) && + !test_bit (EC_FLAGS_NO_GPE , &ec->flags) &&in_interrupt ()) {/* this is non-query, must be confirmation */ if (printk_ratelimit ())
PATCH 5/6 ACPI: EC: Switch off GPE mode during suspend/resume
In reply to Alexey Starikovskiy---
drivers/acpi/ec.c | 60 ++++++++++++++++++++++++++++++++++------------------- 1 files changed, 38 insertions(+), 22 deletions(-)
drivers/acpi/ec.c +38 -22
@@ -83,28 +83,6 @@ enum { EC_FLAGS_RESCHEDULE_POLL /* Re-schedule poll */ }; -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_add (struct acpi_device *device); - -static const struct acpi_device_id ec_device_ids[] = { - {"PNP0C09" ,0 }, - {"" ,0 }, -}; - -static struct acpi_driver acpi_ec_driver = { - .name ="ec" , - .class =ACPI_EC_CLASS , - .ids = ec_device_ids, - .ops = { - .add = acpi_ec_add, - .remove = acpi_ec_remove, - .start = acpi_ec_start, - .stop = acpi_ec_stop, - }, -}; -/* If we find an EC via the ECDT, we need to keep a ptr to its context */ /* External interfaces use first EC only, so remember */ typedef int (*acpi_ec_query_func ) (void *data);
@@ -924,6 +902,11 @@ int __init acpi_boot_ec_enable(void)return -EFAULT ; } +static const struct acpi_device_id ec_device_ids[] = { + {"PNP0C09" ,0 }, + {"" ,0 }, +}; +int __init acpi_ec_ecdt_probe (void ) {int ret;
@@ -973,6 +956,39 @@ int __init acpi_ec_ecdt_probe(void)return -ENODEV ; } +static int acpi_ec_suspend (struct acpi_device *device,pm_message_t state) +{ +struct acpi_ec *ec =acpi_driver_data (device); +/* Stop using GPE */ +set_bit (EC_FLAGS_NO_GPE , &ec->flags); +clear_bit (EC_FLAGS_GPE_MODE , &ec->flags); +acpi_disable_gpe (NULL , ec->gpe,ACPI_NOT_ISR ); +return 0 ; +} + +static int acpi_ec_resume (struct acpi_device *device) +{ +struct acpi_ec *ec =acpi_driver_data (device); +/* Enable use of GPE back */ +clear_bit (EC_FLAGS_NO_GPE , &ec->flags); +acpi_enable_gpe (NULL , ec->gpe,ACPI_NOT_ISR ); +return 0 ; +} + +static struct acpi_driver acpi_ec_driver = { + .name ="ec" , + .class =ACPI_EC_CLASS , + .ids = ec_device_ids, + .ops = { + .add = acpi_ec_add, + .remove = acpi_ec_remove, + .start = acpi_ec_start, + .stop = acpi_ec_stop, + .suspend = acpi_ec_suspend, + .resume = acpi_ec_resume, + }, +}; +static int __init acpi_ec_init (void ) {int result =0 ;
PATCH 6/6 ACPI: EC: Detect irq storm
In reply to Alexey StarikovskiyProblem seems to be that hw fails to clear GPE after we service it and write 1 into corresponding bit. Thus, as soon as we get interrupts enabled again, we receive a new one. Google gives too many results for "acer interrupt storm" for this being one-broken-machine case.
Reference: http://bugzilla.kernel.org/show_bug.cgi?id=9998
---
drivers/acpi/ec.c | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-)
drivers/acpi/ec.c +10 -0
@@ -106,6 +106,7 @@ static struct acpi_ec {wait_queue_head_t wait;struct list_head list;struct delayed_work work; +atomic_t irq_count;u8 handlers_installed; } *boot_ec, *first_ec;
@@ -171,6 +172,7 @@ static void ec_switch_to_poll_mode(struct acpi_ec *ec)static int acpi_ec_wait (struct acpi_ec *ec,enum ec_event event,int force_poll) { +atomic_set (&ec->irq_count,0 );if (likely (test_bit (EC_FLAGS_GPE_MODE , &ec->flags)) &&likely (!force_poll)) { if (wait_event_timeout(ec->wait, acpi_ec_check_status(ec, event),
@@ -489,6 +491,12 @@ static u32 acpi_ec_gpe_handler(void *data)u8 state =acpi_ec_read_status (ec);pr_debug (PREFIX "~~~> interrupt\n" ); +atomic_inc (&ec->irq_count); +if (atomic_read (&ec->irq_count) >5 ) { +pr_err (PREFIX "GPE storm detected, disabling EC GPE\n" ); +ec_switch_to_poll_mode (ec); + goto end; + }clear_bit (EC_FLAGS_WAIT_GPE , &ec->flags);if (test_bit (EC_FLAGS_GPE_MODE , &ec->flags))wake_up (&ec->wait);
@@ -507,6 +515,7 @@ static u32 acpi_ec_gpe_handler(void *data)set_bit (EC_FLAGS_GPE_MODE , &ec->flags);clear_bit (EC_FLAGS_RESCHEDULE_POLL , &ec->flags); } +end:ec_schedule_ec_poll (ec);return ACPI_SUCCESS (status) ?ACPI_INTERRUPT_HANDLED :ACPI_INTERRUPT_NOT_HANDLED ;
@@ -679,6 +688,7 @@ static struct acpi_ec *make_acpi_ec(void)init_waitqueue_head (&ec->wait);INIT_LIST_HEAD (&ec->list);INIT_DELAYED_WORK_DEFERRABLE (&ec->work, do_ec_poll); +atomic_set (&ec->irq_count,0 );return ec; }
PATCH 1/1 ACPI: EC: Detect irq storm
Problem seems to be that hw fails to clear GPE after we service it and write 1 into corresponding bit. Thus, as soon as we get interrupts enabled again, we receive a new one. Google gives too many results for "acer interrupt storm" for this being one-broken-machine case.
Reference: http://bugzilla.kernel.org/show_bug.cgi?id=9998
---
drivers/acpi/ec.c | 11 +++++++++++ 1 files changed, 11 insertions(+), 0 deletions(-)
drivers/acpi/ec.c +11 -0
@@ -106,6 +106,7 @@ static struct acpi_ec {wait_queue_head_t wait;struct list_head list;struct delayed_work work; +atomic_t irq_count;u8 handlers_installed; } *boot_ec, *first_ec;
@@ -171,6 +172,7 @@ static void ec_switch_to_poll_mode(struct acpi_ec *ec)static int acpi_ec_wait (struct acpi_ec *ec,enum ec_event event,int force_poll) { +atomic_set (&ec->irq_count,0 );if (likely (test_bit (EC_FLAGS_GPE_MODE , &ec->flags)) &&likely (!force_poll)) { if (wait_event_timeout(ec->wait, acpi_ec_check_status(ec, event),
@@ -489,6 +491,12 @@ static u32 acpi_ec_gpe_handler(void *data)u8 state =acpi_ec_read_status (ec);pr_debug (PREFIX "~~~> interrupt\n" ); +atomic_inc (&ec->irq_count); +if (atomic_read (&ec->irq_count) >5 ) { +pr_err (PREFIX "GPE storm detected, disabling EC GPE\n" ); +ec_switch_to_poll_mode (ec); + goto end; + }clear_bit (EC_FLAGS_WAIT_GPE , &ec->flags);if (test_bit (EC_FLAGS_GPE_MODE , &ec->flags))wake_up (&ec->wait);
@@ -507,6 +515,7 @@ static u32 acpi_ec_gpe_handler(void *data)set_bit (EC_FLAGS_GPE_MODE , &ec->flags);clear_bit (EC_FLAGS_RESCHEDULE_POLL , &ec->flags); } +end:ec_schedule_ec_poll (ec);return ACPI_SUCCESS (status) ?ACPI_INTERRUPT_HANDLED :ACPI_INTERRUPT_NOT_HANDLED ;
@@ -515,6 +524,7 @@ static u32 acpi_ec_gpe_handler(void *data)static void do_ec_poll (struct work_struct *work) { struct acpi_ec *ec = container_of(work, struct acpi_ec, work.work); +atomic_set (&ec->irq_count,0 ); (void )acpi_ec_gpe_handler (ec); }
@@ -679,6 +689,7 @@ static struct acpi_ec *make_acpi_ec(void)init_waitqueue_head (&ec->wait);INIT_LIST_HEAD (&ec->list);INIT_DELAYED_WORK_DEFERRABLE (&ec->work, do_ec_poll); +atomic_set (&ec->irq_count,0 );return ec; }
Len Brown
reviewer1-6 added to acpi-test
thanks,
-Len
Quoted 34 lines · Alexey Starikovskiy
Allow EC to serve keyboard even then we polling it.
Signed-off-by Alexey Starikovskiy <[email protected]>---
drivers/acpi/ec.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)drivers/acpi/ec.c +2
-1@@ -73,6 +73,7 @@ enum ec_event {#define ACPI_EC_DELAY 500/* Wait 500ms max. during EC ops */ #define ACPI_EC_UDELAY_GLK 1000/* Wait 1ms max. to get global lock */ +#define ACPI_EC_UDELAY 100/* Wait 100us before polling EC again */ enum {EC_FLAGS_WAIT_GPE =0 ,/* Don't check status until GPE arrives */ @@ -227,6 +228,7 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll)while (time_before (jiffies, delay)) {if (acpi_ec_check_status (ec, event)) goto end; +udelay (ACPI_EC_UDELAY ); } } pr_err(PREFIX "acpi_ec_wait timeout," --To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html