blob: 1ac5731d45e5aece6a021e7bb548139a9034b9b5 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * acpi_ec.c - ACPI Embedded Controller Driver ($Revision: 38 $)
3 *
4 * Copyright (C) 2004 Luming Yu <luming.yu@intel.com>
5 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
6 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
7 *
8 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or (at
13 * your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
23 *
24 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
25 */
26
27#include <linux/kernel.h>
28#include <linux/module.h>
29#include <linux/init.h>
30#include <linux/types.h>
31#include <linux/delay.h>
32#include <linux/proc_fs.h>
33#include <linux/seq_file.h>
Dmitry Torokhov451566f2005-03-19 01:10:05 -050034#include <linux/interrupt.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <asm/io.h>
36#include <acpi/acpi_bus.h>
37#include <acpi/acpi_drivers.h>
38#include <acpi/actypes.h>
39
40#define _COMPONENT ACPI_EC_COMPONENT
41ACPI_MODULE_NAME ("acpi_ec")
42
43#define ACPI_EC_COMPONENT 0x00100000
44#define ACPI_EC_CLASS "embedded_controller"
45#define ACPI_EC_HID "PNP0C09"
46#define ACPI_EC_DRIVER_NAME "ACPI Embedded Controller Driver"
47#define ACPI_EC_DEVICE_NAME "Embedded Controller"
48#define ACPI_EC_FILE_INFO "info"
49
50
51#define ACPI_EC_FLAG_OBF 0x01 /* Output buffer full */
52#define ACPI_EC_FLAG_IBF 0x02 /* Input buffer full */
Dmitry Torokhov451566f2005-03-19 01:10:05 -050053#define ACPI_EC_FLAG_BURST 0x10 /* burst mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -070054#define ACPI_EC_FLAG_SCI 0x20 /* EC-SCI occurred */
55
56#define ACPI_EC_EVENT_OBF 0x01 /* Output buffer full */
57#define ACPI_EC_EVENT_IBE 0x02 /* Input buffer empty */
58
Dmitry Torokhov451566f2005-03-19 01:10:05 -050059#define ACPI_EC_DELAY 50 /* Wait 50ms max. during EC ops */
Linus Torvalds1da177e2005-04-16 15:20:36 -070060#define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */
61
Luming Yu45bea152005-07-23 04:08:00 -040062#define ACPI_EC_UDELAY 100 /* Poll @ 100us increments */
63#define ACPI_EC_UDELAY_COUNT 1000 /* Wait 10ms max. during EC ops */
64
Linus Torvalds1da177e2005-04-16 15:20:36 -070065#define ACPI_EC_COMMAND_READ 0x80
66#define ACPI_EC_COMMAND_WRITE 0x81
Dmitry Torokhov451566f2005-03-19 01:10:05 -050067#define ACPI_EC_BURST_ENABLE 0x82
68#define ACPI_EC_BURST_DISABLE 0x83
Linus Torvalds1da177e2005-04-16 15:20:36 -070069#define ACPI_EC_COMMAND_QUERY 0x84
70
Luming Yu45bea152005-07-23 04:08:00 -040071#define EC_POLLING 0xFF
72#define EC_BURST 0x00
73
74
Linus Torvalds1da177e2005-04-16 15:20:36 -070075static int acpi_ec_remove (struct acpi_device *device, int type);
76static int acpi_ec_start (struct acpi_device *device);
77static int acpi_ec_stop (struct acpi_device *device, int type);
Luming Yu45bea152005-07-23 04:08:00 -040078static int acpi_ec_burst_add ( struct acpi_device *device);
Luming Yu7b15f5e2005-08-03 17:38:04 -040079static int acpi_ec_polling_add ( struct acpi_device *device);
Linus Torvalds1da177e2005-04-16 15:20:36 -070080
81static struct acpi_driver acpi_ec_driver = {
82 .name = ACPI_EC_DRIVER_NAME,
83 .class = ACPI_EC_CLASS,
84 .ids = ACPI_EC_HID,
85 .ops = {
Luming Yu7b15f5e2005-08-03 17:38:04 -040086 .add = acpi_ec_polling_add,
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 .remove = acpi_ec_remove,
88 .start = acpi_ec_start,
89 .stop = acpi_ec_stop,
90 },
91};
Luming Yu45bea152005-07-23 04:08:00 -040092union acpi_ec {
93 struct {
94 u32 mode;
95 acpi_handle handle;
96 unsigned long uid;
97 unsigned long gpe_bit;
98 struct acpi_generic_address status_addr;
99 struct acpi_generic_address command_addr;
100 struct acpi_generic_address data_addr;
101 unsigned long global_lock;
102 } common;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103
Luming Yu45bea152005-07-23 04:08:00 -0400104 struct {
105 u32 mode;
106 acpi_handle handle;
107 unsigned long uid;
108 unsigned long gpe_bit;
109 struct acpi_generic_address status_addr;
110 struct acpi_generic_address command_addr;
111 struct acpi_generic_address data_addr;
112 unsigned long global_lock;
113 unsigned int expect_event;
114 atomic_t leaving_burst; /* 0 : No, 1 : Yes, 2: abort*/
115 atomic_t pending_gpe;
116 struct semaphore sem;
117 wait_queue_head_t wait;
118 }burst;
119
120 struct {
121 u32 mode;
122 acpi_handle handle;
123 unsigned long uid;
124 unsigned long gpe_bit;
125 struct acpi_generic_address status_addr;
126 struct acpi_generic_address command_addr;
127 struct acpi_generic_address data_addr;
128 unsigned long global_lock;
129 spinlock_t lock;
130 }polling;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131};
132
Luming Yu45bea152005-07-23 04:08:00 -0400133static int acpi_ec_polling_wait ( union acpi_ec *ec, u8 event);
134static int acpi_ec_burst_wait(union acpi_ec *ec, unsigned int event);
135static int acpi_ec_polling_read ( union acpi_ec *ec, u8 address, u32 *data);
136static int acpi_ec_burst_read( union acpi_ec *ec, u8 address, u32 *data);
137static int acpi_ec_polling_write ( union acpi_ec *ec, u8 address, u8 data);
138static int acpi_ec_burst_write ( union acpi_ec *ec, u8 address, u8 data);
139static int acpi_ec_polling_query ( union acpi_ec *ec, u32 *data);
140static int acpi_ec_burst_query ( union acpi_ec *ec, u32 *data);
141static void acpi_ec_gpe_polling_query ( void *ec_cxt);
142static void acpi_ec_gpe_burst_query ( void *ec_cxt);
143static u32 acpi_ec_gpe_polling_handler ( void *data);
144static u32 acpi_ec_gpe_burst_handler ( void *data);
145static acpi_status __init
146acpi_fake_ecdt_polling_callback (
147 acpi_handle handle,
148 u32 Level,
149 void *context,
150 void **retval);
151
152static acpi_status __init
153acpi_fake_ecdt_burst_callback (
154 acpi_handle handle,
155 u32 Level,
156 void *context,
157 void **retval);
158
159static int __init
160acpi_ec_polling_get_real_ecdt(void);
161static int __init
162acpi_ec_burst_get_real_ecdt(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163/* If we find an EC via the ECDT, we need to keep a ptr to its context */
Luming Yu45bea152005-07-23 04:08:00 -0400164static union acpi_ec *ec_ecdt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165
166/* External interfaces use first EC only, so remember */
167static struct acpi_device *first_ec;
Luming Yu7b15f5e2005-08-03 17:38:04 -0400168static int acpi_ec_polling_mode = EC_POLLING;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169
170/* --------------------------------------------------------------------------
171 Transaction Management
172 -------------------------------------------------------------------------- */
173
Luming Yu45bea152005-07-23 04:08:00 -0400174static inline u32 acpi_ec_read_status(union acpi_ec *ec)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175{
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500176 u32 status = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177
Luming Yu45bea152005-07-23 04:08:00 -0400178 acpi_hw_low_level_read(8, &status, &ec->common.status_addr);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500179 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180}
181
Luming Yu45bea152005-07-23 04:08:00 -0400182static int
183acpi_ec_wait (
184 union acpi_ec *ec,
185 u8 event)
186{
187 if (acpi_ec_polling_mode)
188 return acpi_ec_polling_wait (ec, event);
189 else
190 return acpi_ec_burst_wait (ec, event);
191}
192
193static int
194acpi_ec_polling_wait (
195 union acpi_ec *ec,
196 u8 event)
197{
198 u32 acpi_ec_status = 0;
199 u32 i = ACPI_EC_UDELAY_COUNT;
200
201 if (!ec)
202 return -EINVAL;
203
204 /* Poll the EC status register waiting for the event to occur. */
205 switch (event) {
206 case ACPI_EC_EVENT_OBF:
207 do {
208 acpi_hw_low_level_read(8, &acpi_ec_status, &ec->common.status_addr);
209 if (acpi_ec_status & ACPI_EC_FLAG_OBF)
210 return 0;
211 udelay(ACPI_EC_UDELAY);
212 } while (--i>0);
213 break;
214 case ACPI_EC_EVENT_IBE:
215 do {
216 acpi_hw_low_level_read(8, &acpi_ec_status, &ec->common.status_addr);
217 if (!(acpi_ec_status & ACPI_EC_FLAG_IBF))
218 return 0;
219 udelay(ACPI_EC_UDELAY);
220 } while (--i>0);
221 break;
222 default:
223 return -EINVAL;
224 }
225
226 return -ETIME;
227}
228static int acpi_ec_burst_wait(union acpi_ec *ec, unsigned int event)
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500229{
230 int result = 0;
231
232 ACPI_FUNCTION_TRACE("acpi_ec_wait");
233
Luming Yu45bea152005-07-23 04:08:00 -0400234 ec->burst.expect_event = event;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500235 smp_mb();
236
Luming Yu45bea152005-07-23 04:08:00 -0400237 result = wait_event_interruptible_timeout(ec->burst.wait,
238 !ec->burst.expect_event,
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500239 msecs_to_jiffies(ACPI_EC_DELAY));
240
Luming Yu45bea152005-07-23 04:08:00 -0400241 ec->burst.expect_event = 0;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500242 smp_mb();
243
244 if (result < 0){
245 ACPI_DEBUG_PRINT((ACPI_DB_ERROR," result = %d ", result));
246 return_VALUE(result);
247 }
248
249 /*
250 * Verify that the event in question has actually happened by
251 * querying EC status. Do the check even if operation timed-out
252 * to make sure that we did not miss interrupt.
253 */
254 switch (event) {
255 case ACPI_EC_EVENT_OBF:
256 if (acpi_ec_read_status(ec) & ACPI_EC_FLAG_OBF)
257 return_VALUE(0);
258 break;
259
260 case ACPI_EC_EVENT_IBE:
261 if (~acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF)
262 return_VALUE(0);
263 break;
264 }
265
266 return_VALUE(-ETIME);
267}
268
269
270
271static int
272acpi_ec_enter_burst_mode (
Luming Yu45bea152005-07-23 04:08:00 -0400273 union acpi_ec *ec)
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500274{
275 u32 tmp = 0;
276 int status = 0;
277
278 ACPI_FUNCTION_TRACE("acpi_ec_enter_burst_mode");
279
280 status = acpi_ec_read_status(ec);
281 if (status != -EINVAL &&
282 !(status & ACPI_EC_FLAG_BURST)){
Luming Yu45bea152005-07-23 04:08:00 -0400283 acpi_hw_low_level_write(8, ACPI_EC_BURST_ENABLE, &ec->common.command_addr);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500284 status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF);
285 if (status){
Luming Yu45bea152005-07-23 04:08:00 -0400286 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500287 return_VALUE(-EINVAL);
288 }
Luming Yu45bea152005-07-23 04:08:00 -0400289 acpi_hw_low_level_read(8, &tmp, &ec->common.data_addr);
290 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500291 if(tmp != 0x90 ) {/* Burst ACK byte*/
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500292 return_VALUE(-EINVAL);
293 }
Luming Yu668d74c2005-07-23 00:26:33 -0400294 }
295
Luming Yu45bea152005-07-23 04:08:00 -0400296 atomic_set(&ec->burst.leaving_burst , 0);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500297 return_VALUE(0);
298}
299
300static int
301acpi_ec_leave_burst_mode (
Luming Yu45bea152005-07-23 04:08:00 -0400302 union acpi_ec *ec)
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500303{
304 int status =0;
305
306 ACPI_FUNCTION_TRACE("acpi_ec_leave_burst_mode");
307
Luming Yu45bea152005-07-23 04:08:00 -0400308 atomic_set(&ec->burst.leaving_burst , 1);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500309 status = acpi_ec_read_status(ec);
310 if (status != -EINVAL &&
311 (status & ACPI_EC_FLAG_BURST)){
Luming Yu45bea152005-07-23 04:08:00 -0400312 acpi_hw_low_level_write(8, ACPI_EC_BURST_DISABLE, &ec->common.command_addr);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500313 status = acpi_ec_wait(ec, ACPI_EC_FLAG_IBF);
314 if (status){
Luming Yu45bea152005-07-23 04:08:00 -0400315 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500316 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,"------->wait fail\n"));
317 return_VALUE(-EINVAL);
318 }
Luming Yu45bea152005-07-23 04:08:00 -0400319 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500320 status = acpi_ec_read_status(ec);
Luming Yu668d74c2005-07-23 00:26:33 -0400321 }
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500322
323 return_VALUE(0);
324}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325
326static int
327acpi_ec_read (
Luming Yu45bea152005-07-23 04:08:00 -0400328 union acpi_ec *ec,
329 u8 address,
330 u32 *data)
331{
332 if (acpi_ec_polling_mode)
333 return acpi_ec_polling_read(ec, address, data);
334 else
335 return acpi_ec_burst_read(ec, address, data);
336}
337static int
338acpi_ec_write (
339 union acpi_ec *ec,
340 u8 address,
341 u8 data)
342{
343 if (acpi_ec_polling_mode)
344 return acpi_ec_polling_write(ec, address, data);
345 else
346 return acpi_ec_burst_write(ec, address, data);
347}
348static int
349acpi_ec_polling_read (
350 union acpi_ec *ec,
351 u8 address,
352 u32 *data)
353{
354 acpi_status status = AE_OK;
355 int result = 0;
356 unsigned long flags = 0;
357 u32 glk = 0;
358
359 ACPI_FUNCTION_TRACE("acpi_ec_read");
360
361 if (!ec || !data)
362 return_VALUE(-EINVAL);
363
364 *data = 0;
365
366 if (ec->common.global_lock) {
367 status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
368 if (ACPI_FAILURE(status))
369 return_VALUE(-ENODEV);
370 }
371
372 spin_lock_irqsave(&ec->polling.lock, flags);
373
374 acpi_hw_low_level_write(8, ACPI_EC_COMMAND_READ, &ec->common.command_addr);
375 result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
376 if (result)
377 goto end;
378
379 acpi_hw_low_level_write(8, address, &ec->common.data_addr);
380 result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF);
381 if (result)
382 goto end;
383
384 acpi_hw_low_level_read(8, data, &ec->common.data_addr);
385
386 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Read [%02x] from address [%02x]\n",
387 *data, address));
388
389end:
390 spin_unlock_irqrestore(&ec->polling.lock, flags);
391
392 if (ec->common.global_lock)
393 acpi_release_global_lock(glk);
394
395 return_VALUE(result);
396}
397
398
399static int
400acpi_ec_polling_write (
401 union acpi_ec *ec,
402 u8 address,
403 u8 data)
404{
405 int result = 0;
406 acpi_status status = AE_OK;
407 unsigned long flags = 0;
408 u32 glk = 0;
409
410 ACPI_FUNCTION_TRACE("acpi_ec_write");
411
412 if (!ec)
413 return_VALUE(-EINVAL);
414
415 if (ec->common.global_lock) {
416 status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
417 if (ACPI_FAILURE(status))
418 return_VALUE(-ENODEV);
419 }
420
421 spin_lock_irqsave(&ec->polling.lock, flags);
422
423 acpi_hw_low_level_write(8, ACPI_EC_COMMAND_WRITE, &ec->common.command_addr);
424 result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
425 if (result)
426 goto end;
427
428 acpi_hw_low_level_write(8, address, &ec->common.data_addr);
429 result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
430 if (result)
431 goto end;
432
433 acpi_hw_low_level_write(8, data, &ec->common.data_addr);
434 result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
435 if (result)
436 goto end;
437
438 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Wrote [%02x] to address [%02x]\n",
439 data, address));
440
441end:
442 spin_unlock_irqrestore(&ec->polling.lock, flags);
443
444 if (ec->common.global_lock)
445 acpi_release_global_lock(glk);
446
447 return_VALUE(result);
448}
449
450static int
451acpi_ec_burst_read (
452 union acpi_ec *ec,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 u8 address,
454 u32 *data)
455{
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500456 int status = 0;
457 u32 glk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458
459 ACPI_FUNCTION_TRACE("acpi_ec_read");
460
461 if (!ec || !data)
462 return_VALUE(-EINVAL);
463
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500464retry:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 *data = 0;
466
Luming Yu45bea152005-07-23 04:08:00 -0400467 if (ec->common.global_lock) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
469 if (ACPI_FAILURE(status))
470 return_VALUE(-ENODEV);
471 }
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500472
473 WARN_ON(in_interrupt());
Luming Yu45bea152005-07-23 04:08:00 -0400474 down(&ec->burst.sem);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500475
476 if(acpi_ec_enter_burst_mode(ec))
477 goto end;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478
Luming Yu45bea152005-07-23 04:08:00 -0400479 acpi_hw_low_level_write(8, ACPI_EC_COMMAND_READ, &ec->common.command_addr);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500480 status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
Luming Yu45bea152005-07-23 04:08:00 -0400481 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500482 if (status) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 goto end;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500484 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485
Luming Yu45bea152005-07-23 04:08:00 -0400486 acpi_hw_low_level_write(8, address, &ec->common.data_addr);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500487 status= acpi_ec_wait(ec, ACPI_EC_EVENT_OBF);
488 if (status){
Luming Yu45bea152005-07-23 04:08:00 -0400489 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 goto end;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500491 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492
Luming Yu45bea152005-07-23 04:08:00 -0400493 acpi_hw_low_level_read(8, data, &ec->common.data_addr);
494 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495
496 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Read [%02x] from address [%02x]\n",
497 *data, address));
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500498
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499end:
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500500 acpi_ec_leave_burst_mode(ec);
Luming Yu45bea152005-07-23 04:08:00 -0400501 up(&ec->burst.sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
Luming Yu45bea152005-07-23 04:08:00 -0400503 if (ec->common.global_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 acpi_release_global_lock(glk);
505
Luming Yu45bea152005-07-23 04:08:00 -0400506 if(atomic_read(&ec->burst.leaving_burst) == 2){
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500507 ACPI_DEBUG_PRINT((ACPI_DB_INFO,"aborted, retry ...\n"));
Luming Yu45bea152005-07-23 04:08:00 -0400508 while(atomic_read(&ec->burst.pending_gpe)){
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500509 msleep(1);
510 }
Luming Yu45bea152005-07-23 04:08:00 -0400511 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500512 goto retry;
513 }
514
515 return_VALUE(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516}
517
518
519static int
Luming Yu45bea152005-07-23 04:08:00 -0400520acpi_ec_burst_write (
521 union acpi_ec *ec,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 u8 address,
523 u8 data)
524{
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500525 int status = 0;
526 u32 glk;
527 u32 tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528
529 ACPI_FUNCTION_TRACE("acpi_ec_write");
530
531 if (!ec)
532 return_VALUE(-EINVAL);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500533retry:
Luming Yu45bea152005-07-23 04:08:00 -0400534 if (ec->common.global_lock) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
536 if (ACPI_FAILURE(status))
537 return_VALUE(-ENODEV);
538 }
539
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500540 WARN_ON(in_interrupt());
Luming Yu45bea152005-07-23 04:08:00 -0400541 down(&ec->burst.sem);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500542
543 if(acpi_ec_enter_burst_mode(ec))
544 goto end;
545
546 status = acpi_ec_read_status(ec);
547 if (status != -EINVAL &&
548 !(status & ACPI_EC_FLAG_BURST)){
Luming Yu45bea152005-07-23 04:08:00 -0400549 acpi_hw_low_level_write(8, ACPI_EC_BURST_ENABLE, &ec->common.command_addr);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500550 status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF);
551 if (status)
552 goto end;
Luming Yu45bea152005-07-23 04:08:00 -0400553 acpi_hw_low_level_read(8, &tmp, &ec->common.data_addr);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500554 if(tmp != 0x90 ) /* Burst ACK byte*/
555 goto end;
556 }
557 /*Now we are in burst mode*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558
Luming Yu45bea152005-07-23 04:08:00 -0400559 acpi_hw_low_level_write(8, ACPI_EC_COMMAND_WRITE, &ec->common.command_addr);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500560 status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
Luming Yu45bea152005-07-23 04:08:00 -0400561 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500562 if (status){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 goto end;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500564 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565
Luming Yu45bea152005-07-23 04:08:00 -0400566 acpi_hw_low_level_write(8, address, &ec->common.data_addr);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500567 status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
568 if (status){
Luming Yu45bea152005-07-23 04:08:00 -0400569 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 goto end;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500571 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572
Luming Yu45bea152005-07-23 04:08:00 -0400573 acpi_hw_low_level_write(8, data, &ec->common.data_addr);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500574 status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
Luming Yu45bea152005-07-23 04:08:00 -0400575 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500576 if (status)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 goto end;
578
579 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Wrote [%02x] to address [%02x]\n",
580 data, address));
581
582end:
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500583 acpi_ec_leave_burst_mode(ec);
Luming Yu45bea152005-07-23 04:08:00 -0400584 up(&ec->burst.sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585
Luming Yu45bea152005-07-23 04:08:00 -0400586 if (ec->common.global_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 acpi_release_global_lock(glk);
588
Luming Yu45bea152005-07-23 04:08:00 -0400589 if(atomic_read(&ec->burst.leaving_burst) == 2){
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500590 ACPI_DEBUG_PRINT((ACPI_DB_INFO,"aborted, retry ...\n"));
Luming Yu45bea152005-07-23 04:08:00 -0400591 while(atomic_read(&ec->burst.pending_gpe)){
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500592 msleep(1);
593 }
Luming Yu45bea152005-07-23 04:08:00 -0400594 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500595 goto retry;
596 }
597
598 return_VALUE(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599}
600
601/*
602 * Externally callable EC access functions. For now, assume 1 EC only
603 */
604int
605ec_read(u8 addr, u8 *val)
606{
Luming Yu45bea152005-07-23 04:08:00 -0400607 union acpi_ec *ec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 int err;
609 u32 temp_data;
610
611 if (!first_ec)
612 return -ENODEV;
613
614 ec = acpi_driver_data(first_ec);
615
616 err = acpi_ec_read(ec, addr, &temp_data);
617
618 if (!err) {
619 *val = temp_data;
620 return 0;
621 }
622 else
623 return err;
624}
625EXPORT_SYMBOL(ec_read);
626
627int
628ec_write(u8 addr, u8 val)
629{
Luming Yu45bea152005-07-23 04:08:00 -0400630 union acpi_ec *ec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 int err;
632
633 if (!first_ec)
634 return -ENODEV;
635
636 ec = acpi_driver_data(first_ec);
637
638 err = acpi_ec_write(ec, addr, val);
639
640 return err;
641}
642EXPORT_SYMBOL(ec_write);
643
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644static int
645acpi_ec_query (
Luming Yu45bea152005-07-23 04:08:00 -0400646 union acpi_ec *ec,
647 u32 *data)
648{
649 if (acpi_ec_polling_mode)
650 return acpi_ec_polling_query(ec, data);
651 else
652 return acpi_ec_burst_query(ec, data);
653}
654static int
655acpi_ec_polling_query (
656 union acpi_ec *ec,
657 u32 *data)
658{
659 int result = 0;
660 acpi_status status = AE_OK;
661 unsigned long flags = 0;
662 u32 glk = 0;
663
664 ACPI_FUNCTION_TRACE("acpi_ec_query");
665
666 if (!ec || !data)
667 return_VALUE(-EINVAL);
668
669 *data = 0;
670
671 if (ec->common.global_lock) {
672 status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
673 if (ACPI_FAILURE(status))
674 return_VALUE(-ENODEV);
675 }
676
677 /*
678 * Query the EC to find out which _Qxx method we need to evaluate.
679 * Note that successful completion of the query causes the ACPI_EC_SCI
680 * bit to be cleared (and thus clearing the interrupt source).
681 */
682 spin_lock_irqsave(&ec->polling.lock, flags);
683
684 acpi_hw_low_level_write(8, ACPI_EC_COMMAND_QUERY, &ec->common.command_addr);
685 result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF);
686 if (result)
687 goto end;
688
689 acpi_hw_low_level_read(8, data, &ec->common.data_addr);
690 if (!*data)
691 result = -ENODATA;
692
693end:
694 spin_unlock_irqrestore(&ec->polling.lock, flags);
695
696 if (ec->common.global_lock)
697 acpi_release_global_lock(glk);
698
699 return_VALUE(result);
700}
701static int
702acpi_ec_burst_query (
703 union acpi_ec *ec,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704 u32 *data)
705{
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500706 int status = 0;
707 u32 glk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708
709 ACPI_FUNCTION_TRACE("acpi_ec_query");
710
711 if (!ec || !data)
712 return_VALUE(-EINVAL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 *data = 0;
714
Luming Yu45bea152005-07-23 04:08:00 -0400715 if (ec->common.global_lock) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
717 if (ACPI_FAILURE(status))
718 return_VALUE(-ENODEV);
719 }
720
Luming Yu45bea152005-07-23 04:08:00 -0400721 down(&ec->burst.sem);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500722 if(acpi_ec_enter_burst_mode(ec))
723 goto end;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 /*
725 * Query the EC to find out which _Qxx method we need to evaluate.
726 * Note that successful completion of the query causes the ACPI_EC_SCI
727 * bit to be cleared (and thus clearing the interrupt source).
728 */
Luming Yu45bea152005-07-23 04:08:00 -0400729 acpi_hw_low_level_write(8, ACPI_EC_COMMAND_QUERY, &ec->common.command_addr);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500730 status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF);
731 if (status){
Luming Yu45bea152005-07-23 04:08:00 -0400732 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 goto end;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500734 }
735
Luming Yu45bea152005-07-23 04:08:00 -0400736 acpi_hw_low_level_read(8, data, &ec->common.data_addr);
737 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 if (!*data)
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500739 status = -ENODATA;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740
741end:
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500742 acpi_ec_leave_burst_mode(ec);
Luming Yu45bea152005-07-23 04:08:00 -0400743 up(&ec->burst.sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744
Luming Yu45bea152005-07-23 04:08:00 -0400745 if (ec->common.global_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 acpi_release_global_lock(glk);
747
Luming Yu45bea152005-07-23 04:08:00 -0400748 if(atomic_read(&ec->burst.leaving_burst) == 2){
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500749 ACPI_DEBUG_PRINT((ACPI_DB_INFO,"aborted, retry ...\n"));
Luming Yu45bea152005-07-23 04:08:00 -0400750 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Luming Yu17e9c782005-04-22 23:07:10 -0400751 status = -ENODATA;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500752 }
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500753 return_VALUE(status);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754}
755
756
757/* --------------------------------------------------------------------------
758 Event Management
759 -------------------------------------------------------------------------- */
760
Luming Yu45bea152005-07-23 04:08:00 -0400761union acpi_ec_query_data {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 acpi_handle handle;
763 u8 data;
764};
765
766static void
767acpi_ec_gpe_query (
768 void *ec_cxt)
769{
Luming Yu45bea152005-07-23 04:08:00 -0400770 if (acpi_ec_polling_mode)
771 acpi_ec_gpe_polling_query(ec_cxt);
772 else
773 acpi_ec_gpe_burst_query(ec_cxt);
774}
775
776static void
777acpi_ec_gpe_polling_query (
778 void *ec_cxt)
779{
780 union acpi_ec *ec = (union acpi_ec *) ec_cxt;
781 u32 value = 0;
782 unsigned long flags = 0;
783 static char object_name[5] = {'_','Q','0','0','\0'};
784 const char hex[] = {'0','1','2','3','4','5','6','7',
785 '8','9','A','B','C','D','E','F'};
786
787 ACPI_FUNCTION_TRACE("acpi_ec_gpe_query");
788
789 if (!ec_cxt)
790 goto end;
791
792 spin_lock_irqsave(&ec->polling.lock, flags);
793 acpi_hw_low_level_read(8, &value, &ec->common.command_addr);
794 spin_unlock_irqrestore(&ec->polling.lock, flags);
795
796 /* TBD: Implement asynch events!
797 * NOTE: All we care about are EC-SCI's. Other EC events are
798 * handled via polling (yuck!). This is because some systems
799 * treat EC-SCIs as level (versus EDGE!) triggered, preventing
800 * a purely interrupt-driven approach (grumble, grumble).
801 */
802 if (!(value & ACPI_EC_FLAG_SCI))
803 goto end;
804
805 if (acpi_ec_query(ec, &value))
806 goto end;
807
808 object_name[2] = hex[((value >> 4) & 0x0F)];
809 object_name[3] = hex[(value & 0x0F)];
810
811 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s\n", object_name));
812
813 acpi_evaluate_object(ec->common.handle, object_name, NULL, NULL);
814
815end:
816 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
817}
818static void
819acpi_ec_gpe_burst_query (
820 void *ec_cxt)
821{
822 union acpi_ec *ec = (union acpi_ec *) ec_cxt;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500823 u32 value;
824 int result = -ENODATA;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 static char object_name[5] = {'_','Q','0','0','\0'};
826 const char hex[] = {'0','1','2','3','4','5','6','7',
827 '8','9','A','B','C','D','E','F'};
828
829 ACPI_FUNCTION_TRACE("acpi_ec_gpe_query");
830
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500831 if (acpi_ec_read_status(ec) & ACPI_EC_FLAG_SCI)
832 result = acpi_ec_query(ec, &value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500834 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 goto end;
836
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 object_name[2] = hex[((value >> 4) & 0x0F)];
838 object_name[3] = hex[(value & 0x0F)];
839
840 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s\n", object_name));
841
Luming Yu45bea152005-07-23 04:08:00 -0400842 acpi_evaluate_object(ec->common.handle, object_name, NULL, NULL);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500843end:
Luming Yu45bea152005-07-23 04:08:00 -0400844 atomic_dec(&ec->burst.pending_gpe);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500845 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846}
847
848static u32
849acpi_ec_gpe_handler (
850 void *data)
851{
Luming Yu45bea152005-07-23 04:08:00 -0400852 if (acpi_ec_polling_mode)
853 return acpi_ec_gpe_polling_handler(data);
854 else
855 return acpi_ec_gpe_burst_handler(data);
856}
857static u32
858acpi_ec_gpe_polling_handler (
859 void *data)
860{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 acpi_status status = AE_OK;
Luming Yu45bea152005-07-23 04:08:00 -0400862 union acpi_ec *ec = (union acpi_ec *) data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863
864 if (!ec)
865 return ACPI_INTERRUPT_NOT_HANDLED;
866
Luming Yu45bea152005-07-23 04:08:00 -0400867 acpi_disable_gpe(NULL, ec->common.gpe_bit, ACPI_ISR);
868
869 status = acpi_os_queue_for_execution(OSD_PRIORITY_GPE,
870 acpi_ec_gpe_query, ec);
871
872 if (status == AE_OK)
873 return ACPI_INTERRUPT_HANDLED;
874 else
875 return ACPI_INTERRUPT_NOT_HANDLED;
876}
877static u32
878acpi_ec_gpe_burst_handler (
879 void *data)
880{
881 acpi_status status = AE_OK;
882 u32 value;
883 union acpi_ec *ec = (union acpi_ec *) data;
884
885 if (!ec)
886 return ACPI_INTERRUPT_NOT_HANDLED;
887
888 acpi_disable_gpe(NULL, ec->common.gpe_bit, ACPI_ISR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500890 value = acpi_ec_read_status(ec);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500892 if((value & ACPI_EC_FLAG_IBF) &&
893 !(value & ACPI_EC_FLAG_BURST) &&
Luming Yu45bea152005-07-23 04:08:00 -0400894 (atomic_read(&ec->burst.leaving_burst) == 0)) {
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500895 /*
896 * the embedded controller disables
897 * burst mode for any reason other
898 * than the burst disable command
899 * to process critical event.
900 */
Luming Yu45bea152005-07-23 04:08:00 -0400901 atomic_set(&ec->burst.leaving_burst , 2); /* block current pending transaction
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500902 and retry */
Luming Yu45bea152005-07-23 04:08:00 -0400903 wake_up(&ec->burst.wait);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500904 }else {
Luming Yu45bea152005-07-23 04:08:00 -0400905 if ((ec->burst.expect_event == ACPI_EC_EVENT_OBF &&
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500906 (value & ACPI_EC_FLAG_OBF)) ||
Luming Yu45bea152005-07-23 04:08:00 -0400907 (ec->burst.expect_event == ACPI_EC_EVENT_IBE &&
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500908 !(value & ACPI_EC_FLAG_IBF))) {
Luming Yu45bea152005-07-23 04:08:00 -0400909 ec->burst.expect_event = 0;
910 wake_up(&ec->burst.wait);
Luming Yu17e9c782005-04-22 23:07:10 -0400911 return ACPI_INTERRUPT_HANDLED;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500912 }
913 }
914
915 if (value & ACPI_EC_FLAG_SCI){
Luming Yu45bea152005-07-23 04:08:00 -0400916 atomic_add(1, &ec->burst.pending_gpe) ;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500917 status = acpi_os_queue_for_execution(OSD_PRIORITY_GPE,
918 acpi_ec_gpe_query, ec);
Luming Yu17e9c782005-04-22 23:07:10 -0400919 return status == AE_OK ?
920 ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500921 }
Luming Yu45bea152005-07-23 04:08:00 -0400922 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_ISR);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500923 return status == AE_OK ?
924 ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925}
926
927/* --------------------------------------------------------------------------
928 Address Space Management
929 -------------------------------------------------------------------------- */
930
931static acpi_status
932acpi_ec_space_setup (
933 acpi_handle region_handle,
934 u32 function,
935 void *handler_context,
936 void **return_context)
937{
938 /*
939 * The EC object is in the handler context and is needed
940 * when calling the acpi_ec_space_handler.
941 */
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500942 *return_context = (function != ACPI_REGION_DEACTIVATE) ?
943 handler_context : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944
945 return AE_OK;
946}
947
948
949static acpi_status
950acpi_ec_space_handler (
951 u32 function,
952 acpi_physical_address address,
953 u32 bit_width,
954 acpi_integer *value,
955 void *handler_context,
956 void *region_context)
957{
958 int result = 0;
Luming Yu45bea152005-07-23 04:08:00 -0400959 union acpi_ec *ec = NULL;
Luming Yufa9cd542005-03-19 01:54:47 -0500960 u64 temp = *value;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 acpi_integer f_v = 0;
962 int i = 0;
963
964 ACPI_FUNCTION_TRACE("acpi_ec_space_handler");
965
966 if ((address > 0xFF) || !value || !handler_context)
967 return_VALUE(AE_BAD_PARAMETER);
968
Luming Yufa9cd542005-03-19 01:54:47 -0500969 if (bit_width != 8 && acpi_strict) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 printk(KERN_WARNING PREFIX "acpi_ec_space_handler: bit_width should be 8\n");
Luming Yufa9cd542005-03-19 01:54:47 -0500971 return_VALUE(AE_BAD_PARAMETER);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 }
973
Luming Yu45bea152005-07-23 04:08:00 -0400974 ec = (union acpi_ec *) handler_context;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975
976next_byte:
977 switch (function) {
978 case ACPI_READ:
Luming Yufa9cd542005-03-19 01:54:47 -0500979 temp = 0;
980 result = acpi_ec_read(ec, (u8) address, (u32 *)&temp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 break;
982 case ACPI_WRITE:
Luming Yufa9cd542005-03-19 01:54:47 -0500983 result = acpi_ec_write(ec, (u8) address, (u8) temp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 break;
985 default:
986 result = -EINVAL;
987 goto out;
988 break;
989 }
990
991 bit_width -= 8;
Luming Yufa9cd542005-03-19 01:54:47 -0500992 if (bit_width) {
993 if (function == ACPI_READ)
994 f_v |= temp << 8 * i;
995 if (function == ACPI_WRITE)
996 temp >>= 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 i++;
Andrew Morton83ea7442005-03-30 22:12:13 -0500998 address++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 goto next_byte;
1000 }
1001
Luming Yufa9cd542005-03-19 01:54:47 -05001002 if (function == ACPI_READ) {
1003 f_v |= temp << 8 * i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 *value = f_v;
1005 }
1006
1007
1008out:
1009 switch (result) {
1010 case -EINVAL:
1011 return_VALUE(AE_BAD_PARAMETER);
1012 break;
1013 case -ENODEV:
1014 return_VALUE(AE_NOT_FOUND);
1015 break;
1016 case -ETIME:
1017 return_VALUE(AE_TIME);
1018 break;
1019 default:
1020 return_VALUE(AE_OK);
1021 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022}
1023
1024
1025/* --------------------------------------------------------------------------
1026 FS Interface (/proc)
1027 -------------------------------------------------------------------------- */
1028
1029static struct proc_dir_entry *acpi_ec_dir;
1030
1031
1032static int
1033acpi_ec_read_info (struct seq_file *seq, void *offset)
1034{
Luming Yu45bea152005-07-23 04:08:00 -04001035 union acpi_ec *ec = (union acpi_ec *) seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036
1037 ACPI_FUNCTION_TRACE("acpi_ec_read_info");
1038
1039 if (!ec)
1040 goto end;
1041
1042 seq_printf(seq, "gpe bit: 0x%02x\n",
Luming Yu45bea152005-07-23 04:08:00 -04001043 (u32) ec->common.gpe_bit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 seq_printf(seq, "ports: 0x%02x, 0x%02x\n",
Luming Yu45bea152005-07-23 04:08:00 -04001045 (u32) ec->common.status_addr.address, (u32) ec->common.data_addr.address);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 seq_printf(seq, "use global lock: %s\n",
Luming Yu45bea152005-07-23 04:08:00 -04001047 ec->common.global_lock?"yes":"no");
1048 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049
1050end:
1051 return_VALUE(0);
1052}
1053
1054static int acpi_ec_info_open_fs(struct inode *inode, struct file *file)
1055{
1056 return single_open(file, acpi_ec_read_info, PDE(inode)->data);
1057}
1058
1059static struct file_operations acpi_ec_info_ops = {
1060 .open = acpi_ec_info_open_fs,
1061 .read = seq_read,
1062 .llseek = seq_lseek,
1063 .release = single_release,
1064 .owner = THIS_MODULE,
1065};
1066
1067static int
1068acpi_ec_add_fs (
1069 struct acpi_device *device)
1070{
Luming Yu45bea152005-07-23 04:08:00 -04001071 struct proc_dir_entry *entry = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072
1073 ACPI_FUNCTION_TRACE("acpi_ec_add_fs");
1074
1075 if (!acpi_device_dir(device)) {
1076 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
1077 acpi_ec_dir);
1078 if (!acpi_device_dir(device))
1079 return_VALUE(-ENODEV);
1080 }
1081
1082 entry = create_proc_entry(ACPI_EC_FILE_INFO, S_IRUGO,
1083 acpi_device_dir(device));
1084 if (!entry)
1085 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
1086 "Unable to create '%s' fs entry\n",
1087 ACPI_EC_FILE_INFO));
1088 else {
1089 entry->proc_fops = &acpi_ec_info_ops;
1090 entry->data = acpi_driver_data(device);
1091 entry->owner = THIS_MODULE;
1092 }
1093
1094 return_VALUE(0);
1095}
1096
1097
1098static int
1099acpi_ec_remove_fs (
1100 struct acpi_device *device)
1101{
1102 ACPI_FUNCTION_TRACE("acpi_ec_remove_fs");
1103
1104 if (acpi_device_dir(device)) {
1105 remove_proc_entry(ACPI_EC_FILE_INFO, acpi_device_dir(device));
1106 remove_proc_entry(acpi_device_bid(device), acpi_ec_dir);
1107 acpi_device_dir(device) = NULL;
1108 }
1109
1110 return_VALUE(0);
1111}
1112
1113
1114/* --------------------------------------------------------------------------
1115 Driver Interface
1116 -------------------------------------------------------------------------- */
1117
Luming Yu45bea152005-07-23 04:08:00 -04001118
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119static int
Luming Yu45bea152005-07-23 04:08:00 -04001120acpi_ec_polling_add (
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 struct acpi_device *device)
1122{
Luming Yu45bea152005-07-23 04:08:00 -04001123 int result = 0;
1124 acpi_status status = AE_OK;
1125 union acpi_ec *ec = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 unsigned long uid;
1127
1128 ACPI_FUNCTION_TRACE("acpi_ec_add");
1129
1130 if (!device)
1131 return_VALUE(-EINVAL);
1132
Luming Yu45bea152005-07-23 04:08:00 -04001133 ec = kmalloc(sizeof(union acpi_ec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134 if (!ec)
1135 return_VALUE(-ENOMEM);
Luming Yu45bea152005-07-23 04:08:00 -04001136 memset(ec, 0, sizeof(union acpi_ec));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137
Luming Yu45bea152005-07-23 04:08:00 -04001138 ec->common.handle = device->handle;
1139 ec->common.uid = -1;
1140 spin_lock_init(&ec->polling.lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME);
1142 strcpy(acpi_device_class(device), ACPI_EC_CLASS);
1143 acpi_driver_data(device) = ec;
1144
1145 /* Use the global lock for all EC transactions? */
Luming Yu45bea152005-07-23 04:08:00 -04001146 acpi_evaluate_integer(ec->common.handle, "_GLK", NULL, &ec->common.global_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147
1148 /* If our UID matches the UID for the ECDT-enumerated EC,
1149 we now have the *real* EC info, so kill the makeshift one.*/
Luming Yu45bea152005-07-23 04:08:00 -04001150 acpi_evaluate_integer(ec->common.handle, "_UID", NULL, &uid);
1151 if (ec_ecdt && ec_ecdt->common.uid == uid) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
1153 ACPI_ADR_SPACE_EC, &acpi_ec_space_handler);
Luming Yu45bea152005-07-23 04:08:00 -04001154
1155 acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit, &acpi_ec_gpe_handler);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156
1157 kfree(ec_ecdt);
1158 }
1159
1160 /* Get GPE bit assignment (EC events). */
1161 /* TODO: Add support for _GPE returning a package */
Luming Yu45bea152005-07-23 04:08:00 -04001162 status = acpi_evaluate_integer(ec->common.handle, "_GPE", NULL, &ec->common.gpe_bit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 if (ACPI_FAILURE(status)) {
1164 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
1165 "Error obtaining GPE bit assignment\n"));
1166 result = -ENODEV;
1167 goto end;
1168 }
1169
1170 result = acpi_ec_add_fs(device);
1171 if (result)
1172 goto end;
1173
1174 printk(KERN_INFO PREFIX "%s [%s] (gpe %d)\n",
1175 acpi_device_name(device), acpi_device_bid(device),
Luming Yu45bea152005-07-23 04:08:00 -04001176 (u32) ec->common.gpe_bit);
1177
1178 if (!first_ec)
1179 first_ec = device;
1180
1181end:
1182 if (result)
1183 kfree(ec);
1184
1185 return_VALUE(result);
1186}
1187static int
1188acpi_ec_burst_add (
1189 struct acpi_device *device)
1190{
1191 int result = 0;
1192 acpi_status status = AE_OK;
1193 union acpi_ec *ec = NULL;
1194 unsigned long uid;
1195
1196 ACPI_FUNCTION_TRACE("acpi_ec_add");
1197
1198 if (!device)
1199 return_VALUE(-EINVAL);
1200
1201 ec = kmalloc(sizeof(union acpi_ec), GFP_KERNEL);
1202 if (!ec)
1203 return_VALUE(-ENOMEM);
1204 memset(ec, 0, sizeof(union acpi_ec));
1205
1206 ec->common.handle = device->handle;
1207 ec->common.uid = -1;
1208 atomic_set(&ec->burst.pending_gpe, 0);
1209 atomic_set(&ec->burst.leaving_burst , 1);
1210 init_MUTEX(&ec->burst.sem);
1211 init_waitqueue_head(&ec->burst.wait);
1212 strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME);
1213 strcpy(acpi_device_class(device), ACPI_EC_CLASS);
1214 acpi_driver_data(device) = ec;
1215
1216 /* Use the global lock for all EC transactions? */
1217 acpi_evaluate_integer(ec->common.handle, "_GLK", NULL, &ec->common.global_lock);
1218
1219 /* If our UID matches the UID for the ECDT-enumerated EC,
1220 we now have the *real* EC info, so kill the makeshift one.*/
1221 acpi_evaluate_integer(ec->common.handle, "_UID", NULL, &uid);
1222 if (ec_ecdt && ec_ecdt->common.uid == uid) {
1223 acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
1224 ACPI_ADR_SPACE_EC, &acpi_ec_space_handler);
1225
1226 acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit, &acpi_ec_gpe_handler);
1227
1228 kfree(ec_ecdt);
1229 }
1230
1231 /* Get GPE bit assignment (EC events). */
1232 /* TODO: Add support for _GPE returning a package */
1233 status = acpi_evaluate_integer(ec->common.handle, "_GPE", NULL, &ec->common.gpe_bit);
1234 if (ACPI_FAILURE(status)) {
1235 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
1236 "Error obtaining GPE bit assignment\n"));
1237 result = -ENODEV;
1238 goto end;
1239 }
1240
1241 result = acpi_ec_add_fs(device);
1242 if (result)
1243 goto end;
1244
1245 printk(KERN_INFO PREFIX "%s [%s] (gpe %d)\n",
1246 acpi_device_name(device), acpi_device_bid(device),
1247 (u32) ec->common.gpe_bit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248
1249 if (!first_ec)
1250 first_ec = device;
1251
1252end:
1253 if (result)
1254 kfree(ec);
1255
1256 return_VALUE(result);
1257}
1258
1259
1260static int
1261acpi_ec_remove (
1262 struct acpi_device *device,
1263 int type)
1264{
Luming Yu45bea152005-07-23 04:08:00 -04001265 union acpi_ec *ec = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266
1267 ACPI_FUNCTION_TRACE("acpi_ec_remove");
1268
1269 if (!device)
1270 return_VALUE(-EINVAL);
1271
1272 ec = acpi_driver_data(device);
1273
1274 acpi_ec_remove_fs(device);
1275
1276 kfree(ec);
1277
1278 return_VALUE(0);
1279}
1280
1281
1282static acpi_status
1283acpi_ec_io_ports (
1284 struct acpi_resource *resource,
1285 void *context)
1286{
Luming Yu45bea152005-07-23 04:08:00 -04001287 union acpi_ec *ec = (union acpi_ec *) context;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 struct acpi_generic_address *addr;
1289
1290 if (resource->id != ACPI_RSTYPE_IO) {
1291 return AE_OK;
1292 }
1293
1294 /*
1295 * The first address region returned is the data port, and
1296 * the second address region returned is the status/command
1297 * port.
1298 */
Luming Yu45bea152005-07-23 04:08:00 -04001299 if (ec->common.data_addr.register_bit_width == 0) {
1300 addr = &ec->common.data_addr;
1301 } else if (ec->common.command_addr.register_bit_width == 0) {
1302 addr = &ec->common.command_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 } else {
1304 return AE_CTRL_TERMINATE;
1305 }
1306
1307 addr->address_space_id = ACPI_ADR_SPACE_SYSTEM_IO;
1308 addr->register_bit_width = 8;
1309 addr->register_bit_offset = 0;
1310 addr->address = resource->data.io.min_base_address;
1311
1312 return AE_OK;
1313}
1314
1315
1316static int
1317acpi_ec_start (
1318 struct acpi_device *device)
1319{
Luming Yu45bea152005-07-23 04:08:00 -04001320 acpi_status status = AE_OK;
1321 union acpi_ec *ec = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322
1323 ACPI_FUNCTION_TRACE("acpi_ec_start");
1324
1325 if (!device)
1326 return_VALUE(-EINVAL);
1327
1328 ec = acpi_driver_data(device);
1329
1330 if (!ec)
1331 return_VALUE(-EINVAL);
1332
1333 /*
1334 * Get I/O port addresses. Convert to GAS format.
1335 */
Luming Yu45bea152005-07-23 04:08:00 -04001336 status = acpi_walk_resources(ec->common.handle, METHOD_NAME__CRS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 acpi_ec_io_ports, ec);
Luming Yu45bea152005-07-23 04:08:00 -04001338 if (ACPI_FAILURE(status) || ec->common.command_addr.register_bit_width == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error getting I/O port addresses"));
1340 return_VALUE(-ENODEV);
1341 }
1342
Luming Yu45bea152005-07-23 04:08:00 -04001343 ec->common.status_addr = ec->common.command_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344
1345 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02x, ports=0x%2x,0x%2x\n",
Luming Yu45bea152005-07-23 04:08:00 -04001346 (u32) ec->common.gpe_bit, (u32) ec->common.command_addr.address,
1347 (u32) ec->common.data_addr.address));
1348
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349
1350 /*
1351 * Install GPE handler
1352 */
Luming Yu45bea152005-07-23 04:08:00 -04001353 status = acpi_install_gpe_handler(NULL, ec->common.gpe_bit,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354 ACPI_GPE_EDGE_TRIGGERED, &acpi_ec_gpe_handler, ec);
1355 if (ACPI_FAILURE(status)) {
1356 return_VALUE(-ENODEV);
1357 }
Luming Yu45bea152005-07-23 04:08:00 -04001358 acpi_set_gpe_type (NULL, ec->common.gpe_bit, ACPI_GPE_TYPE_RUNTIME);
1359 acpi_enable_gpe (NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360
Luming Yu45bea152005-07-23 04:08:00 -04001361 status = acpi_install_address_space_handler (ec->common.handle,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 ACPI_ADR_SPACE_EC, &acpi_ec_space_handler,
1363 &acpi_ec_space_setup, ec);
1364 if (ACPI_FAILURE(status)) {
Luming Yu45bea152005-07-23 04:08:00 -04001365 acpi_remove_gpe_handler(NULL, ec->common.gpe_bit, &acpi_ec_gpe_handler);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366 return_VALUE(-ENODEV);
1367 }
1368
1369 return_VALUE(AE_OK);
1370}
1371
1372
1373static int
1374acpi_ec_stop (
1375 struct acpi_device *device,
1376 int type)
1377{
Luming Yu45bea152005-07-23 04:08:00 -04001378 acpi_status status = AE_OK;
1379 union acpi_ec *ec = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380
1381 ACPI_FUNCTION_TRACE("acpi_ec_stop");
1382
1383 if (!device)
1384 return_VALUE(-EINVAL);
1385
1386 ec = acpi_driver_data(device);
1387
Luming Yu45bea152005-07-23 04:08:00 -04001388 status = acpi_remove_address_space_handler(ec->common.handle,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389 ACPI_ADR_SPACE_EC, &acpi_ec_space_handler);
1390 if (ACPI_FAILURE(status))
1391 return_VALUE(-ENODEV);
1392
Luming Yu45bea152005-07-23 04:08:00 -04001393 status = acpi_remove_gpe_handler(NULL, ec->common.gpe_bit, &acpi_ec_gpe_handler);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 if (ACPI_FAILURE(status))
1395 return_VALUE(-ENODEV);
1396
1397 return_VALUE(0);
1398}
1399
1400static acpi_status __init
1401acpi_fake_ecdt_callback (
1402 acpi_handle handle,
1403 u32 Level,
1404 void *context,
1405 void **retval)
1406{
Luming Yu45bea152005-07-23 04:08:00 -04001407
1408 if (acpi_ec_polling_mode)
1409 return acpi_fake_ecdt_polling_callback(handle,
1410 Level, context, retval);
1411 else
1412 return acpi_fake_ecdt_burst_callback(handle,
1413 Level, context, retval);
1414}
1415
1416static acpi_status __init
1417acpi_fake_ecdt_polling_callback (
1418 acpi_handle handle,
1419 u32 Level,
1420 void *context,
1421 void **retval)
1422{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423 acpi_status status;
1424
1425 status = acpi_walk_resources(handle, METHOD_NAME__CRS,
1426 acpi_ec_io_ports, ec_ecdt);
1427 if (ACPI_FAILURE(status))
1428 return status;
Luming Yu45bea152005-07-23 04:08:00 -04001429 ec_ecdt->common.status_addr = ec_ecdt->common.command_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430
Luming Yu45bea152005-07-23 04:08:00 -04001431 ec_ecdt->common.uid = -1;
1432 acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->common.uid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433
Luming Yu45bea152005-07-23 04:08:00 -04001434 status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->common.gpe_bit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 if (ACPI_FAILURE(status))
1436 return status;
Luming Yu45bea152005-07-23 04:08:00 -04001437 spin_lock_init(&ec_ecdt->polling.lock);
1438 ec_ecdt->common.global_lock = TRUE;
1439 ec_ecdt->common.handle = handle;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440
1441 printk(KERN_INFO PREFIX "GPE=0x%02x, ports=0x%2x, 0x%2x\n",
Luming Yu45bea152005-07-23 04:08:00 -04001442 (u32) ec_ecdt->common.gpe_bit, (u32) ec_ecdt->common.command_addr.address,
1443 (u32) ec_ecdt->common.data_addr.address);
1444
1445 return AE_CTRL_TERMINATE;
1446}
1447
1448static acpi_status __init
1449acpi_fake_ecdt_burst_callback (
1450 acpi_handle handle,
1451 u32 Level,
1452 void *context,
1453 void **retval)
1454{
1455 acpi_status status;
1456
1457 init_MUTEX(&ec_ecdt->burst.sem);
1458 init_waitqueue_head(&ec_ecdt->burst.wait);
1459 status = acpi_walk_resources(handle, METHOD_NAME__CRS,
1460 acpi_ec_io_ports, ec_ecdt);
1461 if (ACPI_FAILURE(status))
1462 return status;
1463 ec_ecdt->common.status_addr = ec_ecdt->common.command_addr;
1464
1465 ec_ecdt->common.uid = -1;
1466 acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->common.uid);
1467
1468 status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->common.gpe_bit);
1469 if (ACPI_FAILURE(status))
1470 return status;
1471 ec_ecdt->common.global_lock = TRUE;
1472 ec_ecdt->common.handle = handle;
1473
1474 printk(KERN_INFO PREFIX "GPE=0x%02x, ports=0x%2x, 0x%2x\n",
1475 (u32) ec_ecdt->common.gpe_bit, (u32) ec_ecdt->common.command_addr.address,
1476 (u32) ec_ecdt->common.data_addr.address);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001477
1478 return AE_CTRL_TERMINATE;
1479}
1480
1481/*
1482 * Some BIOS (such as some from Gateway laptops) access EC region very early
1483 * such as in BAT0._INI or EC._INI before an EC device is found and
1484 * do not provide an ECDT. According to ACPI spec, ECDT isn't mandatorily
1485 * required, but if EC regison is accessed early, it is required.
1486 * The routine tries to workaround the BIOS bug by pre-scan EC device
1487 * It assumes that _CRS, _HID, _GPE, _UID methods of EC don't touch any
1488 * op region (since _REG isn't invoked yet). The assumption is true for
1489 * all systems found.
1490 */
1491static int __init
1492acpi_ec_fake_ecdt(void)
1493{
1494 acpi_status status;
1495 int ret = 0;
1496
1497 printk(KERN_INFO PREFIX "Try to make an fake ECDT\n");
1498
Luming Yu45bea152005-07-23 04:08:00 -04001499 ec_ecdt = kmalloc(sizeof(union acpi_ec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 if (!ec_ecdt) {
1501 ret = -ENOMEM;
1502 goto error;
1503 }
Luming Yu45bea152005-07-23 04:08:00 -04001504 memset(ec_ecdt, 0, sizeof(union acpi_ec));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505
1506 status = acpi_get_devices (ACPI_EC_HID,
1507 acpi_fake_ecdt_callback,
1508 NULL,
1509 NULL);
1510 if (ACPI_FAILURE(status)) {
1511 kfree(ec_ecdt);
1512 ec_ecdt = NULL;
1513 ret = -ENODEV;
1514 goto error;
1515 }
1516 return 0;
1517error:
1518 printk(KERN_ERR PREFIX "Can't make an fake ECDT\n");
1519 return ret;
1520}
1521
1522static int __init
1523acpi_ec_get_real_ecdt(void)
1524{
Luming Yu45bea152005-07-23 04:08:00 -04001525 if (acpi_ec_polling_mode)
1526 return acpi_ec_polling_get_real_ecdt();
1527 else
1528 return acpi_ec_burst_get_real_ecdt();
1529}
1530
1531static int __init
1532acpi_ec_polling_get_real_ecdt(void)
1533{
1534 acpi_status status;
1535 struct acpi_table_ecdt *ecdt_ptr;
1536
1537 status = acpi_get_firmware_table("ECDT", 1, ACPI_LOGICAL_ADDRESSING,
1538 (struct acpi_table_header **) &ecdt_ptr);
1539 if (ACPI_FAILURE(status))
1540 return -ENODEV;
1541
1542 printk(KERN_INFO PREFIX "Found ECDT\n");
1543
1544 /*
1545 * Generate a temporary ec context to use until the namespace is scanned
1546 */
1547 ec_ecdt = kmalloc(sizeof(union acpi_ec), GFP_KERNEL);
1548 if (!ec_ecdt)
1549 return -ENOMEM;
1550 memset(ec_ecdt, 0, sizeof(union acpi_ec));
1551
1552 ec_ecdt->common.command_addr = ecdt_ptr->ec_control;
1553 ec_ecdt->common.status_addr = ecdt_ptr->ec_control;
1554 ec_ecdt->common.data_addr = ecdt_ptr->ec_data;
1555 ec_ecdt->common.gpe_bit = ecdt_ptr->gpe_bit;
1556 spin_lock_init(&ec_ecdt->polling.lock);
1557 /* use the GL just to be safe */
1558 ec_ecdt->common.global_lock = TRUE;
1559 ec_ecdt->common.uid = ecdt_ptr->uid;
1560
1561 status = acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->common.handle);
1562 if (ACPI_FAILURE(status)) {
1563 goto error;
1564 }
1565
1566 return 0;
1567error:
1568 printk(KERN_ERR PREFIX "Could not use ECDT\n");
1569 kfree(ec_ecdt);
1570 ec_ecdt = NULL;
1571
1572 return -ENODEV;
1573}
1574
1575
1576static int __init
1577acpi_ec_burst_get_real_ecdt(void)
1578{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579 acpi_status status;
1580 struct acpi_table_ecdt *ecdt_ptr;
1581
Dmitry Torokhov451566f2005-03-19 01:10:05 -05001582 status = acpi_get_firmware_table("ECDT", 1, ACPI_LOGICAL_ADDRESSING,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 (struct acpi_table_header **) &ecdt_ptr);
1584 if (ACPI_FAILURE(status))
1585 return -ENODEV;
1586
1587 printk(KERN_INFO PREFIX "Found ECDT\n");
1588
1589 /*
1590 * Generate a temporary ec context to use until the namespace is scanned
1591 */
Luming Yu45bea152005-07-23 04:08:00 -04001592 ec_ecdt = kmalloc(sizeof(union acpi_ec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593 if (!ec_ecdt)
1594 return -ENOMEM;
Luming Yu45bea152005-07-23 04:08:00 -04001595 memset(ec_ecdt, 0, sizeof(union acpi_ec));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001596
Luming Yu45bea152005-07-23 04:08:00 -04001597 init_MUTEX(&ec_ecdt->burst.sem);
1598 init_waitqueue_head(&ec_ecdt->burst.wait);
1599 ec_ecdt->common.command_addr = ecdt_ptr->ec_control;
1600 ec_ecdt->common.status_addr = ecdt_ptr->ec_control;
1601 ec_ecdt->common.data_addr = ecdt_ptr->ec_data;
1602 ec_ecdt->common.gpe_bit = ecdt_ptr->gpe_bit;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001603 /* use the GL just to be safe */
Luming Yu45bea152005-07-23 04:08:00 -04001604 ec_ecdt->common.global_lock = TRUE;
1605 ec_ecdt->common.uid = ecdt_ptr->uid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001606
Luming Yu45bea152005-07-23 04:08:00 -04001607 status = acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->common.handle);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608 if (ACPI_FAILURE(status)) {
1609 goto error;
1610 }
1611
1612 return 0;
1613error:
1614 printk(KERN_ERR PREFIX "Could not use ECDT\n");
1615 kfree(ec_ecdt);
1616 ec_ecdt = NULL;
1617
1618 return -ENODEV;
1619}
1620
1621static int __initdata acpi_fake_ecdt_enabled;
1622int __init
1623acpi_ec_ecdt_probe (void)
1624{
1625 acpi_status status;
1626 int ret;
1627
1628 ret = acpi_ec_get_real_ecdt();
1629 /* Try to make a fake ECDT */
1630 if (ret && acpi_fake_ecdt_enabled) {
1631 ret = acpi_ec_fake_ecdt();
1632 }
1633
1634 if (ret)
1635 return 0;
1636
1637 /*
1638 * Install GPE handler
1639 */
Luming Yu45bea152005-07-23 04:08:00 -04001640 status = acpi_install_gpe_handler(NULL, ec_ecdt->common.gpe_bit,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001641 ACPI_GPE_EDGE_TRIGGERED, &acpi_ec_gpe_handler,
1642 ec_ecdt);
1643 if (ACPI_FAILURE(status)) {
1644 goto error;
1645 }
Luming Yu45bea152005-07-23 04:08:00 -04001646 acpi_set_gpe_type (NULL, ec_ecdt->common.gpe_bit, ACPI_GPE_TYPE_RUNTIME);
1647 acpi_enable_gpe (NULL, ec_ecdt->common.gpe_bit, ACPI_NOT_ISR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648
1649 status = acpi_install_address_space_handler (ACPI_ROOT_OBJECT,
1650 ACPI_ADR_SPACE_EC, &acpi_ec_space_handler,
1651 &acpi_ec_space_setup, ec_ecdt);
1652 if (ACPI_FAILURE(status)) {
Luming Yu45bea152005-07-23 04:08:00 -04001653 acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 &acpi_ec_gpe_handler);
1655 goto error;
1656 }
1657
1658 return 0;
1659
1660error:
1661 printk(KERN_ERR PREFIX "Could not use ECDT\n");
1662 kfree(ec_ecdt);
1663 ec_ecdt = NULL;
1664
1665 return -ENODEV;
1666}
1667
1668
1669static int __init acpi_ec_init (void)
1670{
Luming Yu45bea152005-07-23 04:08:00 -04001671 int result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672
1673 ACPI_FUNCTION_TRACE("acpi_ec_init");
1674
1675 if (acpi_disabled)
1676 return_VALUE(0);
1677
1678 acpi_ec_dir = proc_mkdir(ACPI_EC_CLASS, acpi_root_dir);
1679 if (!acpi_ec_dir)
1680 return_VALUE(-ENODEV);
1681
1682 /* Now register the driver for the EC */
1683 result = acpi_bus_register_driver(&acpi_ec_driver);
1684 if (result < 0) {
1685 remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir);
1686 return_VALUE(-ENODEV);
1687 }
1688
1689 return_VALUE(result);
1690}
1691
1692subsys_initcall(acpi_ec_init);
1693
1694/* EC driver currently not unloadable */
1695#if 0
1696static void __exit
1697acpi_ec_exit (void)
1698{
1699 ACPI_FUNCTION_TRACE("acpi_ec_exit");
1700
1701 acpi_bus_unregister_driver(&acpi_ec_driver);
1702
1703 remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir);
1704
1705 return_VOID;
1706}
1707#endif /* 0 */
1708
1709static int __init acpi_fake_ecdt_setup(char *str)
1710{
1711 acpi_fake_ecdt_enabled = 1;
1712 return 0;
1713}
Luming Yu7b15f5e2005-08-03 17:38:04 -04001714
Linus Torvalds1da177e2005-04-16 15:20:36 -07001715__setup("acpi_fake_ecdt", acpi_fake_ecdt_setup);
Luming Yu45bea152005-07-23 04:08:00 -04001716static int __init acpi_ec_set_polling_mode(char *str)
1717{
Luming Yu7b15f5e2005-08-03 17:38:04 -04001718 int burst;
1719
1720 if (!get_option(&str, &burst))
1721 return 0;
1722
1723 if (burst) {
1724 acpi_ec_polling_mode = EC_BURST;
1725 acpi_ec_driver.ops.add = acpi_ec_burst_add;
1726 } else {
1727 acpi_ec_polling_mode = EC_POLLING;
1728 acpi_ec_driver.ops.add = acpi_ec_polling_add;
1729 }
1730 printk(KERN_INFO PREFIX "EC %s mode.\n",
1731 burst ? "burst": "polling");
Luming Yu45bea152005-07-23 04:08:00 -04001732 return 0;
1733}
Luming Yu7b15f5e2005-08-03 17:38:04 -04001734__setup("ec_burst=", acpi_ec_set_polling_mode);