aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/i2c_ec.c
diff options
context:
space:
mode:
authorRich Townsend <rhdt@bartol.udel.edu>2006-07-01 11:36:54 -0400
committerLen Brown <len.brown@intel.com>2006-07-01 16:36:14 -0400
commit3f86b83243d59bb50caf5938d284d22e10d082a4 (patch)
treeebc93aff4abae0b3f4aa96c19973782eede3411d /drivers/acpi/i2c_ec.c
parent37672d4c5263d54ee4302f55242f6fd5317b0f9f (diff)
ACPI: add support for Smart Battery
Most batteries today are ACPI "Control Method" batteries, but some models ship with the older "Smart Battery" that requires this code. Rich Townsend and Bruno Ducrot were the original authors. Vladimir Lebedev updated to run on latest kernel. http://bugzilla.kernel.org/show_bug.cgi?id=3734 Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi/i2c_ec.c')
-rw-r--r--drivers/acpi/i2c_ec.c420
1 files changed, 420 insertions, 0 deletions
diff --git a/drivers/acpi/i2c_ec.c b/drivers/acpi/i2c_ec.c
new file mode 100644
index 000000000000..72478a665c8c
--- /dev/null
+++ b/drivers/acpi/i2c_ec.c
@@ -0,0 +1,420 @@
1/*
2 * SMBus driver for ACPI Embedded Controller ($Revision: 1.3 $)
3 *
4 * Copyright (c) 2002, 2005 Ducrot Bruno
5 * Copyright (c) 2005 Rich Townsend (tiny hacks & tweaks)
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation version 2.
10 */
11
12#include <linux/version.h>
13#include <linux/module.h>
14#include <linux/slab.h>
15#include <linux/kernel.h>
16#include <linux/stddef.h>
17#include <linux/sched.h>
18#include <linux/init.h>
19#include <linux/i2c.h>
20#include <linux/acpi.h>
21#include <linux/delay.h>
22
23#include "i2c_ec.h"
24
25#define xudelay(t) udelay(t)
26#define xmsleep(t) msleep(t)
27
28#define ACPI_EC_HC_COMPONENT 0x00080000
29#define ACPI_EC_HC_CLASS "ec_hc_smbus"
30#define ACPI_EC_HC_HID "ACPI0001"
31#define ACPI_EC_HC_DRIVER_NAME "ACPI EC HC smbus driver"
32#define ACPI_EC_HC_DEVICE_NAME "EC HC smbus"
33
34#define _COMPONENT ACPI_EC_HC_COMPONENT
35
36ACPI_MODULE_NAME("acpi_smbus")
37
38static int acpi_ec_hc_add(struct acpi_device *device);
39static int acpi_ec_hc_remove(struct acpi_device *device, int type);
40
41static struct acpi_driver acpi_ec_hc_driver = {
42 .name = ACPI_EC_HC_DRIVER_NAME,
43 .class = ACPI_EC_HC_CLASS,
44 .ids = ACPI_EC_HC_HID,
45 .ops = {
46 .add = acpi_ec_hc_add,
47 .remove = acpi_ec_hc_remove,
48 },
49};
50
51/* Various bit mask for EC_SC (R) */
52#define OBF 0x01
53#define IBF 0x02
54#define CMD 0x08
55#define BURST 0x10
56#define SCI_EVT 0x20
57#define SMI_EVT 0x40
58
59/* Commands for EC_SC (W) */
60#define RD_EC 0x80
61#define WR_EC 0x81
62#define BE_EC 0x82
63#define BD_EC 0x83
64#define QR_EC 0x84
65
66/*
67 * ACPI 2.0 chapter 13 SMBus 2.0 EC register model
68 */
69
70#define ACPI_EC_SMB_PRTCL 0x00 /* protocol, PEC */
71#define ACPI_EC_SMB_STS 0x01 /* status */
72#define ACPI_EC_SMB_ADDR 0x02 /* address */
73#define ACPI_EC_SMB_CMD 0x03 /* command */
74#define ACPI_EC_SMB_DATA 0x04 /* 32 data registers */
75#define ACPI_EC_SMB_BCNT 0x24 /* number of data bytes */
76#define ACPI_EC_SMB_ALRM_A 0x25 /* alarm address */
77#define ACPI_EC_SMB_ALRM_D 0x26 /* 2 bytes alarm data */
78
79#define ACPI_EC_SMB_STS_DONE 0x80
80#define ACPI_EC_SMB_STS_ALRM 0x40
81#define ACPI_EC_SMB_STS_RES 0x20
82#define ACPI_EC_SMB_STS_STATUS 0x1f
83
84#define ACPI_EC_SMB_STATUS_OK 0x00
85#define ACPI_EC_SMB_STATUS_FAIL 0x07
86#define ACPI_EC_SMB_STATUS_DNAK 0x10
87#define ACPI_EC_SMB_STATUS_DERR 0x11
88#define ACPI_EC_SMB_STATUS_CMD_DENY 0x12
89#define ACPI_EC_SMB_STATUS_UNKNOWN 0x13
90#define ACPI_EC_SMB_STATUS_ACC_DENY 0x17
91#define ACPI_EC_SMB_STATUS_TIMEOUT 0x18
92#define ACPI_EC_SMB_STATUS_NOTSUP 0x19
93#define ACPI_EC_SMB_STATUS_BUSY 0x1A
94#define ACPI_EC_SMB_STATUS_PEC 0x1F
95
96#define ACPI_EC_SMB_PRTCL_WRITE 0x00
97#define ACPI_EC_SMB_PRTCL_READ 0x01
98#define ACPI_EC_SMB_PRTCL_QUICK 0x02
99#define ACPI_EC_SMB_PRTCL_BYTE 0x04
100#define ACPI_EC_SMB_PRTCL_BYTE_DATA 0x06
101#define ACPI_EC_SMB_PRTCL_WORD_DATA 0x08
102#define ACPI_EC_SMB_PRTCL_BLOCK_DATA 0x0a
103#define ACPI_EC_SMB_PRTCL_PROC_CALL 0x0c
104#define ACPI_EC_SMB_PRTCL_BLOCK_PROC_CALL 0x0d
105#define ACPI_EC_SMB_PRTCL_I2C_BLOCK_DATA 0x4a
106#define ACPI_EC_SMB_PRTCL_PEC 0x80
107
108/* Length of pre/post transaction sleep (msec) */
109#define ACPI_EC_SMB_TRANSACTION_SLEEP 1
110#define ACPI_EC_SMB_ACCESS_SLEEP1 1
111#define ACPI_EC_SMB_ACCESS_SLEEP2 10
112
113static int acpi_ec_smb_read(struct acpi_ec_smbus *smbus, u8 address, u8 * data)
114{
115 u8 val;
116 int err;
117
118 ACPI_FUNCTION_TRACE("acpi_ec_smb_read");
119
120 err = ec_read(smbus->base + address, &val);
121 if (!err) {
122 *data = val;
123 }
124 xmsleep(ACPI_EC_SMB_TRANSACTION_SLEEP);
125 return (err);
126}
127
128static int acpi_ec_smb_write(struct acpi_ec_smbus *smbus, u8 address, u8 data)
129{
130 int err;
131
132 ACPI_FUNCTION_TRACE("acpi_ec_smb_write");
133
134 err = ec_write(smbus->base + address, data);
135 return (err);
136}
137
138static int
139acpi_ec_smb_access(struct i2c_adapter *adap, u16 addr, unsigned short flags,
140 char read_write, u8 command, int size,
141 union i2c_smbus_data *data)
142{
143 struct acpi_ec_smbus *smbus = adap->algo_data;
144 unsigned char protocol, len = 0, pec, temp[2] = { 0, 0 };
145 int i;
146
147 ACPI_FUNCTION_TRACE("acpi_ec_smb_access");
148
149 if (read_write == I2C_SMBUS_READ) {
150 protocol = ACPI_EC_SMB_PRTCL_READ;
151 } else {
152 protocol = ACPI_EC_SMB_PRTCL_WRITE;
153 }
154 pec = (flags & I2C_CLIENT_PEC) ? ACPI_EC_SMB_PRTCL_PEC : 0;
155
156 switch (size) {
157
158 case I2C_SMBUS_QUICK:
159 protocol |= ACPI_EC_SMB_PRTCL_QUICK;
160 read_write = I2C_SMBUS_WRITE;
161 break;
162
163 case I2C_SMBUS_BYTE:
164 if (read_write == I2C_SMBUS_WRITE) {
165 acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->byte);
166 }
167 protocol |= ACPI_EC_SMB_PRTCL_BYTE;
168 break;
169
170 case I2C_SMBUS_BYTE_DATA:
171 acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
172 if (read_write == I2C_SMBUS_WRITE) {
173 acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->byte);
174 }
175 protocol |= ACPI_EC_SMB_PRTCL_BYTE_DATA;
176 break;
177
178 case I2C_SMBUS_WORD_DATA:
179 acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
180 if (read_write == I2C_SMBUS_WRITE) {
181 acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->word);
182 acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + 1,
183 data->word >> 8);
184 }
185 protocol |= ACPI_EC_SMB_PRTCL_WORD_DATA | pec;
186 break;
187
188 case I2C_SMBUS_BLOCK_DATA:
189 acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
190 if (read_write == I2C_SMBUS_WRITE) {
191 len = min_t(u8, data->block[0], 32);
192 acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len);
193 for (i = 0; i < len; i++)
194 acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i,
195 data->block[i + 1]);
196 }
197 protocol |= ACPI_EC_SMB_PRTCL_BLOCK_DATA | pec;
198 break;
199
200 case I2C_SMBUS_I2C_BLOCK_DATA:
201 len = min_t(u8, data->block[0], 32);
202 acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
203 acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len);
204 if (read_write == I2C_SMBUS_WRITE) {
205 for (i = 0; i < len; i++) {
206 acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i,
207 data->block[i + 1]);
208 }
209 }
210 protocol |= ACPI_EC_SMB_PRTCL_I2C_BLOCK_DATA;
211 break;
212
213 case I2C_SMBUS_PROC_CALL:
214 acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
215 acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->word);
216 acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + 1, data->word >> 8);
217 protocol = ACPI_EC_SMB_PRTCL_PROC_CALL | pec;
218 read_write = I2C_SMBUS_READ;
219 break;
220
221 case I2C_SMBUS_BLOCK_PROC_CALL:
222 protocol |= pec;
223 len = min_t(u8, data->block[0], 31);
224 acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command);
225 acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len);
226 for (i = 0; i < len; i++)
227 acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i,
228 data->block[i + 1]);
229 protocol = ACPI_EC_SMB_PRTCL_BLOCK_PROC_CALL | pec;
230 read_write = I2C_SMBUS_READ;
231 break;
232
233 default:
234 ACPI_DEBUG_PRINT((ACPI_DB_WARN, "EC SMBus adapter: "
235 "Unsupported transaction %d\n", size));
236 return (-1);
237 }
238
239 acpi_ec_smb_write(smbus, ACPI_EC_SMB_ADDR, addr << 1);
240 acpi_ec_smb_write(smbus, ACPI_EC_SMB_PRTCL, protocol);
241
242 acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0);
243
244 if (~temp[0] & ACPI_EC_SMB_STS_DONE) {
245 xudelay(500);
246 acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0);
247 }
248 if (~temp[0] & ACPI_EC_SMB_STS_DONE) {
249 xmsleep(ACPI_EC_SMB_ACCESS_SLEEP2);
250 acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0);
251 }
252 if ((~temp[0] & ACPI_EC_SMB_STS_DONE)
253 || (temp[0] & ACPI_EC_SMB_STS_STATUS)) {
254 return (-1);
255 }
256
257 if (read_write == I2C_SMBUS_WRITE) {
258 return (0);
259 }
260
261 switch (size) {
262
263 case I2C_SMBUS_BYTE:
264 case I2C_SMBUS_BYTE_DATA:
265 acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA, &data->byte);
266 break;
267
268 case I2C_SMBUS_WORD_DATA:
269 case I2C_SMBUS_PROC_CALL:
270 acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA, temp + 0);
271 acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA + 1, temp + 1);
272 data->word = (temp[1] << 8) | temp[0];
273 break;
274
275 case I2C_SMBUS_BLOCK_DATA:
276 case I2C_SMBUS_BLOCK_PROC_CALL:
277 len = 0;
278 acpi_ec_smb_read(smbus, ACPI_EC_SMB_BCNT, &len);
279 len = min_t(u8, len, 32);
280 case I2C_SMBUS_I2C_BLOCK_DATA:
281 for (i = 0; i < len; i++)
282 acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA + i,
283 data->block + i + 1);
284 data->block[0] = len;
285 break;
286 }
287
288 return (0);
289}
290
291static u32 acpi_ec_smb_func(struct i2c_adapter *adapter)
292{
293 ACPI_FUNCTION_TRACE("acpi_ec_smb_func");
294
295 return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
296 I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
297 I2C_FUNC_SMBUS_BLOCK_DATA |
298 I2C_FUNC_SMBUS_PROC_CALL |
299 I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
300 I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_HWPEC_CALC);
301}
302
303static struct i2c_algorithm acpi_ec_smbus_algorithm = {
304 .smbus_xfer = acpi_ec_smb_access,
305 .functionality = acpi_ec_smb_func,
306};
307
308static int acpi_ec_hc_add(struct acpi_device *device)
309{
310 int status;
311 unsigned long val;
312 struct acpi_ec_hc *ec_hc;
313 struct acpi_ec_smbus *smbus;
314
315 ACPI_FUNCTION_TRACE("acpi_ec_hc_add");
316
317 if (!device) {
318 return_VALUE(-EINVAL);
319 }
320
321 ec_hc = kmalloc(sizeof(struct acpi_ec_hc), GFP_KERNEL);
322 if (!ec_hc) {
323 return_VALUE(-ENOMEM);
324 }
325 memset(ec_hc, 0, sizeof(struct acpi_ec_hc));
326
327 smbus = kmalloc(sizeof(struct acpi_ec_smbus), GFP_KERNEL);
328 if (!smbus) {
329 kfree(ec_hc);
330 return_VALUE(-ENOMEM);
331 }
332 memset(smbus, 0, sizeof(struct acpi_ec_smbus));
333
334 ec_hc->handle = device->handle;
335 strcpy(acpi_device_name(device), ACPI_EC_HC_DEVICE_NAME);
336 strcpy(acpi_device_class(device), ACPI_EC_HC_CLASS);
337 acpi_driver_data(device) = ec_hc;
338
339 status = acpi_evaluate_integer(ec_hc->handle, "_EC", NULL, &val);
340 if (ACPI_FAILURE(status)) {
341 ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Error obtaining _EC\n"));
342 kfree(ec_hc->smbus);
343 kfree(smbus);
344 return_VALUE(-EIO);
345 }
346
347 smbus->ec = acpi_driver_data(device->parent);
348 smbus->base = (val & 0xff00ull) >> 8;
349 smbus->alert = val & 0xffull;
350
351 smbus->adapter.owner = THIS_MODULE;
352 smbus->adapter.algo = &acpi_ec_smbus_algorithm;
353 smbus->adapter.algo_data = smbus;
354
355 if (i2c_add_adapter(&smbus->adapter)) {
356 ACPI_DEBUG_PRINT((ACPI_DB_WARN,
357 "EC SMBus adapter: Failed to register adapter\n"));
358 kfree(smbus);
359 kfree(ec_hc);
360 return_VALUE(-EIO);
361 }
362
363 ec_hc->smbus = smbus;
364
365 printk(KERN_INFO PREFIX "%s [%s]\n",
366 acpi_device_name(device), acpi_device_bid(device));
367
368 return_VALUE(AE_OK);
369}
370
371static int acpi_ec_hc_remove(struct acpi_device *device, int type)
372{
373 struct acpi_ec_hc *ec_hc;
374
375 ACPI_FUNCTION_TRACE("acpi_ec_hc_remove");
376
377 if (!device) {
378 return_VALUE(-EINVAL);
379 }
380 ec_hc = acpi_driver_data(device);
381
382 i2c_del_adapter(&ec_hc->smbus->adapter);
383 kfree(ec_hc->smbus);
384 kfree(ec_hc);
385
386 return_VALUE(AE_OK);
387}
388
389static int __init acpi_ec_hc_init(void)
390{
391 int result;
392
393 ACPI_FUNCTION_TRACE("acpi_ec_hc_init");
394 result = acpi_bus_register_driver(&acpi_ec_hc_driver);
395 if (result < 0) {
396 return_VALUE(-ENODEV);
397 }
398 return_VALUE(0);
399}
400
401static void __exit acpi_ec_hc_exit(void)
402{
403 ACPI_FUNCTION_TRACE("acpi_ec_hc_exit");
404 acpi_bus_unregister_driver(&acpi_ec_hc_driver);
405}
406
407struct acpi_ec_hc *acpi_get_ec_hc(struct acpi_device *device)
408{
409 ACPI_FUNCTION_TRACE("acpi_get_ec_hc");
410 return ((struct acpi_ec_hc *)acpi_driver_data(device->parent));
411}
412
413EXPORT_SYMBOL(acpi_get_ec_hc);
414
415module_init(acpi_ec_hc_init);
416module_exit(acpi_ec_hc_exit);
417
418MODULE_LICENSE("GPL");
419MODULE_AUTHOR("Ducrot Bruno");
420MODULE_DESCRIPTION("ACPI EC SMBus driver");