ACPI: EC: Add poll timer
If we can not use interrupt mode of EC for some reason, start polling EC for events periodically. Signed-off-by: Alexey Starikovskiy <astarikovskiy@suse.de> Signed-off-by: Len Brown <len.brown@intel.com>
This commit is contained in:
committed by
Len Brown
parent
e6e82a3087
commit
845625cdcb
@@ -84,6 +84,7 @@ enum {
|
|||||||
EC_FLAGS_NO_WDATA_GPE, /* Don't expect WDATA GPE event */
|
EC_FLAGS_NO_WDATA_GPE, /* Don't expect WDATA GPE event */
|
||||||
EC_FLAGS_WDATA, /* Data is being written */
|
EC_FLAGS_WDATA, /* Data is being written */
|
||||||
EC_FLAGS_NO_OBF1_GPE, /* Don't expect GPE before read */
|
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);
|
static int acpi_ec_remove(struct acpi_device *device, int type);
|
||||||
@@ -130,6 +131,7 @@ static struct acpi_ec {
|
|||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
wait_queue_head_t wait;
|
wait_queue_head_t wait;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
struct delayed_work work;
|
||||||
u8 handlers_installed;
|
u8 handlers_installed;
|
||||||
} *boot_ec, *first_ec;
|
} *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;
|
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)
|
static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
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())
|
if (printk_ratelimit())
|
||||||
pr_info(PREFIX "missing confirmations, "
|
pr_info(PREFIX "missing confirmations, "
|
||||||
"switch off interrupt mode.\n");
|
"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;
|
goto end;
|
||||||
}
|
}
|
||||||
@@ -529,28 +546,37 @@ static u32 acpi_ec_gpe_handler(void *data)
|
|||||||
{
|
{
|
||||||
acpi_status status = AE_OK;
|
acpi_status status = AE_OK;
|
||||||
struct acpi_ec *ec = data;
|
struct acpi_ec *ec = data;
|
||||||
|
u8 state = acpi_ec_read_status(ec);
|
||||||
|
|
||||||
pr_debug(PREFIX "~~~> interrupt\n");
|
pr_debug(PREFIX "~~~> interrupt\n");
|
||||||
clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags);
|
clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags);
|
||||||
if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags))
|
if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags))
|
||||||
wake_up(&ec->wait);
|
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))
|
if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags))
|
||||||
status = acpi_os_execute(OSL_EC_BURST_HANDLER,
|
status = acpi_os_execute(OSL_EC_BURST_HANDLER,
|
||||||
acpi_ec_gpe_query, ec);
|
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 */
|
/* this is non-query, must be confirmation */
|
||||||
if (printk_ratelimit())
|
if (printk_ratelimit())
|
||||||
pr_info(PREFIX "non-query interrupt received,"
|
pr_info(PREFIX "non-query interrupt received,"
|
||||||
" switching to interrupt mode\n");
|
" switching to interrupt mode\n");
|
||||||
set_bit(EC_FLAGS_GPE_MODE, &ec->flags);
|
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) ?
|
return ACPI_SUCCESS(status) ?
|
||||||
ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED;
|
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
|
Address Space Management
|
||||||
-------------------------------------------------------------------------- */
|
-------------------------------------------------------------------------- */
|
||||||
@@ -711,6 +737,7 @@ static struct acpi_ec *make_acpi_ec(void)
|
|||||||
mutex_init(&ec->lock);
|
mutex_init(&ec->lock);
|
||||||
init_waitqueue_head(&ec->wait);
|
init_waitqueue_head(&ec->wait);
|
||||||
INIT_LIST_HEAD(&ec->list);
|
INIT_LIST_HEAD(&ec->list);
|
||||||
|
INIT_DELAYED_WORK_DEFERRABLE(&ec->work, do_ec_poll);
|
||||||
return ec;
|
return ec;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,8 +779,15 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval)
|
|||||||
return AE_CTRL_TERMINATE;
|
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)
|
static void ec_remove_handlers(struct acpi_ec *ec)
|
||||||
{
|
{
|
||||||
|
ec_poll_stop(ec);
|
||||||
if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle,
|
if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle,
|
||||||
ACPI_ADR_SPACE_EC, &acpi_ec_space_handler)))
|
ACPI_ADR_SPACE_EC, &acpi_ec_space_handler)))
|
||||||
pr_err(PREFIX "failed to remove space handler\n");
|
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 */
|
/* EC is fully operational, allow queries */
|
||||||
clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);
|
clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags);
|
||||||
|
ec_schedule_ec_poll(ec);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user