diff options
-rw-r--r-- | drivers/acpi/ec.c | 83 |
1 files changed, 48 insertions, 35 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 762b4cc9d7b1..f09386e9745f 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c | |||
@@ -78,6 +78,9 @@ enum { | |||
78 | EC_FLAGS_BLOCKED, /* Transactions are blocked */ | 78 | EC_FLAGS_BLOCKED, /* Transactions are blocked */ |
79 | }; | 79 | }; |
80 | 80 | ||
81 | #define ACPI_EC_COMMAND_POLL 0x01 /* Available for command byte */ | ||
82 | #define ACPI_EC_COMMAND_COMPLETE 0x02 /* Completed last byte */ | ||
83 | |||
81 | /* ec.c is compiled in acpi namespace so this shows up as acpi.ec_delay param */ | 84 | /* ec.c is compiled in acpi namespace so this shows up as acpi.ec_delay param */ |
82 | static unsigned int ec_delay __read_mostly = ACPI_EC_DELAY; | 85 | static unsigned int ec_delay __read_mostly = ACPI_EC_DELAY; |
83 | module_param(ec_delay, uint, 0644); | 86 | module_param(ec_delay, uint, 0644); |
@@ -109,7 +112,7 @@ struct transaction { | |||
109 | u8 ri; | 112 | u8 ri; |
110 | u8 wlen; | 113 | u8 wlen; |
111 | u8 rlen; | 114 | u8 rlen; |
112 | bool done; | 115 | u8 flags; |
113 | }; | 116 | }; |
114 | 117 | ||
115 | struct acpi_ec *boot_ec, *first_ec; | 118 | struct acpi_ec *boot_ec, *first_ec; |
@@ -150,63 +153,68 @@ static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) | |||
150 | outb(data, ec->data_addr); | 153 | outb(data, ec->data_addr); |
151 | } | 154 | } |
152 | 155 | ||
153 | static int ec_transaction_done(struct acpi_ec *ec) | 156 | static int ec_transaction_completed(struct acpi_ec *ec) |
154 | { | 157 | { |
155 | unsigned long flags; | 158 | unsigned long flags; |
156 | int ret = 0; | 159 | int ret = 0; |
157 | spin_lock_irqsave(&ec->lock, flags); | 160 | spin_lock_irqsave(&ec->lock, flags); |
158 | if (!ec->curr || ec->curr->done) | 161 | if (!ec->curr || (ec->curr->flags & ACPI_EC_COMMAND_COMPLETE)) |
159 | ret = 1; | 162 | ret = 1; |
160 | spin_unlock_irqrestore(&ec->lock, flags); | 163 | spin_unlock_irqrestore(&ec->lock, flags); |
161 | return ret; | 164 | return ret; |
162 | } | 165 | } |
163 | 166 | ||
164 | static void start_transaction(struct acpi_ec *ec) | ||
165 | { | ||
166 | ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0; | ||
167 | ec->curr->done = false; | ||
168 | acpi_ec_write_cmd(ec, ec->curr->command); | ||
169 | } | ||
170 | |||
171 | static void advance_transaction(struct acpi_ec *ec) | 167 | static void advance_transaction(struct acpi_ec *ec) |
172 | { | 168 | { |
173 | unsigned long flags; | ||
174 | struct transaction *t; | 169 | struct transaction *t; |
175 | u8 status; | 170 | u8 status; |
176 | 171 | ||
177 | spin_lock_irqsave(&ec->lock, flags); | ||
178 | pr_debug("===== %s =====\n", in_interrupt() ? "IRQ" : "TASK"); | 172 | pr_debug("===== %s =====\n", in_interrupt() ? "IRQ" : "TASK"); |
179 | status = acpi_ec_read_status(ec); | 173 | status = acpi_ec_read_status(ec); |
180 | t = ec->curr; | 174 | t = ec->curr; |
181 | if (!t) | 175 | if (!t) |
182 | goto unlock; | 176 | goto err; |
183 | if (t->wlen > t->wi) { | 177 | if (t->flags & ACPI_EC_COMMAND_POLL) { |
184 | if ((status & ACPI_EC_FLAG_IBF) == 0) | 178 | if (t->wlen > t->wi) { |
185 | acpi_ec_write_data(ec, | 179 | if ((status & ACPI_EC_FLAG_IBF) == 0) |
186 | t->wdata[t->wi++]); | 180 | acpi_ec_write_data(ec, t->wdata[t->wi++]); |
187 | else | 181 | else |
188 | goto err; | 182 | goto err; |
189 | } else if (t->rlen > t->ri) { | 183 | } else if (t->rlen > t->ri) { |
190 | if ((status & ACPI_EC_FLAG_OBF) == 1) { | 184 | if ((status & ACPI_EC_FLAG_OBF) == 1) { |
191 | t->rdata[t->ri++] = acpi_ec_read_data(ec); | 185 | t->rdata[t->ri++] = acpi_ec_read_data(ec); |
192 | if (t->rlen == t->ri) | 186 | if (t->rlen == t->ri) |
193 | t->done = true; | 187 | t->flags |= ACPI_EC_COMMAND_COMPLETE; |
188 | } else | ||
189 | goto err; | ||
190 | } else if (t->wlen == t->wi && | ||
191 | (status & ACPI_EC_FLAG_IBF) == 0) | ||
192 | t->flags |= ACPI_EC_COMMAND_COMPLETE; | ||
193 | return; | ||
194 | } else { | ||
195 | if ((status & ACPI_EC_FLAG_IBF) == 0) { | ||
196 | acpi_ec_write_cmd(ec, t->command); | ||
197 | t->flags |= ACPI_EC_COMMAND_POLL; | ||
194 | } else | 198 | } else |
195 | goto err; | 199 | goto err; |
196 | } else if (t->wlen == t->wi && | 200 | return; |
197 | (status & ACPI_EC_FLAG_IBF) == 0) | 201 | } |
198 | t->done = true; | ||
199 | goto unlock; | ||
200 | err: | 202 | err: |
201 | /* | 203 | /* |
202 | * If SCI bit is set, then don't think it's a false IRQ | 204 | * If SCI bit is set, then don't think it's a false IRQ |
203 | * otherwise will take a not handled IRQ as a false one. | 205 | * otherwise will take a not handled IRQ as a false one. |
204 | */ | 206 | */ |
205 | if (in_interrupt() && !(status & ACPI_EC_FLAG_SCI)) | 207 | if (!(status & ACPI_EC_FLAG_SCI)) { |
206 | ++t->irq_count; | 208 | if (in_interrupt() && t) |
209 | ++t->irq_count; | ||
210 | } | ||
211 | } | ||
207 | 212 | ||
208 | unlock: | 213 | static void start_transaction(struct acpi_ec *ec) |
209 | spin_unlock_irqrestore(&ec->lock, flags); | 214 | { |
215 | ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0; | ||
216 | ec->curr->flags = 0; | ||
217 | advance_transaction(ec); | ||
210 | } | 218 | } |
211 | 219 | ||
212 | static int acpi_ec_sync_query(struct acpi_ec *ec, u8 *data); | 220 | static int acpi_ec_sync_query(struct acpi_ec *ec, u8 *data); |
@@ -231,15 +239,17 @@ static int ec_poll(struct acpi_ec *ec) | |||
231 | /* don't sleep with disabled interrupts */ | 239 | /* don't sleep with disabled interrupts */ |
232 | if (EC_FLAGS_MSI || irqs_disabled()) { | 240 | if (EC_FLAGS_MSI || irqs_disabled()) { |
233 | udelay(ACPI_EC_MSI_UDELAY); | 241 | udelay(ACPI_EC_MSI_UDELAY); |
234 | if (ec_transaction_done(ec)) | 242 | if (ec_transaction_completed(ec)) |
235 | return 0; | 243 | return 0; |
236 | } else { | 244 | } else { |
237 | if (wait_event_timeout(ec->wait, | 245 | if (wait_event_timeout(ec->wait, |
238 | ec_transaction_done(ec), | 246 | ec_transaction_completed(ec), |
239 | msecs_to_jiffies(1))) | 247 | msecs_to_jiffies(1))) |
240 | return 0; | 248 | return 0; |
241 | } | 249 | } |
250 | spin_lock_irqsave(&ec->lock, flags); | ||
242 | advance_transaction(ec); | 251 | advance_transaction(ec); |
252 | spin_unlock_irqrestore(&ec->lock, flags); | ||
243 | } while (time_before(jiffies, delay)); | 253 | } while (time_before(jiffies, delay)); |
244 | pr_debug("controller reset, restart transaction\n"); | 254 | pr_debug("controller reset, restart transaction\n"); |
245 | spin_lock_irqsave(&ec->lock, flags); | 255 | spin_lock_irqsave(&ec->lock, flags); |
@@ -637,10 +647,13 @@ static int ec_check_sci(struct acpi_ec *ec, u8 state) | |||
637 | static u32 acpi_ec_gpe_handler(acpi_handle gpe_device, | 647 | static u32 acpi_ec_gpe_handler(acpi_handle gpe_device, |
638 | u32 gpe_number, void *data) | 648 | u32 gpe_number, void *data) |
639 | { | 649 | { |
650 | unsigned long flags; | ||
640 | struct acpi_ec *ec = data; | 651 | struct acpi_ec *ec = data; |
641 | 652 | ||
653 | spin_lock_irqsave(&ec->lock, flags); | ||
642 | advance_transaction(ec); | 654 | advance_transaction(ec); |
643 | if (ec_transaction_done(ec) && | 655 | spin_unlock_irqrestore(&ec->lock, flags); |
656 | if (ec_transaction_completed(ec) && | ||
644 | (acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) == 0) { | 657 | (acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) == 0) { |
645 | wake_up(&ec->wait); | 658 | wake_up(&ec->wait); |
646 | ec_check_sci(ec, acpi_ec_read_status(ec)); | 659 | ec_check_sci(ec, acpi_ec_read_status(ec)); |