static int ec_poll(struct acpi_ec *ec) { unsigned long flags; int repeat = 2; /* number of command restarts */ while (repeat--) { unsigned long delay = jiffies + msecs_to_jiffies(ec_delay); do { /* don't sleep with disabled interrupts */ if (EC_FLAGS_MSI || irqs_disabled()) { udelay(ACPI_EC_MSI_UDELAY); if (ec_transaction_done(ec)) return 0; } else { if (wait_event_timeout(ec->wait, ec_transaction_done(ec), msecs_to_jiffies(1))) return 0; } advance_transaction(ec, acpi_ec_read_status(ec)); } while (time_before(jiffies, delay)); if (acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) break; pr_debug(PREFIX "controller reset, restart transaction\n"); spin_lock_irqsave(&ec->curr_lock, flags); start_transaction(ec); spin_unlock_irqrestore(&ec->curr_lock, flags); } return -ETIME; }
static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) { int status; u32 glk; if (!ec || (!t) || (t->wlen && !t->wdata) || (t->rlen && !t->rdata)) return -EINVAL; if (t->rdata) memset(t->rdata, 0, t->rlen); mutex_lock(&ec->lock); if (test_bit(EC_FLAGS_BLOCKED, &ec->flags)) { status = -EINVAL; goto unlock; } if (ec->global_lock) { status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); if (ACPI_FAILURE(status)) { status = -ENODEV; goto unlock; } } if (ec_wait_ibf0(ec)) { pr_err(PREFIX "input buffer is not empty, " "aborting transaction\n"); status = -ETIME; goto end; } pr_debug(PREFIX "transaction start\n"); /* disable GPE during transaction if storm is detected */ if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { /* It has to be disabled, so that it doesn't trigger. */ acpi_disable_gpe(NULL, ec->gpe); } status = acpi_ec_transaction_unlocked(ec, t); /* check if we received SCI during transaction */ ec_check_sci_sync(ec, acpi_ec_read_status(ec)); if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { msleep(1); /* It is safe to enable the GPE outside of the transaction. */ acpi_enable_gpe(NULL, ec->gpe); } else if (t->irq_count > ACPI_EC_STORM_THRESHOLD) { pr_info(PREFIX "GPE storm detected, " "transactions will use polling mode\n"); set_bit(EC_FLAGS_GPE_STORM, &ec->flags); } pr_debug(PREFIX "transaction end\n"); end: if (ec->global_lock) acpi_release_global_lock(glk); unlock: mutex_unlock(&ec->lock); return status; }
static inline int acpi_ec_check_status(struct acpi_ec *ec, enum ec_event event, unsigned old_count) { u8 status = acpi_ec_read_status(ec); if (old_count == atomic_read(&ec->event_count)) return 0; if (event == ACPI_EC_EVENT_OBF_1) { if (status & ACPI_EC_FLAG_OBF) return 1; } else if (event == ACPI_EC_EVENT_IBF_0) { if (!(status & ACPI_EC_FLAG_IBF)) return 1; } return 0; }
int acpi_ec_leave_burst_mode(struct acpi_ec *ec) { u8 status = 0; status = acpi_ec_read_status(ec); if (status != -EINVAL && (status & ACPI_EC_FLAG_BURST)) { status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); if (status) goto end; acpi_ec_write_cmd(ec, ACPI_EC_BURST_DISABLE); acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); } atomic_set(&ec->leaving_burst, 1); return 0; end: ACPI_EXCEPTION((AE_INFO, status, "EC leave burst mode")); return -1; }
static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, unsigned count) { if (acpi_ec_mode == EC_POLL) { unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY); while (time_before(jiffies, delay)) { if (acpi_ec_check_status(ec, event, 0)) return 0; } } else { if (wait_event_timeout(ec->wait, acpi_ec_check_status(ec, event, count), msecs_to_jiffies(ACPI_EC_DELAY)) || acpi_ec_check_status(ec, event, 0)) { return 0; } else { printk(KERN_ERR PREFIX "acpi_ec_wait timeout," " status = %d, expect_event = %d\n", acpi_ec_read_status(ec), event); } } return -ETIME; }
/* * Note: samsung nv5000 doesn't work with ec burst mode. * http://bugzilla.kernel.org/show_bug.cgi?id=4980 */ int acpi_ec_enter_burst_mode(struct acpi_ec *ec) { u8 tmp = 0; u8 status = 0; status = acpi_ec_read_status(ec); if (status != -EINVAL && !(status & ACPI_EC_FLAG_BURST)) { status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); if (status) goto end; acpi_ec_write_cmd(ec, ACPI_EC_BURST_ENABLE); status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1); tmp = acpi_ec_read_data(ec); if (tmp != 0x90) { /* Burst ACK byte */ return -EINVAL; } } atomic_set(&ec->leaving_burst, 0); return 0; end: ACPI_EXCEPTION((AE_INFO, status, "EC wait, burst mode")); return -1; }
static int acpi_ec_burst_enable(struct acpi_ec *ec) { u8 d; struct transaction t = {.command = ACPI_EC_BURST_ENABLE, .wdata = NULL, .rdata = &d, .wlen = 0, .rlen = 1}; return acpi_ec_transaction(ec, &t); } static int acpi_ec_burst_disable(struct acpi_ec *ec) { struct transaction t = {.command = ACPI_EC_BURST_DISABLE, .wdata = NULL, .rdata = NULL, .wlen = 0, .rlen = 0}; return (acpi_ec_read_status(ec) & ACPI_EC_FLAG_BURST) ? acpi_ec_transaction(ec, &t) : 0; } static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) { int result; u8 d; struct transaction t = {.command = ACPI_EC_COMMAND_READ, .wdata = &address, .rdata = &d, .wlen = 1, .rlen = 1}; result = acpi_ec_transaction(ec, &t); *data = d; return result; } static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data) { u8 wdata[2] = { address, data }; struct transaction t = {.command = ACPI_EC_COMMAND_WRITE, .wdata = wdata, .rdata = NULL, .wlen = 2, .rlen = 0}; return acpi_ec_transaction(ec, &t); } /* * Externally callable EC access functions. For now, assume 1 EC only */ int ec_burst_enable(void) { if (!first_ec) return -ENODEV; return acpi_ec_burst_enable(first_ec); } EXPORT_SYMBOL(ec_burst_enable); int ec_burst_disable(void) { if (!first_ec) return -ENODEV; return acpi_ec_burst_disable(first_ec); } EXPORT_SYMBOL(ec_burst_disable); int ec_read(u8 addr, u8 * val) { int err; u8 temp_data; if (!first_ec) return -ENODEV; err = acpi_ec_read(first_ec, addr, &temp_data); if (!err) { *val = temp_data; return 0; } else return err; } EXPORT_SYMBOL(ec_read); int ec_write(u8 addr, u8 val) { int err; if (!first_ec) return -ENODEV; err = acpi_ec_write(first_ec, addr, val); return err; }
static int ec_check_ibf0(struct acpi_ec *ec) { u8 status = acpi_ec_read_status(ec); return (status & ACPI_EC_FLAG_IBF) == 0; }
struct acpi_ec *ec = (struct acpi_ec *)data; atomic_inc(&ec->event_count); if (acpi_ec_mode == EC_INTR) { #if 0 wake_up(&ec->wait); #else // hack ... if (waitqueue_active(&ec->wait)) { struct task_struct *task = list_entry(ec->wait.task_list.next, wait_queue_t, task_list)->private; if (task) wake_up_process(task); } #endif } value = acpi_ec_read_status(ec); if ((value & ACPI_EC_FLAG_SCI) && !atomic_read(&ec->query_pending)) { atomic_set(&ec->query_pending, 1); status = acpi_os_execute(OSL_EC_BURST_HANDLER, acpi_ec_gpe_query, ec); } return status == AE_OK ? ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED; } /* -------------------------------------------------------------------------- Address Space Management -------------------------------------------------------------------------- */