diff options
-rw-r--r-- | drivers/acpi/ec.c | 118 |
1 files changed, 63 insertions, 55 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 7f0d81c0ec99..453ba1e534ef 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c | |||
@@ -95,10 +95,11 @@ struct acpi_ec_query_handler { | |||
95 | u8 query_bit; | 95 | u8 query_bit; |
96 | }; | 96 | }; |
97 | 97 | ||
98 | struct transaction_data { | 98 | struct transaction { |
99 | const u8 *wdata; | 99 | const u8 *wdata; |
100 | u8 *rdata; | 100 | u8 *rdata; |
101 | unsigned short irq_count; | 101 | unsigned short irq_count; |
102 | u8 command; | ||
102 | u8 wlen; | 103 | u8 wlen; |
103 | u8 rlen; | 104 | u8 rlen; |
104 | }; | 105 | }; |
@@ -113,8 +114,8 @@ static struct acpi_ec { | |||
113 | struct mutex lock; | 114 | struct mutex lock; |
114 | wait_queue_head_t wait; | 115 | wait_queue_head_t wait; |
115 | struct list_head list; | 116 | struct list_head list; |
116 | struct transaction_data *t; | 117 | struct transaction *curr; |
117 | spinlock_t t_lock; | 118 | spinlock_t curr_lock; |
118 | } *boot_ec, *first_ec; | 119 | } *boot_ec, *first_ec; |
119 | 120 | ||
120 | /* | 121 | /* |
@@ -176,37 +177,37 @@ static int ec_transaction_done(struct acpi_ec *ec) | |||
176 | { | 177 | { |
177 | unsigned long flags; | 178 | unsigned long flags; |
178 | int ret = 0; | 179 | int ret = 0; |
179 | spin_lock_irqsave(&ec->t_lock, flags); | 180 | spin_lock_irqsave(&ec->curr_lock, flags); |
180 | if (!ec->t || (!ec->t->wlen && !ec->t->rlen)) | 181 | if (!ec->curr || (!ec->curr->wlen && !ec->curr->rlen)) |
181 | ret = 1; | 182 | ret = 1; |
182 | spin_unlock_irqrestore(&ec->t_lock, flags); | 183 | spin_unlock_irqrestore(&ec->curr_lock, flags); |
183 | return ret; | 184 | return ret; |
184 | } | 185 | } |
185 | 186 | ||
186 | static void gpe_transaction(struct acpi_ec *ec, u8 status) | 187 | static void gpe_transaction(struct acpi_ec *ec, u8 status) |
187 | { | 188 | { |
188 | unsigned long flags; | 189 | unsigned long flags; |
189 | spin_lock_irqsave(&ec->t_lock, flags); | 190 | spin_lock_irqsave(&ec->curr_lock, flags); |
190 | if (!ec->t) | 191 | if (!ec->curr) |
191 | goto unlock; | 192 | goto unlock; |
192 | if (ec->t->wlen > 0) { | 193 | if (ec->curr->wlen > 0) { |
193 | if ((status & ACPI_EC_FLAG_IBF) == 0) { | 194 | if ((status & ACPI_EC_FLAG_IBF) == 0) { |
194 | acpi_ec_write_data(ec, *(ec->t->wdata++)); | 195 | acpi_ec_write_data(ec, *(ec->curr->wdata++)); |
195 | --ec->t->wlen; | 196 | --ec->curr->wlen; |
196 | } else | 197 | } else |
197 | /* false interrupt, state didn't change */ | 198 | /* false interrupt, state didn't change */ |
198 | ++ec->t->irq_count; | 199 | ++ec->curr->irq_count; |
199 | 200 | ||
200 | } else if (ec->t->rlen > 0) { | 201 | } else if (ec->curr->rlen > 0) { |
201 | if ((status & ACPI_EC_FLAG_OBF) == 1) { | 202 | if ((status & ACPI_EC_FLAG_OBF) == 1) { |
202 | *(ec->t->rdata++) = acpi_ec_read_data(ec); | 203 | *(ec->curr->rdata++) = acpi_ec_read_data(ec); |
203 | --ec->t->rlen; | 204 | --ec->curr->rlen; |
204 | } else | 205 | } else |
205 | /* false interrupt, state didn't change */ | 206 | /* false interrupt, state didn't change */ |
206 | ++ec->t->irq_count; | 207 | ++ec->curr->irq_count; |
207 | } | 208 | } |
208 | unlock: | 209 | unlock: |
209 | spin_unlock_irqrestore(&ec->t_lock, flags); | 210 | spin_unlock_irqrestore(&ec->curr_lock, flags); |
210 | } | 211 | } |
211 | 212 | ||
212 | static int acpi_ec_wait(struct acpi_ec *ec) | 213 | static int acpi_ec_wait(struct acpi_ec *ec) |
@@ -248,15 +249,11 @@ static int ec_poll(struct acpi_ec *ec) | |||
248 | return -ETIME; | 249 | return -ETIME; |
249 | } | 250 | } |
250 | 251 | ||
251 | static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, | 252 | static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, |
252 | const u8 * wdata, unsigned wdata_len, | 253 | struct transaction *t, |
253 | u8 * rdata, unsigned rdata_len, | ||
254 | int force_poll) | 254 | int force_poll) |
255 | { | 255 | { |
256 | unsigned long tmp; | 256 | unsigned long tmp; |
257 | struct transaction_data t = {.wdata = wdata, .rdata = rdata, | ||
258 | .wlen = wdata_len, .rlen = rdata_len, | ||
259 | .irq_count = 0}; | ||
260 | int ret = 0; | 257 | int ret = 0; |
261 | pr_debug(PREFIX "transaction start\n"); | 258 | pr_debug(PREFIX "transaction start\n"); |
262 | /* disable GPE during transaction if storm is detected */ | 259 | /* disable GPE during transaction if storm is detected */ |
@@ -265,29 +262,30 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, | |||
265 | acpi_disable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); | 262 | acpi_disable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); |
266 | } | 263 | } |
267 | /* start transaction */ | 264 | /* start transaction */ |
268 | spin_lock_irqsave(&ec->t_lock, tmp); | 265 | spin_lock_irqsave(&ec->curr_lock, tmp); |
269 | /* following two actions should be kept atomic */ | 266 | /* following two actions should be kept atomic */ |
270 | ec->t = &t; | 267 | t->irq_count = 0; |
271 | acpi_ec_write_cmd(ec, command); | 268 | ec->curr = t; |
272 | if (command == ACPI_EC_COMMAND_QUERY) | 269 | acpi_ec_write_cmd(ec, ec->curr->command); |
270 | if (ec->curr->command == ACPI_EC_COMMAND_QUERY) | ||
273 | clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); | 271 | clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); |
274 | spin_unlock_irqrestore(&ec->t_lock, tmp); | 272 | spin_unlock_irqrestore(&ec->curr_lock, tmp); |
275 | /* if we selected poll mode or failed in GPE-mode do a poll loop */ | 273 | /* if we selected poll mode or failed in GPE-mode do a poll loop */ |
276 | if (force_poll || | 274 | if (force_poll || |
277 | !test_bit(EC_FLAGS_GPE_MODE, &ec->flags) || | 275 | !test_bit(EC_FLAGS_GPE_MODE, &ec->flags) || |
278 | acpi_ec_wait(ec)) | 276 | acpi_ec_wait(ec)) |
279 | ret = ec_poll(ec); | 277 | ret = ec_poll(ec); |
280 | pr_debug(PREFIX "transaction end\n"); | 278 | pr_debug(PREFIX "transaction end\n"); |
281 | spin_lock_irqsave(&ec->t_lock, tmp); | 279 | spin_lock_irqsave(&ec->curr_lock, tmp); |
282 | ec->t = NULL; | 280 | ec->curr = NULL; |
283 | spin_unlock_irqrestore(&ec->t_lock, tmp); | 281 | spin_unlock_irqrestore(&ec->curr_lock, tmp); |
284 | if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { | 282 | if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { |
285 | /* check if we received SCI during transaction */ | 283 | /* check if we received SCI during transaction */ |
286 | ec_check_sci(ec, acpi_ec_read_status(ec)); | 284 | ec_check_sci(ec, acpi_ec_read_status(ec)); |
287 | /* it is safe to enable GPE outside of transaction */ | 285 | /* it is safe to enable GPE outside of transaction */ |
288 | acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); | 286 | acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); |
289 | } else if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags) && | 287 | } else if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags) && |
290 | t.irq_count > ACPI_EC_STORM_THRESHOLD) { | 288 | t->irq_count > ACPI_EC_STORM_THRESHOLD) { |
291 | pr_debug(PREFIX "GPE storm detected\n"); | 289 | pr_debug(PREFIX "GPE storm detected\n"); |
292 | set_bit(EC_FLAGS_GPE_STORM, &ec->flags); | 290 | set_bit(EC_FLAGS_GPE_STORM, &ec->flags); |
293 | } | 291 | } |
@@ -300,17 +298,15 @@ static int ec_check_ibf0(struct acpi_ec *ec) | |||
300 | return (status & ACPI_EC_FLAG_IBF) == 0; | 298 | return (status & ACPI_EC_FLAG_IBF) == 0; |
301 | } | 299 | } |
302 | 300 | ||
303 | static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, | 301 | static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t, |
304 | const u8 * wdata, unsigned wdata_len, | ||
305 | u8 * rdata, unsigned rdata_len, | ||
306 | int force_poll) | 302 | int force_poll) |
307 | { | 303 | { |
308 | int status; | 304 | int status; |
309 | u32 glk; | 305 | u32 glk; |
310 | if (!ec || (wdata_len && !wdata) || (rdata_len && !rdata)) | 306 | if (!ec || (!t) || (t->wlen && !t->wdata) || (t->rlen && !t->rdata)) |
311 | return -EINVAL; | 307 | return -EINVAL; |
312 | if (rdata) | 308 | if (t->rdata) |
313 | memset(rdata, 0, rdata_len); | 309 | memset(t->rdata, 0, t->rlen); |
314 | mutex_lock(&ec->lock); | 310 | mutex_lock(&ec->lock); |
315 | if (ec->global_lock) { | 311 | if (ec->global_lock) { |
316 | status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); | 312 | status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); |
@@ -326,10 +322,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, | |||
326 | status = -ETIME; | 322 | status = -ETIME; |
327 | goto end; | 323 | goto end; |
328 | } | 324 | } |
329 | status = acpi_ec_transaction_unlocked(ec, command, | 325 | status = acpi_ec_transaction_unlocked(ec, t, force_poll); |
330 | wdata, wdata_len, | ||
331 | rdata, rdata_len, | ||
332 | force_poll); | ||
333 | end: | 326 | end: |
334 | if (ec->global_lock) | 327 | if (ec->global_lock) |
335 | acpi_release_global_lock(glk); | 328 | acpi_release_global_lock(glk); |
@@ -345,23 +338,32 @@ unlock: | |||
345 | int acpi_ec_burst_enable(struct acpi_ec *ec) | 338 | int acpi_ec_burst_enable(struct acpi_ec *ec) |
346 | { | 339 | { |
347 | u8 d; | 340 | u8 d; |
348 | return acpi_ec_transaction(ec, ACPI_EC_BURST_ENABLE, NULL, 0, &d, 1, 0); | 341 | struct transaction t = {.command = ACPI_EC_BURST_ENABLE, |
342 | .wdata = NULL, .rdata = &d, | ||
343 | .wlen = 0, .rlen = 1}; | ||
344 | |||
345 | return acpi_ec_transaction(ec, &t, 0); | ||
349 | } | 346 | } |
350 | 347 | ||
351 | int acpi_ec_burst_disable(struct acpi_ec *ec) | 348 | int acpi_ec_burst_disable(struct acpi_ec *ec) |
352 | { | 349 | { |
350 | struct transaction t = {.command = ACPI_EC_BURST_DISABLE, | ||
351 | .wdata = NULL, .rdata = NULL, | ||
352 | .wlen = 0, .rlen = 0}; | ||
353 | |||
353 | return (acpi_ec_read_status(ec) & ACPI_EC_FLAG_BURST) ? | 354 | return (acpi_ec_read_status(ec) & ACPI_EC_FLAG_BURST) ? |
354 | acpi_ec_transaction(ec, ACPI_EC_BURST_DISABLE, | 355 | acpi_ec_transaction(ec, &t, 0) : 0; |
355 | NULL, 0, NULL, 0, 0) : 0; | ||
356 | } | 356 | } |
357 | 357 | ||
358 | static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) | 358 | static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) |
359 | { | 359 | { |
360 | int result; | 360 | int result; |
361 | u8 d; | 361 | u8 d; |
362 | struct transaction t = {.command = ACPI_EC_COMMAND_READ, | ||
363 | .wdata = &address, .rdata = &d, | ||
364 | .wlen = 1, .rlen = 1}; | ||
362 | 365 | ||
363 | result = acpi_ec_transaction(ec, ACPI_EC_COMMAND_READ, | 366 | result = acpi_ec_transaction(ec, &t, 0); |
364 | &address, 1, &d, 1, 0); | ||
365 | *data = d; | 367 | *data = d; |
366 | return result; | 368 | return result; |
367 | } | 369 | } |
@@ -369,8 +371,11 @@ static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) | |||
369 | static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data) | 371 | static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data) |
370 | { | 372 | { |
371 | u8 wdata[2] = { address, data }; | 373 | u8 wdata[2] = { address, data }; |
372 | return acpi_ec_transaction(ec, ACPI_EC_COMMAND_WRITE, | 374 | struct transaction t = {.command = ACPI_EC_COMMAND_WRITE, |
373 | wdata, 2, NULL, 0, 0); | 375 | .wdata = wdata, .rdata = NULL, |
376 | .wlen = 2, .rlen = 0}; | ||
377 | |||
378 | return acpi_ec_transaction(ec, &t, 0); | ||
374 | } | 379 | } |
375 | 380 | ||
376 | /* | 381 | /* |
@@ -432,12 +437,13 @@ int ec_transaction(u8 command, | |||
432 | u8 * rdata, unsigned rdata_len, | 437 | u8 * rdata, unsigned rdata_len, |
433 | int force_poll) | 438 | int force_poll) |
434 | { | 439 | { |
440 | struct transaction t = {.command = command, | ||
441 | .wdata = wdata, .rdata = rdata, | ||
442 | .wlen = wdata_len, .rlen = rdata_len}; | ||
435 | if (!first_ec) | 443 | if (!first_ec) |
436 | return -ENODEV; | 444 | return -ENODEV; |
437 | 445 | ||
438 | return acpi_ec_transaction(first_ec, command, wdata, | 446 | return acpi_ec_transaction(first_ec, &t, force_poll); |
439 | wdata_len, rdata, rdata_len, | ||
440 | force_poll); | ||
441 | } | 447 | } |
442 | 448 | ||
443 | EXPORT_SYMBOL(ec_transaction); | 449 | EXPORT_SYMBOL(ec_transaction); |
@@ -446,7 +452,9 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 * data) | |||
446 | { | 452 | { |
447 | int result; | 453 | int result; |
448 | u8 d; | 454 | u8 d; |
449 | 455 | struct transaction t = {.command = ACPI_EC_COMMAND_QUERY, | |
456 | .wdata = NULL, .rdata = &d, | ||
457 | .wlen = 0, .rlen = 1}; | ||
450 | if (!ec || !data) | 458 | if (!ec || !data) |
451 | return -EINVAL; | 459 | return -EINVAL; |
452 | 460 | ||
@@ -456,7 +464,7 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 * data) | |||
456 | * bit to be cleared (and thus clearing the interrupt source). | 464 | * bit to be cleared (and thus clearing the interrupt source). |
457 | */ | 465 | */ |
458 | 466 | ||
459 | result = acpi_ec_transaction(ec, ACPI_EC_COMMAND_QUERY, NULL, 0, &d, 1, 0); | 467 | result = acpi_ec_transaction(ec, &t, 0); |
460 | if (result) | 468 | if (result) |
461 | return result; | 469 | return result; |
462 | 470 | ||
@@ -696,7 +704,7 @@ static struct acpi_ec *make_acpi_ec(void) | |||
696 | mutex_init(&ec->lock); | 704 | mutex_init(&ec->lock); |
697 | init_waitqueue_head(&ec->wait); | 705 | init_waitqueue_head(&ec->wait); |
698 | INIT_LIST_HEAD(&ec->list); | 706 | INIT_LIST_HEAD(&ec->list); |
699 | spin_lock_init(&ec->t_lock); | 707 | spin_lock_init(&ec->curr_lock); |
700 | return ec; | 708 | return ec; |
701 | } | 709 | } |
702 | 710 | ||