blob: 874f912962c4b8b4caada04670a4cb848bcbaead [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
Len Brown50526df2005-08-11 17:32:05 -040041ACPI_MODULE_NAME("acpi_ec")
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#define ACPI_EC_COMPONENT 0x00100000
43#define ACPI_EC_CLASS "embedded_controller"
44#define ACPI_EC_HID "PNP0C09"
45#define ACPI_EC_DRIVER_NAME "ACPI Embedded Controller Driver"
46#define ACPI_EC_DEVICE_NAME "Embedded Controller"
47#define ACPI_EC_FILE_INFO "info"
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#define ACPI_EC_FLAG_OBF 0x01 /* Output buffer full */
49#define ACPI_EC_FLAG_IBF 0x02 /* Input buffer full */
Dmitry Torokhov451566f2005-03-19 01:10:05 -050050#define ACPI_EC_FLAG_BURST 0x10 /* burst mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -070051#define ACPI_EC_FLAG_SCI 0x20 /* EC-SCI occurred */
Linus Torvalds1da177e2005-04-16 15:20:36 -070052#define ACPI_EC_EVENT_OBF 0x01 /* Output buffer full */
53#define ACPI_EC_EVENT_IBE 0x02 /* Input buffer empty */
Dmitry Torokhov451566f2005-03-19 01:10:05 -050054#define ACPI_EC_DELAY 50 /* Wait 50ms max. during EC ops */
Linus Torvalds1da177e2005-04-16 15:20:36 -070055#define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */
Len Brown50526df2005-08-11 17:32:05 -040056#define ACPI_EC_UDELAY 100 /* Poll @ 100us increments */
57#define ACPI_EC_UDELAY_COUNT 1000 /* Wait 10ms max. during EC ops */
Linus Torvalds1da177e2005-04-16 15:20:36 -070058#define ACPI_EC_COMMAND_READ 0x80
59#define ACPI_EC_COMMAND_WRITE 0x81
Dmitry Torokhov451566f2005-03-19 01:10:05 -050060#define ACPI_EC_BURST_ENABLE 0x82
61#define ACPI_EC_BURST_DISABLE 0x83
Linus Torvalds1da177e2005-04-16 15:20:36 -070062#define ACPI_EC_COMMAND_QUERY 0x84
Len Brown02b28a32005-12-05 16:33:04 -050063#define EC_POLL 0xFF
64#define EC_INTR 0x00
Len Brown50526df2005-08-11 17:32:05 -040065static int acpi_ec_remove(struct acpi_device *device, int type);
66static int acpi_ec_start(struct acpi_device *device);
67static int acpi_ec_stop(struct acpi_device *device, int type);
Len Brown02b28a32005-12-05 16:33:04 -050068static int acpi_ec_intr_add(struct acpi_device *device);
69static int acpi_ec_poll_add(struct acpi_device *device);
Linus Torvalds1da177e2005-04-16 15:20:36 -070070
71static struct acpi_driver acpi_ec_driver = {
Len Brown50526df2005-08-11 17:32:05 -040072 .name = ACPI_EC_DRIVER_NAME,
73 .class = ACPI_EC_CLASS,
74 .ids = ACPI_EC_HID,
75 .ops = {
Len Brown53f11d42005-12-05 16:46:36 -050076 .add = acpi_ec_intr_add,
Len Brown50526df2005-08-11 17:32:05 -040077 .remove = acpi_ec_remove,
78 .start = acpi_ec_start,
79 .stop = acpi_ec_stop,
80 },
Linus Torvalds1da177e2005-04-16 15:20:36 -070081};
Luming Yu45bea152005-07-23 04:08:00 -040082union acpi_ec {
83 struct {
Len Brown50526df2005-08-11 17:32:05 -040084 u32 mode;
85 acpi_handle handle;
86 unsigned long uid;
87 unsigned long gpe_bit;
88 struct acpi_generic_address status_addr;
89 struct acpi_generic_address command_addr;
90 struct acpi_generic_address data_addr;
91 unsigned long global_lock;
Luming Yu45bea152005-07-23 04:08:00 -040092 } common;
Linus Torvalds1da177e2005-04-16 15:20:36 -070093
Luming Yu45bea152005-07-23 04:08:00 -040094 struct {
Len Brown50526df2005-08-11 17:32:05 -040095 u32 mode;
96 acpi_handle handle;
97 unsigned long uid;
98 unsigned long gpe_bit;
99 struct acpi_generic_address status_addr;
100 struct acpi_generic_address command_addr;
101 struct acpi_generic_address data_addr;
102 unsigned long global_lock;
103 unsigned int expect_event;
104 atomic_t leaving_burst; /* 0 : No, 1 : Yes, 2: abort */
105 atomic_t pending_gpe;
106 struct semaphore sem;
107 wait_queue_head_t wait;
Len Brown02b28a32005-12-05 16:33:04 -0500108 } intr;
Luming Yu45bea152005-07-23 04:08:00 -0400109
110 struct {
Len Brown50526df2005-08-11 17:32:05 -0400111 u32 mode;
112 acpi_handle handle;
113 unsigned long uid;
114 unsigned long gpe_bit;
115 struct acpi_generic_address status_addr;
116 struct acpi_generic_address command_addr;
117 struct acpi_generic_address data_addr;
118 unsigned long global_lock;
Rich Townsendf9a6ee1a2005-12-19 23:07:00 -0500119 struct semaphore sem;
Len Brown02b28a32005-12-05 16:33:04 -0500120 } poll;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121};
122
Len Brown02b28a32005-12-05 16:33:04 -0500123static int acpi_ec_poll_wait(union acpi_ec *ec, u8 event);
124static int acpi_ec_intr_wait(union acpi_ec *ec, unsigned int event);
125static int acpi_ec_poll_read(union acpi_ec *ec, u8 address, u32 * data);
126static int acpi_ec_intr_read(union acpi_ec *ec, u8 address, u32 * data);
127static int acpi_ec_poll_write(union acpi_ec *ec, u8 address, u8 data);
128static int acpi_ec_intr_write(union acpi_ec *ec, u8 address, u8 data);
129static int acpi_ec_poll_query(union acpi_ec *ec, u32 * data);
130static int acpi_ec_intr_query(union acpi_ec *ec, u32 * data);
131static void acpi_ec_gpe_poll_query(void *ec_cxt);
132static void acpi_ec_gpe_intr_query(void *ec_cxt);
133static u32 acpi_ec_gpe_poll_handler(void *data);
134static u32 acpi_ec_gpe_intr_handler(void *data);
Luming Yu45bea152005-07-23 04:08:00 -0400135static acpi_status __init
Len Brown02b28a32005-12-05 16:33:04 -0500136acpi_fake_ecdt_poll_callback(acpi_handle handle,
Len Brown50526df2005-08-11 17:32:05 -0400137 u32 Level, void *context, void **retval);
Luming Yu45bea152005-07-23 04:08:00 -0400138
139static acpi_status __init
Len Brown02b28a32005-12-05 16:33:04 -0500140acpi_fake_ecdt_intr_callback(acpi_handle handle,
Len Brown50526df2005-08-11 17:32:05 -0400141 u32 Level, void *context, void **retval);
Luming Yu45bea152005-07-23 04:08:00 -0400142
Len Brown02b28a32005-12-05 16:33:04 -0500143static int __init acpi_ec_poll_get_real_ecdt(void);
144static int __init acpi_ec_intr_get_real_ecdt(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145/* If we find an EC via the ECDT, we need to keep a ptr to its context */
Len Brown50526df2005-08-11 17:32:05 -0400146static union acpi_ec *ec_ecdt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147
148/* External interfaces use first EC only, so remember */
149static struct acpi_device *first_ec;
Len Brown53f11d42005-12-05 16:46:36 -0500150static int acpi_ec_poll_mode = EC_INTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151
152/* --------------------------------------------------------------------------
153 Transaction Management
154 -------------------------------------------------------------------------- */
155
Arjan van de Ven858119e2006-01-14 13:20:43 -0800156static u32 acpi_ec_read_status(union acpi_ec *ec)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157{
Len Brown50526df2005-08-11 17:32:05 -0400158 u32 status = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159
Luming Yu45bea152005-07-23 04:08:00 -0400160 acpi_hw_low_level_read(8, &status, &ec->common.status_addr);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500161 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162}
163
Len Brown50526df2005-08-11 17:32:05 -0400164static int acpi_ec_wait(union acpi_ec *ec, u8 event)
Luming Yu45bea152005-07-23 04:08:00 -0400165{
Len Brown02b28a32005-12-05 16:33:04 -0500166 if (acpi_ec_poll_mode)
167 return acpi_ec_poll_wait(ec, event);
Luming Yu45bea152005-07-23 04:08:00 -0400168 else
Len Brown02b28a32005-12-05 16:33:04 -0500169 return acpi_ec_intr_wait(ec, event);
Luming Yu45bea152005-07-23 04:08:00 -0400170}
171
Len Brown02b28a32005-12-05 16:33:04 -0500172static int acpi_ec_poll_wait(union acpi_ec *ec, u8 event)
Luming Yu45bea152005-07-23 04:08:00 -0400173{
Len Brown50526df2005-08-11 17:32:05 -0400174 u32 acpi_ec_status = 0;
175 u32 i = ACPI_EC_UDELAY_COUNT;
Luming Yu45bea152005-07-23 04:08:00 -0400176
177 if (!ec)
178 return -EINVAL;
179
180 /* Poll the EC status register waiting for the event to occur. */
181 switch (event) {
182 case ACPI_EC_EVENT_OBF:
183 do {
Len Brown50526df2005-08-11 17:32:05 -0400184 acpi_hw_low_level_read(8, &acpi_ec_status,
185 &ec->common.status_addr);
Luming Yu45bea152005-07-23 04:08:00 -0400186 if (acpi_ec_status & ACPI_EC_FLAG_OBF)
187 return 0;
188 udelay(ACPI_EC_UDELAY);
Len Brown50526df2005-08-11 17:32:05 -0400189 } while (--i > 0);
Luming Yu45bea152005-07-23 04:08:00 -0400190 break;
191 case ACPI_EC_EVENT_IBE:
192 do {
Len Brown50526df2005-08-11 17:32:05 -0400193 acpi_hw_low_level_read(8, &acpi_ec_status,
194 &ec->common.status_addr);
Luming Yu45bea152005-07-23 04:08:00 -0400195 if (!(acpi_ec_status & ACPI_EC_FLAG_IBF))
196 return 0;
197 udelay(ACPI_EC_UDELAY);
Len Brown50526df2005-08-11 17:32:05 -0400198 } while (--i > 0);
Luming Yu45bea152005-07-23 04:08:00 -0400199 break;
200 default:
201 return -EINVAL;
202 }
203
204 return -ETIME;
205}
Len Brown02b28a32005-12-05 16:33:04 -0500206static int acpi_ec_intr_wait(union acpi_ec *ec, unsigned int event)
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500207{
Len Brown50526df2005-08-11 17:32:05 -0400208 int result = 0;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500209
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500210
Len Brown02b28a32005-12-05 16:33:04 -0500211 ec->intr.expect_event = event;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500212 smp_mb();
213
Luming Yu716e0842005-08-10 01:40:00 -0400214 switch (event) {
Luming Yu716e0842005-08-10 01:40:00 -0400215 case ACPI_EC_EVENT_IBE:
216 if (~acpi_ec_read_status(ec) & event) {
Len Brown02b28a32005-12-05 16:33:04 -0500217 ec->intr.expect_event = 0;
Patrick Mocheld550d982006-06-27 00:41:40 -0400218 return 0;
Luming Yu716e0842005-08-10 01:40:00 -0400219 }
220 break;
Luming Yu06a2a382005-09-27 00:43:00 -0400221 default:
Luming Yu716e0842005-08-10 01:40:00 -0400222 break;
223 }
224
Len Brown02b28a32005-12-05 16:33:04 -0500225 result = wait_event_timeout(ec->intr.wait,
226 !ec->intr.expect_event,
Len Brown50526df2005-08-11 17:32:05 -0400227 msecs_to_jiffies(ACPI_EC_DELAY));
228
Len Brown02b28a32005-12-05 16:33:04 -0500229 ec->intr.expect_event = 0;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500230 smp_mb();
231
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500232 /*
233 * Verify that the event in question has actually happened by
234 * querying EC status. Do the check even if operation timed-out
235 * to make sure that we did not miss interrupt.
236 */
237 switch (event) {
238 case ACPI_EC_EVENT_OBF:
239 if (acpi_ec_read_status(ec) & ACPI_EC_FLAG_OBF)
Patrick Mocheld550d982006-06-27 00:41:40 -0400240 return 0;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500241 break;
242
243 case ACPI_EC_EVENT_IBE:
244 if (~acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF)
Patrick Mocheld550d982006-06-27 00:41:40 -0400245 return 0;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500246 break;
247 }
248
Patrick Mocheld550d982006-06-27 00:41:40 -0400249 return -ETIME;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500250}
251
Len Brown02b28a32005-12-05 16:33:04 -0500252#ifdef ACPI_FUTURE_USAGE
Luming Yu06a2a382005-09-27 00:43:00 -0400253/*
254 * Note: samsung nv5000 doesn't work with ec burst mode.
255 * http://bugzilla.kernel.org/show_bug.cgi?id=4980
256 */
257int acpi_ec_enter_burst_mode(union acpi_ec *ec)
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500258{
Len Brown50526df2005-08-11 17:32:05 -0400259 u32 tmp = 0;
260 int status = 0;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500261
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500262
263 status = acpi_ec_read_status(ec);
Len Brown50526df2005-08-11 17:32:05 -0400264 if (status != -EINVAL && !(status & ACPI_EC_FLAG_BURST)) {
Luming Yu716e0842005-08-10 01:40:00 -0400265 status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
Len Brown50526df2005-08-11 17:32:05 -0400266 if (status)
Luming Yu716e0842005-08-10 01:40:00 -0400267 goto end;
Len Brown50526df2005-08-11 17:32:05 -0400268 acpi_hw_low_level_write(8, ACPI_EC_BURST_ENABLE,
269 &ec->common.command_addr);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500270 status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF);
Luming Yu45bea152005-07-23 04:08:00 -0400271 acpi_hw_low_level_read(8, &tmp, &ec->common.data_addr);
Len Brown50526df2005-08-11 17:32:05 -0400272 if (tmp != 0x90) { /* Burst ACK byte */
Patrick Mocheld550d982006-06-27 00:41:40 -0400273 return -EINVAL;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500274 }
Luming Yu668d74c2005-07-23 00:26:33 -0400275 }
276
Len Brown02b28a32005-12-05 16:33:04 -0500277 atomic_set(&ec->intr.leaving_burst, 0);
Patrick Mocheld550d982006-06-27 00:41:40 -0400278 return 0;
Len Brown50526df2005-08-11 17:32:05 -0400279 end:
Thomas Renningera6fc6722006-06-26 23:58:43 -0400280 ACPI_EXCEPTION ((AE_INFO, status, "EC wait, burst mode");
Patrick Mocheld550d982006-06-27 00:41:40 -0400281 return -1;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500282}
283
Luming Yu06a2a382005-09-27 00:43:00 -0400284int acpi_ec_leave_burst_mode(union acpi_ec *ec)
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500285{
Luming Yu06a2a382005-09-27 00:43:00 -0400286 int status = 0;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500287
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500288
Luming Yu06a2a382005-09-27 00:43:00 -0400289 status = acpi_ec_read_status(ec);
290 if (status != -EINVAL && (status & ACPI_EC_FLAG_BURST)){
291 status = acpi_ec_wait(ec, ACPI_EC_FLAG_IBF);
292 if(status)
293 goto end;
294 acpi_hw_low_level_write(8, ACPI_EC_BURST_DISABLE, &ec->common.command_addr);
295 acpi_ec_wait(ec, ACPI_EC_FLAG_IBF);
296 }
Len Brown02b28a32005-12-05 16:33:04 -0500297 atomic_set(&ec->intr.leaving_burst, 1);
Patrick Mocheld550d982006-06-27 00:41:40 -0400298 return 0;
Luming Yu06a2a382005-09-27 00:43:00 -0400299end:
Thomas Renningera6fc6722006-06-26 23:58:43 -0400300 ACPI_EXCEPTION((AE_INFO, status, "EC leave burst mode");
Patrick Mocheld550d982006-06-27 00:41:40 -0400301 return -1;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500302}
Len Brown02b28a32005-12-05 16:33:04 -0500303#endif /* ACPI_FUTURE_USAGE */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304
Len Brown50526df2005-08-11 17:32:05 -0400305static int acpi_ec_read(union acpi_ec *ec, u8 address, u32 * data)
Luming Yu45bea152005-07-23 04:08:00 -0400306{
Len Brown02b28a32005-12-05 16:33:04 -0500307 if (acpi_ec_poll_mode)
308 return acpi_ec_poll_read(ec, address, data);
Luming Yu45bea152005-07-23 04:08:00 -0400309 else
Len Brown02b28a32005-12-05 16:33:04 -0500310 return acpi_ec_intr_read(ec, address, data);
Luming Yu45bea152005-07-23 04:08:00 -0400311}
Len Brown50526df2005-08-11 17:32:05 -0400312static int acpi_ec_write(union acpi_ec *ec, u8 address, u8 data)
Luming Yu45bea152005-07-23 04:08:00 -0400313{
Len Brown02b28a32005-12-05 16:33:04 -0500314 if (acpi_ec_poll_mode)
315 return acpi_ec_poll_write(ec, address, data);
Luming Yu45bea152005-07-23 04:08:00 -0400316 else
Len Brown02b28a32005-12-05 16:33:04 -0500317 return acpi_ec_intr_write(ec, address, data);
Luming Yu45bea152005-07-23 04:08:00 -0400318}
Len Brown02b28a32005-12-05 16:33:04 -0500319static int acpi_ec_poll_read(union acpi_ec *ec, u8 address, u32 * data)
Luming Yu45bea152005-07-23 04:08:00 -0400320{
Len Brown50526df2005-08-11 17:32:05 -0400321 acpi_status status = AE_OK;
322 int result = 0;
Len Brown50526df2005-08-11 17:32:05 -0400323 u32 glk = 0;
Luming Yu45bea152005-07-23 04:08:00 -0400324
Luming Yu45bea152005-07-23 04:08:00 -0400325
326 if (!ec || !data)
Patrick Mocheld550d982006-06-27 00:41:40 -0400327 return -EINVAL;
Luming Yu45bea152005-07-23 04:08:00 -0400328
329 *data = 0;
330
331 if (ec->common.global_lock) {
332 status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
333 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400334 return -ENODEV;
Luming Yu45bea152005-07-23 04:08:00 -0400335 }
336
Rich Townsendf9a6ee1a2005-12-19 23:07:00 -0500337 if (down_interruptible(&ec->poll.sem)) {
338 result = -ERESTARTSYS;
339 goto end_nosem;
340 }
341
Len Brown50526df2005-08-11 17:32:05 -0400342 acpi_hw_low_level_write(8, ACPI_EC_COMMAND_READ,
343 &ec->common.command_addr);
Luming Yu45bea152005-07-23 04:08:00 -0400344 result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
345 if (result)
346 goto end;
347
348 acpi_hw_low_level_write(8, address, &ec->common.data_addr);
349 result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF);
350 if (result)
351 goto end;
352
353 acpi_hw_low_level_read(8, data, &ec->common.data_addr);
354
355 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Read [%02x] from address [%02x]\n",
Len Brown50526df2005-08-11 17:32:05 -0400356 *data, address));
357
358 end:
Rich Townsendf9a6ee1a2005-12-19 23:07:00 -0500359 up(&ec->poll.sem);
360end_nosem:
Luming Yu45bea152005-07-23 04:08:00 -0400361 if (ec->common.global_lock)
362 acpi_release_global_lock(glk);
363
Patrick Mocheld550d982006-06-27 00:41:40 -0400364 return result;
Luming Yu45bea152005-07-23 04:08:00 -0400365}
366
Len Brown02b28a32005-12-05 16:33:04 -0500367static int acpi_ec_poll_write(union acpi_ec *ec, u8 address, u8 data)
Luming Yu45bea152005-07-23 04:08:00 -0400368{
Len Brown50526df2005-08-11 17:32:05 -0400369 int result = 0;
370 acpi_status status = AE_OK;
Len Brown50526df2005-08-11 17:32:05 -0400371 u32 glk = 0;
Luming Yu45bea152005-07-23 04:08:00 -0400372
Luming Yu45bea152005-07-23 04:08:00 -0400373
374 if (!ec)
Patrick Mocheld550d982006-06-27 00:41:40 -0400375 return -EINVAL;
Luming Yu45bea152005-07-23 04:08:00 -0400376
377 if (ec->common.global_lock) {
378 status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
379 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400380 return -ENODEV;
Luming Yu45bea152005-07-23 04:08:00 -0400381 }
382
Rich Townsendf9a6ee1a2005-12-19 23:07:00 -0500383 if (down_interruptible(&ec->poll.sem)) {
384 result = -ERESTARTSYS;
385 goto end_nosem;
386 }
387
Len Brown50526df2005-08-11 17:32:05 -0400388 acpi_hw_low_level_write(8, ACPI_EC_COMMAND_WRITE,
389 &ec->common.command_addr);
Luming Yu45bea152005-07-23 04:08:00 -0400390 result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
391 if (result)
392 goto end;
393
394 acpi_hw_low_level_write(8, address, &ec->common.data_addr);
395 result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
396 if (result)
397 goto end;
398
399 acpi_hw_low_level_write(8, data, &ec->common.data_addr);
400 result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
401 if (result)
402 goto end;
403
404 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Wrote [%02x] to address [%02x]\n",
Len Brown50526df2005-08-11 17:32:05 -0400405 data, address));
Luming Yu45bea152005-07-23 04:08:00 -0400406
Len Brown50526df2005-08-11 17:32:05 -0400407 end:
Rich Townsendf9a6ee1a2005-12-19 23:07:00 -0500408 up(&ec->poll.sem);
409end_nosem:
Luming Yu45bea152005-07-23 04:08:00 -0400410 if (ec->common.global_lock)
411 acpi_release_global_lock(glk);
412
Patrick Mocheld550d982006-06-27 00:41:40 -0400413 return result;
Luming Yu45bea152005-07-23 04:08:00 -0400414}
415
Len Brown02b28a32005-12-05 16:33:04 -0500416static int acpi_ec_intr_read(union acpi_ec *ec, u8 address, u32 * data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417{
Len Brown50526df2005-08-11 17:32:05 -0400418 int status = 0;
419 u32 glk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421
422 if (!ec || !data)
Patrick Mocheld550d982006-06-27 00:41:40 -0400423 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424
425 *data = 0;
426
Luming Yu45bea152005-07-23 04:08:00 -0400427 if (ec->common.global_lock) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
429 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400430 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 }
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500432
433 WARN_ON(in_interrupt());
Len Brown02b28a32005-12-05 16:33:04 -0500434 down(&ec->intr.sem);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500435
Luming Yu716e0842005-08-10 01:40:00 -0400436 status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
437 if (status) {
Len Brown1e8df532005-12-05 16:47:46 -0500438 printk(KERN_DEBUG PREFIX "read EC, IB not empty\n");
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500439 goto end;
Luming Yu716e0842005-08-10 01:40:00 -0400440 }
Len Brown50526df2005-08-11 17:32:05 -0400441 acpi_hw_low_level_write(8, ACPI_EC_COMMAND_READ,
442 &ec->common.command_addr);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500443 status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500444 if (status) {
Len Brown1e8df532005-12-05 16:47:46 -0500445 printk(KERN_DEBUG PREFIX "read EC, IB not empty\n");
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500446 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447
Luming Yu45bea152005-07-23 04:08:00 -0400448 acpi_hw_low_level_write(8, address, &ec->common.data_addr);
Len Brown50526df2005-08-11 17:32:05 -0400449 status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF);
450 if (status) {
Len Brown1e8df532005-12-05 16:47:46 -0500451 printk(KERN_DEBUG PREFIX "read EC, OB not full\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 goto end;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500453 }
Luming Yu45bea152005-07-23 04:08:00 -0400454 acpi_hw_low_level_read(8, data, &ec->common.data_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Read [%02x] from address [%02x]\n",
Len Brown50526df2005-08-11 17:32:05 -0400456 *data, address));
457
458 end:
Len Brown02b28a32005-12-05 16:33:04 -0500459 up(&ec->intr.sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460
Luming Yu45bea152005-07-23 04:08:00 -0400461 if (ec->common.global_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 acpi_release_global_lock(glk);
463
Patrick Mocheld550d982006-06-27 00:41:40 -0400464 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465}
466
Len Brown02b28a32005-12-05 16:33:04 -0500467static int acpi_ec_intr_write(union acpi_ec *ec, u8 address, u8 data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468{
Len Brown50526df2005-08-11 17:32:05 -0400469 int status = 0;
470 u32 glk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472
473 if (!ec)
Patrick Mocheld550d982006-06-27 00:41:40 -0400474 return -EINVAL;
Luming Yu716e0842005-08-10 01:40:00 -0400475
Luming Yu45bea152005-07-23 04:08:00 -0400476 if (ec->common.global_lock) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
478 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400479 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 }
481
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500482 WARN_ON(in_interrupt());
Len Brown02b28a32005-12-05 16:33:04 -0500483 down(&ec->intr.sem);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500484
Luming Yu716e0842005-08-10 01:40:00 -0400485 status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
Len Brown50526df2005-08-11 17:32:05 -0400486 if (status) {
Len Brown1e8df532005-12-05 16:47:46 -0500487 printk(KERN_DEBUG PREFIX "write EC, IB not empty\n");
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500488 }
Len Brown50526df2005-08-11 17:32:05 -0400489 acpi_hw_low_level_write(8, ACPI_EC_COMMAND_WRITE,
490 &ec->common.command_addr);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500491 status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
Luming Yu716e0842005-08-10 01:40:00 -0400492 if (status) {
Len Brown1e8df532005-12-05 16:47:46 -0500493 printk(KERN_DEBUG PREFIX "write EC, IB not empty\n");
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500494 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495
Luming Yu45bea152005-07-23 04:08:00 -0400496 acpi_hw_low_level_write(8, address, &ec->common.data_addr);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500497 status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
Len Brown50526df2005-08-11 17:32:05 -0400498 if (status) {
Len Brown1e8df532005-12-05 16:47:46 -0500499 printk(KERN_DEBUG PREFIX "write EC, IB not empty\n");
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500500 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
Luming Yu45bea152005-07-23 04:08:00 -0400502 acpi_hw_low_level_write(8, data, &ec->common.data_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503
504 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Wrote [%02x] to address [%02x]\n",
Len Brown50526df2005-08-11 17:32:05 -0400505 data, address));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506
Len Brown02b28a32005-12-05 16:33:04 -0500507 up(&ec->intr.sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508
Luming Yu45bea152005-07-23 04:08:00 -0400509 if (ec->common.global_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 acpi_release_global_lock(glk);
511
Patrick Mocheld550d982006-06-27 00:41:40 -0400512 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513}
514
515/*
516 * Externally callable EC access functions. For now, assume 1 EC only
517 */
Len Brown50526df2005-08-11 17:32:05 -0400518int ec_read(u8 addr, u8 * val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519{
Luming Yu45bea152005-07-23 04:08:00 -0400520 union acpi_ec *ec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 int err;
522 u32 temp_data;
523
524 if (!first_ec)
525 return -ENODEV;
526
527 ec = acpi_driver_data(first_ec);
528
529 err = acpi_ec_read(ec, addr, &temp_data);
530
531 if (!err) {
532 *val = temp_data;
533 return 0;
Len Brown50526df2005-08-11 17:32:05 -0400534 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 return err;
536}
Len Brown50526df2005-08-11 17:32:05 -0400537
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538EXPORT_SYMBOL(ec_read);
539
Len Brown50526df2005-08-11 17:32:05 -0400540int ec_write(u8 addr, u8 val)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541{
Luming Yu45bea152005-07-23 04:08:00 -0400542 union acpi_ec *ec;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 int err;
544
545 if (!first_ec)
546 return -ENODEV;
547
548 ec = acpi_driver_data(first_ec);
549
550 err = acpi_ec_write(ec, addr, val);
551
552 return err;
553}
Len Brown50526df2005-08-11 17:32:05 -0400554
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555EXPORT_SYMBOL(ec_write);
556
Len Brown50526df2005-08-11 17:32:05 -0400557static int acpi_ec_query(union acpi_ec *ec, u32 * data)
Luming Yu45bea152005-07-23 04:08:00 -0400558{
Len Brown02b28a32005-12-05 16:33:04 -0500559 if (acpi_ec_poll_mode)
560 return acpi_ec_poll_query(ec, data);
Luming Yu45bea152005-07-23 04:08:00 -0400561 else
Len Brown02b28a32005-12-05 16:33:04 -0500562 return acpi_ec_intr_query(ec, data);
Luming Yu45bea152005-07-23 04:08:00 -0400563}
Len Brown02b28a32005-12-05 16:33:04 -0500564static int acpi_ec_poll_query(union acpi_ec *ec, u32 * data)
Luming Yu45bea152005-07-23 04:08:00 -0400565{
Len Brown50526df2005-08-11 17:32:05 -0400566 int result = 0;
567 acpi_status status = AE_OK;
Len Brown50526df2005-08-11 17:32:05 -0400568 u32 glk = 0;
Luming Yu45bea152005-07-23 04:08:00 -0400569
Luming Yu45bea152005-07-23 04:08:00 -0400570
571 if (!ec || !data)
Patrick Mocheld550d982006-06-27 00:41:40 -0400572 return -EINVAL;
Luming Yu45bea152005-07-23 04:08:00 -0400573
574 *data = 0;
575
576 if (ec->common.global_lock) {
577 status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
578 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400579 return -ENODEV;
Luming Yu45bea152005-07-23 04:08:00 -0400580 }
581
582 /*
583 * Query the EC to find out which _Qxx method we need to evaluate.
584 * Note that successful completion of the query causes the ACPI_EC_SCI
585 * bit to be cleared (and thus clearing the interrupt source).
586 */
Rich Townsendf9a6ee1a2005-12-19 23:07:00 -0500587 if (down_interruptible(&ec->poll.sem)) {
588 result = -ERESTARTSYS;
589 goto end_nosem;
590 }
591
Len Brown50526df2005-08-11 17:32:05 -0400592 acpi_hw_low_level_write(8, ACPI_EC_COMMAND_QUERY,
593 &ec->common.command_addr);
Luming Yu45bea152005-07-23 04:08:00 -0400594 result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF);
595 if (result)
596 goto end;
597
598 acpi_hw_low_level_read(8, data, &ec->common.data_addr);
599 if (!*data)
600 result = -ENODATA;
601
Len Brown50526df2005-08-11 17:32:05 -0400602 end:
Rich Townsendf9a6ee1a2005-12-19 23:07:00 -0500603 up(&ec->poll.sem);
604end_nosem:
Luming Yu45bea152005-07-23 04:08:00 -0400605 if (ec->common.global_lock)
606 acpi_release_global_lock(glk);
607
Patrick Mocheld550d982006-06-27 00:41:40 -0400608 return result;
Luming Yu45bea152005-07-23 04:08:00 -0400609}
Len Brown02b28a32005-12-05 16:33:04 -0500610static int acpi_ec_intr_query(union acpi_ec *ec, u32 * data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611{
Len Brown50526df2005-08-11 17:32:05 -0400612 int status = 0;
613 u32 glk;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615
616 if (!ec || !data)
Patrick Mocheld550d982006-06-27 00:41:40 -0400617 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 *data = 0;
619
Luming Yu45bea152005-07-23 04:08:00 -0400620 if (ec->common.global_lock) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk);
622 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400623 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 }
625
Len Brown02b28a32005-12-05 16:33:04 -0500626 down(&ec->intr.sem);
Luming Yu716e0842005-08-10 01:40:00 -0400627
628 status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE);
629 if (status) {
Len Brown1e8df532005-12-05 16:47:46 -0500630 printk(KERN_DEBUG PREFIX "query EC, IB not empty\n");
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500631 goto end;
Luming Yu716e0842005-08-10 01:40:00 -0400632 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 /*
634 * Query the EC to find out which _Qxx method we need to evaluate.
635 * Note that successful completion of the query causes the ACPI_EC_SCI
636 * bit to be cleared (and thus clearing the interrupt source).
637 */
Len Brown50526df2005-08-11 17:32:05 -0400638 acpi_hw_low_level_write(8, ACPI_EC_COMMAND_QUERY,
639 &ec->common.command_addr);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500640 status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF);
Len Brown50526df2005-08-11 17:32:05 -0400641 if (status) {
Len Brown1e8df532005-12-05 16:47:46 -0500642 printk(KERN_DEBUG PREFIX "query EC, OB not full\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 goto end;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500644 }
645
Luming Yu45bea152005-07-23 04:08:00 -0400646 acpi_hw_low_level_read(8, data, &ec->common.data_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 if (!*data)
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500648 status = -ENODATA;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649
Len Brown50526df2005-08-11 17:32:05 -0400650 end:
Len Brown02b28a32005-12-05 16:33:04 -0500651 up(&ec->intr.sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
Luming Yu45bea152005-07-23 04:08:00 -0400653 if (ec->common.global_lock)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 acpi_release_global_lock(glk);
655
Patrick Mocheld550d982006-06-27 00:41:40 -0400656 return status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657}
658
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659/* --------------------------------------------------------------------------
660 Event Management
661 -------------------------------------------------------------------------- */
662
Luming Yu45bea152005-07-23 04:08:00 -0400663union acpi_ec_query_data {
Len Brown50526df2005-08-11 17:32:05 -0400664 acpi_handle handle;
665 u8 data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666};
667
Len Brown50526df2005-08-11 17:32:05 -0400668static void acpi_ec_gpe_query(void *ec_cxt)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669{
Len Brown02b28a32005-12-05 16:33:04 -0500670 if (acpi_ec_poll_mode)
671 acpi_ec_gpe_poll_query(ec_cxt);
Luming Yu45bea152005-07-23 04:08:00 -0400672 else
Len Brown02b28a32005-12-05 16:33:04 -0500673 acpi_ec_gpe_intr_query(ec_cxt);
Luming Yu45bea152005-07-23 04:08:00 -0400674}
675
Len Brown02b28a32005-12-05 16:33:04 -0500676static void acpi_ec_gpe_poll_query(void *ec_cxt)
Luming Yu45bea152005-07-23 04:08:00 -0400677{
Len Brown50526df2005-08-11 17:32:05 -0400678 union acpi_ec *ec = (union acpi_ec *)ec_cxt;
679 u32 value = 0;
Len Brown50526df2005-08-11 17:32:05 -0400680 static char object_name[5] = { '_', 'Q', '0', '0', '\0' };
681 const char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7',
682 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
683 };
Luming Yu45bea152005-07-23 04:08:00 -0400684
Luming Yu45bea152005-07-23 04:08:00 -0400685
686 if (!ec_cxt)
687 goto end;
688
Rich Townsendf9a6ee1a2005-12-19 23:07:00 -0500689 if (down_interruptible (&ec->poll.sem)) {
Patrick Mocheld550d982006-06-27 00:41:40 -0400690 return;
Rich Townsendf9a6ee1a2005-12-19 23:07:00 -0500691 }
Luming Yu45bea152005-07-23 04:08:00 -0400692 acpi_hw_low_level_read(8, &value, &ec->common.command_addr);
Rich Townsendf9a6ee1a2005-12-19 23:07:00 -0500693 up(&ec->poll.sem);
Luming Yu45bea152005-07-23 04:08:00 -0400694
695 /* TBD: Implement asynch events!
696 * NOTE: All we care about are EC-SCI's. Other EC events are
697 * handled via polling (yuck!). This is because some systems
698 * treat EC-SCIs as level (versus EDGE!) triggered, preventing
699 * a purely interrupt-driven approach (grumble, grumble).
700 */
701 if (!(value & ACPI_EC_FLAG_SCI))
702 goto end;
703
704 if (acpi_ec_query(ec, &value))
705 goto end;
706
707 object_name[2] = hex[((value >> 4) & 0x0F)];
708 object_name[3] = hex[(value & 0x0F)];
709
710 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s\n", object_name));
711
712 acpi_evaluate_object(ec->common.handle, object_name, NULL, NULL);
713
Len Brown50526df2005-08-11 17:32:05 -0400714 end:
Luming Yu45bea152005-07-23 04:08:00 -0400715 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
716}
Len Brown02b28a32005-12-05 16:33:04 -0500717static void acpi_ec_gpe_intr_query(void *ec_cxt)
Luming Yu45bea152005-07-23 04:08:00 -0400718{
Len Brown50526df2005-08-11 17:32:05 -0400719 union acpi_ec *ec = (union acpi_ec *)ec_cxt;
720 u32 value;
721 int result = -ENODATA;
722 static char object_name[5] = { '_', 'Q', '0', '0', '\0' };
723 const char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7',
724 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
725 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500728 if (acpi_ec_read_status(ec) & ACPI_EC_FLAG_SCI)
729 result = acpi_ec_query(ec, &value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500731 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 goto end;
733
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 object_name[2] = hex[((value >> 4) & 0x0F)];
735 object_name[3] = hex[(value & 0x0F)];
736
737 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s\n", object_name));
738
Luming Yu45bea152005-07-23 04:08:00 -0400739 acpi_evaluate_object(ec->common.handle, object_name, NULL, NULL);
Len Brown50526df2005-08-11 17:32:05 -0400740 end:
Len Brown02b28a32005-12-05 16:33:04 -0500741 atomic_dec(&ec->intr.pending_gpe);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500742 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743}
744
Len Brown50526df2005-08-11 17:32:05 -0400745static u32 acpi_ec_gpe_handler(void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746{
Len Brown02b28a32005-12-05 16:33:04 -0500747 if (acpi_ec_poll_mode)
748 return acpi_ec_gpe_poll_handler(data);
Luming Yu45bea152005-07-23 04:08:00 -0400749 else
Len Brown02b28a32005-12-05 16:33:04 -0500750 return acpi_ec_gpe_intr_handler(data);
Luming Yu45bea152005-07-23 04:08:00 -0400751}
Len Brown02b28a32005-12-05 16:33:04 -0500752static u32 acpi_ec_gpe_poll_handler(void *data)
Luming Yu45bea152005-07-23 04:08:00 -0400753{
Len Brown50526df2005-08-11 17:32:05 -0400754 acpi_status status = AE_OK;
755 union acpi_ec *ec = (union acpi_ec *)data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
757 if (!ec)
758 return ACPI_INTERRUPT_NOT_HANDLED;
759
Luming Yu45bea152005-07-23 04:08:00 -0400760 acpi_disable_gpe(NULL, ec->common.gpe_bit, ACPI_ISR);
761
Alexey Starikovskiyb8d35192006-05-05 03:23:00 -0400762 status = acpi_os_execute(OSL_EC_POLL_HANDLER, acpi_ec_gpe_query, ec);
Luming Yu45bea152005-07-23 04:08:00 -0400763
764 if (status == AE_OK)
765 return ACPI_INTERRUPT_HANDLED;
766 else
767 return ACPI_INTERRUPT_NOT_HANDLED;
768}
Len Brown02b28a32005-12-05 16:33:04 -0500769static u32 acpi_ec_gpe_intr_handler(void *data)
Luming Yu45bea152005-07-23 04:08:00 -0400770{
Len Brown50526df2005-08-11 17:32:05 -0400771 acpi_status status = AE_OK;
772 u32 value;
773 union acpi_ec *ec = (union acpi_ec *)data;
Luming Yu45bea152005-07-23 04:08:00 -0400774
775 if (!ec)
776 return ACPI_INTERRUPT_NOT_HANDLED;
777
Luming Yu716e0842005-08-10 01:40:00 -0400778 acpi_clear_gpe(NULL, ec->common.gpe_bit, ACPI_ISR);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500779 value = acpi_ec_read_status(ec);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780
Len Brown02b28a32005-12-05 16:33:04 -0500781 switch (ec->intr.expect_event) {
Luming Yu716e0842005-08-10 01:40:00 -0400782 case ACPI_EC_EVENT_OBF:
783 if (!(value & ACPI_EC_FLAG_OBF))
784 break;
785 case ACPI_EC_EVENT_IBE:
786 if ((value & ACPI_EC_FLAG_IBF))
787 break;
Len Brown02b28a32005-12-05 16:33:04 -0500788 ec->intr.expect_event = 0;
789 wake_up(&ec->intr.wait);
Luming Yu716e0842005-08-10 01:40:00 -0400790 return ACPI_INTERRUPT_HANDLED;
791 default:
792 break;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500793 }
794
Len Brown50526df2005-08-11 17:32:05 -0400795 if (value & ACPI_EC_FLAG_SCI) {
Len Brown02b28a32005-12-05 16:33:04 -0500796 atomic_add(1, &ec->intr.pending_gpe);
Alexey Starikovskiyb8d35192006-05-05 03:23:00 -0400797 status = acpi_os_execute(OSL_EC_BURST_HANDLER,
Len Brown50526df2005-08-11 17:32:05 -0400798 acpi_ec_gpe_query, ec);
Luming Yu17e9c782005-04-22 23:07:10 -0400799 return status == AE_OK ?
Len Brown50526df2005-08-11 17:32:05 -0400800 ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED;
801 }
Luming Yu45bea152005-07-23 04:08:00 -0400802 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_ISR);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500803 return status == AE_OK ?
Len Brown50526df2005-08-11 17:32:05 -0400804 ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805}
806
807/* --------------------------------------------------------------------------
808 Address Space Management
809 -------------------------------------------------------------------------- */
810
811static acpi_status
Len Brown50526df2005-08-11 17:32:05 -0400812acpi_ec_space_setup(acpi_handle region_handle,
813 u32 function, void *handler_context, void **return_context)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814{
815 /*
816 * The EC object is in the handler context and is needed
817 * when calling the acpi_ec_space_handler.
818 */
Len Brown50526df2005-08-11 17:32:05 -0400819 *return_context = (function != ACPI_REGION_DEACTIVATE) ?
820 handler_context : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821
822 return AE_OK;
823}
824
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825static acpi_status
Len Brown50526df2005-08-11 17:32:05 -0400826acpi_ec_space_handler(u32 function,
827 acpi_physical_address address,
828 u32 bit_width,
829 acpi_integer * value,
830 void *handler_context, void *region_context)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831{
Len Brown50526df2005-08-11 17:32:05 -0400832 int result = 0;
833 union acpi_ec *ec = NULL;
834 u64 temp = *value;
835 acpi_integer f_v = 0;
836 int i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838
839 if ((address > 0xFF) || !value || !handler_context)
Patrick Mocheld550d982006-06-27 00:41:40 -0400840 return AE_BAD_PARAMETER;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841
Luming Yufa9cd542005-03-19 01:54:47 -0500842 if (bit_width != 8 && acpi_strict) {
Len Brown50526df2005-08-11 17:32:05 -0400843 printk(KERN_WARNING PREFIX
844 "acpi_ec_space_handler: bit_width should be 8\n");
Patrick Mocheld550d982006-06-27 00:41:40 -0400845 return AE_BAD_PARAMETER;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 }
847
Len Brown50526df2005-08-11 17:32:05 -0400848 ec = (union acpi_ec *)handler_context;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849
Len Brown50526df2005-08-11 17:32:05 -0400850 next_byte:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 switch (function) {
852 case ACPI_READ:
Luming Yufa9cd542005-03-19 01:54:47 -0500853 temp = 0;
Len Brown50526df2005-08-11 17:32:05 -0400854 result = acpi_ec_read(ec, (u8) address, (u32 *) & temp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 break;
856 case ACPI_WRITE:
Luming Yufa9cd542005-03-19 01:54:47 -0500857 result = acpi_ec_write(ec, (u8) address, (u8) temp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 break;
859 default:
860 result = -EINVAL;
861 goto out;
862 break;
863 }
864
865 bit_width -= 8;
Luming Yufa9cd542005-03-19 01:54:47 -0500866 if (bit_width) {
867 if (function == ACPI_READ)
868 f_v |= temp << 8 * i;
869 if (function == ACPI_WRITE)
870 temp >>= 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 i++;
Andrew Morton83ea7442005-03-30 22:12:13 -0500872 address++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 goto next_byte;
874 }
875
Luming Yufa9cd542005-03-19 01:54:47 -0500876 if (function == ACPI_READ) {
877 f_v |= temp << 8 * i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878 *value = f_v;
879 }
880
Len Brown50526df2005-08-11 17:32:05 -0400881 out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 switch (result) {
883 case -EINVAL:
Patrick Mocheld550d982006-06-27 00:41:40 -0400884 return AE_BAD_PARAMETER;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 break;
886 case -ENODEV:
Patrick Mocheld550d982006-06-27 00:41:40 -0400887 return AE_NOT_FOUND;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 break;
889 case -ETIME:
Patrick Mocheld550d982006-06-27 00:41:40 -0400890 return AE_TIME;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 break;
892 default:
Patrick Mocheld550d982006-06-27 00:41:40 -0400893 return AE_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895}
896
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897/* --------------------------------------------------------------------------
898 FS Interface (/proc)
899 -------------------------------------------------------------------------- */
900
Len Brown50526df2005-08-11 17:32:05 -0400901static struct proc_dir_entry *acpi_ec_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902
Len Brown50526df2005-08-11 17:32:05 -0400903static int acpi_ec_read_info(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904{
Len Brown50526df2005-08-11 17:32:05 -0400905 union acpi_ec *ec = (union acpi_ec *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907
908 if (!ec)
909 goto end;
910
911 seq_printf(seq, "gpe bit: 0x%02x\n",
Len Brown50526df2005-08-11 17:32:05 -0400912 (u32) ec->common.gpe_bit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 seq_printf(seq, "ports: 0x%02x, 0x%02x\n",
Len Brown50526df2005-08-11 17:32:05 -0400914 (u32) ec->common.status_addr.address,
915 (u32) ec->common.data_addr.address);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 seq_printf(seq, "use global lock: %s\n",
Len Brown50526df2005-08-11 17:32:05 -0400917 ec->common.global_lock ? "yes" : "no");
Luming Yu45bea152005-07-23 04:08:00 -0400918 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919
Len Brown50526df2005-08-11 17:32:05 -0400920 end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400921 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922}
923
924static int acpi_ec_info_open_fs(struct inode *inode, struct file *file)
925{
926 return single_open(file, acpi_ec_read_info, PDE(inode)->data);
927}
928
929static struct file_operations acpi_ec_info_ops = {
Len Brown50526df2005-08-11 17:32:05 -0400930 .open = acpi_ec_info_open_fs,
931 .read = seq_read,
932 .llseek = seq_lseek,
933 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 .owner = THIS_MODULE,
935};
936
Len Brown50526df2005-08-11 17:32:05 -0400937static int acpi_ec_add_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938{
Len Brown50526df2005-08-11 17:32:05 -0400939 struct proc_dir_entry *entry = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941
942 if (!acpi_device_dir(device)) {
943 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
Len Brown50526df2005-08-11 17:32:05 -0400944 acpi_ec_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 if (!acpi_device_dir(device))
Patrick Mocheld550d982006-06-27 00:41:40 -0400946 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947 }
948
949 entry = create_proc_entry(ACPI_EC_FILE_INFO, S_IRUGO,
Len Brown50526df2005-08-11 17:32:05 -0400950 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -0400952 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 else {
954 entry->proc_fops = &acpi_ec_info_ops;
955 entry->data = acpi_driver_data(device);
956 entry->owner = THIS_MODULE;
957 }
958
Patrick Mocheld550d982006-06-27 00:41:40 -0400959 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960}
961
Len Brown50526df2005-08-11 17:32:05 -0400962static int acpi_ec_remove_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964
965 if (acpi_device_dir(device)) {
966 remove_proc_entry(ACPI_EC_FILE_INFO, acpi_device_dir(device));
967 remove_proc_entry(acpi_device_bid(device), acpi_ec_dir);
968 acpi_device_dir(device) = NULL;
969 }
970
Patrick Mocheld550d982006-06-27 00:41:40 -0400971 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972}
973
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974/* --------------------------------------------------------------------------
975 Driver Interface
976 -------------------------------------------------------------------------- */
977
Len Brown02b28a32005-12-05 16:33:04 -0500978static int acpi_ec_poll_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979{
Len Brown50526df2005-08-11 17:32:05 -0400980 int result = 0;
981 acpi_status status = AE_OK;
982 union acpi_ec *ec = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984
985 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -0400986 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987
Luming Yu45bea152005-07-23 04:08:00 -0400988 ec = kmalloc(sizeof(union acpi_ec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989 if (!ec)
Patrick Mocheld550d982006-06-27 00:41:40 -0400990 return -ENOMEM;
Luming Yu45bea152005-07-23 04:08:00 -0400991 memset(ec, 0, sizeof(union acpi_ec));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992
Luming Yu45bea152005-07-23 04:08:00 -0400993 ec->common.handle = device->handle;
994 ec->common.uid = -1;
Rich Townsendf9a6ee1a2005-12-19 23:07:00 -0500995 init_MUTEX(&ec->poll.sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996 strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME);
997 strcpy(acpi_device_class(device), ACPI_EC_CLASS);
998 acpi_driver_data(device) = ec;
999
1000 /* Use the global lock for all EC transactions? */
Len Brown50526df2005-08-11 17:32:05 -04001001 acpi_evaluate_integer(ec->common.handle, "_GLK", NULL,
1002 &ec->common.global_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003
Jiri Slabyff2fc3e2006-03-28 17:04:00 -05001004 /* XXX we don't test uids, because on some boxes ecdt uid = 0, see:
1005 http://bugzilla.kernel.org/show_bug.cgi?id=6111 */
1006 if (ec_ecdt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
Len Brown50526df2005-08-11 17:32:05 -04001008 ACPI_ADR_SPACE_EC,
1009 &acpi_ec_space_handler);
1010
1011 acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit,
1012 &acpi_ec_gpe_handler);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013
1014 kfree(ec_ecdt);
1015 }
1016
1017 /* Get GPE bit assignment (EC events). */
1018 /* TODO: Add support for _GPE returning a package */
Len Brown50526df2005-08-11 17:32:05 -04001019 status =
1020 acpi_evaluate_integer(ec->common.handle, "_GPE", NULL,
1021 &ec->common.gpe_bit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -04001023 ACPI_EXCEPTION((AE_INFO, status, "Obtaining GPE bit"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024 result = -ENODEV;
1025 goto end;
1026 }
1027
1028 result = acpi_ec_add_fs(device);
1029 if (result)
1030 goto end;
1031
Len Brown02b28a32005-12-05 16:33:04 -05001032 printk(KERN_INFO PREFIX "%s [%s] (gpe %d) polling mode.\n",
Len Brown50526df2005-08-11 17:32:05 -04001033 acpi_device_name(device), acpi_device_bid(device),
1034 (u32) ec->common.gpe_bit);
Luming Yu45bea152005-07-23 04:08:00 -04001035
1036 if (!first_ec)
1037 first_ec = device;
1038
Len Brown50526df2005-08-11 17:32:05 -04001039 end:
Luming Yu45bea152005-07-23 04:08:00 -04001040 if (result)
1041 kfree(ec);
1042
Patrick Mocheld550d982006-06-27 00:41:40 -04001043 return result;
Luming Yu45bea152005-07-23 04:08:00 -04001044}
Len Brown02b28a32005-12-05 16:33:04 -05001045static int acpi_ec_intr_add(struct acpi_device *device)
Luming Yu45bea152005-07-23 04:08:00 -04001046{
Len Brown50526df2005-08-11 17:32:05 -04001047 int result = 0;
1048 acpi_status status = AE_OK;
1049 union acpi_ec *ec = NULL;
Luming Yu45bea152005-07-23 04:08:00 -04001050
Luming Yu45bea152005-07-23 04:08:00 -04001051
1052 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -04001053 return -EINVAL;
Luming Yu45bea152005-07-23 04:08:00 -04001054
1055 ec = kmalloc(sizeof(union acpi_ec), GFP_KERNEL);
1056 if (!ec)
Patrick Mocheld550d982006-06-27 00:41:40 -04001057 return -ENOMEM;
Luming Yu45bea152005-07-23 04:08:00 -04001058 memset(ec, 0, sizeof(union acpi_ec));
1059
1060 ec->common.handle = device->handle;
1061 ec->common.uid = -1;
Len Brown02b28a32005-12-05 16:33:04 -05001062 atomic_set(&ec->intr.pending_gpe, 0);
1063 atomic_set(&ec->intr.leaving_burst, 1);
1064 init_MUTEX(&ec->intr.sem);
1065 init_waitqueue_head(&ec->intr.wait);
Luming Yu45bea152005-07-23 04:08:00 -04001066 strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME);
1067 strcpy(acpi_device_class(device), ACPI_EC_CLASS);
1068 acpi_driver_data(device) = ec;
1069
1070 /* Use the global lock for all EC transactions? */
Len Brown50526df2005-08-11 17:32:05 -04001071 acpi_evaluate_integer(ec->common.handle, "_GLK", NULL,
1072 &ec->common.global_lock);
Luming Yu45bea152005-07-23 04:08:00 -04001073
Jiri Slabyff2fc3e2006-03-28 17:04:00 -05001074 /* XXX we don't test uids, because on some boxes ecdt uid = 0, see:
1075 http://bugzilla.kernel.org/show_bug.cgi?id=6111 */
1076 if (ec_ecdt) {
Luming Yu45bea152005-07-23 04:08:00 -04001077 acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
Len Brown50526df2005-08-11 17:32:05 -04001078 ACPI_ADR_SPACE_EC,
1079 &acpi_ec_space_handler);
Luming Yu45bea152005-07-23 04:08:00 -04001080
Len Brown50526df2005-08-11 17:32:05 -04001081 acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit,
1082 &acpi_ec_gpe_handler);
Luming Yu45bea152005-07-23 04:08:00 -04001083
1084 kfree(ec_ecdt);
1085 }
1086
1087 /* Get GPE bit assignment (EC events). */
1088 /* TODO: Add support for _GPE returning a package */
Len Brown50526df2005-08-11 17:32:05 -04001089 status =
1090 acpi_evaluate_integer(ec->common.handle, "_GPE", NULL,
1091 &ec->common.gpe_bit);
Luming Yu45bea152005-07-23 04:08:00 -04001092 if (ACPI_FAILURE(status)) {
Len Brown64684632006-06-26 23:41:38 -04001093 printk(KERN_ERR PREFIX "Obtaining GPE bit assignment\n");
Luming Yu45bea152005-07-23 04:08:00 -04001094 result = -ENODEV;
1095 goto end;
1096 }
1097
1098 result = acpi_ec_add_fs(device);
1099 if (result)
1100 goto end;
1101
Len Brown02b28a32005-12-05 16:33:04 -05001102 printk(KERN_INFO PREFIX "%s [%s] (gpe %d) interrupt mode.\n",
Len Brown50526df2005-08-11 17:32:05 -04001103 acpi_device_name(device), acpi_device_bid(device),
1104 (u32) ec->common.gpe_bit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105
1106 if (!first_ec)
1107 first_ec = device;
1108
Len Brown50526df2005-08-11 17:32:05 -04001109 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 if (result)
1111 kfree(ec);
1112
Patrick Mocheld550d982006-06-27 00:41:40 -04001113 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114}
1115
Len Brown50526df2005-08-11 17:32:05 -04001116static int acpi_ec_remove(struct acpi_device *device, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117{
Len Brown50526df2005-08-11 17:32:05 -04001118 union acpi_ec *ec = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120
1121 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -04001122 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123
1124 ec = acpi_driver_data(device);
1125
1126 acpi_ec_remove_fs(device);
1127
1128 kfree(ec);
1129
Patrick Mocheld550d982006-06-27 00:41:40 -04001130 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131}
1132
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133static acpi_status
Len Brown50526df2005-08-11 17:32:05 -04001134acpi_ec_io_ports(struct acpi_resource *resource, void *context)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135{
Len Brown50526df2005-08-11 17:32:05 -04001136 union acpi_ec *ec = (union acpi_ec *)context;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 struct acpi_generic_address *addr;
1138
Bob Moore50eca3e2005-09-30 19:03:00 -04001139 if (resource->type != ACPI_RESOURCE_TYPE_IO) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140 return AE_OK;
1141 }
1142
1143 /*
1144 * The first address region returned is the data port, and
1145 * the second address region returned is the status/command
1146 * port.
1147 */
Luming Yu45bea152005-07-23 04:08:00 -04001148 if (ec->common.data_addr.register_bit_width == 0) {
1149 addr = &ec->common.data_addr;
1150 } else if (ec->common.command_addr.register_bit_width == 0) {
1151 addr = &ec->common.command_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 } else {
1153 return AE_CTRL_TERMINATE;
1154 }
1155
1156 addr->address_space_id = ACPI_ADR_SPACE_SYSTEM_IO;
1157 addr->register_bit_width = 8;
1158 addr->register_bit_offset = 0;
Bob Moore50eca3e2005-09-30 19:03:00 -04001159 addr->address = resource->data.io.minimum;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160
1161 return AE_OK;
1162}
1163
Len Brown50526df2005-08-11 17:32:05 -04001164static int acpi_ec_start(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165{
Len Brown50526df2005-08-11 17:32:05 -04001166 acpi_status status = AE_OK;
1167 union acpi_ec *ec = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169
1170 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -04001171 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172
1173 ec = acpi_driver_data(device);
1174
1175 if (!ec)
Patrick Mocheld550d982006-06-27 00:41:40 -04001176 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177
1178 /*
1179 * Get I/O port addresses. Convert to GAS format.
1180 */
Luming Yu45bea152005-07-23 04:08:00 -04001181 status = acpi_walk_resources(ec->common.handle, METHOD_NAME__CRS,
Len Brown50526df2005-08-11 17:32:05 -04001182 acpi_ec_io_ports, ec);
1183 if (ACPI_FAILURE(status)
1184 || ec->common.command_addr.register_bit_width == 0) {
Len Brown64684632006-06-26 23:41:38 -04001185 printk(KERN_ERR PREFIX "Error getting I/O port addresses\n");
Patrick Mocheld550d982006-06-27 00:41:40 -04001186 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 }
1188
Luming Yu45bea152005-07-23 04:08:00 -04001189 ec->common.status_addr = ec->common.command_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190
1191 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02x, ports=0x%2x,0x%2x\n",
Len Brown50526df2005-08-11 17:32:05 -04001192 (u32) ec->common.gpe_bit,
1193 (u32) ec->common.command_addr.address,
1194 (u32) ec->common.data_addr.address));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195
1196 /*
1197 * Install GPE handler
1198 */
Luming Yu45bea152005-07-23 04:08:00 -04001199 status = acpi_install_gpe_handler(NULL, ec->common.gpe_bit,
Len Brown50526df2005-08-11 17:32:05 -04001200 ACPI_GPE_EDGE_TRIGGERED,
1201 &acpi_ec_gpe_handler, ec);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 if (ACPI_FAILURE(status)) {
Patrick Mocheld550d982006-06-27 00:41:40 -04001203 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 }
Len Brown50526df2005-08-11 17:32:05 -04001205 acpi_set_gpe_type(NULL, ec->common.gpe_bit, ACPI_GPE_TYPE_RUNTIME);
1206 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207
Len Brown50526df2005-08-11 17:32:05 -04001208 status = acpi_install_address_space_handler(ec->common.handle,
1209 ACPI_ADR_SPACE_EC,
1210 &acpi_ec_space_handler,
1211 &acpi_ec_space_setup, ec);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001212 if (ACPI_FAILURE(status)) {
Len Brown50526df2005-08-11 17:32:05 -04001213 acpi_remove_gpe_handler(NULL, ec->common.gpe_bit,
1214 &acpi_ec_gpe_handler);
Patrick Mocheld550d982006-06-27 00:41:40 -04001215 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 }
1217
Patrick Mocheld550d982006-06-27 00:41:40 -04001218 return AE_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219}
1220
Len Brown50526df2005-08-11 17:32:05 -04001221static int acpi_ec_stop(struct acpi_device *device, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222{
Len Brown50526df2005-08-11 17:32:05 -04001223 acpi_status status = AE_OK;
1224 union acpi_ec *ec = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226
1227 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -04001228 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229
1230 ec = acpi_driver_data(device);
1231
Luming Yu45bea152005-07-23 04:08:00 -04001232 status = acpi_remove_address_space_handler(ec->common.handle,
Len Brown50526df2005-08-11 17:32:05 -04001233 ACPI_ADR_SPACE_EC,
1234 &acpi_ec_space_handler);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -04001236 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237
Len Brown50526df2005-08-11 17:32:05 -04001238 status =
1239 acpi_remove_gpe_handler(NULL, ec->common.gpe_bit,
1240 &acpi_ec_gpe_handler);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -04001242 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243
Patrick Mocheld550d982006-06-27 00:41:40 -04001244 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245}
1246
1247static acpi_status __init
Len Brown50526df2005-08-11 17:32:05 -04001248acpi_fake_ecdt_callback(acpi_handle handle,
1249 u32 Level, void *context, void **retval)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250{
Luming Yu45bea152005-07-23 04:08:00 -04001251
Len Brown02b28a32005-12-05 16:33:04 -05001252 if (acpi_ec_poll_mode)
1253 return acpi_fake_ecdt_poll_callback(handle,
Len Brown50526df2005-08-11 17:32:05 -04001254 Level, context, retval);
Luming Yu45bea152005-07-23 04:08:00 -04001255 else
Len Brown02b28a32005-12-05 16:33:04 -05001256 return acpi_fake_ecdt_intr_callback(handle,
Len Brown50526df2005-08-11 17:32:05 -04001257 Level, context, retval);
Luming Yu45bea152005-07-23 04:08:00 -04001258}
1259
1260static acpi_status __init
Len Brown02b28a32005-12-05 16:33:04 -05001261acpi_fake_ecdt_poll_callback(acpi_handle handle,
Len Brown50526df2005-08-11 17:32:05 -04001262 u32 Level, void *context, void **retval)
Luming Yu45bea152005-07-23 04:08:00 -04001263{
Len Brown50526df2005-08-11 17:32:05 -04001264 acpi_status status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265
1266 status = acpi_walk_resources(handle, METHOD_NAME__CRS,
Len Brown50526df2005-08-11 17:32:05 -04001267 acpi_ec_io_ports, ec_ecdt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 if (ACPI_FAILURE(status))
1269 return status;
Luming Yu45bea152005-07-23 04:08:00 -04001270 ec_ecdt->common.status_addr = ec_ecdt->common.command_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271
Luming Yu45bea152005-07-23 04:08:00 -04001272 ec_ecdt->common.uid = -1;
1273 acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->common.uid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274
Len Brown50526df2005-08-11 17:32:05 -04001275 status =
1276 acpi_evaluate_integer(handle, "_GPE", NULL,
1277 &ec_ecdt->common.gpe_bit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 if (ACPI_FAILURE(status))
1279 return status;
Rich Townsendf9a6ee1a2005-12-19 23:07:00 -05001280 init_MUTEX(&ec_ecdt->poll.sem);
Luming Yu45bea152005-07-23 04:08:00 -04001281 ec_ecdt->common.global_lock = TRUE;
1282 ec_ecdt->common.handle = handle;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283
Len Brown50526df2005-08-11 17:32:05 -04001284 printk(KERN_INFO PREFIX "GPE=0x%02x, ports=0x%2x, 0x%2x\n",
1285 (u32) ec_ecdt->common.gpe_bit,
1286 (u32) ec_ecdt->common.command_addr.address,
1287 (u32) ec_ecdt->common.data_addr.address);
Luming Yu45bea152005-07-23 04:08:00 -04001288
1289 return AE_CTRL_TERMINATE;
1290}
1291
1292static acpi_status __init
Len Brown02b28a32005-12-05 16:33:04 -05001293acpi_fake_ecdt_intr_callback(acpi_handle handle,
Len Brown50526df2005-08-11 17:32:05 -04001294 u32 Level, void *context, void **retval)
Luming Yu45bea152005-07-23 04:08:00 -04001295{
Len Brown50526df2005-08-11 17:32:05 -04001296 acpi_status status;
Luming Yu45bea152005-07-23 04:08:00 -04001297
Len Brown02b28a32005-12-05 16:33:04 -05001298 init_MUTEX(&ec_ecdt->intr.sem);
1299 init_waitqueue_head(&ec_ecdt->intr.wait);
Luming Yu45bea152005-07-23 04:08:00 -04001300 status = acpi_walk_resources(handle, METHOD_NAME__CRS,
Len Brown50526df2005-08-11 17:32:05 -04001301 acpi_ec_io_ports, ec_ecdt);
Luming Yu45bea152005-07-23 04:08:00 -04001302 if (ACPI_FAILURE(status))
1303 return status;
1304 ec_ecdt->common.status_addr = ec_ecdt->common.command_addr;
1305
1306 ec_ecdt->common.uid = -1;
1307 acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->common.uid);
1308
Len Brown50526df2005-08-11 17:32:05 -04001309 status =
1310 acpi_evaluate_integer(handle, "_GPE", NULL,
1311 &ec_ecdt->common.gpe_bit);
Luming Yu45bea152005-07-23 04:08:00 -04001312 if (ACPI_FAILURE(status))
1313 return status;
1314 ec_ecdt->common.global_lock = TRUE;
1315 ec_ecdt->common.handle = handle;
1316
Len Brown50526df2005-08-11 17:32:05 -04001317 printk(KERN_INFO PREFIX "GPE=0x%02x, ports=0x%2x, 0x%2x\n",
1318 (u32) ec_ecdt->common.gpe_bit,
1319 (u32) ec_ecdt->common.command_addr.address,
1320 (u32) ec_ecdt->common.data_addr.address);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321
1322 return AE_CTRL_TERMINATE;
1323}
1324
1325/*
1326 * Some BIOS (such as some from Gateway laptops) access EC region very early
1327 * such as in BAT0._INI or EC._INI before an EC device is found and
1328 * do not provide an ECDT. According to ACPI spec, ECDT isn't mandatorily
1329 * required, but if EC regison is accessed early, it is required.
1330 * The routine tries to workaround the BIOS bug by pre-scan EC device
1331 * It assumes that _CRS, _HID, _GPE, _UID methods of EC don't touch any
1332 * op region (since _REG isn't invoked yet). The assumption is true for
1333 * all systems found.
1334 */
Len Brown50526df2005-08-11 17:32:05 -04001335static int __init acpi_ec_fake_ecdt(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336{
Len Brown50526df2005-08-11 17:32:05 -04001337 acpi_status status;
1338 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339
1340 printk(KERN_INFO PREFIX "Try to make an fake ECDT\n");
1341
Luming Yu45bea152005-07-23 04:08:00 -04001342 ec_ecdt = kmalloc(sizeof(union acpi_ec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 if (!ec_ecdt) {
1344 ret = -ENOMEM;
1345 goto error;
1346 }
Luming Yu45bea152005-07-23 04:08:00 -04001347 memset(ec_ecdt, 0, sizeof(union acpi_ec));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348
Len Brown50526df2005-08-11 17:32:05 -04001349 status = acpi_get_devices(ACPI_EC_HID,
1350 acpi_fake_ecdt_callback, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351 if (ACPI_FAILURE(status)) {
1352 kfree(ec_ecdt);
1353 ec_ecdt = NULL;
1354 ret = -ENODEV;
1355 goto error;
1356 }
1357 return 0;
Len Brown50526df2005-08-11 17:32:05 -04001358 error:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 printk(KERN_ERR PREFIX "Can't make an fake ECDT\n");
1360 return ret;
1361}
1362
Len Brown50526df2005-08-11 17:32:05 -04001363static int __init acpi_ec_get_real_ecdt(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364{
Len Brown02b28a32005-12-05 16:33:04 -05001365 if (acpi_ec_poll_mode)
1366 return acpi_ec_poll_get_real_ecdt();
Luming Yu45bea152005-07-23 04:08:00 -04001367 else
Len Brown02b28a32005-12-05 16:33:04 -05001368 return acpi_ec_intr_get_real_ecdt();
Luming Yu45bea152005-07-23 04:08:00 -04001369}
1370
Len Brown02b28a32005-12-05 16:33:04 -05001371static int __init acpi_ec_poll_get_real_ecdt(void)
Luming Yu45bea152005-07-23 04:08:00 -04001372{
Len Brown50526df2005-08-11 17:32:05 -04001373 acpi_status status;
1374 struct acpi_table_ecdt *ecdt_ptr;
Luming Yu45bea152005-07-23 04:08:00 -04001375
Len Brown50526df2005-08-11 17:32:05 -04001376 status = acpi_get_firmware_table("ECDT", 1, ACPI_LOGICAL_ADDRESSING,
1377 (struct acpi_table_header **)
1378 &ecdt_ptr);
Luming Yu45bea152005-07-23 04:08:00 -04001379 if (ACPI_FAILURE(status))
1380 return -ENODEV;
1381
1382 printk(KERN_INFO PREFIX "Found ECDT\n");
1383
1384 /*
1385 * Generate a temporary ec context to use until the namespace is scanned
1386 */
1387 ec_ecdt = kmalloc(sizeof(union acpi_ec), GFP_KERNEL);
1388 if (!ec_ecdt)
1389 return -ENOMEM;
1390 memset(ec_ecdt, 0, sizeof(union acpi_ec));
1391
1392 ec_ecdt->common.command_addr = ecdt_ptr->ec_control;
1393 ec_ecdt->common.status_addr = ecdt_ptr->ec_control;
1394 ec_ecdt->common.data_addr = ecdt_ptr->ec_data;
1395 ec_ecdt->common.gpe_bit = ecdt_ptr->gpe_bit;
Rich Townsendf9a6ee1a2005-12-19 23:07:00 -05001396 init_MUTEX(&ec_ecdt->poll.sem);
Luming Yu45bea152005-07-23 04:08:00 -04001397 /* use the GL just to be safe */
1398 ec_ecdt->common.global_lock = TRUE;
1399 ec_ecdt->common.uid = ecdt_ptr->uid;
1400
Len Brown50526df2005-08-11 17:32:05 -04001401 status =
1402 acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->common.handle);
Luming Yu45bea152005-07-23 04:08:00 -04001403 if (ACPI_FAILURE(status)) {
1404 goto error;
1405 }
1406
1407 return 0;
Len Brown50526df2005-08-11 17:32:05 -04001408 error:
Luming Yu45bea152005-07-23 04:08:00 -04001409 printk(KERN_ERR PREFIX "Could not use ECDT\n");
1410 kfree(ec_ecdt);
1411 ec_ecdt = NULL;
1412
1413 return -ENODEV;
1414}
1415
Len Brown02b28a32005-12-05 16:33:04 -05001416static int __init acpi_ec_intr_get_real_ecdt(void)
Luming Yu45bea152005-07-23 04:08:00 -04001417{
Len Brown50526df2005-08-11 17:32:05 -04001418 acpi_status status;
1419 struct acpi_table_ecdt *ecdt_ptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420
Dmitry Torokhov451566f2005-03-19 01:10:05 -05001421 status = acpi_get_firmware_table("ECDT", 1, ACPI_LOGICAL_ADDRESSING,
Len Brown50526df2005-08-11 17:32:05 -04001422 (struct acpi_table_header **)
1423 &ecdt_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 if (ACPI_FAILURE(status))
1425 return -ENODEV;
1426
1427 printk(KERN_INFO PREFIX "Found ECDT\n");
1428
1429 /*
1430 * Generate a temporary ec context to use until the namespace is scanned
1431 */
Luming Yu45bea152005-07-23 04:08:00 -04001432 ec_ecdt = kmalloc(sizeof(union acpi_ec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433 if (!ec_ecdt)
1434 return -ENOMEM;
Luming Yu45bea152005-07-23 04:08:00 -04001435 memset(ec_ecdt, 0, sizeof(union acpi_ec));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436
Len Brown02b28a32005-12-05 16:33:04 -05001437 init_MUTEX(&ec_ecdt->intr.sem);
1438 init_waitqueue_head(&ec_ecdt->intr.wait);
Luming Yu45bea152005-07-23 04:08:00 -04001439 ec_ecdt->common.command_addr = ecdt_ptr->ec_control;
1440 ec_ecdt->common.status_addr = ecdt_ptr->ec_control;
1441 ec_ecdt->common.data_addr = ecdt_ptr->ec_data;
1442 ec_ecdt->common.gpe_bit = ecdt_ptr->gpe_bit;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443 /* use the GL just to be safe */
Luming Yu45bea152005-07-23 04:08:00 -04001444 ec_ecdt->common.global_lock = TRUE;
1445 ec_ecdt->common.uid = ecdt_ptr->uid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446
Len Brown50526df2005-08-11 17:32:05 -04001447 status =
1448 acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->common.handle);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 if (ACPI_FAILURE(status)) {
1450 goto error;
1451 }
1452
1453 return 0;
Len Brown50526df2005-08-11 17:32:05 -04001454 error:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455 printk(KERN_ERR PREFIX "Could not use ECDT\n");
1456 kfree(ec_ecdt);
1457 ec_ecdt = NULL;
1458
1459 return -ENODEV;
1460}
1461
1462static int __initdata acpi_fake_ecdt_enabled;
Len Brown50526df2005-08-11 17:32:05 -04001463int __init acpi_ec_ecdt_probe(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464{
Len Brown50526df2005-08-11 17:32:05 -04001465 acpi_status status;
1466 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467
1468 ret = acpi_ec_get_real_ecdt();
1469 /* Try to make a fake ECDT */
1470 if (ret && acpi_fake_ecdt_enabled) {
1471 ret = acpi_ec_fake_ecdt();
1472 }
1473
1474 if (ret)
1475 return 0;
1476
1477 /*
1478 * Install GPE handler
1479 */
Luming Yu45bea152005-07-23 04:08:00 -04001480 status = acpi_install_gpe_handler(NULL, ec_ecdt->common.gpe_bit,
Len Brown50526df2005-08-11 17:32:05 -04001481 ACPI_GPE_EDGE_TRIGGERED,
1482 &acpi_ec_gpe_handler, ec_ecdt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483 if (ACPI_FAILURE(status)) {
1484 goto error;
1485 }
Len Brown50526df2005-08-11 17:32:05 -04001486 acpi_set_gpe_type(NULL, ec_ecdt->common.gpe_bit, ACPI_GPE_TYPE_RUNTIME);
1487 acpi_enable_gpe(NULL, ec_ecdt->common.gpe_bit, ACPI_NOT_ISR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488
Len Brown50526df2005-08-11 17:32:05 -04001489 status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
1490 ACPI_ADR_SPACE_EC,
1491 &acpi_ec_space_handler,
1492 &acpi_ec_space_setup,
1493 ec_ecdt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494 if (ACPI_FAILURE(status)) {
Luming Yu45bea152005-07-23 04:08:00 -04001495 acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit,
Len Brown50526df2005-08-11 17:32:05 -04001496 &acpi_ec_gpe_handler);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497 goto error;
1498 }
1499
1500 return 0;
1501
Len Brown50526df2005-08-11 17:32:05 -04001502 error:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 printk(KERN_ERR PREFIX "Could not use ECDT\n");
1504 kfree(ec_ecdt);
1505 ec_ecdt = NULL;
1506
1507 return -ENODEV;
1508}
1509
Len Brown50526df2005-08-11 17:32:05 -04001510static int __init acpi_ec_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511{
Len Brown50526df2005-08-11 17:32:05 -04001512 int result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514
1515 if (acpi_disabled)
Patrick Mocheld550d982006-06-27 00:41:40 -04001516 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517
1518 acpi_ec_dir = proc_mkdir(ACPI_EC_CLASS, acpi_root_dir);
1519 if (!acpi_ec_dir)
Patrick Mocheld550d982006-06-27 00:41:40 -04001520 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521
1522 /* Now register the driver for the EC */
1523 result = acpi_bus_register_driver(&acpi_ec_driver);
1524 if (result < 0) {
1525 remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir);
Patrick Mocheld550d982006-06-27 00:41:40 -04001526 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 }
1528
Patrick Mocheld550d982006-06-27 00:41:40 -04001529 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530}
1531
1532subsys_initcall(acpi_ec_init);
1533
1534/* EC driver currently not unloadable */
1535#if 0
Len Brown50526df2005-08-11 17:32:05 -04001536static void __exit acpi_ec_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538
1539 acpi_bus_unregister_driver(&acpi_ec_driver);
1540
1541 remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir);
1542
Patrick Mocheld550d982006-06-27 00:41:40 -04001543 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544}
Len Brown50526df2005-08-11 17:32:05 -04001545#endif /* 0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546
1547static int __init acpi_fake_ecdt_setup(char *str)
1548{
1549 acpi_fake_ecdt_enabled = 1;
OGAWA Hirofumi9b410462006-03-31 02:30:33 -08001550 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551}
Luming Yu7b15f5e2005-08-03 17:38:04 -04001552
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553__setup("acpi_fake_ecdt", acpi_fake_ecdt_setup);
Len Brown02b28a32005-12-05 16:33:04 -05001554static int __init acpi_ec_set_intr_mode(char *str)
Luming Yu45bea152005-07-23 04:08:00 -04001555{
Len Brown02b28a32005-12-05 16:33:04 -05001556 int intr;
Luming Yu7b15f5e2005-08-03 17:38:04 -04001557
Len Brown02b28a32005-12-05 16:33:04 -05001558 if (!get_option(&str, &intr))
Luming Yu7b15f5e2005-08-03 17:38:04 -04001559 return 0;
1560
Len Brown02b28a32005-12-05 16:33:04 -05001561 if (intr) {
1562 acpi_ec_poll_mode = EC_INTR;
1563 acpi_ec_driver.ops.add = acpi_ec_intr_add;
Luming Yu7b15f5e2005-08-03 17:38:04 -04001564 } else {
Len Brown02b28a32005-12-05 16:33:04 -05001565 acpi_ec_poll_mode = EC_POLL;
1566 acpi_ec_driver.ops.add = acpi_ec_poll_add;
Luming Yu7b15f5e2005-08-03 17:38:04 -04001567 }
Len Brown02b28a32005-12-05 16:33:04 -05001568 printk(KERN_INFO PREFIX "EC %s mode.\n", intr ? "interrupt" : "polling");
OGAWA Hirofumi9b410462006-03-31 02:30:33 -08001569 return 1;
Luming Yu45bea152005-07-23 04:08:00 -04001570}
Len Brown50526df2005-08-11 17:32:05 -04001571
Len Brown53f11d42005-12-05 16:46:36 -05001572__setup("ec_intr=", acpi_ec_set_intr_mode);