Merge branch 'for-linus' of git://git.kernel.dk/linux-2.6-block
[sfrench/cifs-2.6.git] / drivers / acpi / sbshc.c
1 /*
2  * SMBus driver for ACPI Embedded Controller (v0.1)
3  *
4  * Copyright (c) 2007 Alexey Starikovskiy
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation version 2.
9  */
10
11 #include <acpi/acpi_bus.h>
12 #include <acpi/acpi_drivers.h>
13 #include <linux/wait.h>
14 #include <linux/delay.h>
15 #include <linux/interrupt.h>
16 #include "sbshc.h"
17
18 #define PREFIX "ACPI: "
19
20 #define ACPI_SMB_HC_CLASS       "smbus_host_controller"
21 #define ACPI_SMB_HC_DEVICE_NAME "ACPI SMBus HC"
22
23 struct acpi_smb_hc {
24         struct acpi_ec *ec;
25         struct mutex lock;
26         wait_queue_head_t wait;
27         u8 offset;
28         u8 query_bit;
29         smbus_alarm_callback callback;
30         void *context;
31 };
32
33 static int acpi_smbus_hc_add(struct acpi_device *device);
34 static int acpi_smbus_hc_remove(struct acpi_device *device, int type);
35
36 static const struct acpi_device_id sbs_device_ids[] = {
37         {"ACPI0001", 0},
38         {"ACPI0005", 0},
39         {"", 0},
40 };
41
42 MODULE_DEVICE_TABLE(acpi, sbs_device_ids);
43
44 static struct acpi_driver acpi_smb_hc_driver = {
45         .name = "smbus_hc",
46         .class = ACPI_SMB_HC_CLASS,
47         .ids = sbs_device_ids,
48         .ops = {
49                 .add = acpi_smbus_hc_add,
50                 .remove = acpi_smbus_hc_remove,
51                 },
52 };
53
54 union acpi_smb_status {
55         u8 raw;
56         struct {
57                 u8 status:5;
58                 u8 reserved:1;
59                 u8 alarm:1;
60                 u8 done:1;
61         } fields;
62 };
63
64 enum acpi_smb_status_codes {
65         SMBUS_OK = 0,
66         SMBUS_UNKNOWN_FAILURE = 0x07,
67         SMBUS_DEVICE_ADDRESS_NACK = 0x10,
68         SMBUS_DEVICE_ERROR = 0x11,
69         SMBUS_DEVICE_COMMAND_ACCESS_DENIED = 0x12,
70         SMBUS_UNKNOWN_ERROR = 0x13,
71         SMBUS_DEVICE_ACCESS_DENIED = 0x17,
72         SMBUS_TIMEOUT = 0x18,
73         SMBUS_HOST_UNSUPPORTED_PROTOCOL = 0x19,
74         SMBUS_BUSY = 0x1a,
75         SMBUS_PEC_ERROR = 0x1f,
76 };
77
78 enum acpi_smb_offset {
79         ACPI_SMB_PROTOCOL = 0,  /* protocol, PEC */
80         ACPI_SMB_STATUS = 1,    /* status */
81         ACPI_SMB_ADDRESS = 2,   /* address */
82         ACPI_SMB_COMMAND = 3,   /* command */
83         ACPI_SMB_DATA = 4,      /* 32 data registers */
84         ACPI_SMB_BLOCK_COUNT = 0x24,    /* number of data bytes */
85         ACPI_SMB_ALARM_ADDRESS = 0x25,  /* alarm address */
86         ACPI_SMB_ALARM_DATA = 0x26,     /* 2 bytes alarm data */
87 };
88
89 static inline int smb_hc_read(struct acpi_smb_hc *hc, u8 address, u8 *data)
90 {
91         return ec_read(hc->offset + address, data);
92 }
93
94 static inline int smb_hc_write(struct acpi_smb_hc *hc, u8 address, u8 data)
95 {
96         return ec_write(hc->offset + address, data);
97 }
98
99 static inline int smb_check_done(struct acpi_smb_hc *hc)
100 {
101         union acpi_smb_status status = {.raw = 0};
102         smb_hc_read(hc, ACPI_SMB_STATUS, &status.raw);
103         return status.fields.done && (status.fields.status == SMBUS_OK);
104 }
105
106 static int wait_transaction_complete(struct acpi_smb_hc *hc, int timeout)
107 {
108         if (wait_event_timeout(hc->wait, smb_check_done(hc),
109                                msecs_to_jiffies(timeout)))
110                 return 0;
111         /*
112          * After the timeout happens, OS will try to check the status of SMbus.
113          * If the status is what OS expected, it will be regarded as the bogus
114          * timeout.
115          */
116         if (smb_check_done(hc))
117                 return 0;
118         else
119                 return -ETIME;
120 }
121
122 static int acpi_smbus_transaction(struct acpi_smb_hc *hc, u8 protocol,
123                                   u8 address, u8 command, u8 *data, u8 length)
124 {
125         int ret = -EFAULT, i;
126         u8 temp, sz = 0;
127
128         if (!hc) {
129                 printk(KERN_ERR PREFIX "host controller is not configured\n");
130                 return ret;
131         }
132
133         mutex_lock(&hc->lock);
134         if (smb_hc_read(hc, ACPI_SMB_PROTOCOL, &temp))
135                 goto end;
136         if (temp) {
137                 ret = -EBUSY;
138                 goto end;
139         }
140         smb_hc_write(hc, ACPI_SMB_COMMAND, command);
141         if (!(protocol & 0x01)) {
142                 smb_hc_write(hc, ACPI_SMB_BLOCK_COUNT, length);
143                 for (i = 0; i < length; ++i)
144                         smb_hc_write(hc, ACPI_SMB_DATA + i, data[i]);
145         }
146         smb_hc_write(hc, ACPI_SMB_ADDRESS, address << 1);
147         smb_hc_write(hc, ACPI_SMB_PROTOCOL, protocol);
148         /*
149          * Wait for completion. Save the status code, data size,
150          * and data into the return package (if required by the protocol).
151          */
152         ret = wait_transaction_complete(hc, 1000);
153         if (ret || !(protocol & 0x01))
154                 goto end;
155         switch (protocol) {
156         case SMBUS_RECEIVE_BYTE:
157         case SMBUS_READ_BYTE:
158                 sz = 1;
159                 break;
160         case SMBUS_READ_WORD:
161                 sz = 2;
162                 break;
163         case SMBUS_READ_BLOCK:
164                 if (smb_hc_read(hc, ACPI_SMB_BLOCK_COUNT, &sz)) {
165                         ret = -EFAULT;
166                         goto end;
167                 }
168                 sz &= 0x1f;
169                 break;
170         }
171         for (i = 0; i < sz; ++i)
172                 smb_hc_read(hc, ACPI_SMB_DATA + i, &data[i]);
173       end:
174         mutex_unlock(&hc->lock);
175         return ret;
176 }
177
178 int acpi_smbus_read(struct acpi_smb_hc *hc, u8 protocol, u8 address,
179                     u8 command, u8 *data)
180 {
181         return acpi_smbus_transaction(hc, protocol, address, command, data, 0);
182 }
183
184 EXPORT_SYMBOL_GPL(acpi_smbus_read);
185
186 int acpi_smbus_write(struct acpi_smb_hc *hc, u8 protocol, u8 address,
187                      u8 command, u8 *data, u8 length)
188 {
189         return acpi_smbus_transaction(hc, protocol, address, command, data, length);
190 }
191
192 EXPORT_SYMBOL_GPL(acpi_smbus_write);
193
194 int acpi_smbus_register_callback(struct acpi_smb_hc *hc,
195                                  smbus_alarm_callback callback, void *context)
196 {
197         mutex_lock(&hc->lock);
198         hc->callback = callback;
199         hc->context = context;
200         mutex_unlock(&hc->lock);
201         return 0;
202 }
203
204 EXPORT_SYMBOL_GPL(acpi_smbus_register_callback);
205
206 int acpi_smbus_unregister_callback(struct acpi_smb_hc *hc)
207 {
208         mutex_lock(&hc->lock);
209         hc->callback = NULL;
210         hc->context = NULL;
211         mutex_unlock(&hc->lock);
212         return 0;
213 }
214
215 EXPORT_SYMBOL_GPL(acpi_smbus_unregister_callback);
216
217 static inline void acpi_smbus_callback(void *context)
218 {
219         struct acpi_smb_hc *hc = context;
220         if (hc->callback)
221                 hc->callback(hc->context);
222 }
223
224 static int smbus_alarm(void *context)
225 {
226         struct acpi_smb_hc *hc = context;
227         union acpi_smb_status status;
228         u8 address;
229         if (smb_hc_read(hc, ACPI_SMB_STATUS, &status.raw))
230                 return 0;
231         /* Check if it is only a completion notify */
232         if (status.fields.done)
233                 wake_up(&hc->wait);
234         if (!status.fields.alarm)
235                 return 0;
236         mutex_lock(&hc->lock);
237         smb_hc_read(hc, ACPI_SMB_ALARM_ADDRESS, &address);
238         status.fields.alarm = 0;
239         smb_hc_write(hc, ACPI_SMB_STATUS, status.raw);
240         /* We are only interested in events coming from known devices */
241         switch (address >> 1) {
242                 case ACPI_SBS_CHARGER:
243                 case ACPI_SBS_MANAGER:
244                 case ACPI_SBS_BATTERY:
245                         acpi_os_execute(OSL_NOTIFY_HANDLER,
246                                         acpi_smbus_callback, hc);
247                 default:;
248         }
249         mutex_unlock(&hc->lock);
250         return 0;
251 }
252
253 typedef int (*acpi_ec_query_func) (void *data);
254
255 extern int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
256                               acpi_handle handle, acpi_ec_query_func func,
257                               void *data);
258
259 static int acpi_smbus_hc_add(struct acpi_device *device)
260 {
261         int status;
262         unsigned long long val;
263         struct acpi_smb_hc *hc;
264
265         if (!device)
266                 return -EINVAL;
267
268         status = acpi_evaluate_integer(device->handle, "_EC", NULL, &val);
269         if (ACPI_FAILURE(status)) {
270                 printk(KERN_ERR PREFIX "error obtaining _EC.\n");
271                 return -EIO;
272         }
273
274         strcpy(acpi_device_name(device), ACPI_SMB_HC_DEVICE_NAME);
275         strcpy(acpi_device_class(device), ACPI_SMB_HC_CLASS);
276
277         hc = kzalloc(sizeof(struct acpi_smb_hc), GFP_KERNEL);
278         if (!hc)
279                 return -ENOMEM;
280         mutex_init(&hc->lock);
281         init_waitqueue_head(&hc->wait);
282
283         hc->ec = acpi_driver_data(device->parent);
284         hc->offset = (val >> 8) & 0xff;
285         hc->query_bit = val & 0xff;
286         device->driver_data = hc;
287
288         acpi_ec_add_query_handler(hc->ec, hc->query_bit, NULL, smbus_alarm, hc);
289         printk(KERN_INFO PREFIX "SBS HC: EC = 0x%p, offset = 0x%0x, query_bit = 0x%0x\n",
290                 hc->ec, hc->offset, hc->query_bit);
291
292         return 0;
293 }
294
295 extern void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit);
296
297 static int acpi_smbus_hc_remove(struct acpi_device *device, int type)
298 {
299         struct acpi_smb_hc *hc;
300
301         if (!device)
302                 return -EINVAL;
303
304         hc = acpi_driver_data(device);
305         acpi_ec_remove_query_handler(hc->ec, hc->query_bit);
306         kfree(hc);
307         device->driver_data = NULL;
308         return 0;
309 }
310
311 static int __init acpi_smb_hc_init(void)
312 {
313         int result;
314
315         result = acpi_bus_register_driver(&acpi_smb_hc_driver);
316         if (result < 0)
317                 return -ENODEV;
318         return 0;
319 }
320
321 static void __exit acpi_smb_hc_exit(void)
322 {
323         acpi_bus_unregister_driver(&acpi_smb_hc_driver);
324 }
325
326 module_init(acpi_smb_hc_init);
327 module_exit(acpi_smb_hc_exit);
328
329 MODULE_LICENSE("GPL");
330 MODULE_AUTHOR("Alexey Starikovskiy");
331 MODULE_DESCRIPTION("ACPI SMBus HC driver");