blob: e5d7963628543edd9eb6d91042d795df11433e19 [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 Townsendf9a6ee12005-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:
Vladimir Lebedev49fee982006-06-20 16:46:00 -0400216 if (~acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) {
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 Townsendf9a6ee12005-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 Townsendf9a6ee12005-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 Townsendf9a6ee12005-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 Townsendf9a6ee12005-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 Townsendf9a6ee12005-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 Townsendf9a6ee12005-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 Townsendf9a6ee12005-12-19 23:07:00 -0500689 if (down_interruptible (&ec->poll.sem)) {
Patrick Mocheld550d982006-06-27 00:41:40 -0400690 return;
Rich Townsendf9a6ee12005-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 Townsendf9a6ee12005-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;
Vladimir Lebedev49fee982006-06-20 16:46:00 -0400785 ec->intr.expect_event = 0;
786 wake_up(&ec->intr.wait);
787 break;
Luming Yu716e0842005-08-10 01:40:00 -0400788 case ACPI_EC_EVENT_IBE:
789 if ((value & ACPI_EC_FLAG_IBF))
790 break;
Len Brown02b28a32005-12-05 16:33:04 -0500791 ec->intr.expect_event = 0;
792 wake_up(&ec->intr.wait);
Vladimir Lebedev49fee982006-06-20 16:46:00 -0400793 break;
Luming Yu716e0842005-08-10 01:40:00 -0400794 default:
795 break;
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500796 }
797
Len Brown50526df2005-08-11 17:32:05 -0400798 if (value & ACPI_EC_FLAG_SCI) {
Len Brown02b28a32005-12-05 16:33:04 -0500799 atomic_add(1, &ec->intr.pending_gpe);
Alexey Starikovskiyb8d35192006-05-05 03:23:00 -0400800 status = acpi_os_execute(OSL_EC_BURST_HANDLER,
Len Brown50526df2005-08-11 17:32:05 -0400801 acpi_ec_gpe_query, ec);
Luming Yu17e9c782005-04-22 23:07:10 -0400802 return status == AE_OK ?
Len Brown50526df2005-08-11 17:32:05 -0400803 ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED;
804 }
Luming Yu45bea152005-07-23 04:08:00 -0400805 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_ISR);
Dmitry Torokhov451566f2005-03-19 01:10:05 -0500806 return status == AE_OK ?
Len Brown50526df2005-08-11 17:32:05 -0400807 ACPI_INTERRUPT_HANDLED : ACPI_INTERRUPT_NOT_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808}
809
810/* --------------------------------------------------------------------------
811 Address Space Management
812 -------------------------------------------------------------------------- */
813
814static acpi_status
Len Brown50526df2005-08-11 17:32:05 -0400815acpi_ec_space_setup(acpi_handle region_handle,
816 u32 function, void *handler_context, void **return_context)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817{
818 /*
819 * The EC object is in the handler context and is needed
820 * when calling the acpi_ec_space_handler.
821 */
Len Brown50526df2005-08-11 17:32:05 -0400822 *return_context = (function != ACPI_REGION_DEACTIVATE) ?
823 handler_context : NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824
825 return AE_OK;
826}
827
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828static acpi_status
Len Brown50526df2005-08-11 17:32:05 -0400829acpi_ec_space_handler(u32 function,
830 acpi_physical_address address,
831 u32 bit_width,
832 acpi_integer * value,
833 void *handler_context, void *region_context)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834{
Len Brown50526df2005-08-11 17:32:05 -0400835 int result = 0;
836 union acpi_ec *ec = NULL;
837 u64 temp = *value;
838 acpi_integer f_v = 0;
839 int i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841
842 if ((address > 0xFF) || !value || !handler_context)
Patrick Mocheld550d982006-06-27 00:41:40 -0400843 return AE_BAD_PARAMETER;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844
Luming Yufa9cd542005-03-19 01:54:47 -0500845 if (bit_width != 8 && acpi_strict) {
Len Brown50526df2005-08-11 17:32:05 -0400846 printk(KERN_WARNING PREFIX
847 "acpi_ec_space_handler: bit_width should be 8\n");
Patrick Mocheld550d982006-06-27 00:41:40 -0400848 return AE_BAD_PARAMETER;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 }
850
Len Brown50526df2005-08-11 17:32:05 -0400851 ec = (union acpi_ec *)handler_context;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852
Len Brown50526df2005-08-11 17:32:05 -0400853 next_byte:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 switch (function) {
855 case ACPI_READ:
Luming Yufa9cd542005-03-19 01:54:47 -0500856 temp = 0;
Len Brown50526df2005-08-11 17:32:05 -0400857 result = acpi_ec_read(ec, (u8) address, (u32 *) & temp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 break;
859 case ACPI_WRITE:
Luming Yufa9cd542005-03-19 01:54:47 -0500860 result = acpi_ec_write(ec, (u8) address, (u8) temp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 break;
862 default:
863 result = -EINVAL;
864 goto out;
865 break;
866 }
867
868 bit_width -= 8;
Luming Yufa9cd542005-03-19 01:54:47 -0500869 if (bit_width) {
870 if (function == ACPI_READ)
871 f_v |= temp << 8 * i;
872 if (function == ACPI_WRITE)
873 temp >>= 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 i++;
Andrew Morton83ea7442005-03-30 22:12:13 -0500875 address++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 goto next_byte;
877 }
878
Luming Yufa9cd542005-03-19 01:54:47 -0500879 if (function == ACPI_READ) {
880 f_v |= temp << 8 * i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 *value = f_v;
882 }
883
Len Brown50526df2005-08-11 17:32:05 -0400884 out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 switch (result) {
886 case -EINVAL:
Patrick Mocheld550d982006-06-27 00:41:40 -0400887 return AE_BAD_PARAMETER;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 break;
889 case -ENODEV:
Patrick Mocheld550d982006-06-27 00:41:40 -0400890 return AE_NOT_FOUND;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 break;
892 case -ETIME:
Patrick Mocheld550d982006-06-27 00:41:40 -0400893 return AE_TIME;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 break;
895 default:
Patrick Mocheld550d982006-06-27 00:41:40 -0400896 return AE_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898}
899
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900/* --------------------------------------------------------------------------
901 FS Interface (/proc)
902 -------------------------------------------------------------------------- */
903
Len Brown50526df2005-08-11 17:32:05 -0400904static struct proc_dir_entry *acpi_ec_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905
Len Brown50526df2005-08-11 17:32:05 -0400906static int acpi_ec_read_info(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907{
Len Brown50526df2005-08-11 17:32:05 -0400908 union acpi_ec *ec = (union acpi_ec *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910
911 if (!ec)
912 goto end;
913
914 seq_printf(seq, "gpe bit: 0x%02x\n",
Len Brown50526df2005-08-11 17:32:05 -0400915 (u32) ec->common.gpe_bit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 seq_printf(seq, "ports: 0x%02x, 0x%02x\n",
Len Brown50526df2005-08-11 17:32:05 -0400917 (u32) ec->common.status_addr.address,
918 (u32) ec->common.data_addr.address);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919 seq_printf(seq, "use global lock: %s\n",
Len Brown50526df2005-08-11 17:32:05 -0400920 ec->common.global_lock ? "yes" : "no");
Luming Yu45bea152005-07-23 04:08:00 -0400921 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922
Len Brown50526df2005-08-11 17:32:05 -0400923 end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400924 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925}
926
927static int acpi_ec_info_open_fs(struct inode *inode, struct file *file)
928{
929 return single_open(file, acpi_ec_read_info, PDE(inode)->data);
930}
931
Arjan van de Vend7508032006-07-04 13:06:00 -0400932static const struct file_operations acpi_ec_info_ops = {
Len Brown50526df2005-08-11 17:32:05 -0400933 .open = acpi_ec_info_open_fs,
934 .read = seq_read,
935 .llseek = seq_lseek,
936 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 .owner = THIS_MODULE,
938};
939
Len Brown50526df2005-08-11 17:32:05 -0400940static int acpi_ec_add_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941{
Len Brown50526df2005-08-11 17:32:05 -0400942 struct proc_dir_entry *entry = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944
945 if (!acpi_device_dir(device)) {
946 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
Len Brown50526df2005-08-11 17:32:05 -0400947 acpi_ec_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 if (!acpi_device_dir(device))
Patrick Mocheld550d982006-06-27 00:41:40 -0400949 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 }
951
952 entry = create_proc_entry(ACPI_EC_FILE_INFO, S_IRUGO,
Len Brown50526df2005-08-11 17:32:05 -0400953 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -0400955 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 else {
957 entry->proc_fops = &acpi_ec_info_ops;
958 entry->data = acpi_driver_data(device);
959 entry->owner = THIS_MODULE;
960 }
961
Patrick Mocheld550d982006-06-27 00:41:40 -0400962 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963}
964
Len Brown50526df2005-08-11 17:32:05 -0400965static int acpi_ec_remove_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967
968 if (acpi_device_dir(device)) {
969 remove_proc_entry(ACPI_EC_FILE_INFO, acpi_device_dir(device));
970 remove_proc_entry(acpi_device_bid(device), acpi_ec_dir);
971 acpi_device_dir(device) = NULL;
972 }
973
Patrick Mocheld550d982006-06-27 00:41:40 -0400974 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975}
976
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977/* --------------------------------------------------------------------------
978 Driver Interface
979 -------------------------------------------------------------------------- */
980
Len Brown02b28a32005-12-05 16:33:04 -0500981static int acpi_ec_poll_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982{
Len Brown50526df2005-08-11 17:32:05 -0400983 int result = 0;
984 acpi_status status = AE_OK;
985 union acpi_ec *ec = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987
988 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -0400989 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990
Luming Yu45bea152005-07-23 04:08:00 -0400991 ec = kmalloc(sizeof(union acpi_ec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992 if (!ec)
Patrick Mocheld550d982006-06-27 00:41:40 -0400993 return -ENOMEM;
Luming Yu45bea152005-07-23 04:08:00 -0400994 memset(ec, 0, sizeof(union acpi_ec));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995
Luming Yu45bea152005-07-23 04:08:00 -0400996 ec->common.handle = device->handle;
997 ec->common.uid = -1;
Rich Townsendf9a6ee12005-12-19 23:07:00 -0500998 init_MUTEX(&ec->poll.sem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME);
1000 strcpy(acpi_device_class(device), ACPI_EC_CLASS);
1001 acpi_driver_data(device) = ec;
1002
1003 /* Use the global lock for all EC transactions? */
Len Brown50526df2005-08-11 17:32:05 -04001004 acpi_evaluate_integer(ec->common.handle, "_GLK", NULL,
1005 &ec->common.global_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006
Jiri Slabyff2fc3e2006-03-28 17:04:00 -05001007 /* XXX we don't test uids, because on some boxes ecdt uid = 0, see:
1008 http://bugzilla.kernel.org/show_bug.cgi?id=6111 */
1009 if (ec_ecdt) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
Len Brown50526df2005-08-11 17:32:05 -04001011 ACPI_ADR_SPACE_EC,
1012 &acpi_ec_space_handler);
1013
1014 acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit,
1015 &acpi_ec_gpe_handler);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016
1017 kfree(ec_ecdt);
1018 }
1019
1020 /* Get GPE bit assignment (EC events). */
1021 /* TODO: Add support for _GPE returning a package */
Len Brown50526df2005-08-11 17:32:05 -04001022 status =
1023 acpi_evaluate_integer(ec->common.handle, "_GPE", NULL,
1024 &ec->common.gpe_bit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -04001026 ACPI_EXCEPTION((AE_INFO, status, "Obtaining GPE bit"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 result = -ENODEV;
1028 goto end;
1029 }
1030
1031 result = acpi_ec_add_fs(device);
1032 if (result)
1033 goto end;
1034
Len Brown02b28a32005-12-05 16:33:04 -05001035 printk(KERN_INFO PREFIX "%s [%s] (gpe %d) polling mode.\n",
Len Brown50526df2005-08-11 17:32:05 -04001036 acpi_device_name(device), acpi_device_bid(device),
1037 (u32) ec->common.gpe_bit);
Luming Yu45bea152005-07-23 04:08:00 -04001038
1039 if (!first_ec)
1040 first_ec = device;
1041
Len Brown50526df2005-08-11 17:32:05 -04001042 end:
Luming Yu45bea152005-07-23 04:08:00 -04001043 if (result)
1044 kfree(ec);
1045
Patrick Mocheld550d982006-06-27 00:41:40 -04001046 return result;
Luming Yu45bea152005-07-23 04:08:00 -04001047}
Len Brown02b28a32005-12-05 16:33:04 -05001048static int acpi_ec_intr_add(struct acpi_device *device)
Luming Yu45bea152005-07-23 04:08:00 -04001049{
Len Brown50526df2005-08-11 17:32:05 -04001050 int result = 0;
1051 acpi_status status = AE_OK;
1052 union acpi_ec *ec = NULL;
Luming Yu45bea152005-07-23 04:08:00 -04001053
Luming Yu45bea152005-07-23 04:08:00 -04001054
1055 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -04001056 return -EINVAL;
Luming Yu45bea152005-07-23 04:08:00 -04001057
1058 ec = kmalloc(sizeof(union acpi_ec), GFP_KERNEL);
1059 if (!ec)
Patrick Mocheld550d982006-06-27 00:41:40 -04001060 return -ENOMEM;
Luming Yu45bea152005-07-23 04:08:00 -04001061 memset(ec, 0, sizeof(union acpi_ec));
1062
1063 ec->common.handle = device->handle;
1064 ec->common.uid = -1;
Len Brown02b28a32005-12-05 16:33:04 -05001065 atomic_set(&ec->intr.pending_gpe, 0);
1066 atomic_set(&ec->intr.leaving_burst, 1);
1067 init_MUTEX(&ec->intr.sem);
1068 init_waitqueue_head(&ec->intr.wait);
Luming Yu45bea152005-07-23 04:08:00 -04001069 strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME);
1070 strcpy(acpi_device_class(device), ACPI_EC_CLASS);
1071 acpi_driver_data(device) = ec;
1072
1073 /* Use the global lock for all EC transactions? */
Len Brown50526df2005-08-11 17:32:05 -04001074 acpi_evaluate_integer(ec->common.handle, "_GLK", NULL,
1075 &ec->common.global_lock);
Luming Yu45bea152005-07-23 04:08:00 -04001076
Jiri Slabyff2fc3e2006-03-28 17:04:00 -05001077 /* XXX we don't test uids, because on some boxes ecdt uid = 0, see:
1078 http://bugzilla.kernel.org/show_bug.cgi?id=6111 */
1079 if (ec_ecdt) {
Luming Yu45bea152005-07-23 04:08:00 -04001080 acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,
Len Brown50526df2005-08-11 17:32:05 -04001081 ACPI_ADR_SPACE_EC,
1082 &acpi_ec_space_handler);
Luming Yu45bea152005-07-23 04:08:00 -04001083
Len Brown50526df2005-08-11 17:32:05 -04001084 acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit,
1085 &acpi_ec_gpe_handler);
Luming Yu45bea152005-07-23 04:08:00 -04001086
1087 kfree(ec_ecdt);
1088 }
1089
1090 /* Get GPE bit assignment (EC events). */
1091 /* TODO: Add support for _GPE returning a package */
Len Brown50526df2005-08-11 17:32:05 -04001092 status =
1093 acpi_evaluate_integer(ec->common.handle, "_GPE", NULL,
1094 &ec->common.gpe_bit);
Luming Yu45bea152005-07-23 04:08:00 -04001095 if (ACPI_FAILURE(status)) {
Len Brown64684632006-06-26 23:41:38 -04001096 printk(KERN_ERR PREFIX "Obtaining GPE bit assignment\n");
Luming Yu45bea152005-07-23 04:08:00 -04001097 result = -ENODEV;
1098 goto end;
1099 }
1100
1101 result = acpi_ec_add_fs(device);
1102 if (result)
1103 goto end;
1104
Len Brown02b28a32005-12-05 16:33:04 -05001105 printk(KERN_INFO PREFIX "%s [%s] (gpe %d) interrupt mode.\n",
Len Brown50526df2005-08-11 17:32:05 -04001106 acpi_device_name(device), acpi_device_bid(device),
1107 (u32) ec->common.gpe_bit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108
1109 if (!first_ec)
1110 first_ec = device;
1111
Len Brown50526df2005-08-11 17:32:05 -04001112 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 if (result)
1114 kfree(ec);
1115
Patrick Mocheld550d982006-06-27 00:41:40 -04001116 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117}
1118
Len Brown50526df2005-08-11 17:32:05 -04001119static int acpi_ec_remove(struct acpi_device *device, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120{
Len Brown50526df2005-08-11 17:32:05 -04001121 union acpi_ec *ec = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123
1124 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -04001125 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126
1127 ec = acpi_driver_data(device);
1128
1129 acpi_ec_remove_fs(device);
1130
1131 kfree(ec);
1132
Patrick Mocheld550d982006-06-27 00:41:40 -04001133 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134}
1135
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136static acpi_status
Len Brown50526df2005-08-11 17:32:05 -04001137acpi_ec_io_ports(struct acpi_resource *resource, void *context)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138{
Len Brown50526df2005-08-11 17:32:05 -04001139 union acpi_ec *ec = (union acpi_ec *)context;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140 struct acpi_generic_address *addr;
1141
Bob Moore50eca3e2005-09-30 19:03:00 -04001142 if (resource->type != ACPI_RESOURCE_TYPE_IO) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 return AE_OK;
1144 }
1145
1146 /*
1147 * The first address region returned is the data port, and
1148 * the second address region returned is the status/command
1149 * port.
1150 */
Luming Yu45bea152005-07-23 04:08:00 -04001151 if (ec->common.data_addr.register_bit_width == 0) {
1152 addr = &ec->common.data_addr;
1153 } else if (ec->common.command_addr.register_bit_width == 0) {
1154 addr = &ec->common.command_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 } else {
1156 return AE_CTRL_TERMINATE;
1157 }
1158
1159 addr->address_space_id = ACPI_ADR_SPACE_SYSTEM_IO;
1160 addr->register_bit_width = 8;
1161 addr->register_bit_offset = 0;
Bob Moore50eca3e2005-09-30 19:03:00 -04001162 addr->address = resource->data.io.minimum;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163
1164 return AE_OK;
1165}
1166
Len Brown50526df2005-08-11 17:32:05 -04001167static int acpi_ec_start(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168{
Len Brown50526df2005-08-11 17:32:05 -04001169 acpi_status status = AE_OK;
1170 union acpi_ec *ec = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172
1173 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -04001174 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175
1176 ec = acpi_driver_data(device);
1177
1178 if (!ec)
Patrick Mocheld550d982006-06-27 00:41:40 -04001179 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180
1181 /*
1182 * Get I/O port addresses. Convert to GAS format.
1183 */
Luming Yu45bea152005-07-23 04:08:00 -04001184 status = acpi_walk_resources(ec->common.handle, METHOD_NAME__CRS,
Len Brown50526df2005-08-11 17:32:05 -04001185 acpi_ec_io_ports, ec);
1186 if (ACPI_FAILURE(status)
1187 || ec->common.command_addr.register_bit_width == 0) {
Len Brown64684632006-06-26 23:41:38 -04001188 printk(KERN_ERR PREFIX "Error getting I/O port addresses\n");
Patrick Mocheld550d982006-06-27 00:41:40 -04001189 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 }
1191
Luming Yu45bea152005-07-23 04:08:00 -04001192 ec->common.status_addr = ec->common.command_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193
1194 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02x, ports=0x%2x,0x%2x\n",
Len Brown50526df2005-08-11 17:32:05 -04001195 (u32) ec->common.gpe_bit,
1196 (u32) ec->common.command_addr.address,
1197 (u32) ec->common.data_addr.address));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198
1199 /*
1200 * Install GPE handler
1201 */
Luming Yu45bea152005-07-23 04:08:00 -04001202 status = acpi_install_gpe_handler(NULL, ec->common.gpe_bit,
Len Brown50526df2005-08-11 17:32:05 -04001203 ACPI_GPE_EDGE_TRIGGERED,
1204 &acpi_ec_gpe_handler, ec);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 if (ACPI_FAILURE(status)) {
Patrick Mocheld550d982006-06-27 00:41:40 -04001206 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207 }
Len Brown50526df2005-08-11 17:32:05 -04001208 acpi_set_gpe_type(NULL, ec->common.gpe_bit, ACPI_GPE_TYPE_RUNTIME);
1209 acpi_enable_gpe(NULL, ec->common.gpe_bit, ACPI_NOT_ISR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210
Len Brown50526df2005-08-11 17:32:05 -04001211 status = acpi_install_address_space_handler(ec->common.handle,
1212 ACPI_ADR_SPACE_EC,
1213 &acpi_ec_space_handler,
1214 &acpi_ec_space_setup, ec);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215 if (ACPI_FAILURE(status)) {
Len Brown50526df2005-08-11 17:32:05 -04001216 acpi_remove_gpe_handler(NULL, ec->common.gpe_bit,
1217 &acpi_ec_gpe_handler);
Patrick Mocheld550d982006-06-27 00:41:40 -04001218 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 }
1220
Patrick Mocheld550d982006-06-27 00:41:40 -04001221 return AE_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222}
1223
Len Brown50526df2005-08-11 17:32:05 -04001224static int acpi_ec_stop(struct acpi_device *device, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225{
Len Brown50526df2005-08-11 17:32:05 -04001226 acpi_status status = AE_OK;
1227 union acpi_ec *ec = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229
1230 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -04001231 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232
1233 ec = acpi_driver_data(device);
1234
Luming Yu45bea152005-07-23 04:08:00 -04001235 status = acpi_remove_address_space_handler(ec->common.handle,
Len Brown50526df2005-08-11 17:32:05 -04001236 ACPI_ADR_SPACE_EC,
1237 &acpi_ec_space_handler);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -04001239 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240
Len Brown50526df2005-08-11 17:32:05 -04001241 status =
1242 acpi_remove_gpe_handler(NULL, ec->common.gpe_bit,
1243 &acpi_ec_gpe_handler);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -04001245 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246
Patrick Mocheld550d982006-06-27 00:41:40 -04001247 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248}
1249
1250static acpi_status __init
Len Brown50526df2005-08-11 17:32:05 -04001251acpi_fake_ecdt_callback(acpi_handle handle,
1252 u32 Level, void *context, void **retval)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253{
Luming Yu45bea152005-07-23 04:08:00 -04001254
Len Brown02b28a32005-12-05 16:33:04 -05001255 if (acpi_ec_poll_mode)
1256 return acpi_fake_ecdt_poll_callback(handle,
Len Brown50526df2005-08-11 17:32:05 -04001257 Level, context, retval);
Luming Yu45bea152005-07-23 04:08:00 -04001258 else
Len Brown02b28a32005-12-05 16:33:04 -05001259 return acpi_fake_ecdt_intr_callback(handle,
Len Brown50526df2005-08-11 17:32:05 -04001260 Level, context, retval);
Luming Yu45bea152005-07-23 04:08:00 -04001261}
1262
1263static acpi_status __init
Len Brown02b28a32005-12-05 16:33:04 -05001264acpi_fake_ecdt_poll_callback(acpi_handle handle,
Len Brown50526df2005-08-11 17:32:05 -04001265 u32 Level, void *context, void **retval)
Luming Yu45bea152005-07-23 04:08:00 -04001266{
Len Brown50526df2005-08-11 17:32:05 -04001267 acpi_status status;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268
1269 status = acpi_walk_resources(handle, METHOD_NAME__CRS,
Len Brown50526df2005-08-11 17:32:05 -04001270 acpi_ec_io_ports, ec_ecdt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271 if (ACPI_FAILURE(status))
1272 return status;
Luming Yu45bea152005-07-23 04:08:00 -04001273 ec_ecdt->common.status_addr = ec_ecdt->common.command_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274
Luming Yu45bea152005-07-23 04:08:00 -04001275 ec_ecdt->common.uid = -1;
1276 acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->common.uid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277
Len Brown50526df2005-08-11 17:32:05 -04001278 status =
1279 acpi_evaluate_integer(handle, "_GPE", NULL,
1280 &ec_ecdt->common.gpe_bit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 if (ACPI_FAILURE(status))
1282 return status;
Rich Townsendf9a6ee12005-12-19 23:07:00 -05001283 init_MUTEX(&ec_ecdt->poll.sem);
Luming Yu45bea152005-07-23 04:08:00 -04001284 ec_ecdt->common.global_lock = TRUE;
1285 ec_ecdt->common.handle = handle;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286
Len Brown50526df2005-08-11 17:32:05 -04001287 printk(KERN_INFO PREFIX "GPE=0x%02x, ports=0x%2x, 0x%2x\n",
1288 (u32) ec_ecdt->common.gpe_bit,
1289 (u32) ec_ecdt->common.command_addr.address,
1290 (u32) ec_ecdt->common.data_addr.address);
Luming Yu45bea152005-07-23 04:08:00 -04001291
1292 return AE_CTRL_TERMINATE;
1293}
1294
1295static acpi_status __init
Len Brown02b28a32005-12-05 16:33:04 -05001296acpi_fake_ecdt_intr_callback(acpi_handle handle,
Len Brown50526df2005-08-11 17:32:05 -04001297 u32 Level, void *context, void **retval)
Luming Yu45bea152005-07-23 04:08:00 -04001298{
Len Brown50526df2005-08-11 17:32:05 -04001299 acpi_status status;
Luming Yu45bea152005-07-23 04:08:00 -04001300
Len Brown02b28a32005-12-05 16:33:04 -05001301 init_MUTEX(&ec_ecdt->intr.sem);
1302 init_waitqueue_head(&ec_ecdt->intr.wait);
Luming Yu45bea152005-07-23 04:08:00 -04001303 status = acpi_walk_resources(handle, METHOD_NAME__CRS,
Len Brown50526df2005-08-11 17:32:05 -04001304 acpi_ec_io_ports, ec_ecdt);
Luming Yu45bea152005-07-23 04:08:00 -04001305 if (ACPI_FAILURE(status))
1306 return status;
1307 ec_ecdt->common.status_addr = ec_ecdt->common.command_addr;
1308
1309 ec_ecdt->common.uid = -1;
1310 acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->common.uid);
1311
Len Brown50526df2005-08-11 17:32:05 -04001312 status =
1313 acpi_evaluate_integer(handle, "_GPE", NULL,
1314 &ec_ecdt->common.gpe_bit);
Luming Yu45bea152005-07-23 04:08:00 -04001315 if (ACPI_FAILURE(status))
1316 return status;
1317 ec_ecdt->common.global_lock = TRUE;
1318 ec_ecdt->common.handle = handle;
1319
Len Brown50526df2005-08-11 17:32:05 -04001320 printk(KERN_INFO PREFIX "GPE=0x%02x, ports=0x%2x, 0x%2x\n",
1321 (u32) ec_ecdt->common.gpe_bit,
1322 (u32) ec_ecdt->common.command_addr.address,
1323 (u32) ec_ecdt->common.data_addr.address);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324
1325 return AE_CTRL_TERMINATE;
1326}
1327
1328/*
1329 * Some BIOS (such as some from Gateway laptops) access EC region very early
1330 * such as in BAT0._INI or EC._INI before an EC device is found and
1331 * do not provide an ECDT. According to ACPI spec, ECDT isn't mandatorily
1332 * required, but if EC regison is accessed early, it is required.
1333 * The routine tries to workaround the BIOS bug by pre-scan EC device
1334 * It assumes that _CRS, _HID, _GPE, _UID methods of EC don't touch any
1335 * op region (since _REG isn't invoked yet). The assumption is true for
1336 * all systems found.
1337 */
Len Brown50526df2005-08-11 17:32:05 -04001338static int __init acpi_ec_fake_ecdt(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339{
Len Brown50526df2005-08-11 17:32:05 -04001340 acpi_status status;
1341 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342
1343 printk(KERN_INFO PREFIX "Try to make an fake ECDT\n");
1344
Luming Yu45bea152005-07-23 04:08:00 -04001345 ec_ecdt = kmalloc(sizeof(union acpi_ec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 if (!ec_ecdt) {
1347 ret = -ENOMEM;
1348 goto error;
1349 }
Luming Yu45bea152005-07-23 04:08:00 -04001350 memset(ec_ecdt, 0, sizeof(union acpi_ec));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351
Len Brown50526df2005-08-11 17:32:05 -04001352 status = acpi_get_devices(ACPI_EC_HID,
1353 acpi_fake_ecdt_callback, NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354 if (ACPI_FAILURE(status)) {
1355 kfree(ec_ecdt);
1356 ec_ecdt = NULL;
1357 ret = -ENODEV;
1358 goto error;
1359 }
1360 return 0;
Len Brown50526df2005-08-11 17:32:05 -04001361 error:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 printk(KERN_ERR PREFIX "Can't make an fake ECDT\n");
1363 return ret;
1364}
1365
Len Brown50526df2005-08-11 17:32:05 -04001366static int __init acpi_ec_get_real_ecdt(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367{
Len Brown02b28a32005-12-05 16:33:04 -05001368 if (acpi_ec_poll_mode)
1369 return acpi_ec_poll_get_real_ecdt();
Luming Yu45bea152005-07-23 04:08:00 -04001370 else
Len Brown02b28a32005-12-05 16:33:04 -05001371 return acpi_ec_intr_get_real_ecdt();
Luming Yu45bea152005-07-23 04:08:00 -04001372}
1373
Len Brown02b28a32005-12-05 16:33:04 -05001374static int __init acpi_ec_poll_get_real_ecdt(void)
Luming Yu45bea152005-07-23 04:08:00 -04001375{
Len Brown50526df2005-08-11 17:32:05 -04001376 acpi_status status;
1377 struct acpi_table_ecdt *ecdt_ptr;
Luming Yu45bea152005-07-23 04:08:00 -04001378
Len Brown50526df2005-08-11 17:32:05 -04001379 status = acpi_get_firmware_table("ECDT", 1, ACPI_LOGICAL_ADDRESSING,
1380 (struct acpi_table_header **)
1381 &ecdt_ptr);
Luming Yu45bea152005-07-23 04:08:00 -04001382 if (ACPI_FAILURE(status))
1383 return -ENODEV;
1384
1385 printk(KERN_INFO PREFIX "Found ECDT\n");
1386
1387 /*
1388 * Generate a temporary ec context to use until the namespace is scanned
1389 */
1390 ec_ecdt = kmalloc(sizeof(union acpi_ec), GFP_KERNEL);
1391 if (!ec_ecdt)
1392 return -ENOMEM;
1393 memset(ec_ecdt, 0, sizeof(union acpi_ec));
1394
1395 ec_ecdt->common.command_addr = ecdt_ptr->ec_control;
1396 ec_ecdt->common.status_addr = ecdt_ptr->ec_control;
1397 ec_ecdt->common.data_addr = ecdt_ptr->ec_data;
1398 ec_ecdt->common.gpe_bit = ecdt_ptr->gpe_bit;
Rich Townsendf9a6ee12005-12-19 23:07:00 -05001399 init_MUTEX(&ec_ecdt->poll.sem);
Luming Yu45bea152005-07-23 04:08:00 -04001400 /* use the GL just to be safe */
1401 ec_ecdt->common.global_lock = TRUE;
1402 ec_ecdt->common.uid = ecdt_ptr->uid;
1403
Len Brown50526df2005-08-11 17:32:05 -04001404 status =
1405 acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->common.handle);
Luming Yu45bea152005-07-23 04:08:00 -04001406 if (ACPI_FAILURE(status)) {
1407 goto error;
1408 }
1409
1410 return 0;
Len Brown50526df2005-08-11 17:32:05 -04001411 error:
Luming Yu45bea152005-07-23 04:08:00 -04001412 printk(KERN_ERR PREFIX "Could not use ECDT\n");
1413 kfree(ec_ecdt);
1414 ec_ecdt = NULL;
1415
1416 return -ENODEV;
1417}
1418
Len Brown02b28a32005-12-05 16:33:04 -05001419static int __init acpi_ec_intr_get_real_ecdt(void)
Luming Yu45bea152005-07-23 04:08:00 -04001420{
Len Brown50526df2005-08-11 17:32:05 -04001421 acpi_status status;
1422 struct acpi_table_ecdt *ecdt_ptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423
Dmitry Torokhov451566f2005-03-19 01:10:05 -05001424 status = acpi_get_firmware_table("ECDT", 1, ACPI_LOGICAL_ADDRESSING,
Len Brown50526df2005-08-11 17:32:05 -04001425 (struct acpi_table_header **)
1426 &ecdt_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 if (ACPI_FAILURE(status))
1428 return -ENODEV;
1429
1430 printk(KERN_INFO PREFIX "Found ECDT\n");
1431
1432 /*
1433 * Generate a temporary ec context to use until the namespace is scanned
1434 */
Luming Yu45bea152005-07-23 04:08:00 -04001435 ec_ecdt = kmalloc(sizeof(union acpi_ec), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436 if (!ec_ecdt)
1437 return -ENOMEM;
Luming Yu45bea152005-07-23 04:08:00 -04001438 memset(ec_ecdt, 0, sizeof(union acpi_ec));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439
Len Brown02b28a32005-12-05 16:33:04 -05001440 init_MUTEX(&ec_ecdt->intr.sem);
1441 init_waitqueue_head(&ec_ecdt->intr.wait);
Luming Yu45bea152005-07-23 04:08:00 -04001442 ec_ecdt->common.command_addr = ecdt_ptr->ec_control;
1443 ec_ecdt->common.status_addr = ecdt_ptr->ec_control;
1444 ec_ecdt->common.data_addr = ecdt_ptr->ec_data;
1445 ec_ecdt->common.gpe_bit = ecdt_ptr->gpe_bit;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 /* use the GL just to be safe */
Luming Yu45bea152005-07-23 04:08:00 -04001447 ec_ecdt->common.global_lock = TRUE;
1448 ec_ecdt->common.uid = ecdt_ptr->uid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449
Len Brown50526df2005-08-11 17:32:05 -04001450 status =
1451 acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->common.handle);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 if (ACPI_FAILURE(status)) {
1453 goto error;
1454 }
1455
1456 return 0;
Len Brown50526df2005-08-11 17:32:05 -04001457 error:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458 printk(KERN_ERR PREFIX "Could not use ECDT\n");
1459 kfree(ec_ecdt);
1460 ec_ecdt = NULL;
1461
1462 return -ENODEV;
1463}
1464
1465static int __initdata acpi_fake_ecdt_enabled;
Len Brown50526df2005-08-11 17:32:05 -04001466int __init acpi_ec_ecdt_probe(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467{
Len Brown50526df2005-08-11 17:32:05 -04001468 acpi_status status;
1469 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001470
1471 ret = acpi_ec_get_real_ecdt();
1472 /* Try to make a fake ECDT */
1473 if (ret && acpi_fake_ecdt_enabled) {
1474 ret = acpi_ec_fake_ecdt();
1475 }
1476
1477 if (ret)
1478 return 0;
1479
1480 /*
1481 * Install GPE handler
1482 */
Luming Yu45bea152005-07-23 04:08:00 -04001483 status = acpi_install_gpe_handler(NULL, ec_ecdt->common.gpe_bit,
Len Brown50526df2005-08-11 17:32:05 -04001484 ACPI_GPE_EDGE_TRIGGERED,
1485 &acpi_ec_gpe_handler, ec_ecdt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486 if (ACPI_FAILURE(status)) {
1487 goto error;
1488 }
Len Brown50526df2005-08-11 17:32:05 -04001489 acpi_set_gpe_type(NULL, ec_ecdt->common.gpe_bit, ACPI_GPE_TYPE_RUNTIME);
1490 acpi_enable_gpe(NULL, ec_ecdt->common.gpe_bit, ACPI_NOT_ISR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491
Len Brown50526df2005-08-11 17:32:05 -04001492 status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
1493 ACPI_ADR_SPACE_EC,
1494 &acpi_ec_space_handler,
1495 &acpi_ec_space_setup,
1496 ec_ecdt);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497 if (ACPI_FAILURE(status)) {
Luming Yu45bea152005-07-23 04:08:00 -04001498 acpi_remove_gpe_handler(NULL, ec_ecdt->common.gpe_bit,
Len Brown50526df2005-08-11 17:32:05 -04001499 &acpi_ec_gpe_handler);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 goto error;
1501 }
1502
1503 return 0;
1504
Len Brown50526df2005-08-11 17:32:05 -04001505 error:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506 printk(KERN_ERR PREFIX "Could not use ECDT\n");
1507 kfree(ec_ecdt);
1508 ec_ecdt = NULL;
1509
1510 return -ENODEV;
1511}
1512
Len Brown50526df2005-08-11 17:32:05 -04001513static int __init acpi_ec_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514{
Len Brown50526df2005-08-11 17:32:05 -04001515 int result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517
1518 if (acpi_disabled)
Patrick Mocheld550d982006-06-27 00:41:40 -04001519 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520
1521 acpi_ec_dir = proc_mkdir(ACPI_EC_CLASS, acpi_root_dir);
1522 if (!acpi_ec_dir)
Patrick Mocheld550d982006-06-27 00:41:40 -04001523 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524
1525 /* Now register the driver for the EC */
1526 result = acpi_bus_register_driver(&acpi_ec_driver);
1527 if (result < 0) {
1528 remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir);
Patrick Mocheld550d982006-06-27 00:41:40 -04001529 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 }
1531
Patrick Mocheld550d982006-06-27 00:41:40 -04001532 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533}
1534
1535subsys_initcall(acpi_ec_init);
1536
1537/* EC driver currently not unloadable */
1538#if 0
Len Brown50526df2005-08-11 17:32:05 -04001539static void __exit acpi_ec_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541
1542 acpi_bus_unregister_driver(&acpi_ec_driver);
1543
1544 remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir);
1545
Patrick Mocheld550d982006-06-27 00:41:40 -04001546 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547}
Len Brown50526df2005-08-11 17:32:05 -04001548#endif /* 0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001549
1550static int __init acpi_fake_ecdt_setup(char *str)
1551{
1552 acpi_fake_ecdt_enabled = 1;
OGAWA Hirofumi9b410462006-03-31 02:30:33 -08001553 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554}
Luming Yu7b15f5e2005-08-03 17:38:04 -04001555
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556__setup("acpi_fake_ecdt", acpi_fake_ecdt_setup);
Len Brown02b28a32005-12-05 16:33:04 -05001557static int __init acpi_ec_set_intr_mode(char *str)
Luming Yu45bea152005-07-23 04:08:00 -04001558{
Len Brown02b28a32005-12-05 16:33:04 -05001559 int intr;
Luming Yu7b15f5e2005-08-03 17:38:04 -04001560
Len Brown02b28a32005-12-05 16:33:04 -05001561 if (!get_option(&str, &intr))
Luming Yu7b15f5e2005-08-03 17:38:04 -04001562 return 0;
1563
Len Brown02b28a32005-12-05 16:33:04 -05001564 if (intr) {
1565 acpi_ec_poll_mode = EC_INTR;
1566 acpi_ec_driver.ops.add = acpi_ec_intr_add;
Luming Yu7b15f5e2005-08-03 17:38:04 -04001567 } else {
Len Brown02b28a32005-12-05 16:33:04 -05001568 acpi_ec_poll_mode = EC_POLL;
1569 acpi_ec_driver.ops.add = acpi_ec_poll_add;
Luming Yu7b15f5e2005-08-03 17:38:04 -04001570 }
Len Brown02b28a32005-12-05 16:33:04 -05001571 printk(KERN_INFO PREFIX "EC %s mode.\n", intr ? "interrupt" : "polling");
OGAWA Hirofumi9b410462006-03-31 02:30:33 -08001572 return 1;
Luming Yu45bea152005-07-23 04:08:00 -04001573}
Len Brown50526df2005-08-11 17:32:05 -04001574
Len Brown53f11d42005-12-05 16:46:36 -05001575__setup("ec_intr=", acpi_ec_set_intr_mode);