diff options
author | Dmitry Torokhov <dtor@mail.ru> | 2005-03-19 01:10:05 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2005-07-11 23:52:48 -0400 |
commit | 451566f45a2e6cd10ba56e7220a9dd84ba3ef550 (patch) | |
tree | ccc6c1018f6bc6c5f3a664e1c28e78fcaae77065 /drivers/acpi | |
parent | b913100d7304ea9596d8d85ab5f3ae04bd2b0ddb (diff) |
[ACPI] Enable EC Burst Mode
Fixes several Embedded Controller issues, including
button failure and battery status AE_TIME failure.
http://bugzilla.kernel.org/show_bug.cgi?id=3851
Based on patch by: Andi Kleen <ak@suse.de>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Signed-off-by: Luming Yu <luming.yu@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/ec.c | 390 |
1 files changed, 276 insertions, 114 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index fdf143b405be..69b04d430f00 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/delay.h> | 31 | #include <linux/delay.h> |
32 | #include <linux/proc_fs.h> | 32 | #include <linux/proc_fs.h> |
33 | #include <linux/seq_file.h> | 33 | #include <linux/seq_file.h> |
34 | #include <linux/interrupt.h> | ||
34 | #include <asm/io.h> | 35 | #include <asm/io.h> |
35 | #include <acpi/acpi_bus.h> | 36 | #include <acpi/acpi_bus.h> |
36 | #include <acpi/acpi_drivers.h> | 37 | #include <acpi/acpi_drivers.h> |
@@ -49,17 +50,19 @@ ACPI_MODULE_NAME ("acpi_ec") | |||
49 | 50 | ||
50 | #define ACPI_EC_FLAG_OBF 0x01 /* Output buffer full */ | 51 | #define ACPI_EC_FLAG_OBF 0x01 /* Output buffer full */ |
51 | #define ACPI_EC_FLAG_IBF 0x02 /* Input buffer full */ | 52 | #define ACPI_EC_FLAG_IBF 0x02 /* Input buffer full */ |
53 | #define ACPI_EC_FLAG_BURST 0x10 /* burst mode */ | ||
52 | #define ACPI_EC_FLAG_SCI 0x20 /* EC-SCI occurred */ | 54 | #define ACPI_EC_FLAG_SCI 0x20 /* EC-SCI occurred */ |
53 | 55 | ||
54 | #define ACPI_EC_EVENT_OBF 0x01 /* Output buffer full */ | 56 | #define ACPI_EC_EVENT_OBF 0x01 /* Output buffer full */ |
55 | #define ACPI_EC_EVENT_IBE 0x02 /* Input buffer empty */ | 57 | #define ACPI_EC_EVENT_IBE 0x02 /* Input buffer empty */ |
56 | 58 | ||
57 | #define ACPI_EC_UDELAY 100 /* Poll @ 100us increments */ | 59 | #define ACPI_EC_DELAY 50 /* Wait 50ms max. during EC ops */ |
58 | #define ACPI_EC_UDELAY_COUNT 1000 /* Wait 10ms max. during EC ops */ | ||
59 | #define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ | 60 | #define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ |
60 | 61 | ||
61 | #define ACPI_EC_COMMAND_READ 0x80 | 62 | #define ACPI_EC_COMMAND_READ 0x80 |
62 | #define ACPI_EC_COMMAND_WRITE 0x81 | 63 | #define ACPI_EC_COMMAND_WRITE 0x81 |
64 | #define ACPI_EC_BURST_ENABLE 0x82 | ||
65 | #define ACPI_EC_BURST_DISABLE 0x83 | ||
63 | #define ACPI_EC_COMMAND_QUERY 0x84 | 66 | #define ACPI_EC_COMMAND_QUERY 0x84 |
64 | 67 | ||
65 | static int acpi_ec_add (struct acpi_device *device); | 68 | static int acpi_ec_add (struct acpi_device *device); |
@@ -87,7 +90,11 @@ struct acpi_ec { | |||
87 | struct acpi_generic_address command_addr; | 90 | struct acpi_generic_address command_addr; |
88 | struct acpi_generic_address data_addr; | 91 | struct acpi_generic_address data_addr; |
89 | unsigned long global_lock; | 92 | unsigned long global_lock; |
90 | spinlock_t lock; | 93 | unsigned int expect_event; |
94 | atomic_t leaving_burst; /* 0 : No, 1 : Yes, 2: abort*/ | ||
95 | atomic_t pending_gpe; | ||
96 | struct semaphore sem; | ||
97 | wait_queue_head_t wait; | ||
91 | }; | 98 | }; |
92 | 99 | ||
93 | /* If we find an EC via the ECDT, we need to keep a ptr to its context */ | 100 | /* If we find an EC via the ECDT, we need to keep a ptr to its context */ |
@@ -100,59 +107,138 @@ static struct acpi_device *first_ec; | |||
100 | Transaction Management | 107 | Transaction Management |
101 | -------------------------------------------------------------------------- */ | 108 | -------------------------------------------------------------------------- */ |
102 | 109 | ||
103 | static int | 110 | static inline u32 acpi_ec_read_status(struct acpi_ec *ec) |
104 | acpi_ec_wait ( | ||
105 | struct acpi_ec *ec, | ||
106 | u8 event) | ||
107 | { | 111 | { |
108 | u32 acpi_ec_status = 0; | 112 | u32 status = 0; |
109 | u32 i = ACPI_EC_UDELAY_COUNT; | ||
110 | 113 | ||
111 | if (!ec) | 114 | acpi_hw_low_level_read(8, &status, &ec->status_addr); |
112 | return -EINVAL; | 115 | return status; |
116 | } | ||
117 | |||
118 | static int acpi_ec_wait(struct acpi_ec *ec, unsigned int event) | ||
119 | { | ||
120 | int result = 0; | ||
121 | |||
122 | ACPI_FUNCTION_TRACE("acpi_ec_wait"); | ||
113 | 123 | ||
114 | /* Poll the EC status register waiting for the event to occur. */ | 124 | ec->expect_event = event; |
125 | smp_mb(); | ||
126 | |||
127 | result = wait_event_interruptible_timeout(ec->wait, | ||
128 | !ec->expect_event, | ||
129 | msecs_to_jiffies(ACPI_EC_DELAY)); | ||
130 | |||
131 | ec->expect_event = 0; | ||
132 | smp_mb(); | ||
133 | |||
134 | if (result < 0){ | ||
135 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR," result = %d ", result)); | ||
136 | return_VALUE(result); | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * Verify that the event in question has actually happened by | ||
141 | * querying EC status. Do the check even if operation timed-out | ||
142 | * to make sure that we did not miss interrupt. | ||
143 | */ | ||
115 | switch (event) { | 144 | switch (event) { |
116 | case ACPI_EC_EVENT_OBF: | 145 | case ACPI_EC_EVENT_OBF: |
117 | do { | 146 | if (acpi_ec_read_status(ec) & ACPI_EC_FLAG_OBF) |
118 | acpi_hw_low_level_read(8, &acpi_ec_status, &ec->status_addr); | 147 | return_VALUE(0); |
119 | if (acpi_ec_status & ACPI_EC_FLAG_OBF) | ||
120 | return 0; | ||
121 | udelay(ACPI_EC_UDELAY); | ||
122 | } while (--i>0); | ||
123 | break; | 148 | break; |
149 | |||
124 | case ACPI_EC_EVENT_IBE: | 150 | case ACPI_EC_EVENT_IBE: |
125 | do { | 151 | if (~acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) |
126 | acpi_hw_low_level_read(8, &acpi_ec_status, &ec->status_addr); | 152 | return_VALUE(0); |
127 | if (!(acpi_ec_status & ACPI_EC_FLAG_IBF)) | ||
128 | return 0; | ||
129 | udelay(ACPI_EC_UDELAY); | ||
130 | } while (--i>0); | ||
131 | break; | 153 | break; |
132 | default: | ||
133 | return -EINVAL; | ||
134 | } | 154 | } |
135 | 155 | ||
136 | return -ETIME; | 156 | return_VALUE(-ETIME); |
137 | } | 157 | } |
138 | 158 | ||
139 | 159 | ||
160 | |||
161 | static int | ||
162 | acpi_ec_enter_burst_mode ( | ||
163 | struct acpi_ec *ec) | ||
164 | { | ||
165 | u32 tmp = 0; | ||
166 | int status = 0; | ||
167 | |||
168 | ACPI_FUNCTION_TRACE("acpi_ec_enter_burst_mode"); | ||
169 | |||
170 | status = acpi_ec_read_status(ec); | ||
171 | if (status != -EINVAL && | ||
172 | !(status & ACPI_EC_FLAG_BURST)){ | ||
173 | ACPI_DEBUG_PRINT((ACPI_DB_INFO,"entering burst mode \n")); | ||
174 | acpi_hw_low_level_write(8, ACPI_EC_BURST_ENABLE, &ec->command_addr); | ||
175 | status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); | ||
176 | if (status){ | ||
177 | acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); | ||
178 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR," status = %d\n", status)); | ||
179 | return_VALUE(-EINVAL); | ||
180 | } | ||
181 | acpi_hw_low_level_read(8, &tmp, &ec->data_addr); | ||
182 | acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); | ||
183 | if(tmp != 0x90 ) {/* Burst ACK byte*/ | ||
184 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR,"Ack failed \n")); | ||
185 | return_VALUE(-EINVAL); | ||
186 | } | ||
187 | } else | ||
188 | ACPI_DEBUG_PRINT((ACPI_DB_INFO,"already be in burst mode \n")); | ||
189 | atomic_set(&ec->leaving_burst , 0); | ||
190 | return_VALUE(0); | ||
191 | } | ||
192 | |||
193 | static int | ||
194 | acpi_ec_leave_burst_mode ( | ||
195 | struct acpi_ec *ec) | ||
196 | { | ||
197 | int status =0; | ||
198 | |||
199 | ACPI_FUNCTION_TRACE("acpi_ec_leave_burst_mode"); | ||
200 | |||
201 | atomic_set(&ec->leaving_burst , 1); | ||
202 | status = acpi_ec_read_status(ec); | ||
203 | if (status != -EINVAL && | ||
204 | (status & ACPI_EC_FLAG_BURST)){ | ||
205 | ACPI_DEBUG_PRINT((ACPI_DB_INFO,"leaving burst mode\n")); | ||
206 | acpi_hw_low_level_write(8, ACPI_EC_BURST_DISABLE, &ec->command_addr); | ||
207 | status = acpi_ec_wait(ec, ACPI_EC_FLAG_IBF); | ||
208 | if (status){ | ||
209 | acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); | ||
210 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR,"------->wait fail\n")); | ||
211 | return_VALUE(-EINVAL); | ||
212 | } | ||
213 | acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); | ||
214 | status = acpi_ec_read_status(ec); | ||
215 | if (status != -EINVAL && | ||
216 | (status & ACPI_EC_FLAG_BURST)) { | ||
217 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR,"------->status fail\n")); | ||
218 | return_VALUE(-EINVAL); | ||
219 | } | ||
220 | }else | ||
221 | ACPI_DEBUG_PRINT((ACPI_DB_INFO,"already be in Non-burst mode \n")); | ||
222 | ACPI_DEBUG_PRINT((ACPI_DB_INFO,"leaving burst mode\n")); | ||
223 | |||
224 | return_VALUE(0); | ||
225 | } | ||
226 | |||
140 | static int | 227 | static int |
141 | acpi_ec_read ( | 228 | acpi_ec_read ( |
142 | struct acpi_ec *ec, | 229 | struct acpi_ec *ec, |
143 | u8 address, | 230 | u8 address, |
144 | u32 *data) | 231 | u32 *data) |
145 | { | 232 | { |
146 | acpi_status status = AE_OK; | 233 | int status = 0; |
147 | int result = 0; | 234 | u32 glk; |
148 | unsigned long flags = 0; | ||
149 | u32 glk = 0; | ||
150 | 235 | ||
151 | ACPI_FUNCTION_TRACE("acpi_ec_read"); | 236 | ACPI_FUNCTION_TRACE("acpi_ec_read"); |
152 | 237 | ||
153 | if (!ec || !data) | 238 | if (!ec || !data) |
154 | return_VALUE(-EINVAL); | 239 | return_VALUE(-EINVAL); |
155 | 240 | ||
241 | retry: | ||
156 | *data = 0; | 242 | *data = 0; |
157 | 243 | ||
158 | if (ec->global_lock) { | 244 | if (ec->global_lock) { |
@@ -160,32 +246,50 @@ acpi_ec_read ( | |||
160 | if (ACPI_FAILURE(status)) | 246 | if (ACPI_FAILURE(status)) |
161 | return_VALUE(-ENODEV); | 247 | return_VALUE(-ENODEV); |
162 | } | 248 | } |
163 | 249 | ||
164 | spin_lock_irqsave(&ec->lock, flags); | 250 | WARN_ON(in_interrupt()); |
251 | down(&ec->sem); | ||
252 | |||
253 | if(acpi_ec_enter_burst_mode(ec)) | ||
254 | goto end; | ||
165 | 255 | ||
166 | acpi_hw_low_level_write(8, ACPI_EC_COMMAND_READ, &ec->command_addr); | 256 | acpi_hw_low_level_write(8, ACPI_EC_COMMAND_READ, &ec->command_addr); |
167 | result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); | 257 | status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); |
168 | if (result) | 258 | acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); |
259 | if (status) { | ||
169 | goto end; | 260 | goto end; |
261 | } | ||
170 | 262 | ||
171 | acpi_hw_low_level_write(8, address, &ec->data_addr); | 263 | acpi_hw_low_level_write(8, address, &ec->data_addr); |
172 | result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); | 264 | status= acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); |
173 | if (result) | 265 | if (status){ |
266 | acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); | ||
174 | goto end; | 267 | goto end; |
175 | 268 | } | |
176 | 269 | ||
177 | acpi_hw_low_level_read(8, data, &ec->data_addr); | 270 | acpi_hw_low_level_read(8, data, &ec->data_addr); |
271 | acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); | ||
178 | 272 | ||
179 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Read [%02x] from address [%02x]\n", | 273 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Read [%02x] from address [%02x]\n", |
180 | *data, address)); | 274 | *data, address)); |
181 | 275 | ||
182 | end: | 276 | end: |
183 | spin_unlock_irqrestore(&ec->lock, flags); | 277 | acpi_ec_leave_burst_mode(ec); |
278 | up(&ec->sem); | ||
184 | 279 | ||
185 | if (ec->global_lock) | 280 | if (ec->global_lock) |
186 | acpi_release_global_lock(glk); | 281 | acpi_release_global_lock(glk); |
187 | 282 | ||
188 | return_VALUE(result); | 283 | if(atomic_read(&ec->leaving_burst) == 2){ |
284 | ACPI_DEBUG_PRINT((ACPI_DB_INFO,"aborted, retry ...\n")); | ||
285 | while(!atomic_read(&ec->pending_gpe)){ | ||
286 | msleep(1); | ||
287 | } | ||
288 | acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); | ||
289 | goto retry; | ||
290 | } | ||
291 | |||
292 | return_VALUE(status); | ||
189 | } | 293 | } |
190 | 294 | ||
191 | 295 | ||
@@ -195,49 +299,80 @@ acpi_ec_write ( | |||
195 | u8 address, | 299 | u8 address, |
196 | u8 data) | 300 | u8 data) |
197 | { | 301 | { |
198 | int result = 0; | 302 | int status = 0; |
199 | acpi_status status = AE_OK; | 303 | u32 glk; |
200 | unsigned long flags = 0; | 304 | u32 tmp; |
201 | u32 glk = 0; | ||
202 | 305 | ||
203 | ACPI_FUNCTION_TRACE("acpi_ec_write"); | 306 | ACPI_FUNCTION_TRACE("acpi_ec_write"); |
204 | 307 | ||
205 | if (!ec) | 308 | if (!ec) |
206 | return_VALUE(-EINVAL); | 309 | return_VALUE(-EINVAL); |
207 | 310 | retry: | |
208 | if (ec->global_lock) { | 311 | if (ec->global_lock) { |
209 | status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); | 312 | status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); |
210 | if (ACPI_FAILURE(status)) | 313 | if (ACPI_FAILURE(status)) |
211 | return_VALUE(-ENODEV); | 314 | return_VALUE(-ENODEV); |
212 | } | 315 | } |
213 | 316 | ||
214 | spin_lock_irqsave(&ec->lock, flags); | 317 | WARN_ON(in_interrupt()); |
318 | down(&ec->sem); | ||
319 | |||
320 | if(acpi_ec_enter_burst_mode(ec)) | ||
321 | goto end; | ||
322 | |||
323 | status = acpi_ec_read_status(ec); | ||
324 | if (status != -EINVAL && | ||
325 | !(status & ACPI_EC_FLAG_BURST)){ | ||
326 | acpi_hw_low_level_write(8, ACPI_EC_BURST_ENABLE, &ec->command_addr); | ||
327 | status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); | ||
328 | if (status) | ||
329 | goto end; | ||
330 | acpi_hw_low_level_read(8, &tmp, &ec->data_addr); | ||
331 | if(tmp != 0x90 ) /* Burst ACK byte*/ | ||
332 | goto end; | ||
333 | } | ||
334 | /*Now we are in burst mode*/ | ||
215 | 335 | ||
216 | acpi_hw_low_level_write(8, ACPI_EC_COMMAND_WRITE, &ec->command_addr); | 336 | acpi_hw_low_level_write(8, ACPI_EC_COMMAND_WRITE, &ec->command_addr); |
217 | result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); | 337 | status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); |
218 | if (result) | 338 | acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); |
339 | if (status){ | ||
219 | goto end; | 340 | goto end; |
341 | } | ||
220 | 342 | ||
221 | acpi_hw_low_level_write(8, address, &ec->data_addr); | 343 | acpi_hw_low_level_write(8, address, &ec->data_addr); |
222 | result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); | 344 | status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); |
223 | if (result) | 345 | if (status){ |
346 | acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); | ||
224 | goto end; | 347 | goto end; |
348 | } | ||
225 | 349 | ||
226 | acpi_hw_low_level_write(8, data, &ec->data_addr); | 350 | acpi_hw_low_level_write(8, data, &ec->data_addr); |
227 | result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); | 351 | status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); |
228 | if (result) | 352 | acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); |
353 | if (status) | ||
229 | goto end; | 354 | goto end; |
230 | 355 | ||
231 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Wrote [%02x] to address [%02x]\n", | 356 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Wrote [%02x] to address [%02x]\n", |
232 | data, address)); | 357 | data, address)); |
233 | 358 | ||
234 | end: | 359 | end: |
235 | spin_unlock_irqrestore(&ec->lock, flags); | 360 | acpi_ec_leave_burst_mode(ec); |
361 | up(&ec->sem); | ||
236 | 362 | ||
237 | if (ec->global_lock) | 363 | if (ec->global_lock) |
238 | acpi_release_global_lock(glk); | 364 | acpi_release_global_lock(glk); |
239 | 365 | ||
240 | return_VALUE(result); | 366 | if(atomic_read(&ec->leaving_burst) == 2){ |
367 | ACPI_DEBUG_PRINT((ACPI_DB_INFO,"aborted, retry ...\n")); | ||
368 | while(!atomic_read(&ec->pending_gpe)){ | ||
369 | msleep(1); | ||
370 | } | ||
371 | acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); | ||
372 | goto retry; | ||
373 | } | ||
374 | |||
375 | return_VALUE(status); | ||
241 | } | 376 | } |
242 | 377 | ||
243 | /* | 378 | /* |
@@ -289,16 +424,14 @@ acpi_ec_query ( | |||
289 | struct acpi_ec *ec, | 424 | struct acpi_ec *ec, |
290 | u32 *data) | 425 | u32 *data) |
291 | { | 426 | { |
292 | int result = 0; | 427 | int status = 0; |
293 | acpi_status status = AE_OK; | 428 | u32 glk; |
294 | unsigned long flags = 0; | ||
295 | u32 glk = 0; | ||
296 | 429 | ||
297 | ACPI_FUNCTION_TRACE("acpi_ec_query"); | 430 | ACPI_FUNCTION_TRACE("acpi_ec_query"); |
298 | 431 | ||
299 | if (!ec || !data) | 432 | if (!ec || !data) |
300 | return_VALUE(-EINVAL); | 433 | return_VALUE(-EINVAL); |
301 | 434 | retry: | |
302 | *data = 0; | 435 | *data = 0; |
303 | 436 | ||
304 | if (ec->global_lock) { | 437 | if (ec->global_lock) { |
@@ -307,29 +440,43 @@ acpi_ec_query ( | |||
307 | return_VALUE(-ENODEV); | 440 | return_VALUE(-ENODEV); |
308 | } | 441 | } |
309 | 442 | ||
443 | down(&ec->sem); | ||
444 | if(acpi_ec_enter_burst_mode(ec)) | ||
445 | goto end; | ||
310 | /* | 446 | /* |
311 | * Query the EC to find out which _Qxx method we need to evaluate. | 447 | * Query the EC to find out which _Qxx method we need to evaluate. |
312 | * Note that successful completion of the query causes the ACPI_EC_SCI | 448 | * Note that successful completion of the query causes the ACPI_EC_SCI |
313 | * bit to be cleared (and thus clearing the interrupt source). | 449 | * bit to be cleared (and thus clearing the interrupt source). |
314 | */ | 450 | */ |
315 | spin_lock_irqsave(&ec->lock, flags); | ||
316 | |||
317 | acpi_hw_low_level_write(8, ACPI_EC_COMMAND_QUERY, &ec->command_addr); | 451 | acpi_hw_low_level_write(8, ACPI_EC_COMMAND_QUERY, &ec->command_addr); |
318 | result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); | 452 | status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); |
319 | if (result) | 453 | if (status){ |
454 | acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); | ||
320 | goto end; | 455 | goto end; |
321 | 456 | } | |
457 | |||
322 | acpi_hw_low_level_read(8, data, &ec->data_addr); | 458 | acpi_hw_low_level_read(8, data, &ec->data_addr); |
459 | acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); | ||
323 | if (!*data) | 460 | if (!*data) |
324 | result = -ENODATA; | 461 | status = -ENODATA; |
325 | 462 | ||
326 | end: | 463 | end: |
327 | spin_unlock_irqrestore(&ec->lock, flags); | 464 | acpi_ec_leave_burst_mode(ec); |
465 | up(&ec->sem); | ||
328 | 466 | ||
329 | if (ec->global_lock) | 467 | if (ec->global_lock) |
330 | acpi_release_global_lock(glk); | 468 | acpi_release_global_lock(glk); |
331 | 469 | ||
332 | return_VALUE(result); | 470 | if(atomic_read(&ec->leaving_burst) == 2){ |
471 | ACPI_DEBUG_PRINT((ACPI_DB_INFO,"aborted, retry ...\n")); | ||
472 | while(!atomic_read(&ec->pending_gpe)){ | ||
473 | msleep(1); | ||
474 | } | ||
475 | acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); | ||
476 | goto retry; | ||
477 | } | ||
478 | |||
479 | return_VALUE(status); | ||
333 | } | 480 | } |
334 | 481 | ||
335 | 482 | ||
@@ -347,42 +494,29 @@ acpi_ec_gpe_query ( | |||
347 | void *ec_cxt) | 494 | void *ec_cxt) |
348 | { | 495 | { |
349 | struct acpi_ec *ec = (struct acpi_ec *) ec_cxt; | 496 | struct acpi_ec *ec = (struct acpi_ec *) ec_cxt; |
350 | u32 value = 0; | 497 | u32 value; |
351 | unsigned long flags = 0; | 498 | int result = -ENODATA; |
352 | static char object_name[5] = {'_','Q','0','0','\0'}; | 499 | static char object_name[5] = {'_','Q','0','0','\0'}; |
353 | const char hex[] = {'0','1','2','3','4','5','6','7', | 500 | const char hex[] = {'0','1','2','3','4','5','6','7', |
354 | '8','9','A','B','C','D','E','F'}; | 501 | '8','9','A','B','C','D','E','F'}; |
355 | 502 | ||
356 | ACPI_FUNCTION_TRACE("acpi_ec_gpe_query"); | 503 | ACPI_FUNCTION_TRACE("acpi_ec_gpe_query"); |
357 | 504 | ||
358 | if (!ec_cxt) | 505 | if (acpi_ec_read_status(ec) & ACPI_EC_FLAG_SCI) |
359 | goto end; | 506 | result = acpi_ec_query(ec, &value); |
360 | 507 | ||
361 | spin_lock_irqsave(&ec->lock, flags); | 508 | if (result) |
362 | acpi_hw_low_level_read(8, &value, &ec->command_addr); | ||
363 | spin_unlock_irqrestore(&ec->lock, flags); | ||
364 | |||
365 | /* TBD: Implement asynch events! | ||
366 | * NOTE: All we care about are EC-SCI's. Other EC events are | ||
367 | * handled via polling (yuck!). This is because some systems | ||
368 | * treat EC-SCIs as level (versus EDGE!) triggered, preventing | ||
369 | * a purely interrupt-driven approach (grumble, grumble). | ||
370 | */ | ||
371 | if (!(value & ACPI_EC_FLAG_SCI)) | ||
372 | goto end; | 509 | goto end; |
373 | 510 | ||
374 | if (acpi_ec_query(ec, &value)) | ||
375 | goto end; | ||
376 | |||
377 | object_name[2] = hex[((value >> 4) & 0x0F)]; | 511 | object_name[2] = hex[((value >> 4) & 0x0F)]; |
378 | object_name[3] = hex[(value & 0x0F)]; | 512 | object_name[3] = hex[(value & 0x0F)]; |
379 | 513 | ||
380 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s\n", object_name)); | 514 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s\n", object_name)); |
381 | 515 | ||
382 | acpi_evaluate_object(ec->handle, object_name, NULL, NULL); | 516 | acpi_evaluate_object(ec->handle, object_name, NULL, NULL); |
383 | 517 | atomic_dec(&ec->pending_gpe); | |
384 | end: | 518 | end: |
385 | acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); | 519 | return; |
386 | } | 520 | } |
387 | 521 | ||
388 | static u32 | 522 | static u32 |
@@ -390,6 +524,7 @@ acpi_ec_gpe_handler ( | |||
390 | void *data) | 524 | void *data) |
391 | { | 525 | { |
392 | acpi_status status = AE_OK; | 526 | acpi_status status = AE_OK; |
527 | u32 value; | ||
393 | struct acpi_ec *ec = (struct acpi_ec *) data; | 528 | struct acpi_ec *ec = (struct acpi_ec *) data; |
394 | 529 | ||
395 | if (!ec) | 530 | if (!ec) |
@@ -397,13 +532,39 @@ acpi_ec_gpe_handler ( | |||
397 | 532 | ||
398 | acpi_disable_gpe(NULL, ec->gpe_bit, ACPI_ISR); | 533 | acpi_disable_gpe(NULL, ec->gpe_bit, ACPI_ISR); |
399 | 534 | ||
400 | status = acpi_os_queue_for_execution(OSD_PRIORITY_GPE, | 535 | value = acpi_ec_read_status(ec); |
401 | acpi_ec_gpe_query, ec); | ||
402 | 536 | ||
403 | if (status == AE_OK) | 537 | if((value & ACPI_EC_FLAG_IBF) && |
404 | return ACPI_INTERRUPT_HANDLED; | 538 | !(value & ACPI_EC_FLAG_BURST) && |
405 | else | 539 | (atomic_read(&ec->leaving_burst) == 0)) { |
406 | return ACPI_INTERRUPT_NOT_HANDLED; | 540 | /* |
541 | * the embedded controller disables | ||
542 | * burst mode for any reason other | ||
543 | * than the burst disable command | ||
544 | * to process critical event. | ||
545 | */ | ||
546 | atomic_set(&ec->leaving_burst , 2); /* block current pending transaction | ||
547 | and retry */ | ||
548 | wake_up(&ec->wait); | ||
549 | }else { | ||
550 | if ((ec->expect_event == ACPI_EC_EVENT_OBF && | ||
551 | (value & ACPI_EC_FLAG_OBF)) || | ||
552 | (ec->expect_event == ACPI_EC_EVENT_IBE && | ||
553 | !(value & ACPI_EC_FLAG_IBF))) { | ||
554 | ec->expect_event = 0; | ||
555 | wake_up(&ec->wait); | ||
556 | |||
557 | } | ||
558 | } | ||
559 | |||
560 | if (value & ACPI_EC_FLAG_SCI){ | ||
561 | atomic_add(1, &ec->pending_gpe) ; | ||
562 | status = acpi_os_queue_for_execution(OSD_PRIORITY_GPE, | ||
563 | acpi_ec_gpe_query, ec); | ||
564 | } | ||
565 | |||
566 | return status == AE_OK ? | ||
567 | ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED; | ||
407 | } | 568 | } |
408 | 569 | ||
409 | /* -------------------------------------------------------------------------- | 570 | /* -------------------------------------------------------------------------- |
@@ -421,10 +582,8 @@ acpi_ec_space_setup ( | |||
421 | * The EC object is in the handler context and is needed | 582 | * The EC object is in the handler context and is needed |
422 | * when calling the acpi_ec_space_handler. | 583 | * when calling the acpi_ec_space_handler. |
423 | */ | 584 | */ |
424 | if(function == ACPI_REGION_DEACTIVATE) | 585 | *return_context = (function != ACPI_REGION_DEACTIVATE) ? |
425 | *return_context = NULL; | 586 | handler_context : NULL; |
426 | else | ||
427 | *return_context = handler_context; | ||
428 | 587 | ||
429 | return AE_OK; | 588 | return AE_OK; |
430 | } | 589 | } |
@@ -555,7 +714,7 @@ static int | |||
555 | acpi_ec_add_fs ( | 714 | acpi_ec_add_fs ( |
556 | struct acpi_device *device) | 715 | struct acpi_device *device) |
557 | { | 716 | { |
558 | struct proc_dir_entry *entry = NULL; | 717 | struct proc_dir_entry *entry; |
559 | 718 | ||
560 | ACPI_FUNCTION_TRACE("acpi_ec_add_fs"); | 719 | ACPI_FUNCTION_TRACE("acpi_ec_add_fs"); |
561 | 720 | ||
@@ -606,9 +765,9 @@ static int | |||
606 | acpi_ec_add ( | 765 | acpi_ec_add ( |
607 | struct acpi_device *device) | 766 | struct acpi_device *device) |
608 | { | 767 | { |
609 | int result = 0; | 768 | int result; |
610 | acpi_status status = AE_OK; | 769 | acpi_status status; |
611 | struct acpi_ec *ec = NULL; | 770 | struct acpi_ec *ec; |
612 | unsigned long uid; | 771 | unsigned long uid; |
613 | 772 | ||
614 | ACPI_FUNCTION_TRACE("acpi_ec_add"); | 773 | ACPI_FUNCTION_TRACE("acpi_ec_add"); |
@@ -623,7 +782,10 @@ acpi_ec_add ( | |||
623 | 782 | ||
624 | ec->handle = device->handle; | 783 | ec->handle = device->handle; |
625 | ec->uid = -1; | 784 | ec->uid = -1; |
626 | spin_lock_init(&ec->lock); | 785 | atomic_set(&ec->pending_gpe, 0); |
786 | atomic_set(&ec->leaving_burst , 1); | ||
787 | init_MUTEX(&ec->sem); | ||
788 | init_waitqueue_head(&ec->wait); | ||
627 | strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); | 789 | strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); |
628 | strcpy(acpi_device_class(device), ACPI_EC_CLASS); | 790 | strcpy(acpi_device_class(device), ACPI_EC_CLASS); |
629 | acpi_driver_data(device) = ec; | 791 | acpi_driver_data(device) = ec; |
@@ -637,7 +799,7 @@ acpi_ec_add ( | |||
637 | if (ec_ecdt && ec_ecdt->uid == uid) { | 799 | if (ec_ecdt && ec_ecdt->uid == uid) { |
638 | acpi_remove_address_space_handler(ACPI_ROOT_OBJECT, | 800 | acpi_remove_address_space_handler(ACPI_ROOT_OBJECT, |
639 | ACPI_ADR_SPACE_EC, &acpi_ec_space_handler); | 801 | ACPI_ADR_SPACE_EC, &acpi_ec_space_handler); |
640 | 802 | ||
641 | acpi_remove_gpe_handler(NULL, ec_ecdt->gpe_bit, &acpi_ec_gpe_handler); | 803 | acpi_remove_gpe_handler(NULL, ec_ecdt->gpe_bit, &acpi_ec_gpe_handler); |
642 | 804 | ||
643 | kfree(ec_ecdt); | 805 | kfree(ec_ecdt); |
@@ -677,7 +839,7 @@ acpi_ec_remove ( | |||
677 | struct acpi_device *device, | 839 | struct acpi_device *device, |
678 | int type) | 840 | int type) |
679 | { | 841 | { |
680 | struct acpi_ec *ec = NULL; | 842 | struct acpi_ec *ec; |
681 | 843 | ||
682 | ACPI_FUNCTION_TRACE("acpi_ec_remove"); | 844 | ACPI_FUNCTION_TRACE("acpi_ec_remove"); |
683 | 845 | ||
@@ -732,8 +894,8 @@ static int | |||
732 | acpi_ec_start ( | 894 | acpi_ec_start ( |
733 | struct acpi_device *device) | 895 | struct acpi_device *device) |
734 | { | 896 | { |
735 | acpi_status status = AE_OK; | 897 | acpi_status status; |
736 | struct acpi_ec *ec = NULL; | 898 | struct acpi_ec *ec; |
737 | 899 | ||
738 | ACPI_FUNCTION_TRACE("acpi_ec_start"); | 900 | ACPI_FUNCTION_TRACE("acpi_ec_start"); |
739 | 901 | ||
@@ -789,8 +951,8 @@ acpi_ec_stop ( | |||
789 | struct acpi_device *device, | 951 | struct acpi_device *device, |
790 | int type) | 952 | int type) |
791 | { | 953 | { |
792 | acpi_status status = AE_OK; | 954 | acpi_status status; |
793 | struct acpi_ec *ec = NULL; | 955 | struct acpi_ec *ec; |
794 | 956 | ||
795 | ACPI_FUNCTION_TRACE("acpi_ec_stop"); | 957 | ACPI_FUNCTION_TRACE("acpi_ec_stop"); |
796 | 958 | ||
@@ -832,7 +994,6 @@ acpi_fake_ecdt_callback ( | |||
832 | status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->gpe_bit); | 994 | status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->gpe_bit); |
833 | if (ACPI_FAILURE(status)) | 995 | if (ACPI_FAILURE(status)) |
834 | return status; | 996 | return status; |
835 | spin_lock_init(&ec_ecdt->lock); | ||
836 | ec_ecdt->global_lock = TRUE; | 997 | ec_ecdt->global_lock = TRUE; |
837 | ec_ecdt->handle = handle; | 998 | ec_ecdt->handle = handle; |
838 | 999 | ||
@@ -890,7 +1051,7 @@ acpi_ec_get_real_ecdt(void) | |||
890 | acpi_status status; | 1051 | acpi_status status; |
891 | struct acpi_table_ecdt *ecdt_ptr; | 1052 | struct acpi_table_ecdt *ecdt_ptr; |
892 | 1053 | ||
893 | status = acpi_get_firmware_table("ECDT", 1, ACPI_LOGICAL_ADDRESSING, | 1054 | status = acpi_get_firmware_table("ECDT", 1, ACPI_LOGICAL_ADDRESSING, |
894 | (struct acpi_table_header **) &ecdt_ptr); | 1055 | (struct acpi_table_header **) &ecdt_ptr); |
895 | if (ACPI_FAILURE(status)) | 1056 | if (ACPI_FAILURE(status)) |
896 | return -ENODEV; | 1057 | return -ENODEV; |
@@ -905,11 +1066,12 @@ acpi_ec_get_real_ecdt(void) | |||
905 | return -ENOMEM; | 1066 | return -ENOMEM; |
906 | memset(ec_ecdt, 0, sizeof(struct acpi_ec)); | 1067 | memset(ec_ecdt, 0, sizeof(struct acpi_ec)); |
907 | 1068 | ||
1069 | init_MUTEX(&ec_ecdt->sem); | ||
1070 | init_waitqueue_head(&ec_ecdt->wait); | ||
908 | ec_ecdt->command_addr = ecdt_ptr->ec_control; | 1071 | ec_ecdt->command_addr = ecdt_ptr->ec_control; |
909 | ec_ecdt->status_addr = ecdt_ptr->ec_control; | 1072 | ec_ecdt->status_addr = ecdt_ptr->ec_control; |
910 | ec_ecdt->data_addr = ecdt_ptr->ec_data; | 1073 | ec_ecdt->data_addr = ecdt_ptr->ec_data; |
911 | ec_ecdt->gpe_bit = ecdt_ptr->gpe_bit; | 1074 | ec_ecdt->gpe_bit = ecdt_ptr->gpe_bit; |
912 | spin_lock_init(&ec_ecdt->lock); | ||
913 | /* use the GL just to be safe */ | 1075 | /* use the GL just to be safe */ |
914 | ec_ecdt->global_lock = TRUE; | 1076 | ec_ecdt->global_lock = TRUE; |
915 | ec_ecdt->uid = ecdt_ptr->uid; | 1077 | ec_ecdt->uid = ecdt_ptr->uid; |
@@ -978,7 +1140,7 @@ error: | |||
978 | 1140 | ||
979 | static int __init acpi_ec_init (void) | 1141 | static int __init acpi_ec_init (void) |
980 | { | 1142 | { |
981 | int result = 0; | 1143 | int result; |
982 | 1144 | ||
983 | ACPI_FUNCTION_TRACE("acpi_ec_init"); | 1145 | ACPI_FUNCTION_TRACE("acpi_ec_init"); |
984 | 1146 | ||