ACPI: thinkpad-acpi: improve fan control documentation
[deliverable/linux.git] / drivers / misc / thinkpad_acpi.c
CommitLineData
1da177e4 1/*
643f12db 2 * thinkpad_acpi.c - ThinkPad ACPI Extras
1da177e4
LT
3 *
4 *
78f81cc4 5 * Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>
a62bc916 6 * Copyright (C) 2006-2007 Henrique de Moraes Holschuh <hmh@hmh.eng.br>
1da177e4
LT
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
a62bc916
HMH
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 * 02110-1301, USA.
78f81cc4
BD
22 */
23
643f12db 24#define IBM_VERSION "0.14"
176750d6 25#define TPACPI_SYSFS_VERSION 0x000100
78f81cc4
BD
26
27/*
1da177e4 28 * Changelog:
643f12db
HMH
29 * 2007-03-27 0.14 renamed to thinkpad_acpi and moved to
30 * drivers/misc.
f9ff43a6
HMH
31 *
32 * 2006-11-22 0.13 new maintainer
33 * changelog now lives in git commit history, and will
34 * not be updated further in-file.
837ca6dd 35 *
78f81cc4
BD
36 * 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels
37 * 2005-03-17 0.11 support for 600e, 770x
38 * thanks to Jamie Lentin <lentinj@dial.pipex.com>
39 * support for 770e, G41
40 * G40 and G41 don't have a thinklight
41 * temperatures no longer experimental
42 * experimental brightness control
43 * experimental volume control
44 * experimental fan enable/disable
837ca6dd 45 * 2005-01-16 0.10 fix module loading on R30, R31
78f81cc4
BD
46 * 2005-01-16 0.9 support for 570, R30, R31
47 * ultrabay support on A22p, A3x
48 * limit arg for cmos, led, beep, drop experimental status
49 * more capable led control on A21e, A22p, T20-22, X20
50 * experimental temperatures and fan speed
51 * experimental embedded controller register dump
52 * mark more functions as __init, drop incorrect __exit
53 * use MODULE_VERSION
54 * thanks to Henrik Brix Andersen <brix@gentoo.org>
55 * fix parameter passing on module loading
56 * thanks to Rusty Russell <rusty@rustcorp.com.au>
57 * thanks to Jim Radford <radford@blackbean.org>
58 * 2004-11-08 0.8 fix init error case, don't return from a macro
59 * thanks to Chris Wright <chrisw@osdl.org>
60 * 2004-10-23 0.7 fix module loading on A21e, A22p, T20, T21, X20
61 * fix led control on A21e
62 * 2004-10-19 0.6 use acpi_bus_register_driver() to claim HKEY device
1da177e4
LT
63 * 2004-10-18 0.5 thinklight support on A21e, G40, R32, T20, T21, X20
64 * proc file format changed
65 * video_switch command
66 * experimental cmos control
67 * experimental led control
68 * experimental acpi sounds
78f81cc4
BD
69 * 2004-09-16 0.4 support for module parameters
70 * hotkey mask can be prefixed by 0x
71 * video output switching
72 * video expansion control
73 * ultrabay eject support
74 * removed lcd brightness/on/off control, didn't work
75 * 2004-08-17 0.3 support for R40
76 * lcd off, brightness control
77 * thinklight on/off
78 * 2004-08-14 0.2 support for T series, X20
79 * bluetooth enable/disable
80 * hotkey events disabled by default
81 * removed fan control, currently useless
82 * 2004-08-09 0.1 initial release, support for X series
1da177e4
LT
83 */
84
f21f85de 85#include "thinkpad_acpi.h"
1da177e4 86
f9ff43a6 87MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh");
78f81cc4
BD
88MODULE_DESCRIPTION(IBM_DESC);
89MODULE_VERSION(IBM_VERSION);
90MODULE_LICENSE("GPL");
91
d903ac54
HMH
92/* Please remove this in year 2009 */
93MODULE_ALIAS("ibm_acpi");
94
1da177e4
LT
95#define __unused __attribute__ ((unused))
96
56b6aeb0
HMH
97/****************************************************************************
98 ****************************************************************************
99 *
100 * ACPI Helpers and device model
101 *
102 ****************************************************************************
103 ****************************************************************************/
104
105/*************************************************************************
106 * ACPI basic handles
107 */
1da177e4
LT
108
109static acpi_handle root_handle = NULL;
110
111#define IBM_HANDLE(object, parent, paths...) \
112 static acpi_handle object##_handle; \
113 static acpi_handle *object##_parent = &parent##_handle; \
78f81cc4 114 static char *object##_path; \
1da177e4
LT
115 static char *object##_paths[] = { paths }
116
78f81cc4
BD
117IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */
118 "\\_SB.PCI.ISA.EC", /* 570 */
119 "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */
120 "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */
121 "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */
122 "\\_SB.PCI0.ICH3.EC0", /* R31 */
123 "\\_SB.PCI0.LPC.EC", /* all others */
56b6aeb0 124 );
78f81cc4 125
56b6aeb0
HMH
126IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */
127IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */
78f81cc4 128
56b6aeb0
HMH
129
130/*************************************************************************
131 * Misc ACPI handles
132 */
78f81cc4
BD
133
134IBM_HANDLE(cmos, root, "\\UCMS", /* R50, R50e, R50p, R51, T4x, X31, X40 */
135 "\\CMOS", /* A3x, G4x, R32, T23, T30, X22-24, X30 */
136 "\\CMS", /* R40, R40e */
56b6aeb0 137 ); /* all others */
78f81cc4
BD
138
139IBM_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */
140 "^HKEY", /* R30, R31 */
141 "HKEY", /* all others */
56b6aeb0 142 ); /* 570 */
78f81cc4 143
78f81cc4 144
56b6aeb0
HMH
145/*************************************************************************
146 * ACPI helpers
a8b7a662
HMH
147 */
148
1da177e4
LT
149static int acpi_evalf(acpi_handle handle,
150 void *res, char *method, char *fmt, ...)
151{
152 char *fmt0 = fmt;
78f81cc4
BD
153 struct acpi_object_list params;
154 union acpi_object in_objs[IBM_MAX_ACPI_ARGS];
155 struct acpi_buffer result, *resultp;
156 union acpi_object out_obj;
157 acpi_status status;
158 va_list ap;
159 char res_type;
160 int success;
161 int quiet;
1da177e4
LT
162
163 if (!*fmt) {
164 printk(IBM_ERR "acpi_evalf() called with empty format\n");
165 return 0;
166 }
167
168 if (*fmt == 'q') {
169 quiet = 1;
170 fmt++;
171 } else
172 quiet = 0;
173
174 res_type = *(fmt++);
175
176 params.count = 0;
177 params.pointer = &in_objs[0];
178
179 va_start(ap, fmt);
180 while (*fmt) {
181 char c = *(fmt++);
182 switch (c) {
183 case 'd': /* int */
184 in_objs[params.count].integer.value = va_arg(ap, int);
185 in_objs[params.count++].type = ACPI_TYPE_INTEGER;
186 break;
78f81cc4 187 /* add more types as needed */
1da177e4
LT
188 default:
189 printk(IBM_ERR "acpi_evalf() called "
190 "with invalid format character '%c'\n", c);
191 return 0;
192 }
193 }
194 va_end(ap);
195
78f81cc4
BD
196 if (res_type != 'v') {
197 result.length = sizeof(out_obj);
198 result.pointer = &out_obj;
199 resultp = &result;
200 } else
201 resultp = NULL;
1da177e4 202
78f81cc4 203 status = acpi_evaluate_object(handle, method, &params, resultp);
1da177e4
LT
204
205 switch (res_type) {
78f81cc4 206 case 'd': /* int */
1da177e4
LT
207 if (res)
208 *(int *)res = out_obj.integer.value;
209 success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER;
210 break;
78f81cc4 211 case 'v': /* void */
1da177e4
LT
212 success = status == AE_OK;
213 break;
78f81cc4 214 /* add more types as needed */
1da177e4
LT
215 default:
216 printk(IBM_ERR "acpi_evalf() called "
217 "with invalid format character '%c'\n", res_type);
218 return 0;
219 }
220
56b6aeb0
HMH
221 if (!success && !quiet)
222 printk(IBM_ERR "acpi_evalf(%s, %s, ...) failed: %d\n",
223 method, fmt0, status);
224
225 return success;
226}
227
228static void __unused acpi_print_int(acpi_handle handle, char *method)
229{
230 int i;
231
232 if (acpi_evalf(handle, &i, method, "d"))
233 printk(IBM_INFO "%s = 0x%x\n", method, i);
234 else
235 printk(IBM_ERR "error calling %s\n", method);
236}
237
238static int acpi_ec_read(int i, u8 * p)
239{
240 int v;
241
242 if (ecrd_handle) {
243 if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i))
244 return 0;
245 *p = v;
246 } else {
247 if (ec_read(i, p) < 0)
248 return 0;
249 }
250
251 return 1;
252}
253
254static int acpi_ec_write(int i, u8 v)
255{
256 if (ecwr_handle) {
257 if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v))
258 return 0;
259 } else {
260 if (ec_write(i, v) < 0)
261 return 0;
262 }
263
264 return 1;
265}
266
267static int _sta(acpi_handle handle)
268{
269 int status;
270
271 if (!handle || !acpi_evalf(handle, &status, "_STA", "d"))
272 status = 0;
273
274 return status;
275}
276
c9bea99c
HMH
277static int issue_thinkpad_cmos_command(int cmos_cmd)
278{
279 if (!cmos_handle)
280 return -ENXIO;
281
282 if (!acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd))
283 return -EIO;
284
285 return 0;
286}
287
56b6aeb0
HMH
288/*************************************************************************
289 * ACPI device model
290 */
291
8d376cd6 292static void drv_acpi_handle_init(char *name,
5fba344c
HMH
293 acpi_handle *handle, acpi_handle parent,
294 char **paths, int num_paths, char **path)
56b6aeb0
HMH
295{
296 int i;
297 acpi_status status;
298
299 for (i = 0; i < num_paths; i++) {
300 status = acpi_get_handle(parent, paths[i], handle);
301 if (ACPI_SUCCESS(status)) {
302 *path = paths[i];
303 return;
304 }
305 }
306
307 *handle = NULL;
308}
309
8d376cd6 310static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data)
56b6aeb0
HMH
311{
312 struct ibm_struct *ibm = data;
313
8d376cd6 314 if (!ibm || !ibm->acpi || !ibm->acpi->notify)
56b6aeb0
HMH
315 return;
316
8d376cd6 317 ibm->acpi->notify(ibm, event);
56b6aeb0
HMH
318}
319
8d376cd6 320static int __init setup_acpi_notify(struct ibm_struct *ibm)
56b6aeb0
HMH
321{
322 acpi_status status;
323 int ret;
324
8d376cd6
HMH
325 BUG_ON(!ibm->acpi);
326
327 if (!*ibm->acpi->handle)
56b6aeb0
HMH
328 return 0;
329
fe08bc4b
HMH
330 dbg_printk(TPACPI_DBG_INIT,
331 "setting up ACPI notify for %s\n", ibm->name);
332
8d376cd6 333 ret = acpi_bus_get_device(*ibm->acpi->handle, &ibm->acpi->device);
56b6aeb0
HMH
334 if (ret < 0) {
335 printk(IBM_ERR "%s device not present\n", ibm->name);
336 return -ENODEV;
337 }
338
8d376cd6
HMH
339 acpi_driver_data(ibm->acpi->device) = ibm;
340 sprintf(acpi_device_class(ibm->acpi->device), "%s/%s",
643f12db
HMH
341 IBM_ACPI_EVENT_PREFIX,
342 ibm->name);
56b6aeb0 343
8d376cd6
HMH
344 status = acpi_install_notify_handler(*ibm->acpi->handle,
345 ibm->acpi->type, dispatch_acpi_notify, ibm);
56b6aeb0
HMH
346 if (ACPI_FAILURE(status)) {
347 if (status == AE_ALREADY_EXISTS) {
348 printk(IBM_NOTICE "another device driver is already handling %s events\n",
349 ibm->name);
350 } else {
351 printk(IBM_ERR "acpi_install_notify_handler(%s) failed: %d\n",
352 ibm->name, status);
353 }
354 return -ENODEV;
355 }
8d376cd6 356 ibm->flags.acpi_notify_installed = 1;
56b6aeb0
HMH
357 return 0;
358}
359
8d376cd6 360static int __init tpacpi_device_add(struct acpi_device *device)
56b6aeb0
HMH
361{
362 return 0;
363}
364
6700121b 365static int __init register_tpacpi_subdriver(struct ibm_struct *ibm)
56b6aeb0
HMH
366{
367 int ret;
368
fe08bc4b
HMH
369 dbg_printk(TPACPI_DBG_INIT,
370 "registering %s as an ACPI driver\n", ibm->name);
371
8d376cd6
HMH
372 BUG_ON(!ibm->acpi);
373
374 ibm->acpi->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL);
375 if (!ibm->acpi->driver) {
5fba344c
HMH
376 printk(IBM_ERR "kzalloc(ibm->driver) failed\n");
377 return -ENOMEM;
56b6aeb0
HMH
378 }
379
8d376cd6
HMH
380 sprintf(ibm->acpi->driver->name, "%s_%s", IBM_NAME, ibm->name);
381 ibm->acpi->driver->ids = ibm->acpi->hid;
382 ibm->acpi->driver->ops.add = &tpacpi_device_add;
56b6aeb0 383
8d376cd6 384 ret = acpi_bus_register_driver(ibm->acpi->driver);
56b6aeb0
HMH
385 if (ret < 0) {
386 printk(IBM_ERR "acpi_bus_register_driver(%s) failed: %d\n",
8d376cd6
HMH
387 ibm->acpi->hid, ret);
388 kfree(ibm->acpi->driver);
389 ibm->acpi->driver = NULL;
5fba344c 390 } else if (!ret)
8d376cd6 391 ibm->flags.acpi_driver_registered = 1;
56b6aeb0
HMH
392
393 return ret;
394}
395
396
397/****************************************************************************
398 ****************************************************************************
399 *
400 * Procfs Helpers
401 *
402 ****************************************************************************
403 ****************************************************************************/
404
8d376cd6
HMH
405static int dispatch_procfs_read(char *page, char **start, off_t off,
406 int count, int *eof, void *data)
56b6aeb0
HMH
407{
408 struct ibm_struct *ibm = data;
409 int len;
410
411 if (!ibm || !ibm->read)
412 return -EINVAL;
413
414 len = ibm->read(page);
415 if (len < 0)
416 return len;
417
418 if (len <= off + count)
419 *eof = 1;
420 *start = page + off;
421 len -= off;
422 if (len > count)
423 len = count;
424 if (len < 0)
425 len = 0;
426
427 return len;
428}
429
8d376cd6
HMH
430static int dispatch_procfs_write(struct file *file,
431 const char __user * userbuf,
432 unsigned long count, void *data)
56b6aeb0
HMH
433{
434 struct ibm_struct *ibm = data;
435 char *kernbuf;
436 int ret;
437
438 if (!ibm || !ibm->write)
439 return -EINVAL;
440
441 kernbuf = kmalloc(count + 2, GFP_KERNEL);
442 if (!kernbuf)
443 return -ENOMEM;
1da177e4 444
56b6aeb0
HMH
445 if (copy_from_user(kernbuf, userbuf, count)) {
446 kfree(kernbuf);
447 return -EFAULT;
448 }
1da177e4 449
56b6aeb0
HMH
450 kernbuf[count] = 0;
451 strcat(kernbuf, ",");
452 ret = ibm->write(kernbuf);
453 if (ret == 0)
454 ret = count;
1da177e4 455
56b6aeb0
HMH
456 kfree(kernbuf);
457
458 return ret;
1da177e4
LT
459}
460
461static char *next_cmd(char **cmds)
462{
463 char *start = *cmds;
464 char *end;
465
466 while ((end = strchr(start, ',')) && end == start)
467 start = end + 1;
468
469 if (!end)
470 return NULL;
471
472 *end = 0;
473 *cmds = end + 1;
474 return start;
475}
476
56b6aeb0 477
54ae1501
HMH
478/****************************************************************************
479 ****************************************************************************
480 *
481 * Device model: hwmon and platform
482 *
483 ****************************************************************************
484 ****************************************************************************/
485
486static struct platform_device *tpacpi_pdev = NULL;
487static struct class_device *tpacpi_hwmon = NULL;
488
489static struct platform_driver tpacpi_pdriver = {
490 .driver = {
491 .name = IBM_DRVR_NAME,
492 .owner = THIS_MODULE,
493 },
494};
495
496
176750d6
HMH
497/*************************************************************************
498 * thinkpad-acpi driver attributes
499 */
500
501/* interface_version --------------------------------------------------- */
502static ssize_t tpacpi_driver_interface_version_show(
503 struct device_driver *drv,
504 char *buf)
505{
506 return snprintf(buf, PAGE_SIZE, "0x%08x\n", TPACPI_SYSFS_VERSION);
507}
508
509static DRIVER_ATTR(interface_version, S_IRUGO,
510 tpacpi_driver_interface_version_show, NULL);
511
512/* debug_level --------------------------------------------------------- */
513static ssize_t tpacpi_driver_debug_show(struct device_driver *drv,
514 char *buf)
515{
516 return snprintf(buf, PAGE_SIZE, "0x%04x\n", dbg_level);
517}
518
519static ssize_t tpacpi_driver_debug_store(struct device_driver *drv,
520 const char *buf, size_t count)
521{
522 unsigned long t;
176750d6 523
7252374a 524 if (parse_strtoul(buf, 0xffff, &t))
176750d6
HMH
525 return -EINVAL;
526
527 dbg_level = t;
528
529 return count;
530}
531
532static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO,
533 tpacpi_driver_debug_show, tpacpi_driver_debug_store);
534
535/* version ------------------------------------------------------------- */
536static ssize_t tpacpi_driver_version_show(struct device_driver *drv,
537 char *buf)
538{
539 return snprintf(buf, PAGE_SIZE, "%s v%s\n", IBM_DESC, IBM_VERSION);
540}
541
542static DRIVER_ATTR(version, S_IRUGO,
543 tpacpi_driver_version_show, NULL);
544
545/* --------------------------------------------------------------------- */
546
547static struct driver_attribute* tpacpi_driver_attributes[] = {
548 &driver_attr_debug_level, &driver_attr_version,
549 &driver_attr_interface_version,
550};
551
552static int __init tpacpi_create_driver_attributes(struct device_driver *drv)
553{
554 int i, res;
555
556 i = 0;
557 res = 0;
558 while (!res && i < ARRAY_SIZE(tpacpi_driver_attributes)) {
559 res = driver_create_file(drv, tpacpi_driver_attributes[i]);
560 i++;
561 }
562
563 return res;
564}
565
566static void tpacpi_remove_driver_attributes(struct device_driver *drv)
567{
568 int i;
569
570 for(i = 0; i < ARRAY_SIZE(tpacpi_driver_attributes); i++)
571 driver_remove_file(drv, tpacpi_driver_attributes[i]);
572}
573
7252374a
HMH
574/*************************************************************************
575 * sysfs support helpers
576 */
577
578struct attribute_set_obj {
579 struct attribute_set s;
580 struct attribute *a;
581} __attribute__((packed));
582
583static struct attribute_set *create_attr_set(unsigned int max_members,
584 const char* name)
585{
586 struct attribute_set_obj *sobj;
587
588 if (max_members == 0)
589 return NULL;
590
591 /* Allocates space for implicit NULL at the end too */
592 sobj = kzalloc(sizeof(struct attribute_set_obj) +
593 max_members * sizeof(struct attribute *),
594 GFP_KERNEL);
595 if (!sobj)
596 return NULL;
597 sobj->s.max_members = max_members;
598 sobj->s.group.attrs = &sobj->a;
599 sobj->s.group.name = name;
600
601 return &sobj->s;
602}
603
604/* not multi-threaded safe, use it in a single thread per set */
605static int add_to_attr_set(struct attribute_set* s, struct attribute *attr)
606{
607 if (!s || !attr)
608 return -EINVAL;
609
610 if (s->members >= s->max_members)
611 return -ENOMEM;
612
613 s->group.attrs[s->members] = attr;
614 s->members++;
615
616 return 0;
617}
618
619static int add_many_to_attr_set(struct attribute_set* s,
620 struct attribute **attr,
621 unsigned int count)
622{
623 int i, res;
624
625 for (i = 0; i < count; i++) {
626 res = add_to_attr_set(s, attr[i]);
627 if (res)
628 return res;
629 }
630
631 return 0;
632}
633
634static void delete_attr_set(struct attribute_set* s, struct kobject *kobj)
635{
636 sysfs_remove_group(kobj, &s->group);
637 destroy_attr_set(s);
638}
639
640static int parse_strtoul(const char *buf,
641 unsigned long max, unsigned long *value)
642{
643 char *endp;
644
645 *value = simple_strtoul(buf, &endp, 0);
646 while (*endp && isspace(*endp))
647 endp++;
648 if (*endp || *value > max)
649 return -EINVAL;
650
651 return 0;
652}
653
56b6aeb0
HMH
654/****************************************************************************
655 ****************************************************************************
656 *
657 * Subdrivers
658 *
659 ****************************************************************************
660 ****************************************************************************/
661
662/*************************************************************************
142cfc90 663 * thinkpad-acpi init subdriver
56b6aeb0
HMH
664 */
665
a5763f22 666static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm)
1da177e4
LT
667{
668 printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION);
669 printk(IBM_INFO "%s\n", IBM_URL);
670
3945ac36
HMH
671 if (ibm_thinkpad_ec_found)
672 printk(IBM_INFO "ThinkPad EC firmware %s\n",
673 ibm_thinkpad_ec_found);
674
1da177e4
LT
675 return 0;
676}
677
643f12db 678static int thinkpad_acpi_driver_read(char *p)
1da177e4
LT
679{
680 int len = 0;
681
682 len += sprintf(p + len, "driver:\t\t%s\n", IBM_DESC);
683 len += sprintf(p + len, "version:\t%s\n", IBM_VERSION);
684
685 return len;
686}
687
a5763f22
HMH
688static struct ibm_struct thinkpad_acpi_driver_data = {
689 .name = "driver",
690 .read = thinkpad_acpi_driver_read,
691};
692
56b6aeb0
HMH
693/*************************************************************************
694 * Hotkey subdriver
695 */
696
78f81cc4
BD
697static int hotkey_orig_status;
698static int hotkey_orig_mask;
699
a5763f22 700static int __init hotkey_init(struct ibm_init_struct *iibm)
56b6aeb0 701{
b86c4722
HMH
702 int res;
703
fe08bc4b
HMH
704 vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n");
705
8d376cd6 706 IBM_ACPIHANDLE_INIT(hkey);
40ca9fdf 707 mutex_init(&hotkey_mutex);
5fba344c 708
56b6aeb0 709 /* hotkey not supported on 570 */
d8fd94d9 710 tp_features.hotkey = hkey_handle != NULL;
56b6aeb0 711
fe08bc4b 712 vdbg_printk(TPACPI_DBG_INIT, "hotkeys are %s\n",
d8fd94d9 713 str_supported(tp_features.hotkey));
fe08bc4b 714
d8fd94d9 715 if (tp_features.hotkey) {
56b6aeb0
HMH
716 /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
717 A30, R30, R31, T20-22, X20-21, X22-24 */
d8fd94d9
HMH
718 tp_features.hotkey_mask =
719 acpi_evalf(hkey_handle, NULL, "DHKN", "qv");
56b6aeb0 720
fe08bc4b 721 vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n",
d8fd94d9 722 str_supported(tp_features.hotkey_mask));
fe08bc4b 723
b86c4722
HMH
724 res = hotkey_get(&hotkey_orig_status, &hotkey_orig_mask);
725 if (res)
726 return res;
56b6aeb0
HMH
727 }
728
d8fd94d9 729 return (tp_features.hotkey)? 0 : 1;
56b6aeb0
HMH
730}
731
732static void hotkey_exit(void)
733{
b86c4722
HMH
734 int res;
735
d8fd94d9 736 if (tp_features.hotkey) {
fe08bc4b 737 dbg_printk(TPACPI_DBG_EXIT, "restoring original hotkey mask\n");
b86c4722
HMH
738 res = hotkey_set(hotkey_orig_status, hotkey_orig_mask);
739 if (res)
740 printk(IBM_ERR "failed to restore hotkey to BIOS defaults\n");
5fba344c 741 }
56b6aeb0
HMH
742}
743
744static void hotkey_notify(struct ibm_struct *ibm, u32 event)
745{
746 int hkey;
747
748 if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d"))
8d376cd6 749 acpi_bus_generate_event(ibm->acpi->device, event, hkey);
56b6aeb0
HMH
750 else {
751 printk(IBM_ERR "unknown hotkey event %d\n", event);
8d376cd6 752 acpi_bus_generate_event(ibm->acpi->device, event, 0);
56b6aeb0
HMH
753 }
754}
755
40ca9fdf
HMH
756/*
757 * Call with hotkey_mutex held
758 */
78f81cc4 759static int hotkey_get(int *status, int *mask)
1da177e4
LT
760{
761 if (!acpi_evalf(hkey_handle, status, "DHKC", "d"))
b86c4722 762 return -EIO;
78f81cc4 763
d8fd94d9 764 if (tp_features.hotkey_mask)
78f81cc4 765 if (!acpi_evalf(hkey_handle, mask, "DHKN", "d"))
b86c4722 766 return -EIO;
78f81cc4 767
b86c4722 768 return 0;
1da177e4
LT
769}
770
40ca9fdf
HMH
771/*
772 * Call with hotkey_mutex held
773 */
78f81cc4 774static int hotkey_set(int status, int mask)
1da177e4
LT
775{
776 int i;
777
778 if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status))
b86c4722 779 return -EIO;
1da177e4 780
d8fd94d9 781 if (tp_features.hotkey_mask)
78f81cc4
BD
782 for (i = 0; i < 32; i++) {
783 int bit = ((1 << i) & mask) != 0;
784 if (!acpi_evalf(hkey_handle,
785 NULL, "MHKM", "vdd", i + 1, bit))
b86c4722 786 return -EIO;
78f81cc4 787 }
1da177e4 788
b86c4722 789 return 0;
1da177e4
LT
790}
791
78f81cc4 792static int hotkey_read(char *p)
1da177e4 793{
b86c4722 794 int res, status, mask;
1da177e4
LT
795 int len = 0;
796
d8fd94d9 797 if (!tp_features.hotkey) {
78f81cc4
BD
798 len += sprintf(p + len, "status:\t\tnot supported\n");
799 return len;
800 }
801
40ca9fdf
HMH
802 res = mutex_lock_interruptible(&hotkey_mutex);
803 if (res < 0)
804 return res;
b86c4722 805 res = hotkey_get(&status, &mask);
40ca9fdf 806 mutex_unlock(&hotkey_mutex);
b86c4722
HMH
807 if (res)
808 return res;
1da177e4
LT
809
810 len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0));
d8fd94d9 811 if (tp_features.hotkey_mask) {
1da177e4
LT
812 len += sprintf(p + len, "mask:\t\t0x%04x\n", mask);
813 len += sprintf(p + len,
814 "commands:\tenable, disable, reset, <mask>\n");
815 } else {
816 len += sprintf(p + len, "mask:\t\tnot supported\n");
817 len += sprintf(p + len, "commands:\tenable, disable, reset\n");
818 }
819
820 return len;
821}
822
78f81cc4 823static int hotkey_write(char *buf)
1da177e4 824{
b86c4722 825 int res, status, mask;
1da177e4
LT
826 char *cmd;
827 int do_cmd = 0;
828
d8fd94d9 829 if (!tp_features.hotkey)
1da177e4
LT
830 return -ENODEV;
831
40ca9fdf
HMH
832 res = mutex_lock_interruptible(&hotkey_mutex);
833 if (res < 0)
834 return res;
835
b86c4722
HMH
836 res = hotkey_get(&status, &mask);
837 if (res)
40ca9fdf 838 goto errexit;
78f81cc4 839
40ca9fdf 840 res = 0;
1da177e4
LT
841 while ((cmd = next_cmd(&buf))) {
842 if (strlencmp(cmd, "enable") == 0) {
843 status = 1;
844 } else if (strlencmp(cmd, "disable") == 0) {
845 status = 0;
846 } else if (strlencmp(cmd, "reset") == 0) {
78f81cc4
BD
847 status = hotkey_orig_status;
848 mask = hotkey_orig_mask;
1da177e4
LT
849 } else if (sscanf(cmd, "0x%x", &mask) == 1) {
850 /* mask set */
851 } else if (sscanf(cmd, "%x", &mask) == 1) {
852 /* mask set */
40ca9fdf
HMH
853 } else {
854 res = -EINVAL;
855 goto errexit;
856 }
1da177e4
LT
857 do_cmd = 1;
858 }
859
40ca9fdf 860 if (do_cmd)
b86c4722 861 res = hotkey_set(status, mask);
1da177e4 862
40ca9fdf
HMH
863errexit:
864 mutex_unlock(&hotkey_mutex);
865 return res;
78f81cc4 866}
1da177e4 867
8d376cd6
HMH
868static struct tp_acpi_drv_struct ibm_hotkey_acpidriver = {
869 .hid = IBM_HKEY_HID,
870 .notify = hotkey_notify,
871 .handle = &hkey_handle,
872 .type = ACPI_DEVICE_NOTIFY,
873};
874
a5763f22
HMH
875static struct ibm_struct hotkey_driver_data = {
876 .name = "hotkey",
a5763f22
HMH
877 .read = hotkey_read,
878 .write = hotkey_write,
879 .exit = hotkey_exit,
8d376cd6 880 .acpi = &ibm_hotkey_acpidriver,
a5763f22
HMH
881};
882
56b6aeb0
HMH
883/*************************************************************************
884 * Bluetooth subdriver
885 */
1da177e4 886
a5763f22 887static int __init bluetooth_init(struct ibm_init_struct *iibm)
1da177e4 888{
d6fdd1e9
HMH
889 int status = 0;
890
fe08bc4b
HMH
891 vdbg_printk(TPACPI_DBG_INIT, "initializing bluetooth subdriver\n");
892
8d376cd6 893 IBM_ACPIHANDLE_INIT(hkey);
5fba344c 894
78f81cc4
BD
895 /* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
896 G4x, R30, R31, R40e, R50e, T20-22, X20-21 */
d8fd94d9 897 tp_features.bluetooth = hkey_handle &&
d6fdd1e9
HMH
898 acpi_evalf(hkey_handle, &status, "GBDC", "qd");
899
900 vdbg_printk(TPACPI_DBG_INIT, "bluetooth is %s, status 0x%02x\n",
901 str_supported(tp_features.bluetooth),
902 status);
903
904 if (tp_features.bluetooth &&
905 !(status & TP_ACPI_BLUETOOTH_HWPRESENT)) {
906 /* no bluetooth hardware present in system */
907 tp_features.bluetooth = 0;
908 dbg_printk(TPACPI_DBG_INIT,
909 "bluetooth hardware not installed\n");
910 }
fe08bc4b 911
d8fd94d9 912 return (tp_features.bluetooth)? 0 : 1;
1da177e4
LT
913}
914
d6fdd1e9 915static int bluetooth_get_radiosw(void)
1da177e4
LT
916{
917 int status;
918
d6fdd1e9
HMH
919 if (!tp_features.bluetooth)
920 return -ENODEV;
1da177e4 921
d6fdd1e9
HMH
922 if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
923 return -EIO;
924
925 return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0);
926}
927
928static int bluetooth_set_radiosw(int radio_on)
929{
930 int status;
931
932 if (!tp_features.bluetooth)
933 return -ENODEV;
934
935 if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
936 return -EIO;
937 if (radio_on)
938 status |= TP_ACPI_BLUETOOTH_RADIOSSW;
939 else
940 status &= ~TP_ACPI_BLUETOOTH_RADIOSSW;
941 if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
942 return -EIO;
943
944 return 0;
1da177e4
LT
945}
946
78f81cc4 947static int bluetooth_read(char *p)
1da177e4
LT
948{
949 int len = 0;
d6fdd1e9 950 int status = bluetooth_get_radiosw();
1da177e4 951
d8fd94d9 952 if (!tp_features.bluetooth)
1da177e4 953 len += sprintf(p + len, "status:\t\tnot supported\n");
1da177e4 954 else {
d6fdd1e9
HMH
955 len += sprintf(p + len, "status:\t\t%s\n",
956 (status)? "enabled" : "disabled");
1da177e4
LT
957 len += sprintf(p + len, "commands:\tenable, disable\n");
958 }
959
960 return len;
961}
962
78f81cc4 963static int bluetooth_write(char *buf)
1da177e4 964{
1da177e4 965 char *cmd;
1da177e4 966
d8fd94d9 967 if (!tp_features.bluetooth)
78f81cc4 968 return -ENODEV;
1da177e4
LT
969
970 while ((cmd = next_cmd(&buf))) {
971 if (strlencmp(cmd, "enable") == 0) {
d6fdd1e9 972 bluetooth_set_radiosw(1);
1da177e4 973 } else if (strlencmp(cmd, "disable") == 0) {
d6fdd1e9 974 bluetooth_set_radiosw(0);
1da177e4
LT
975 } else
976 return -EINVAL;
1da177e4
LT
977 }
978
1da177e4
LT
979 return 0;
980}
981
a5763f22
HMH
982static struct ibm_struct bluetooth_driver_data = {
983 .name = "bluetooth",
984 .read = bluetooth_read,
985 .write = bluetooth_write,
986};
987
56b6aeb0
HMH
988/*************************************************************************
989 * Wan subdriver
990 */
991
a5763f22 992static int __init wan_init(struct ibm_init_struct *iibm)
42adb53c 993{
d6fdd1e9
HMH
994 int status = 0;
995
fe08bc4b
HMH
996 vdbg_printk(TPACPI_DBG_INIT, "initializing wan subdriver\n");
997
8d376cd6 998 IBM_ACPIHANDLE_INIT(hkey);
5fba344c 999
d8fd94d9 1000 tp_features.wan = hkey_handle &&
d6fdd1e9
HMH
1001 acpi_evalf(hkey_handle, &status, "GWAN", "qd");
1002
1003 vdbg_printk(TPACPI_DBG_INIT, "wan is %s, status 0x%02x\n",
1004 str_supported(tp_features.wan),
1005 status);
1006
1007 if (tp_features.wan &&
1008 !(status & TP_ACPI_WANCARD_HWPRESENT)) {
1009 /* no wan hardware present in system */
1010 tp_features.wan = 0;
1011 dbg_printk(TPACPI_DBG_INIT,
1012 "wan hardware not installed\n");
1013 }
fe08bc4b 1014
d8fd94d9 1015 return (tp_features.wan)? 0 : 1;
42adb53c
JF
1016}
1017
d6fdd1e9 1018static int wan_get_radiosw(void)
42adb53c
JF
1019{
1020 int status;
1021
d6fdd1e9
HMH
1022 if (!tp_features.wan)
1023 return -ENODEV;
42adb53c 1024
d6fdd1e9
HMH
1025 if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
1026 return -EIO;
1027
1028 return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0);
1029}
1030
1031static int wan_set_radiosw(int radio_on)
1032{
1033 int status;
1034
1035 if (!tp_features.wan)
1036 return -ENODEV;
1037
1038 if (!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
1039 return -EIO;
1040 if (radio_on)
1041 status |= TP_ACPI_WANCARD_RADIOSSW;
1042 else
1043 status &= ~TP_ACPI_WANCARD_RADIOSSW;
1044 if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
1045 return -EIO;
1046
1047 return 0;
42adb53c
JF
1048}
1049
1050static int wan_read(char *p)
1051{
1052 int len = 0;
d6fdd1e9 1053 int status = wan_get_radiosw();
42adb53c 1054
d8fd94d9 1055 if (!tp_features.wan)
42adb53c 1056 len += sprintf(p + len, "status:\t\tnot supported\n");
42adb53c 1057 else {
d6fdd1e9
HMH
1058 len += sprintf(p + len, "status:\t\t%s\n",
1059 (status)? "enabled" : "disabled");
42adb53c
JF
1060 len += sprintf(p + len, "commands:\tenable, disable\n");
1061 }
1062
1063 return len;
1064}
1065
1066static int wan_write(char *buf)
1067{
42adb53c 1068 char *cmd;
42adb53c 1069
d8fd94d9 1070 if (!tp_features.wan)
42adb53c
JF
1071 return -ENODEV;
1072
1073 while ((cmd = next_cmd(&buf))) {
1074 if (strlencmp(cmd, "enable") == 0) {
d6fdd1e9 1075 wan_set_radiosw(1);
42adb53c 1076 } else if (strlencmp(cmd, "disable") == 0) {
d6fdd1e9 1077 wan_set_radiosw(0);
42adb53c
JF
1078 } else
1079 return -EINVAL;
42adb53c
JF
1080 }
1081
42adb53c
JF
1082 return 0;
1083}
1084
a5763f22
HMH
1085static struct ibm_struct wan_driver_data = {
1086 .name = "wan",
1087 .read = wan_read,
1088 .write = wan_write,
92641177 1089 .flags.experimental = 1,
a5763f22
HMH
1090};
1091
56b6aeb0
HMH
1092/*************************************************************************
1093 * Video subdriver
1094 */
1095
9a8e1738
HMH
1096static enum video_access_mode video_supported;
1097static int video_orig_autosw;
78f81cc4 1098
56b6aeb0
HMH
1099IBM_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */
1100 "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */
1101 "\\_SB.PCI0.VID0", /* 770e */
1102 "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */
1103 "\\_SB.PCI0.AGP.VID", /* all others */
1104 ); /* R30, R31 */
1105
1106IBM_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */
1107
a5763f22 1108static int __init video_init(struct ibm_init_struct *iibm)
1da177e4 1109{
78f81cc4
BD
1110 int ivga;
1111
fe08bc4b
HMH
1112 vdbg_printk(TPACPI_DBG_INIT, "initializing video subdriver\n");
1113
8d376cd6
HMH
1114 IBM_ACPIHANDLE_INIT(vid);
1115 IBM_ACPIHANDLE_INIT(vid2);
5fba344c 1116
78f81cc4
BD
1117 if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga)
1118 /* G41, assume IVGA doesn't change */
1119 vid_handle = vid2_handle;
1120
1121 if (!vid_handle)
1122 /* video switching not supported on R30, R31 */
efa27145 1123 video_supported = TPACPI_VIDEO_NONE;
78f81cc4
BD
1124 else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd"))
1125 /* 570 */
efa27145 1126 video_supported = TPACPI_VIDEO_570;
78f81cc4
BD
1127 else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd"))
1128 /* 600e/x, 770e, 770x */
efa27145 1129 video_supported = TPACPI_VIDEO_770;
78f81cc4
BD
1130 else
1131 /* all others */
efa27145 1132 video_supported = TPACPI_VIDEO_NEW;
1da177e4 1133
fe08bc4b
HMH
1134 vdbg_printk(TPACPI_DBG_INIT, "video is %s, mode %d\n",
1135 str_supported(video_supported != TPACPI_VIDEO_NONE),
1136 video_supported);
1137
5fba344c 1138 return (video_supported != TPACPI_VIDEO_NONE)? 0 : 1;
1da177e4
LT
1139}
1140
56b6aeb0
HMH
1141static void video_exit(void)
1142{
83f34724
HMH
1143 dbg_printk(TPACPI_DBG_EXIT,
1144 "restoring original video autoswitch mode\n");
1145 if (video_autosw_set(video_orig_autosw))
1146 printk(IBM_ERR "error while trying to restore original "
1147 "video autoswitch mode\n");
56b6aeb0
HMH
1148}
1149
83f34724 1150static int video_outputsw_get(void)
1da177e4
LT
1151{
1152 int status = 0;
1153 int i;
1154
83f34724
HMH
1155 switch (video_supported) {
1156 case TPACPI_VIDEO_570:
1157 if (!acpi_evalf(NULL, &i, "\\_SB.PHS", "dd",
1158 TP_ACPI_VIDEO_570_PHSCMD))
1159 return -EIO;
1160 status = i & TP_ACPI_VIDEO_570_PHSMASK;
1161 break;
1162 case TPACPI_VIDEO_770:
1163 if (!acpi_evalf(NULL, &i, "\\VCDL", "d"))
1164 return -EIO;
1165 if (i)
1166 status |= TP_ACPI_VIDEO_S_LCD;
1167 if (!acpi_evalf(NULL, &i, "\\VCDC", "d"))
1168 return -EIO;
1169 if (i)
1170 status |= TP_ACPI_VIDEO_S_CRT;
1171 break;
1172 case TPACPI_VIDEO_NEW:
1173 if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1) ||
1174 !acpi_evalf(NULL, &i, "\\VCDC", "d"))
1175 return -EIO;
1176 if (i)
1177 status |= TP_ACPI_VIDEO_S_CRT;
1178
1179 if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0) ||
1180 !acpi_evalf(NULL, &i, "\\VCDL", "d"))
1181 return -EIO;
1182 if (i)
1183 status |= TP_ACPI_VIDEO_S_LCD;
1184 if (!acpi_evalf(NULL, &i, "\\VCDD", "d"))
1185 return -EIO;
1186 if (i)
1187 status |= TP_ACPI_VIDEO_S_DVI;
1188 break;
1189 default:
1190 return -ENOSYS;
78f81cc4
BD
1191 }
1192
1193 return status;
1194}
1da177e4 1195
83f34724 1196static int video_outputsw_set(int status)
78f81cc4 1197{
83f34724
HMH
1198 int autosw;
1199 int res = 0;
1200
1201 switch (video_supported) {
1202 case TPACPI_VIDEO_570:
1203 res = acpi_evalf(NULL, NULL,
1204 "\\_SB.PHS2", "vdd",
1205 TP_ACPI_VIDEO_570_PHS2CMD,
1206 status | TP_ACPI_VIDEO_570_PHS2SET);
1207 break;
1208 case TPACPI_VIDEO_770:
1209 autosw = video_autosw_get();
1210 if (autosw < 0)
1211 return autosw;
1da177e4 1212
83f34724
HMH
1213 res = video_autosw_set(1);
1214 if (res)
1215 return res;
1216 res = acpi_evalf(vid_handle, NULL,
1217 "ASWT", "vdd", status * 0x100, 0);
1218 if (!autosw && video_autosw_set(autosw)) {
1219 printk(IBM_ERR "video auto-switch left enabled due to error\n");
1220 return -EIO;
1221 }
1222 break;
1223 case TPACPI_VIDEO_NEW:
1224 res = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) &&
1225 acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1);
1226 break;
1227 default:
1228 return -ENOSYS;
1229 }
1da177e4 1230
83f34724 1231 return (res)? 0 : -EIO;
1da177e4
LT
1232}
1233
83f34724 1234static int video_autosw_get(void)
78f81cc4 1235{
83f34724 1236 int autosw = 0;
78f81cc4 1237
83f34724
HMH
1238 switch (video_supported) {
1239 case TPACPI_VIDEO_570:
1240 if (!acpi_evalf(vid_handle, &autosw, "SWIT", "d"))
1241 return -EIO;
1242 break;
1243 case TPACPI_VIDEO_770:
1244 case TPACPI_VIDEO_NEW:
1245 if (!acpi_evalf(vid_handle, &autosw, "^VDEE", "d"))
1246 return -EIO;
1247 break;
1248 default:
1249 return -ENOSYS;
1250 }
78f81cc4 1251
83f34724 1252 return autosw & 1;
78f81cc4
BD
1253}
1254
83f34724 1255static int video_autosw_set(int enable)
78f81cc4 1256{
83f34724
HMH
1257 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", (enable)? 1 : 0))
1258 return -EIO;
1259 return 0;
78f81cc4
BD
1260}
1261
83f34724 1262static int video_outputsw_cycle(void)
78f81cc4 1263{
83f34724
HMH
1264 int autosw = video_autosw_get();
1265 int res;
78f81cc4 1266
83f34724
HMH
1267 if (autosw < 0)
1268 return autosw;
78f81cc4 1269
83f34724
HMH
1270 switch (video_supported) {
1271 case TPACPI_VIDEO_570:
1272 res = video_autosw_set(1);
1273 if (res)
1274 return res;
1275 res = acpi_evalf(ec_handle, NULL, "_Q16", "v");
1276 break;
1277 case TPACPI_VIDEO_770:
1278 case TPACPI_VIDEO_NEW:
1279 res = video_autosw_set(1);
1280 if (res)
1281 return res;
1282 res = acpi_evalf(vid_handle, NULL, "VSWT", "v");
1283 break;
1284 default:
1285 return -ENOSYS;
1286 }
1287 if (!autosw && video_autosw_set(autosw)) {
1288 printk(IBM_ERR "video auto-switch left enabled due to error\n");
1289 return -EIO;
78f81cc4
BD
1290 }
1291
83f34724
HMH
1292 return (res)? 0 : -EIO;
1293}
1294
1295static int video_expand_toggle(void)
1296{
1297 switch (video_supported) {
1298 case TPACPI_VIDEO_570:
1299 return acpi_evalf(ec_handle, NULL, "_Q17", "v")?
1300 0 : -EIO;
1301 case TPACPI_VIDEO_770:
1302 return acpi_evalf(vid_handle, NULL, "VEXP", "v")?
1303 0 : -EIO;
1304 case TPACPI_VIDEO_NEW:
1305 return acpi_evalf(NULL, NULL, "\\VEXP", "v")?
1306 0 : -EIO;
1307 default:
1308 return -ENOSYS;
1309 }
1310 /* not reached */
78f81cc4
BD
1311}
1312
56b6aeb0
HMH
1313static int video_read(char *p)
1314{
83f34724 1315 int status, autosw;
56b6aeb0
HMH
1316 int len = 0;
1317
83f34724 1318 if (video_supported == TPACPI_VIDEO_NONE) {
56b6aeb0
HMH
1319 len += sprintf(p + len, "status:\t\tnot supported\n");
1320 return len;
1321 }
1322
83f34724
HMH
1323 status = video_outputsw_get();
1324 if (status < 0)
1325 return status;
1326
1327 autosw = video_autosw_get();
1328 if (autosw < 0)
1329 return autosw;
1330
56b6aeb0
HMH
1331 len += sprintf(p + len, "status:\t\tsupported\n");
1332 len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0));
1333 len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1));
efa27145 1334 if (video_supported == TPACPI_VIDEO_NEW)
56b6aeb0
HMH
1335 len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3));
1336 len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0));
1337 len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n");
1338 len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n");
efa27145 1339 if (video_supported == TPACPI_VIDEO_NEW)
56b6aeb0
HMH
1340 len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n");
1341 len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n");
1342 len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n");
1343
1344 return len;
1345}
1346
78f81cc4 1347static int video_write(char *buf)
1da177e4
LT
1348{
1349 char *cmd;
1350 int enable, disable, status;
83f34724 1351 int res;
1da177e4 1352
83f34724 1353 if (video_supported == TPACPI_VIDEO_NONE)
78f81cc4
BD
1354 return -ENODEV;
1355
83f34724
HMH
1356 enable = 0;
1357 disable = 0;
1da177e4
LT
1358
1359 while ((cmd = next_cmd(&buf))) {
1360 if (strlencmp(cmd, "lcd_enable") == 0) {
83f34724 1361 enable |= TP_ACPI_VIDEO_S_LCD;
1da177e4 1362 } else if (strlencmp(cmd, "lcd_disable") == 0) {
83f34724 1363 disable |= TP_ACPI_VIDEO_S_LCD;
1da177e4 1364 } else if (strlencmp(cmd, "crt_enable") == 0) {
83f34724 1365 enable |= TP_ACPI_VIDEO_S_CRT;
1da177e4 1366 } else if (strlencmp(cmd, "crt_disable") == 0) {
83f34724 1367 disable |= TP_ACPI_VIDEO_S_CRT;
efa27145 1368 } else if (video_supported == TPACPI_VIDEO_NEW &&
78f81cc4 1369 strlencmp(cmd, "dvi_enable") == 0) {
83f34724 1370 enable |= TP_ACPI_VIDEO_S_DVI;
efa27145 1371 } else if (video_supported == TPACPI_VIDEO_NEW &&
78f81cc4 1372 strlencmp(cmd, "dvi_disable") == 0) {
83f34724 1373 disable |= TP_ACPI_VIDEO_S_DVI;
1da177e4 1374 } else if (strlencmp(cmd, "auto_enable") == 0) {
83f34724
HMH
1375 res = video_autosw_set(1);
1376 if (res)
1377 return res;
1da177e4 1378 } else if (strlencmp(cmd, "auto_disable") == 0) {
83f34724
HMH
1379 res = video_autosw_set(0);
1380 if (res)
1381 return res;
1da177e4 1382 } else if (strlencmp(cmd, "video_switch") == 0) {
83f34724
HMH
1383 res = video_outputsw_cycle();
1384 if (res)
1385 return res;
1da177e4 1386 } else if (strlencmp(cmd, "expand_toggle") == 0) {
83f34724
HMH
1387 res = video_expand_toggle();
1388 if (res)
1389 return res;
1da177e4
LT
1390 } else
1391 return -EINVAL;
1392 }
1393
1394 if (enable || disable) {
83f34724
HMH
1395 status = video_outputsw_get();
1396 if (status < 0)
1397 return status;
1398 res = video_outputsw_set((status & ~disable) | enable);
1399 if (res)
1400 return res;
1da177e4
LT
1401 }
1402
1403 return 0;
1404}
1405
a5763f22
HMH
1406static struct ibm_struct video_driver_data = {
1407 .name = "video",
1408 .read = video_read,
1409 .write = video_write,
1410 .exit = video_exit,
1411};
1412
56b6aeb0
HMH
1413/*************************************************************************
1414 * Light (thinklight) subdriver
1415 */
1da177e4 1416
56b6aeb0
HMH
1417IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */
1418IBM_HANDLE(ledb, ec, "LEDB"); /* G4x */
1419
a5763f22 1420static int __init light_init(struct ibm_init_struct *iibm)
1da177e4 1421{
fe08bc4b
HMH
1422 vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n");
1423
8d376cd6
HMH
1424 IBM_ACPIHANDLE_INIT(ledb);
1425 IBM_ACPIHANDLE_INIT(lght);
1426 IBM_ACPIHANDLE_INIT(cmos);
5fba344c 1427
78f81cc4 1428 /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */
d8fd94d9 1429 tp_features.light = (cmos_handle || lght_handle) && !ledb_handle;
78f81cc4 1430
d8fd94d9 1431 if (tp_features.light)
78f81cc4
BD
1432 /* light status not supported on
1433 570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */
d8fd94d9
HMH
1434 tp_features.light_status =
1435 acpi_evalf(ec_handle, NULL, "KBLT", "qv");
1da177e4 1436
fe08bc4b 1437 vdbg_printk(TPACPI_DBG_INIT, "light is %s\n",
d8fd94d9 1438 str_supported(tp_features.light));
fe08bc4b 1439
d8fd94d9 1440 return (tp_features.light)? 0 : 1;
1da177e4
LT
1441}
1442
78f81cc4 1443static int light_read(char *p)
1da177e4
LT
1444{
1445 int len = 0;
1446 int status = 0;
1447
d8fd94d9 1448 if (!tp_features.light) {
78f81cc4 1449 len += sprintf(p + len, "status:\t\tnot supported\n");
d8fd94d9 1450 } else if (!tp_features.light_status) {
78f81cc4
BD
1451 len += sprintf(p + len, "status:\t\tunknown\n");
1452 len += sprintf(p + len, "commands:\ton, off\n");
1453 } else {
1da177e4
LT
1454 if (!acpi_evalf(ec_handle, &status, "KBLT", "d"))
1455 return -EIO;
1456 len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0));
78f81cc4
BD
1457 len += sprintf(p + len, "commands:\ton, off\n");
1458 }
1da177e4
LT
1459
1460 return len;
1461}
1462
78f81cc4 1463static int light_write(char *buf)
1da177e4
LT
1464{
1465 int cmos_cmd, lght_cmd;
1466 char *cmd;
1467 int success;
78f81cc4 1468
d8fd94d9 1469 if (!tp_features.light)
78f81cc4
BD
1470 return -ENODEV;
1471
1da177e4
LT
1472 while ((cmd = next_cmd(&buf))) {
1473 if (strlencmp(cmd, "on") == 0) {
1474 cmos_cmd = 0x0c;
1475 lght_cmd = 1;
1476 } else if (strlencmp(cmd, "off") == 0) {
1477 cmos_cmd = 0x0d;
1478 lght_cmd = 0;
1479 } else
1480 return -EINVAL;
78f81cc4 1481
1da177e4 1482 success = cmos_handle ?
78f81cc4
BD
1483 acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) :
1484 acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd);
1da177e4
LT
1485 if (!success)
1486 return -EIO;
1487 }
1488
1489 return 0;
1490}
1491
a5763f22
HMH
1492static struct ibm_struct light_driver_data = {
1493 .name = "light",
1494 .read = light_read,
1495 .write = light_write,
1496};
1497
56b6aeb0
HMH
1498/*************************************************************************
1499 * Dock subdriver
1500 */
1da177e4 1501
85998248 1502#ifdef CONFIG_THINKPAD_ACPI_DOCK
56b6aeb0
HMH
1503
1504IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */
1505 "\\_SB.PCI0.DOCK", /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */
1506 "\\_SB.PCI0.PCI1.DOCK", /* all others */
1507 "\\_SB.PCI.ISA.SLCE", /* 570 */
1508 ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */
1509
5fba344c
HMH
1510/* don't list other alternatives as we install a notify handler on the 570 */
1511IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */
1512
1da177e4
LT
1513#define dock_docked() (_sta(dock_handle) & 1)
1514
a5763f22 1515static int __init dock_init(struct ibm_init_struct *iibm)
5fba344c 1516{
fe08bc4b
HMH
1517 vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver\n");
1518
8d376cd6
HMH
1519 IBM_ACPIHANDLE_INIT(dock);
1520 IBM_ACPIHANDLE_INIT(pci);
5fba344c 1521
fe08bc4b
HMH
1522 vdbg_printk(TPACPI_DBG_INIT, "dock is %s\n",
1523 str_supported(dock_handle != NULL));
1524
5fba344c
HMH
1525 return (dock_handle)? 0 : 1;
1526}
1527
56b6aeb0
HMH
1528static void dock_notify(struct ibm_struct *ibm, u32 event)
1529{
1530 int docked = dock_docked();
8d376cd6 1531 int pci = ibm->acpi->hid && strstr(ibm->acpi->hid, IBM_PCI_HID);
56b6aeb0
HMH
1532
1533 if (event == 1 && !pci) /* 570 */
8d376cd6 1534 acpi_bus_generate_event(ibm->acpi->device, event, 1); /* button */
56b6aeb0 1535 else if (event == 1 && pci) /* 570 */
8d376cd6 1536 acpi_bus_generate_event(ibm->acpi->device, event, 3); /* dock */
56b6aeb0 1537 else if (event == 3 && docked)
8d376cd6 1538 acpi_bus_generate_event(ibm->acpi->device, event, 1); /* button */
56b6aeb0 1539 else if (event == 3 && !docked)
8d376cd6 1540 acpi_bus_generate_event(ibm->acpi->device, event, 2); /* undock */
56b6aeb0 1541 else if (event == 0 && docked)
8d376cd6 1542 acpi_bus_generate_event(ibm->acpi->device, event, 3); /* dock */
56b6aeb0
HMH
1543 else {
1544 printk(IBM_ERR "unknown dock event %d, status %d\n",
1545 event, _sta(dock_handle));
8d376cd6 1546 acpi_bus_generate_event(ibm->acpi->device, event, 0); /* unknown */
56b6aeb0
HMH
1547 }
1548}
1549
78f81cc4 1550static int dock_read(char *p)
1da177e4
LT
1551{
1552 int len = 0;
1553 int docked = dock_docked();
1554
1555 if (!dock_handle)
1556 len += sprintf(p + len, "status:\t\tnot supported\n");
1557 else if (!docked)
1558 len += sprintf(p + len, "status:\t\tundocked\n");
1559 else {
1560 len += sprintf(p + len, "status:\t\tdocked\n");
1561 len += sprintf(p + len, "commands:\tdock, undock\n");
1562 }
1563
1564 return len;
1565}
1566
78f81cc4 1567static int dock_write(char *buf)
1da177e4
LT
1568{
1569 char *cmd;
1570
1571 if (!dock_docked())
78f81cc4 1572 return -ENODEV;
1da177e4
LT
1573
1574 while ((cmd = next_cmd(&buf))) {
1575 if (strlencmp(cmd, "undock") == 0) {
78f81cc4
BD
1576 if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) ||
1577 !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1))
56b6aeb0
HMH
1578 return -EIO;
1579 } else if (strlencmp(cmd, "dock") == 0) {
1580 if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1))
1581 return -EIO;
1582 } else
1583 return -EINVAL;
1da177e4 1584 }
56b6aeb0
HMH
1585
1586 return 0;
1da177e4 1587}
56b6aeb0 1588
8d376cd6 1589static struct tp_acpi_drv_struct ibm_dock_acpidriver[2] = {
a5763f22 1590 {
a5763f22
HMH
1591 .notify = dock_notify,
1592 .handle = &dock_handle,
1593 .type = ACPI_SYSTEM_NOTIFY,
1594 },
1595 {
a5763f22
HMH
1596 .hid = IBM_PCI_HID,
1597 .notify = dock_notify,
1598 .handle = &pci_handle,
1599 .type = ACPI_SYSTEM_NOTIFY,
1600 },
1601};
1602
8d376cd6
HMH
1603static struct ibm_struct dock_driver_data[2] = {
1604 {
1605 .name = "dock",
1606 .read = dock_read,
1607 .write = dock_write,
1608 .acpi = &ibm_dock_acpidriver[0],
1609 },
1610 {
1611 .name = "dock",
1612 .acpi = &ibm_dock_acpidriver[1],
1613 },
1614};
1615
85998248 1616#endif /* CONFIG_THINKPAD_ACPI_DOCK */
56b6aeb0
HMH
1617
1618/*************************************************************************
1619 * Bay subdriver
1620 */
1da177e4 1621
85998248 1622#ifdef CONFIG_THINKPAD_ACPI_BAY
56b6aeb0
HMH
1623IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */
1624 "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */
1625 "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */
1626 "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */
1627 ); /* A21e, R30, R31 */
1628IBM_HANDLE(bay_ej, bay, "_EJ3", /* 600e/x, A2xm/p, A3x */
1629 "_EJ0", /* all others */
1630 ); /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */
1631IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */
1632 "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */
1633 ); /* all others */
1634IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */
1635 "_EJ0", /* 770x */
1636 ); /* all others */
1637
a5763f22 1638static int __init bay_init(struct ibm_init_struct *iibm)
1da177e4 1639{
fe08bc4b
HMH
1640 vdbg_printk(TPACPI_DBG_INIT, "initializing bay subdriver\n");
1641
8d376cd6 1642 IBM_ACPIHANDLE_INIT(bay);
5fba344c 1643 if (bay_handle)
8d376cd6
HMH
1644 IBM_ACPIHANDLE_INIT(bay_ej);
1645 IBM_ACPIHANDLE_INIT(bay2);
5fba344c 1646 if (bay2_handle)
8d376cd6 1647 IBM_ACPIHANDLE_INIT(bay2_ej);
5fba344c 1648
d8fd94d9
HMH
1649 tp_features.bay_status = bay_handle &&
1650 acpi_evalf(bay_handle, NULL, "_STA", "qv");
1651 tp_features.bay_status2 = bay2_handle &&
1652 acpi_evalf(bay2_handle, NULL, "_STA", "qv");
78f81cc4 1653
d8fd94d9
HMH
1654 tp_features.bay_eject = bay_handle && bay_ej_handle &&
1655 (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental);
1656 tp_features.bay_eject2 = bay2_handle && bay2_ej_handle &&
1657 (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental);
1da177e4 1658
fe08bc4b
HMH
1659 vdbg_printk(TPACPI_DBG_INIT,
1660 "bay 1: status %s, eject %s; bay 2: status %s, eject %s\n",
d8fd94d9
HMH
1661 str_supported(tp_features.bay_status),
1662 str_supported(tp_features.bay_eject),
1663 str_supported(tp_features.bay_status2),
1664 str_supported(tp_features.bay_eject2));
fe08bc4b 1665
d8fd94d9
HMH
1666 return (tp_features.bay_status || tp_features.bay_eject ||
1667 tp_features.bay_status2 || tp_features.bay_eject2)? 0 : 1;
1da177e4
LT
1668}
1669
56b6aeb0
HMH
1670static void bay_notify(struct ibm_struct *ibm, u32 event)
1671{
8d376cd6 1672 acpi_bus_generate_event(ibm->acpi->device, event, 0);
56b6aeb0
HMH
1673}
1674
78f81cc4
BD
1675#define bay_occupied(b) (_sta(b##_handle) & 1)
1676
1677static int bay_read(char *p)
1da177e4
LT
1678{
1679 int len = 0;
78f81cc4
BD
1680 int occupied = bay_occupied(bay);
1681 int occupied2 = bay_occupied(bay2);
1682 int eject, eject2;
1683
d8fd94d9
HMH
1684 len += sprintf(p + len, "status:\t\t%s\n",
1685 tp_features.bay_status ?
1686 (occupied ? "occupied" : "unoccupied") :
1687 "not supported");
1688 if (tp_features.bay_status2)
78f81cc4
BD
1689 len += sprintf(p + len, "status2:\t%s\n", occupied2 ?
1690 "occupied" : "unoccupied");
1691
d8fd94d9
HMH
1692 eject = tp_features.bay_eject && occupied;
1693 eject2 = tp_features.bay_eject2 && occupied2;
78f81cc4
BD
1694
1695 if (eject && eject2)
1696 len += sprintf(p + len, "commands:\teject, eject2\n");
1697 else if (eject)
1da177e4 1698 len += sprintf(p + len, "commands:\teject\n");
78f81cc4
BD
1699 else if (eject2)
1700 len += sprintf(p + len, "commands:\teject2\n");
1da177e4
LT
1701
1702 return len;
1703}
1704
78f81cc4 1705static int bay_write(char *buf)
1da177e4
LT
1706{
1707 char *cmd;
1708
d8fd94d9 1709 if (!tp_features.bay_eject && !tp_features.bay_eject2)
78f81cc4
BD
1710 return -ENODEV;
1711
1da177e4 1712 while ((cmd = next_cmd(&buf))) {
d8fd94d9 1713 if (tp_features.bay_eject && strlencmp(cmd, "eject") == 0) {
78f81cc4
BD
1714 if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1))
1715 return -EIO;
d8fd94d9 1716 } else if (tp_features.bay_eject2 &&
78f81cc4
BD
1717 strlencmp(cmd, "eject2") == 0) {
1718 if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1))
1da177e4
LT
1719 return -EIO;
1720 } else
1721 return -EINVAL;
1722 }
1723
1724 return 0;
78f81cc4 1725}
a5763f22 1726
8d376cd6
HMH
1727static struct tp_acpi_drv_struct ibm_bay_acpidriver = {
1728 .notify = bay_notify,
1729 .handle = &bay_handle,
1730 .type = ACPI_SYSTEM_NOTIFY,
1731};
1732
a5763f22
HMH
1733static struct ibm_struct bay_driver_data = {
1734 .name = "bay",
1735 .read = bay_read,
1736 .write = bay_write,
8d376cd6 1737 .acpi = &ibm_bay_acpidriver,
a5763f22
HMH
1738};
1739
85998248 1740#endif /* CONFIG_THINKPAD_ACPI_BAY */
1da177e4 1741
56b6aeb0
HMH
1742/*************************************************************************
1743 * CMOS subdriver
1744 */
1745
b616004c
HMH
1746/* sysfs cmos_command -------------------------------------------------- */
1747static ssize_t cmos_command_store(struct device *dev,
1748 struct device_attribute *attr,
1749 const char *buf, size_t count)
1750{
1751 unsigned long cmos_cmd;
1752 int res;
1753
1754 if (parse_strtoul(buf, 21, &cmos_cmd))
1755 return -EINVAL;
1756
1757 res = issue_thinkpad_cmos_command(cmos_cmd);
1758 return (res)? res : count;
1759}
1760
1761static struct device_attribute dev_attr_cmos_command =
1762 __ATTR(cmos_command, S_IWUSR, NULL, cmos_command_store);
1763
1764/* --------------------------------------------------------------------- */
1765
a5763f22 1766static int __init cmos_init(struct ibm_init_struct *iibm)
5fba344c 1767{
b616004c
HMH
1768 int res;
1769
fe08bc4b
HMH
1770 vdbg_printk(TPACPI_DBG_INIT,
1771 "initializing cmos commands subdriver\n");
1772
8d376cd6 1773 IBM_ACPIHANDLE_INIT(cmos);
5fba344c 1774
fe08bc4b
HMH
1775 vdbg_printk(TPACPI_DBG_INIT, "cmos commands are %s\n",
1776 str_supported(cmos_handle != NULL));
b616004c
HMH
1777
1778 res = device_create_file(&tpacpi_pdev->dev, &dev_attr_cmos_command);
1779 if (res)
1780 return res;
1781
5fba344c
HMH
1782 return (cmos_handle)? 0 : 1;
1783}
1784
b616004c
HMH
1785static void cmos_exit(void)
1786{
1787 device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command);
1788}
1789
78f81cc4 1790static int cmos_read(char *p)
1da177e4
LT
1791{
1792 int len = 0;
1793
78f81cc4
BD
1794 /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
1795 R30, R31, T20-22, X20-21 */
1da177e4
LT
1796 if (!cmos_handle)
1797 len += sprintf(p + len, "status:\t\tnot supported\n");
1798 else {
1799 len += sprintf(p + len, "status:\t\tsupported\n");
78f81cc4 1800 len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n");
1da177e4
LT
1801 }
1802
1803 return len;
1804}
1805
78f81cc4 1806static int cmos_write(char *buf)
1da177e4
LT
1807{
1808 char *cmd;
c9bea99c 1809 int cmos_cmd, res;
1da177e4
LT
1810
1811 while ((cmd = next_cmd(&buf))) {
78f81cc4
BD
1812 if (sscanf(cmd, "%u", &cmos_cmd) == 1 &&
1813 cmos_cmd >= 0 && cmos_cmd <= 21) {
1da177e4
LT
1814 /* cmos_cmd set */
1815 } else
1816 return -EINVAL;
1817
c9bea99c
HMH
1818 res = issue_thinkpad_cmos_command(cmos_cmd);
1819 if (res)
1820 return res;
1da177e4
LT
1821 }
1822
1823 return 0;
78f81cc4
BD
1824}
1825
a5763f22
HMH
1826static struct ibm_struct cmos_driver_data = {
1827 .name = "cmos",
1828 .read = cmos_read,
1829 .write = cmos_write,
b616004c 1830 .exit = cmos_exit,
a5763f22 1831};
56b6aeb0
HMH
1832
1833/*************************************************************************
1834 * LED subdriver
1835 */
1836
9a8e1738 1837static enum led_access_mode led_supported;
78f81cc4 1838
56b6aeb0
HMH
1839IBM_HANDLE(led, ec, "SLED", /* 570 */
1840 "SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
1841 "LED", /* all others */
1842 ); /* R30, R31 */
1843
a5763f22 1844static int __init led_init(struct ibm_init_struct *iibm)
78f81cc4 1845{
fe08bc4b
HMH
1846 vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n");
1847
8d376cd6 1848 IBM_ACPIHANDLE_INIT(led);
5fba344c 1849
78f81cc4
BD
1850 if (!led_handle)
1851 /* led not supported on R30, R31 */
efa27145 1852 led_supported = TPACPI_LED_NONE;
78f81cc4
BD
1853 else if (strlencmp(led_path, "SLED") == 0)
1854 /* 570 */
efa27145 1855 led_supported = TPACPI_LED_570;
78f81cc4
BD
1856 else if (strlencmp(led_path, "SYSL") == 0)
1857 /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
efa27145 1858 led_supported = TPACPI_LED_OLD;
78f81cc4
BD
1859 else
1860 /* all others */
efa27145 1861 led_supported = TPACPI_LED_NEW;
78f81cc4 1862
fe08bc4b
HMH
1863 vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n",
1864 str_supported(led_supported), led_supported);
1865
5fba344c 1866 return (led_supported != TPACPI_LED_NONE)? 0 : 1;
78f81cc4
BD
1867}
1868
1869#define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking"))
1870
1871static int led_read(char *p)
1da177e4
LT
1872{
1873 int len = 0;
1874
78f81cc4
BD
1875 if (!led_supported) {
1876 len += sprintf(p + len, "status:\t\tnot supported\n");
1877 return len;
1878 }
1879 len += sprintf(p + len, "status:\t\tsupported\n");
1880
efa27145 1881 if (led_supported == TPACPI_LED_570) {
78f81cc4
BD
1882 /* 570 */
1883 int i, status;
1884 for (i = 0; i < 8; i++) {
1885 if (!acpi_evalf(ec_handle,
1886 &status, "GLED", "dd", 1 << i))
1887 return -EIO;
1888 len += sprintf(p + len, "%d:\t\t%s\n",
1889 i, led_status(status));
1890 }
1891 }
1892
1da177e4 1893 len += sprintf(p + len, "commands:\t"
78f81cc4 1894 "<led> on, <led> off, <led> blink (<led> is 0-7)\n");
1da177e4
LT
1895
1896 return len;
1897}
1898
78f81cc4
BD
1899/* off, on, blink */
1900static const int led_sled_arg1[] = { 0, 1, 3 };
1901static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */
1902static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */
1903static const int led_led_arg1[] = { 0, 0x80, 0xc0 };
1904
78f81cc4 1905static int led_write(char *buf)
1da177e4
LT
1906{
1907 char *cmd;
78f81cc4
BD
1908 int led, ind, ret;
1909
1910 if (!led_supported)
1911 return -ENODEV;
1da177e4
LT
1912
1913 while ((cmd = next_cmd(&buf))) {
78f81cc4 1914 if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7)
1da177e4
LT
1915 return -EINVAL;
1916
78f81cc4
BD
1917 if (strstr(cmd, "off")) {
1918 ind = 0;
1da177e4 1919 } else if (strstr(cmd, "on")) {
78f81cc4
BD
1920 ind = 1;
1921 } else if (strstr(cmd, "blink")) {
1922 ind = 2;
1da177e4
LT
1923 } else
1924 return -EINVAL;
78f81cc4 1925
efa27145 1926 if (led_supported == TPACPI_LED_570) {
78f81cc4
BD
1927 /* 570 */
1928 led = 1 << led;
1da177e4 1929 if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
78f81cc4 1930 led, led_sled_arg1[ind]))
1da177e4 1931 return -EIO;
efa27145 1932 } else if (led_supported == TPACPI_LED_OLD) {
78f81cc4
BD
1933 /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
1934 led = 1 << led;
efa27145 1935 ret = ec_write(TPACPI_LED_EC_HLMS, led);
78f81cc4
BD
1936 if (ret >= 0)
1937 ret =
efa27145 1938 ec_write(TPACPI_LED_EC_HLBL,
e062e034 1939 led * led_exp_hlbl[ind]);
78f81cc4
BD
1940 if (ret >= 0)
1941 ret =
efa27145 1942 ec_write(TPACPI_LED_EC_HLCL,
e062e034 1943 led * led_exp_hlcl[ind]);
78f81cc4
BD
1944 if (ret < 0)
1945 return ret;
1946 } else {
1947 /* all others */
1948 if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
1949 led, led_led_arg1[ind]))
1da177e4 1950 return -EIO;
78f81cc4
BD
1951 }
1952 }
1953
1954 return 0;
1955}
1956
a5763f22
HMH
1957static struct ibm_struct led_driver_data = {
1958 .name = "led",
1959 .read = led_read,
1960 .write = led_write,
1961};
1962
56b6aeb0
HMH
1963/*************************************************************************
1964 * Beep subdriver
1965 */
1966
1967IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */
1968
a5763f22 1969static int __init beep_init(struct ibm_init_struct *iibm)
5fba344c 1970{
fe08bc4b
HMH
1971 vdbg_printk(TPACPI_DBG_INIT, "initializing beep subdriver\n");
1972
8d376cd6 1973 IBM_ACPIHANDLE_INIT(beep);
5fba344c 1974
fe08bc4b
HMH
1975 vdbg_printk(TPACPI_DBG_INIT, "beep is %s\n",
1976 str_supported(beep_handle != NULL));
1977
5fba344c
HMH
1978 return (beep_handle)? 0 : 1;
1979}
1980
78f81cc4
BD
1981static int beep_read(char *p)
1982{
1983 int len = 0;
1984
1985 if (!beep_handle)
1986 len += sprintf(p + len, "status:\t\tnot supported\n");
1987 else {
1988 len += sprintf(p + len, "status:\t\tsupported\n");
1989 len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n");
1990 }
1991
1992 return len;
1993}
1994
1995static int beep_write(char *buf)
1996{
1997 char *cmd;
1998 int beep_cmd;
1999
2000 if (!beep_handle)
2001 return -ENODEV;
2002
2003 while ((cmd = next_cmd(&buf))) {
2004 if (sscanf(cmd, "%u", &beep_cmd) == 1 &&
2005 beep_cmd >= 0 && beep_cmd <= 17) {
2006 /* beep_cmd set */
2007 } else
2008 return -EINVAL;
2009 if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0))
2010 return -EIO;
2011 }
2012
2013 return 0;
2014}
2015
a5763f22
HMH
2016static struct ibm_struct beep_driver_data = {
2017 .name = "beep",
2018 .read = beep_read,
2019 .write = beep_write,
2020};
2021
56b6aeb0
HMH
2022/*************************************************************************
2023 * Thermal subdriver
2024 */
78f81cc4 2025
a26f878a 2026static enum thermal_access_mode thermal_read_mode;
78f81cc4 2027
2c37aa4e
HMH
2028/* sysfs temp##_input -------------------------------------------------- */
2029
2030static ssize_t thermal_temp_input_show(struct device *dev,
2031 struct device_attribute *attr,
2032 char *buf)
2033{
2034 struct sensor_device_attribute *sensor_attr =
2035 to_sensor_dev_attr(attr);
2036 int idx = sensor_attr->index;
2037 s32 value;
2038 int res;
2039
2040 res = thermal_get_sensor(idx, &value);
2041 if (res)
2042 return res;
2043 if (value == TP_EC_THERMAL_TMP_NA * 1000)
2044 return -ENXIO;
2045
2046 return snprintf(buf, PAGE_SIZE, "%d\n", value);
2047}
2048
2049#define THERMAL_SENSOR_ATTR_TEMP(_idxA, _idxB) \
2050 SENSOR_ATTR(temp##_idxA##_input, S_IRUGO, thermal_temp_input_show, NULL, _idxB)
2051
2052static struct sensor_device_attribute sensor_dev_attr_thermal_temp_input[] = {
2053 THERMAL_SENSOR_ATTR_TEMP(1, 0),
2054 THERMAL_SENSOR_ATTR_TEMP(2, 1),
2055 THERMAL_SENSOR_ATTR_TEMP(3, 2),
2056 THERMAL_SENSOR_ATTR_TEMP(4, 3),
2057 THERMAL_SENSOR_ATTR_TEMP(5, 4),
2058 THERMAL_SENSOR_ATTR_TEMP(6, 5),
2059 THERMAL_SENSOR_ATTR_TEMP(7, 6),
2060 THERMAL_SENSOR_ATTR_TEMP(8, 7),
2061 THERMAL_SENSOR_ATTR_TEMP(9, 8),
2062 THERMAL_SENSOR_ATTR_TEMP(10, 9),
2063 THERMAL_SENSOR_ATTR_TEMP(11, 10),
2064 THERMAL_SENSOR_ATTR_TEMP(12, 11),
2065 THERMAL_SENSOR_ATTR_TEMP(13, 12),
2066 THERMAL_SENSOR_ATTR_TEMP(14, 13),
2067 THERMAL_SENSOR_ATTR_TEMP(15, 14),
2068 THERMAL_SENSOR_ATTR_TEMP(16, 15),
2069};
2070
2071#define THERMAL_ATTRS(X) \
2072 &sensor_dev_attr_thermal_temp_input[X].dev_attr.attr
2073
2074static struct attribute *thermal_temp_input_attr[] = {
2075 THERMAL_ATTRS(8),
2076 THERMAL_ATTRS(9),
2077 THERMAL_ATTRS(10),
2078 THERMAL_ATTRS(11),
2079 THERMAL_ATTRS(12),
2080 THERMAL_ATTRS(13),
2081 THERMAL_ATTRS(14),
2082 THERMAL_ATTRS(15),
2083 THERMAL_ATTRS(0),
2084 THERMAL_ATTRS(1),
2085 THERMAL_ATTRS(2),
2086 THERMAL_ATTRS(3),
2087 THERMAL_ATTRS(4),
2088 THERMAL_ATTRS(5),
2089 THERMAL_ATTRS(6),
2090 THERMAL_ATTRS(7),
2091 NULL
2092};
2093
2094static const struct attribute_group thermal_temp_input16_group = {
2095 .attrs = thermal_temp_input_attr
2096};
2097
2098static const struct attribute_group thermal_temp_input8_group = {
2099 .attrs = &thermal_temp_input_attr[8]
2100};
2101
2102#undef THERMAL_SENSOR_ATTR_TEMP
2103#undef THERMAL_ATTRS
2104
2105/* --------------------------------------------------------------------- */
2106
a5763f22 2107static int __init thermal_init(struct ibm_init_struct *iibm)
78f81cc4 2108{
60eb0b35
HMH
2109 u8 t, ta1, ta2;
2110 int i;
5fba344c 2111 int acpi_tmp7;
2c37aa4e 2112 int res;
5fba344c 2113
fe08bc4b
HMH
2114 vdbg_printk(TPACPI_DBG_INIT, "initializing thermal subdriver\n");
2115
5fba344c 2116 acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv");
60eb0b35
HMH
2117
2118 if (ibm_thinkpad_ec_found && experimental) {
2119 /*
2120 * Direct EC access mode: sensors at registers
2121 * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for
2122 * non-implemented, thermal sensors return 0x80 when
2123 * not available
2124 */
78f81cc4 2125
60eb0b35
HMH
2126 ta1 = ta2 = 0;
2127 for (i = 0; i < 8; i++) {
04cc862c 2128 if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) {
60eb0b35
HMH
2129 ta1 |= t;
2130 } else {
2131 ta1 = 0;
2132 break;
2133 }
04cc862c 2134 if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) {
60eb0b35
HMH
2135 ta2 |= t;
2136 } else {
2137 ta1 = 0;
2138 break;
2139 }
2140 }
2141 if (ta1 == 0) {
2142 /* This is sheer paranoia, but we handle it anyway */
2143 if (acpi_tmp7) {
2144 printk(IBM_ERR
2145 "ThinkPad ACPI EC access misbehaving, "
2146 "falling back to ACPI TMPx access mode\n");
efa27145 2147 thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
60eb0b35
HMH
2148 } else {
2149 printk(IBM_ERR
2150 "ThinkPad ACPI EC access misbehaving, "
2151 "disabling thermal sensors access\n");
efa27145 2152 thermal_read_mode = TPACPI_THERMAL_NONE;
60eb0b35
HMH
2153 }
2154 } else {
2155 thermal_read_mode =
2156 (ta2 != 0) ?
efa27145 2157 TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8;
60eb0b35
HMH
2158 }
2159 } else if (acpi_tmp7) {
a26f878a
HMH
2160 if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) {
2161 /* 600e/x, 770e, 770x */
efa27145 2162 thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT;
a26f878a
HMH
2163 } else {
2164 /* Standard ACPI TMPx access, max 8 sensors */
efa27145 2165 thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07;
a26f878a
HMH
2166 }
2167 } else {
2168 /* temperatures not supported on 570, G4x, R30, R31, R32 */
efa27145 2169 thermal_read_mode = TPACPI_THERMAL_NONE;
a26f878a 2170 }
78f81cc4 2171
fe08bc4b
HMH
2172 vdbg_printk(TPACPI_DBG_INIT, "thermal is %s, mode %d\n",
2173 str_supported(thermal_read_mode != TPACPI_THERMAL_NONE),
2174 thermal_read_mode);
2175
2c37aa4e
HMH
2176 switch(thermal_read_mode) {
2177 case TPACPI_THERMAL_TPEC_16:
2178 res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
2179 &thermal_temp_input16_group);
2180 if (res)
2181 return res;
2182 break;
2183 case TPACPI_THERMAL_TPEC_8:
2184 case TPACPI_THERMAL_ACPI_TMP07:
2185 case TPACPI_THERMAL_ACPI_UPDT:
2186 res = sysfs_create_group(&tpacpi_pdev->dev.kobj,
2187 &thermal_temp_input8_group);
2188 if (res)
2189 return res;
2190 break;
2191 case TPACPI_THERMAL_NONE:
2192 default:
2193 return 1;
2194 }
2195
2196 return 0;
2197}
2198
2199static void thermal_exit(void)
2200{
2201 switch(thermal_read_mode) {
2202 case TPACPI_THERMAL_TPEC_16:
2203 sysfs_remove_group(&tpacpi_pdev->dev.kobj,
2204 &thermal_temp_input16_group);
2205 break;
2206 case TPACPI_THERMAL_TPEC_8:
2207 case TPACPI_THERMAL_ACPI_TMP07:
2208 case TPACPI_THERMAL_ACPI_UPDT:
2209 sysfs_remove_group(&tpacpi_pdev->dev.kobj,
2210 &thermal_temp_input16_group);
2211 break;
2212 case TPACPI_THERMAL_NONE:
2213 default:
2214 break;
2215 }
78f81cc4
BD
2216}
2217
04cc862c
HMH
2218/* idx is zero-based */
2219static int thermal_get_sensor(int idx, s32 *value)
78f81cc4 2220{
04cc862c 2221 int t;
60eb0b35 2222 s8 tmp;
04cc862c 2223 char tmpi[5];
78f81cc4 2224
04cc862c 2225 t = TP_EC_THERMAL_TMP0;
78f81cc4 2226
a26f878a 2227 switch (thermal_read_mode) {
efa27145
HMH
2228#if TPACPI_MAX_THERMAL_SENSORS >= 16
2229 case TPACPI_THERMAL_TPEC_16:
04cc862c
HMH
2230 if (idx >= 8 && idx <= 15) {
2231 t = TP_EC_THERMAL_TMP8;
2232 idx -= 8;
60eb0b35
HMH
2233 }
2234 /* fallthrough */
2235#endif
efa27145 2236 case TPACPI_THERMAL_TPEC_8:
04cc862c
HMH
2237 if (idx <= 7) {
2238 if (!acpi_ec_read(t + idx, &tmp))
78f81cc4 2239 return -EIO;
04cc862c
HMH
2240 *value = tmp * 1000;
2241 return 0;
60eb0b35 2242 }
04cc862c 2243 break;
78f81cc4 2244
efa27145 2245 case TPACPI_THERMAL_ACPI_UPDT:
04cc862c
HMH
2246 if (idx <= 7) {
2247 snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
2248 if (!acpi_evalf(ec_handle, NULL, "UPDT", "v"))
2249 return -EIO;
78f81cc4
BD
2250 if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
2251 return -EIO;
04cc862c
HMH
2252 *value = (t - 2732) * 100;
2253 return 0;
78f81cc4 2254 }
04cc862c 2255 break;
78f81cc4 2256
efa27145 2257 case TPACPI_THERMAL_ACPI_TMP07:
04cc862c
HMH
2258 if (idx <= 7) {
2259 snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx);
78f81cc4
BD
2260 if (!acpi_evalf(ec_handle, &t, tmpi, "d"))
2261 return -EIO;
04cc862c
HMH
2262 *value = t * 1000;
2263 return 0;
78f81cc4 2264 }
04cc862c 2265 break;
78f81cc4 2266
efa27145 2267 case TPACPI_THERMAL_NONE:
a26f878a 2268 default:
04cc862c
HMH
2269 return -ENOSYS;
2270 }
2271
2272 return -EINVAL;
2273}
2274
2275static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s)
2276{
2277 int res, i;
2278 int n;
2279
2280 n = 8;
2281 i = 0;
2282
2283 if (!s)
2284 return -EINVAL;
2285
2286 if (thermal_read_mode == TPACPI_THERMAL_TPEC_16)
2287 n = 16;
2288
2289 for(i = 0 ; i < n; i++) {
2290 res = thermal_get_sensor(i, &s->temp[i]);
2291 if (res)
2292 return res;
78f81cc4 2293 }
04cc862c
HMH
2294
2295 return n;
a26f878a
HMH
2296}
2297
2298static int thermal_read(char *p)
2299{
2300 int len = 0;
2301 int n, i;
2302 struct ibm_thermal_sensors_struct t;
2303
2304 n = thermal_get_sensors(&t);
2305 if (unlikely(n < 0))
2306 return n;
2307
2308 len += sprintf(p + len, "temperatures:\t");
2309
2310 if (n > 0) {
2311 for (i = 0; i < (n - 1); i++)
2312 len += sprintf(p + len, "%d ", t.temp[i] / 1000);
2313 len += sprintf(p + len, "%d\n", t.temp[i] / 1000);
2314 } else
2315 len += sprintf(p + len, "not supported\n");
78f81cc4
BD
2316
2317 return len;
2318}
2319
a5763f22
HMH
2320static struct ibm_struct thermal_driver_data = {
2321 .name = "thermal",
2322 .read = thermal_read,
2c37aa4e 2323 .exit = thermal_exit,
a5763f22
HMH
2324};
2325
56b6aeb0
HMH
2326/*************************************************************************
2327 * EC Dump subdriver
2328 */
2329
78f81cc4
BD
2330static u8 ecdump_regs[256];
2331
2332static int ecdump_read(char *p)
2333{
2334 int len = 0;
2335 int i, j;
2336 u8 v;
2337
2338 len += sprintf(p + len, "EC "
2339 " +00 +01 +02 +03 +04 +05 +06 +07"
2340 " +08 +09 +0a +0b +0c +0d +0e +0f\n");
2341 for (i = 0; i < 256; i += 16) {
2342 len += sprintf(p + len, "EC 0x%02x:", i);
2343 for (j = 0; j < 16; j++) {
2344 if (!acpi_ec_read(i + j, &v))
2345 break;
2346 if (v != ecdump_regs[i + j])
2347 len += sprintf(p + len, " *%02x", v);
2348 else
2349 len += sprintf(p + len, " %02x", v);
2350 ecdump_regs[i + j] = v;
2351 }
2352 len += sprintf(p + len, "\n");
2353 if (j != 16)
2354 break;
2355 }
2356
2357 /* These are way too dangerous to advertise openly... */
2358#if 0
2359 len += sprintf(p + len, "commands:\t0x<offset> 0x<value>"
2360 " (<offset> is 00-ff, <value> is 00-ff)\n");
2361 len += sprintf(p + len, "commands:\t0x<offset> <value> "
2362 " (<offset> is 00-ff, <value> is 0-255)\n");
2363#endif
2364 return len;
2365}
2366
2367static int ecdump_write(char *buf)
2368{
2369 char *cmd;
2370 int i, v;
2371
2372 while ((cmd = next_cmd(&buf))) {
2373 if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) {
2374 /* i and v set */
2375 } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) {
2376 /* i and v set */
2377 } else
2378 return -EINVAL;
2379 if (i >= 0 && i < 256 && v >= 0 && v < 256) {
2380 if (!acpi_ec_write(i, v))
1da177e4
LT
2381 return -EIO;
2382 } else
2383 return -EINVAL;
2384 }
2385
2386 return 0;
78f81cc4
BD
2387}
2388
a5763f22
HMH
2389static struct ibm_struct ecdump_driver_data = {
2390 .name = "ecdump",
2391 .read = ecdump_read,
2392 .write = ecdump_write,
92641177 2393 .flags.experimental = 1,
a5763f22
HMH
2394};
2395
56b6aeb0
HMH
2396/*************************************************************************
2397 * Backlight/brightness subdriver
2398 */
2399
2400static struct backlight_device *ibm_backlight_device = NULL;
2401
2402static struct backlight_ops ibm_backlight_data = {
2403 .get_brightness = brightness_get,
2404 .update_status = brightness_update_status,
2405};
2406
a5763f22 2407static int __init brightness_init(struct ibm_init_struct *iibm)
56b6aeb0
HMH
2408{
2409 int b;
2410
fe08bc4b
HMH
2411 vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n");
2412
56b6aeb0
HMH
2413 b = brightness_get(NULL);
2414 if (b < 0)
2415 return b;
2416
7d5a015e
HMH
2417 ibm_backlight_device = backlight_device_register(
2418 TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL,
2419 &ibm_backlight_data);
56b6aeb0
HMH
2420 if (IS_ERR(ibm_backlight_device)) {
2421 printk(IBM_ERR "Could not register backlight device\n");
2422 return PTR_ERR(ibm_backlight_device);
2423 }
fe08bc4b 2424 vdbg_printk(TPACPI_DBG_INIT, "brightness is supported\n");
56b6aeb0
HMH
2425
2426 ibm_backlight_device->props.max_brightness = 7;
2427 ibm_backlight_device->props.brightness = b;
2428 backlight_update_status(ibm_backlight_device);
2429
2430 return 0;
2431}
2432
2433static void brightness_exit(void)
2434{
2435 if (ibm_backlight_device) {
fe08bc4b
HMH
2436 vdbg_printk(TPACPI_DBG_EXIT,
2437 "calling backlight_device_unregister()\n");
56b6aeb0
HMH
2438 backlight_device_unregister(ibm_backlight_device);
2439 ibm_backlight_device = NULL;
2440 }
2441}
2442
2443static int brightness_update_status(struct backlight_device *bd)
2444{
2445 return brightness_set(
2446 (bd->props.fb_blank == FB_BLANK_UNBLANK &&
2447 bd->props.power == FB_BLANK_UNBLANK) ?
2448 bd->props.brightness : 0);
2449}
2450
8acb0250
HM
2451static int brightness_get(struct backlight_device *bd)
2452{
8d297264
HMH
2453 u8 level;
2454 if (!acpi_ec_read(brightness_offset, &level))
2455 return -EIO;
8acb0250 2456
8d297264 2457 level &= 0x7;
fb87a811 2458
8d297264 2459 return level;
8acb0250
HM
2460}
2461
8acb0250 2462static int brightness_set(int value)
1da177e4 2463{
78f81cc4 2464 int cmos_cmd, inc, i;
8acb0250
HM
2465 int current_value = brightness_get(NULL);
2466
2467 value &= 7;
2468
56b6aeb0 2469 cmos_cmd = value > current_value ? TP_CMOS_BRIGHTNESS_UP : TP_CMOS_BRIGHTNESS_DOWN;
8acb0250
HM
2470 inc = value > current_value ? 1 : -1;
2471 for (i = current_value; i != value; i += inc) {
c9bea99c 2472 if (issue_thinkpad_cmos_command(cmos_cmd))
8acb0250
HM
2473 return -EIO;
2474 if (!acpi_ec_write(brightness_offset, i + inc))
2475 return -EIO;
2476 }
2477
2478 return 0;
2479}
2480
56b6aeb0
HMH
2481static int brightness_read(char *p)
2482{
2483 int len = 0;
2484 int level;
2485
2486 if ((level = brightness_get(NULL)) < 0) {
2487 len += sprintf(p + len, "level:\t\tunreadable\n");
2488 } else {
2489 len += sprintf(p + len, "level:\t\t%d\n", level & 0x7);
2490 len += sprintf(p + len, "commands:\tup, down\n");
2491 len += sprintf(p + len, "commands:\tlevel <level>"
2492 " (<level> is 0-7)\n");
2493 }
2494
2495 return len;
2496}
2497
8acb0250
HM
2498static int brightness_write(char *buf)
2499{
2500 int level;
78f81cc4 2501 int new_level;
1da177e4 2502 char *cmd;
1da177e4
LT
2503
2504 while ((cmd = next_cmd(&buf))) {
8acb0250
HM
2505 if ((level = brightness_get(NULL)) < 0)
2506 return level;
78f81cc4
BD
2507 level &= 7;
2508
2509 if (strlencmp(cmd, "up") == 0) {
2510 new_level = level == 7 ? 7 : level + 1;
2511 } else if (strlencmp(cmd, "down") == 0) {
2512 new_level = level == 0 ? 0 : level - 1;
2513 } else if (sscanf(cmd, "level %d", &new_level) == 1 &&
2514 new_level >= 0 && new_level <= 7) {
2515 /* new_level set */
2516 } else
2517 return -EINVAL;
2518
8acb0250 2519 brightness_set(new_level);
78f81cc4
BD
2520 }
2521
2522 return 0;
2523}
2524
a5763f22
HMH
2525static struct ibm_struct brightness_driver_data = {
2526 .name = "brightness",
2527 .read = brightness_read,
2528 .write = brightness_write,
2529 .exit = brightness_exit,
2530};
2531
56b6aeb0
HMH
2532/*************************************************************************
2533 * Volume subdriver
2534 */
fb87a811 2535
78f81cc4
BD
2536static int volume_read(char *p)
2537{
2538 int len = 0;
2539 u8 level;
2540
2541 if (!acpi_ec_read(volume_offset, &level)) {
2542 len += sprintf(p + len, "level:\t\tunreadable\n");
2543 } else {
2544 len += sprintf(p + len, "level:\t\t%d\n", level & 0xf);
2545 len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6));
2546 len += sprintf(p + len, "commands:\tup, down, mute\n");
2547 len += sprintf(p + len, "commands:\tlevel <level>"
2548 " (<level> is 0-15)\n");
2549 }
2550
2551 return len;
2552}
2553
78f81cc4
BD
2554static int volume_write(char *buf)
2555{
2556 int cmos_cmd, inc, i;
2557 u8 level, mute;
2558 int new_level, new_mute;
2559 char *cmd;
2560
2561 while ((cmd = next_cmd(&buf))) {
2562 if (!acpi_ec_read(volume_offset, &level))
2563 return -EIO;
2564 new_mute = mute = level & 0x40;
2565 new_level = level = level & 0xf;
2566
2567 if (strlencmp(cmd, "up") == 0) {
2568 if (mute)
2569 new_mute = 0;
2570 else
2571 new_level = level == 15 ? 15 : level + 1;
2572 } else if (strlencmp(cmd, "down") == 0) {
2573 if (mute)
2574 new_mute = 0;
2575 else
2576 new_level = level == 0 ? 0 : level - 1;
2577 } else if (sscanf(cmd, "level %d", &new_level) == 1 &&
2578 new_level >= 0 && new_level <= 15) {
2579 /* new_level set */
2580 } else if (strlencmp(cmd, "mute") == 0) {
2581 new_mute = 0x40;
1da177e4
LT
2582 } else
2583 return -EINVAL;
2584
78f81cc4 2585 if (new_level != level) { /* mute doesn't change */
56b6aeb0 2586 cmos_cmd = new_level > level ? TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN;
78f81cc4
BD
2587 inc = new_level > level ? 1 : -1;
2588
c9bea99c 2589 if (mute && (issue_thinkpad_cmos_command(cmos_cmd) ||
78f81cc4
BD
2590 !acpi_ec_write(volume_offset, level)))
2591 return -EIO;
2592
2593 for (i = level; i != new_level; i += inc)
c9bea99c 2594 if (issue_thinkpad_cmos_command(cmos_cmd) ||
78f81cc4
BD
2595 !acpi_ec_write(volume_offset, i + inc))
2596 return -EIO;
2597
c9bea99c 2598 if (mute && (issue_thinkpad_cmos_command(TP_CMOS_VOLUME_MUTE) ||
78f81cc4
BD
2599 !acpi_ec_write(volume_offset,
2600 new_level + mute)))
2601 return -EIO;
2602 }
2603
2604 if (new_mute != mute) { /* level doesn't change */
56b6aeb0 2605 cmos_cmd = new_mute ? TP_CMOS_VOLUME_MUTE : TP_CMOS_VOLUME_UP;
78f81cc4 2606
c9bea99c 2607 if (issue_thinkpad_cmos_command(cmos_cmd) ||
78f81cc4
BD
2608 !acpi_ec_write(volume_offset, level + new_mute))
2609 return -EIO;
2610 }
2611 }
2612
2613 return 0;
2614}
2615
a5763f22
HMH
2616static struct ibm_struct volume_driver_data = {
2617 .name = "volume",
2618 .read = volume_read,
2619 .write = volume_write,
2620};
56b6aeb0
HMH
2621
2622/*************************************************************************
2623 * Fan subdriver
2624 */
2625
2626/*
2627 * FAN ACCESS MODES
2628 *
efa27145 2629 * TPACPI_FAN_RD_ACPI_GFAN:
56b6aeb0
HMH
2630 * ACPI GFAN method: returns fan level
2631 *
efa27145 2632 * see TPACPI_FAN_WR_ACPI_SFAN
f51d1a39 2633 * EC 0x2f (HFSP) not available if GFAN exists
56b6aeb0 2634 *
efa27145 2635 * TPACPI_FAN_WR_ACPI_SFAN:
56b6aeb0
HMH
2636 * ACPI SFAN method: sets fan level, 0 (stop) to 7 (max)
2637 *
f51d1a39
HMH
2638 * EC 0x2f (HFSP) might be available *for reading*, but do not use
2639 * it for writing.
56b6aeb0 2640 *
efa27145 2641 * TPACPI_FAN_WR_TPEC:
f51d1a39
HMH
2642 * ThinkPad EC register 0x2f (HFSP): fan control loop mode
2643 * Supported on almost all ThinkPads
56b6aeb0
HMH
2644 *
2645 * Fan speed changes of any sort (including those caused by the
2646 * disengaged mode) are usually done slowly by the firmware as the
2647 * maximum ammount of fan duty cycle change per second seems to be
2648 * limited.
2649 *
2650 * Reading is not available if GFAN exists.
2651 * Writing is not available if SFAN exists.
2652 *
2653 * Bits
2654 * 7 automatic mode engaged;
2655 * (default operation mode of the ThinkPad)
2656 * fan level is ignored in this mode.
f51d1a39 2657 * 6 full speed mode (takes precedence over bit 7);
56b6aeb0 2658 * not available on all thinkpads. May disable
f51d1a39
HMH
2659 * the tachometer while the fan controller ramps up
2660 * the speed (which can take up to a few *minutes*).
2661 * Speeds up fan to 100% duty-cycle, which is far above
2662 * the standard RPM levels. It is not impossible that
2663 * it could cause hardware damage.
56b6aeb0
HMH
2664 * 5-3 unused in some models. Extra bits for fan level
2665 * in others, but still useless as all values above
2666 * 7 map to the same speed as level 7 in these models.
2667 * 2-0 fan level (0..7 usually)
2668 * 0x00 = stop
2669 * 0x07 = max (set when temperatures critical)
2670 * Some ThinkPads may have other levels, see
efa27145 2671 * TPACPI_FAN_WR_ACPI_FANS (X31/X40/X41)
56b6aeb0
HMH
2672 *
2673 * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at
2674 * boot. Apparently the EC does not intialize it, so unless ACPI DSDT
2675 * does so, its initial value is meaningless (0x07).
2676 *
2677 * For firmware bugs, refer to:
2678 * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
2679 *
2680 * ----
2681 *
2682 * ThinkPad EC register 0x84 (LSB), 0x85 (MSB):
2683 * Main fan tachometer reading (in RPM)
2684 *
2685 * This register is present on all ThinkPads with a new-style EC, and
2686 * it is known not to be present on the A21m/e, and T22, as there is
2687 * something else in offset 0x84 according to the ACPI DSDT. Other
2688 * ThinkPads from this same time period (and earlier) probably lack the
2689 * tachometer as well.
2690 *
2691 * Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare
2692 * was never fixed by IBM to report the EC firmware version string
2693 * probably support the tachometer (like the early X models), so
2694 * detecting it is quite hard. We need more data to know for sure.
2695 *
2696 * FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings
2697 * might result.
2698 *
f51d1a39
HMH
2699 * FIRMWARE BUG: may go stale while the EC is switching to full speed
2700 * mode.
56b6aeb0
HMH
2701 *
2702 * For firmware bugs, refer to:
2703 * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues
2704 *
efa27145 2705 * TPACPI_FAN_WR_ACPI_FANS:
56b6aeb0
HMH
2706 * ThinkPad X31, X40, X41. Not available in the X60.
2707 *
2708 * FANS ACPI handle: takes three arguments: low speed, medium speed,
2709 * high speed. ACPI DSDT seems to map these three speeds to levels
2710 * as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH
2711 * (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3")
2712 *
2713 * The speeds are stored on handles
2714 * (FANA:FAN9), (FANC:FANB), (FANE:FAND).
2715 *
2716 * There are three default speed sets, acessible as handles:
2717 * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H
2718 *
2719 * ACPI DSDT switches which set is in use depending on various
2720 * factors.
2721 *
efa27145 2722 * TPACPI_FAN_WR_TPEC is also available and should be used to
56b6aeb0
HMH
2723 * command the fan. The X31/X40/X41 seems to have 8 fan levels,
2724 * but the ACPI tables just mention level 7.
2725 */
2726
69ba91cb
HMH
2727static enum fan_status_access_mode fan_status_access_mode;
2728static enum fan_control_access_mode fan_control_access_mode;
2729static enum fan_control_commands fan_control_commands;
78f81cc4 2730
778b4d74 2731static u8 fan_control_initial_status;
fe98a52c 2732static u8 fan_control_desired_level;
778b4d74 2733
25c68a33 2734static void fan_watchdog_fire(struct work_struct *ignored);
16663a87 2735static int fan_watchdog_maxinterval;
25c68a33 2736static DECLARE_DELAYED_WORK(fan_watchdog_task, fan_watchdog_fire);
16663a87 2737
56b6aeb0
HMH
2738IBM_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */
2739IBM_HANDLE(gfan, ec, "GFAN", /* 570 */
2740 "\\FSPD", /* 600e/x, 770e, 770x */
2741 ); /* all others */
2742IBM_HANDLE(sfan, ec, "SFAN", /* 570 */
2743 "JFNS", /* 770x-JL */
2744 ); /* all others */
2745
fe98a52c
HMH
2746/*
2747 * SYSFS fan layout: hwmon compatible (device)
2748 *
2749 * pwm*_enable:
2750 * 0: "disengaged" mode
2751 * 1: manual mode
2752 * 2: native EC "auto" mode (recommended, hardware default)
2753 *
2754 * pwm*: set speed in manual mode, ignored otherwise.
2755 * 0 is level 0; 255 is level 7. Intermediate points done with linear
2756 * interpolation.
2757 *
2758 * fan*_input: tachometer reading, RPM
2759 *
2760 *
2761 * SYSFS fan layout: extensions
2762 *
2763 * fan_watchdog (driver):
2764 * fan watchdog interval in seconds, 0 disables (default), max 120
2765 */
2766
2767/* sysfs fan pwm1_enable ----------------------------------------------- */
2768static ssize_t fan_pwm1_enable_show(struct device *dev,
2769 struct device_attribute *attr,
2770 char *buf)
2771{
2772 int res, mode;
2773 u8 status;
2774
2775 res = fan_get_status_safe(&status);
2776 if (res)
2777 return res;
2778
2779 if (unlikely(tp_features.fan_ctrl_status_undef)) {
2780 if (status != fan_control_initial_status) {
2781 tp_features.fan_ctrl_status_undef = 0;
2782 } else {
2783 /* Return most likely status. In fact, it
2784 * might be the only possible status */
2785 status = TP_EC_FAN_AUTO;
2786 }
2787 }
2788
2789 if (status & TP_EC_FAN_FULLSPEED) {
2790 mode = 0;
2791 } else if (status & TP_EC_FAN_AUTO) {
2792 mode = 2;
2793 } else
2794 mode = 1;
2795
2796 return snprintf(buf, PAGE_SIZE, "%d\n", mode);
2797}
2798
2799static ssize_t fan_pwm1_enable_store(struct device *dev,
2800 struct device_attribute *attr,
2801 const char *buf, size_t count)
2802{
2803 unsigned long t;
2804 int res, level;
2805
2806 if (parse_strtoul(buf, 2, &t))
2807 return -EINVAL;
2808
2809 switch (t) {
2810 case 0:
2811 level = TP_EC_FAN_FULLSPEED;
2812 break;
2813 case 1:
2814 level = TPACPI_FAN_LAST_LEVEL;
2815 break;
2816 case 2:
2817 level = TP_EC_FAN_AUTO;
2818 break;
2819 case 3:
2820 /* reserved for software-controlled auto mode */
2821 return -ENOSYS;
2822 default:
2823 return -EINVAL;
2824 }
2825
2826 res = fan_set_level_safe(level);
c573ddb9
HMH
2827 if (res == -ENXIO)
2828 return -EINVAL;
2829 else if (res < 0)
fe98a52c
HMH
2830 return res;
2831
2832 fan_watchdog_reset();
2833
2834 return count;
2835}
2836
2837static struct device_attribute dev_attr_fan_pwm1_enable =
2838 __ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
2839 fan_pwm1_enable_show, fan_pwm1_enable_store);
2840
2841/* sysfs fan pwm1 ------------------------------------------------------ */
2842static ssize_t fan_pwm1_show(struct device *dev,
2843 struct device_attribute *attr,
2844 char *buf)
2845{
2846 int res;
2847 u8 status;
2848
2849 res = fan_get_status_safe(&status);
2850 if (res)
2851 return res;
2852
2853 if (unlikely(tp_features.fan_ctrl_status_undef)) {
2854 if (status != fan_control_initial_status) {
2855 tp_features.fan_ctrl_status_undef = 0;
2856 } else {
2857 status = TP_EC_FAN_AUTO;
2858 }
2859 }
2860
2861 if ((status &
2862 (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) != 0)
2863 status = fan_control_desired_level;
2864
2865 if (status > 7)
2866 status = 7;
2867
2868 return snprintf(buf, PAGE_SIZE, "%u\n", (status * 255) / 7);
2869}
2870
2871static ssize_t fan_pwm1_store(struct device *dev,
2872 struct device_attribute *attr,
2873 const char *buf, size_t count)
2874{
2875 unsigned long s;
2876 int rc;
2877 u8 status, newlevel;
2878
2879 if (parse_strtoul(buf, 255, &s))
2880 return -EINVAL;
2881
2882 /* scale down from 0-255 to 0-7 */
2883 newlevel = (s >> 5) & 0x07;
2884
2885 rc = mutex_lock_interruptible(&fan_mutex);
2886 if (rc < 0)
2887 return rc;
2888
2889 rc = fan_get_status(&status);
2890 if (!rc && (status &
2891 (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) {
2892 rc = fan_set_level(newlevel);
c573ddb9
HMH
2893 if (rc == -ENXIO)
2894 rc = -EINVAL;
2895 else if (!rc) {
fe98a52c 2896 fan_update_desired_level(newlevel);
ca4ac2f4
HMH
2897 fan_watchdog_reset();
2898 }
fe98a52c
HMH
2899 }
2900
2901 mutex_unlock(&fan_mutex);
2902 return (rc)? rc : count;
2903}
2904
2905static struct device_attribute dev_attr_fan_pwm1 =
2906 __ATTR(pwm1, S_IWUSR | S_IRUGO,
2907 fan_pwm1_show, fan_pwm1_store);
2908
2909/* sysfs fan fan1_input ------------------------------------------------ */
2910static ssize_t fan_fan1_input_show(struct device *dev,
2911 struct device_attribute *attr,
2912 char *buf)
2913{
2914 int res;
2915 unsigned int speed;
2916
2917 res = fan_get_speed(&speed);
2918 if (res < 0)
2919 return res;
2920
2921 return snprintf(buf, PAGE_SIZE, "%u\n", speed);
2922}
2923
2924static struct device_attribute dev_attr_fan_fan1_input =
2925 __ATTR(fan1_input, S_IRUGO,
2926 fan_fan1_input_show, NULL);
2927
2928/* sysfs fan fan_watchdog (driver) ------------------------------------- */
2929static ssize_t fan_fan_watchdog_show(struct device_driver *drv,
2930 char *buf)
2931{
2932 return snprintf(buf, PAGE_SIZE, "%u\n", fan_watchdog_maxinterval);
2933}
2934
2935static ssize_t fan_fan_watchdog_store(struct device_driver *drv,
2936 const char *buf, size_t count)
2937{
2938 unsigned long t;
2939
2940 if (parse_strtoul(buf, 120, &t))
2941 return -EINVAL;
2942
ecf2a80a
HMH
2943 if (!fan_control_allowed)
2944 return -EPERM;
2945
fe98a52c
HMH
2946 fan_watchdog_maxinterval = t;
2947 fan_watchdog_reset();
2948
2949 return count;
2950}
2951
2952static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO,
2953 fan_fan_watchdog_show, fan_fan_watchdog_store);
2954
2955/* --------------------------------------------------------------------- */
2956static struct attribute *fan_attributes[] = {
2957 &dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr,
2958 &dev_attr_fan_fan1_input.attr,
2959 NULL
2960};
2961
2962static const struct attribute_group fan_attr_group = {
2963 .attrs = fan_attributes,
2964};
2965
a5763f22 2966static int __init fan_init(struct ibm_init_struct *iibm)
78f81cc4 2967{
fe98a52c
HMH
2968 int rc;
2969
fe08bc4b
HMH
2970 vdbg_printk(TPACPI_DBG_INIT, "initializing fan subdriver\n");
2971
40ca9fdf 2972 mutex_init(&fan_mutex);
efa27145
HMH
2973 fan_status_access_mode = TPACPI_FAN_NONE;
2974 fan_control_access_mode = TPACPI_FAN_WR_NONE;
69ba91cb 2975 fan_control_commands = 0;
16663a87 2976 fan_watchdog_maxinterval = 0;
d8fd94d9 2977 tp_features.fan_ctrl_status_undef = 0;
fe98a52c 2978 fan_control_desired_level = 7;
78f81cc4 2979
8d376cd6
HMH
2980 IBM_ACPIHANDLE_INIT(fans);
2981 IBM_ACPIHANDLE_INIT(gfan);
2982 IBM_ACPIHANDLE_INIT(sfan);
5fba344c 2983
78f81cc4
BD
2984 if (gfan_handle) {
2985 /* 570, 600e/x, 770e, 770x */
efa27145 2986 fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN;
69ba91cb
HMH
2987 } else {
2988 /* all other ThinkPads: note that even old-style
2989 * ThinkPad ECs supports the fan control register */
778b4d74
HMH
2990 if (likely(acpi_ec_read(fan_status_offset,
2991 &fan_control_initial_status))) {
efa27145 2992 fan_status_access_mode = TPACPI_FAN_RD_TPEC;
778b4d74
HMH
2993
2994 /* In some ThinkPads, neither the EC nor the ACPI
2995 * DSDT initialize the fan status, and it ends up
2996 * being set to 0x07 when it *could* be either
2997 * 0x07 or 0x80.
2998 *
2999 * Enable for TP-1Y (T43), TP-78 (R51e),
3000 * TP-76 (R52), TP-70 (T43, R52), which are known
3001 * to be buggy. */
3002 if (fan_control_initial_status == 0x07 &&
3003 ibm_thinkpad_ec_found &&
3004 ((ibm_thinkpad_ec_found[0] == '1' &&
3005 ibm_thinkpad_ec_found[1] == 'Y') ||
3006 (ibm_thinkpad_ec_found[0] == '7' &&
3007 (ibm_thinkpad_ec_found[1] == '6' ||
3008 ibm_thinkpad_ec_found[1] == '8' ||
3009 ibm_thinkpad_ec_found[1] == '0'))
3010 )) {
3011 printk(IBM_NOTICE
3012 "fan_init: initial fan status is "
3013 "unknown, assuming it is in auto "
3014 "mode\n");
d8fd94d9 3015 tp_features.fan_ctrl_status_undef = 1;
778b4d74 3016 }
69ba91cb
HMH
3017 } else {
3018 printk(IBM_ERR
3019 "ThinkPad ACPI EC access misbehaving, "
3020 "fan status and control unavailable\n");
5fba344c 3021 return 1;
69ba91cb
HMH
3022 }
3023 }
78f81cc4 3024
69ba91cb
HMH
3025 if (sfan_handle) {
3026 /* 570, 770x-JL */
efa27145 3027 fan_control_access_mode = TPACPI_FAN_WR_ACPI_SFAN;
1c6a334e 3028 fan_control_commands |=
efa27145 3029 TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_ENABLE;
78f81cc4 3030 } else {
69ba91cb
HMH
3031 if (!gfan_handle) {
3032 /* gfan without sfan means no fan control */
3033 /* all other models implement TP EC 0x2f control */
3034
3035 if (fans_handle) {
a8b7a662 3036 /* X31, X40, X41 */
69ba91cb 3037 fan_control_access_mode =
efa27145 3038 TPACPI_FAN_WR_ACPI_FANS;
69ba91cb 3039 fan_control_commands |=
efa27145
HMH
3040 TPACPI_FAN_CMD_SPEED |
3041 TPACPI_FAN_CMD_LEVEL |
3042 TPACPI_FAN_CMD_ENABLE;
69ba91cb 3043 } else {
efa27145 3044 fan_control_access_mode = TPACPI_FAN_WR_TPEC;
a12095c2 3045 fan_control_commands |=
efa27145
HMH
3046 TPACPI_FAN_CMD_LEVEL |
3047 TPACPI_FAN_CMD_ENABLE;
69ba91cb
HMH
3048 }
3049 }
3050 }
3051
fe08bc4b
HMH
3052 vdbg_printk(TPACPI_DBG_INIT, "fan is %s, modes %d, %d\n",
3053 str_supported(fan_status_access_mode != TPACPI_FAN_NONE ||
3054 fan_control_access_mode != TPACPI_FAN_WR_NONE),
3055 fan_status_access_mode, fan_control_access_mode);
3056
ecf2a80a
HMH
3057 /* fan control master switch */
3058 if (!fan_control_allowed) {
3059 fan_control_access_mode = TPACPI_FAN_WR_NONE;
3060 fan_control_commands = 0;
3061 dbg_printk(TPACPI_DBG_INIT,
3062 "fan control features disabled by parameter\n");
3063 }
3064
fe98a52c
HMH
3065 /* update fan_control_desired_level */
3066 if (fan_status_access_mode != TPACPI_FAN_NONE)
3067 fan_get_status_safe(NULL);
3068
3069 if (fan_status_access_mode != TPACPI_FAN_NONE ||
3070 fan_control_access_mode != TPACPI_FAN_WR_NONE) {
3071 rc = sysfs_create_group(&tpacpi_pdev->dev.kobj,
3072 &fan_attr_group);
3073 if (!(rc < 0))
3074 rc = driver_create_file(&tpacpi_pdriver.driver,
3075 &driver_attr_fan_watchdog);
3076 if (rc < 0)
3077 return rc;
3078 return 0;
3079 } else
3080 return 1;
3081}
3082
3083/*
3084 * Call with fan_mutex held
3085 */
3086static void fan_update_desired_level(u8 status)
3087{
3088 if ((status &
3089 (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) {
3090 if (status > 7)
3091 fan_control_desired_level = 7;
3092 else
3093 fan_control_desired_level = status;
3094 }
69ba91cb 3095}
78f81cc4 3096
c52f0aa5 3097static int fan_get_status(u8 *status)
78f81cc4 3098{
c52f0aa5 3099 u8 s;
78f81cc4 3100
a8b7a662 3101 /* TODO:
efa27145 3102 * Add TPACPI_FAN_RD_ACPI_FANS ? */
a8b7a662 3103
3ef8a609 3104 switch (fan_status_access_mode) {
efa27145 3105 case TPACPI_FAN_RD_ACPI_GFAN:
78f81cc4 3106 /* 570, 600e/x, 770e, 770x */
c52f0aa5
HMH
3107
3108 if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d")))
1da177e4 3109 return -EIO;
78f81cc4 3110
c52f0aa5
HMH
3111 if (likely(status))
3112 *status = s & 0x07;
3ef8a609
HMH
3113
3114 break;
3115
efa27145 3116 case TPACPI_FAN_RD_TPEC:
78f81cc4 3117 /* all except 570, 600e/x, 770e, 770x */
c52f0aa5 3118 if (unlikely(!acpi_ec_read(fan_status_offset, &s)))
3ef8a609 3119 return -EIO;
78f81cc4 3120
c52f0aa5
HMH
3121 if (likely(status))
3122 *status = s;
3123
3124 break;
3125
3126 default:
3127 return -ENXIO;
78f81cc4
BD
3128 }
3129
c52f0aa5
HMH
3130 return 0;
3131}
3132
fe98a52c
HMH
3133static int fan_get_status_safe(u8 *status)
3134{
3135 int rc;
3136 u8 s;
3137
3138 rc = mutex_lock_interruptible(&fan_mutex);
3139 if (rc < 0)
3140 return rc;
3141 rc = fan_get_status(&s);
3142 if (!rc)
3143 fan_update_desired_level(s);
3144 mutex_unlock(&fan_mutex);
3145
3146 if (status)
3147 *status = s;
3148
3149 return rc;
3150}
3151
56b6aeb0
HMH
3152static void fan_exit(void)
3153{
99fba3f8 3154 vdbg_printk(TPACPI_DBG_EXIT, "cancelling any pending fan watchdog tasks\n");
fe98a52c
HMH
3155
3156 /* FIXME: can we really do this unconditionally? */
3157 sysfs_remove_group(&tpacpi_pdev->dev.kobj, &fan_attr_group);
3158 driver_remove_file(&tpacpi_pdriver.driver, &driver_attr_fan_watchdog);
3159
56b6aeb0
HMH
3160 cancel_delayed_work(&fan_watchdog_task);
3161 flush_scheduled_work();
3162}
3163
c52f0aa5
HMH
3164static int fan_get_speed(unsigned int *speed)
3165{
3166 u8 hi, lo;
3167
3168 switch (fan_status_access_mode) {
efa27145 3169 case TPACPI_FAN_RD_TPEC:
78f81cc4 3170 /* all except 570, 600e/x, 770e, 770x */
3ef8a609
HMH
3171 if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) ||
3172 !acpi_ec_read(fan_rpm_offset + 1, &hi)))
3173 return -EIO;
3ef8a609 3174
c52f0aa5
HMH
3175 if (likely(speed))
3176 *speed = (hi << 8) | lo;
3177
3178 break;
3179
3180 default:
3181 return -ENXIO;
3182 }
3183
3184 return 0;
3185}
3186
56b6aeb0 3187static void fan_watchdog_fire(struct work_struct *ignored)
16663a87 3188{
99fba3f8
HMH
3189 int rc;
3190
56b6aeb0 3191 printk(IBM_NOTICE "fan watchdog: enabling fan\n");
99fba3f8
HMH
3192 rc = fan_set_enable();
3193 if (rc < 0) {
3194 printk(IBM_ERR "fan watchdog: error %d while enabling fan, "
3195 "will try again later...\n", -rc);
56b6aeb0
HMH
3196 /* reschedule for later */
3197 fan_watchdog_reset();
3198 }
16663a87
HMH
3199}
3200
3201static void fan_watchdog_reset(void)
3202{
3203 static int fan_watchdog_active = 0;
3204
4985cd0a
HMH
3205 if (fan_control_access_mode == TPACPI_FAN_WR_NONE)
3206 return;
3207
16663a87
HMH
3208 if (fan_watchdog_active)
3209 cancel_delayed_work(&fan_watchdog_task);
3210
3211 if (fan_watchdog_maxinterval > 0) {
3212 fan_watchdog_active = 1;
3213 if (!schedule_delayed_work(&fan_watchdog_task,
3214 msecs_to_jiffies(fan_watchdog_maxinterval
3215 * 1000))) {
3216 printk(IBM_ERR "failed to schedule the fan watchdog, "
3217 "watchdog will not trigger\n");
3218 }
3219 } else
3220 fan_watchdog_active = 0;
3221}
3222
18ad7996 3223static int fan_set_level(int level)
78f81cc4 3224{
ecf2a80a
HMH
3225 if (!fan_control_allowed)
3226 return -EPERM;
3227
18ad7996 3228 switch (fan_control_access_mode) {
efa27145 3229 case TPACPI_FAN_WR_ACPI_SFAN:
18ad7996 3230 if (level >= 0 && level <= 7) {
fe98a52c 3231 if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level))
78f81cc4 3232 return -EIO;
18ad7996
HMH
3233 } else
3234 return -EINVAL;
3235 break;
3236
efa27145
HMH
3237 case TPACPI_FAN_WR_ACPI_FANS:
3238 case TPACPI_FAN_WR_TPEC:
3239 if ((level != TP_EC_FAN_AUTO) &&
3240 (level != TP_EC_FAN_FULLSPEED) &&
a12095c2
HMH
3241 ((level < 0) || (level > 7)))
3242 return -EINVAL;
3243
eaa7571b
HMH
3244 /* safety net should the EC not support AUTO
3245 * or FULLSPEED mode bits and just ignore them */
3246 if (level & TP_EC_FAN_FULLSPEED)
3247 level |= 7; /* safety min speed 7 */
3248 else if (level & TP_EC_FAN_FULLSPEED)
3249 level |= 4; /* safety min speed 4 */
3250
fe98a52c 3251 if (!acpi_ec_write(fan_status_offset, level))
a12095c2 3252 return -EIO;
778b4d74 3253 else
d8fd94d9 3254 tp_features.fan_ctrl_status_undef = 0;
a12095c2
HMH
3255 break;
3256
18ad7996
HMH
3257 default:
3258 return -ENXIO;
3259 }
3260 return 0;
3261}
3262
fe98a52c
HMH
3263static int fan_set_level_safe(int level)
3264{
3265 int rc;
3266
ecf2a80a
HMH
3267 if (!fan_control_allowed)
3268 return -EPERM;
3269
fe98a52c
HMH
3270 rc = mutex_lock_interruptible(&fan_mutex);
3271 if (rc < 0)
3272 return rc;
3273
3274 if (level == TPACPI_FAN_LAST_LEVEL)
3275 level = fan_control_desired_level;
3276
3277 rc = fan_set_level(level);
3278 if (!rc)
3279 fan_update_desired_level(level);
3280
3281 mutex_unlock(&fan_mutex);
3282 return rc;
3283}
3284
18ad7996
HMH
3285static int fan_set_enable(void)
3286{
1c6a334e
HMH
3287 u8 s;
3288 int rc;
3289
ecf2a80a
HMH
3290 if (!fan_control_allowed)
3291 return -EPERM;
3292
40ca9fdf
HMH
3293 rc = mutex_lock_interruptible(&fan_mutex);
3294 if (rc < 0)
3295 return rc;
3296
18ad7996 3297 switch (fan_control_access_mode) {
efa27145
HMH
3298 case TPACPI_FAN_WR_ACPI_FANS:
3299 case TPACPI_FAN_WR_TPEC:
40ca9fdf
HMH
3300 rc = fan_get_status(&s);
3301 if (rc < 0)
3302 break;
1c6a334e
HMH
3303
3304 /* Don't go out of emergency fan mode */
eaa7571b
HMH
3305 if (s != 7) {
3306 s &= 0x07;
3307 s |= TP_EC_FAN_AUTO | 4; /* min fan speed 4 */
3308 }
1c6a334e
HMH
3309
3310 if (!acpi_ec_write(fan_status_offset, s))
40ca9fdf
HMH
3311 rc = -EIO;
3312 else {
d8fd94d9 3313 tp_features.fan_ctrl_status_undef = 0;
40ca9fdf
HMH
3314 rc = 0;
3315 }
1c6a334e
HMH
3316 break;
3317
efa27145 3318 case TPACPI_FAN_WR_ACPI_SFAN:
40ca9fdf
HMH
3319 rc = fan_get_status(&s);
3320 if (rc < 0)
3321 break;
1c6a334e
HMH
3322
3323 s &= 0x07;
3324
3325 /* Set fan to at least level 4 */
eaa7571b 3326 s |= 4;
1c6a334e
HMH
3327
3328 if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s))
40ca9fdf
HMH
3329 rc= -EIO;
3330 else
3331 rc = 0;
18ad7996
HMH
3332 break;
3333
3334 default:
40ca9fdf 3335 rc = -ENXIO;
18ad7996 3336 }
40ca9fdf
HMH
3337
3338 mutex_unlock(&fan_mutex);
3339 return rc;
18ad7996
HMH
3340}
3341
3342static int fan_set_disable(void)
3343{
40ca9fdf
HMH
3344 int rc;
3345
ecf2a80a
HMH
3346 if (!fan_control_allowed)
3347 return -EPERM;
3348
40ca9fdf
HMH
3349 rc = mutex_lock_interruptible(&fan_mutex);
3350 if (rc < 0)
3351 return rc;
3352
3353 rc = 0;
18ad7996 3354 switch (fan_control_access_mode) {
efa27145
HMH
3355 case TPACPI_FAN_WR_ACPI_FANS:
3356 case TPACPI_FAN_WR_TPEC:
18ad7996 3357 if (!acpi_ec_write(fan_status_offset, 0x00))
40ca9fdf 3358 rc = -EIO;
fe98a52c
HMH
3359 else {
3360 fan_control_desired_level = 0;
d8fd94d9 3361 tp_features.fan_ctrl_status_undef = 0;
fe98a52c 3362 }
18ad7996
HMH
3363 break;
3364
efa27145 3365 case TPACPI_FAN_WR_ACPI_SFAN:
1c6a334e 3366 if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00))
40ca9fdf 3367 rc = -EIO;
fe98a52c
HMH
3368 else
3369 fan_control_desired_level = 0;
1c6a334e
HMH
3370 break;
3371
18ad7996 3372 default:
40ca9fdf 3373 rc = -ENXIO;
18ad7996 3374 }
40ca9fdf 3375
fe98a52c 3376
40ca9fdf
HMH
3377 mutex_unlock(&fan_mutex);
3378 return rc;
56b6aeb0
HMH
3379}
3380
3381static int fan_set_speed(int speed)
3382{
40ca9fdf
HMH
3383 int rc;
3384
ecf2a80a
HMH
3385 if (!fan_control_allowed)
3386 return -EPERM;
3387
40ca9fdf
HMH
3388 rc = mutex_lock_interruptible(&fan_mutex);
3389 if (rc < 0)
3390 return rc;
3391
3392 rc = 0;
56b6aeb0 3393 switch (fan_control_access_mode) {
efa27145 3394 case TPACPI_FAN_WR_ACPI_FANS:
56b6aeb0
HMH
3395 if (speed >= 0 && speed <= 65535) {
3396 if (!acpi_evalf(fans_handle, NULL, NULL, "vddd",
3397 speed, speed, speed))
40ca9fdf 3398 rc = -EIO;
56b6aeb0 3399 } else
40ca9fdf 3400 rc = -EINVAL;
56b6aeb0
HMH
3401 break;
3402
3403 default:
40ca9fdf 3404 rc = -ENXIO;
56b6aeb0 3405 }
40ca9fdf
HMH
3406
3407 mutex_unlock(&fan_mutex);
3408 return rc;
56b6aeb0
HMH
3409}
3410
3411static int fan_read(char *p)
3412{
3413 int len = 0;
3414 int rc;
3415 u8 status;
3416 unsigned int speed = 0;
3417
3418 switch (fan_status_access_mode) {
efa27145 3419 case TPACPI_FAN_RD_ACPI_GFAN:
56b6aeb0 3420 /* 570, 600e/x, 770e, 770x */
fe98a52c 3421 if ((rc = fan_get_status_safe(&status)) < 0)
56b6aeb0
HMH
3422 return rc;
3423
3424 len += sprintf(p + len, "status:\t\t%s\n"
3425 "level:\t\t%d\n",
3426 (status != 0) ? "enabled" : "disabled", status);
3427 break;
3428
efa27145 3429 case TPACPI_FAN_RD_TPEC:
56b6aeb0 3430 /* all except 570, 600e/x, 770e, 770x */
fe98a52c 3431 if ((rc = fan_get_status_safe(&status)) < 0)
56b6aeb0
HMH
3432 return rc;
3433
d8fd94d9 3434 if (unlikely(tp_features.fan_ctrl_status_undef)) {
56b6aeb0 3435 if (status != fan_control_initial_status)
d8fd94d9 3436 tp_features.fan_ctrl_status_undef = 0;
56b6aeb0
HMH
3437 else
3438 /* Return most likely status. In fact, it
3439 * might be the only possible status */
efa27145 3440 status = TP_EC_FAN_AUTO;
56b6aeb0
HMH
3441 }
3442
3443 len += sprintf(p + len, "status:\t\t%s\n",
3444 (status != 0) ? "enabled" : "disabled");
3445
56b6aeb0
HMH
3446 if ((rc = fan_get_speed(&speed)) < 0)
3447 return rc;
3448
3449 len += sprintf(p + len, "speed:\t\t%d\n", speed);
3450
efa27145 3451 if (status & TP_EC_FAN_FULLSPEED)
56b6aeb0
HMH
3452 /* Disengaged mode takes precedence */
3453 len += sprintf(p + len, "level:\t\tdisengaged\n");
efa27145 3454 else if (status & TP_EC_FAN_AUTO)
56b6aeb0
HMH
3455 len += sprintf(p + len, "level:\t\tauto\n");
3456 else
3457 len += sprintf(p + len, "level:\t\t%d\n", status);
3458 break;
3459
efa27145 3460 case TPACPI_FAN_NONE:
56b6aeb0
HMH
3461 default:
3462 len += sprintf(p + len, "status:\t\tnot supported\n");
3463 }
3464
efa27145 3465 if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) {
56b6aeb0
HMH
3466 len += sprintf(p + len, "commands:\tlevel <level>");
3467
3468 switch (fan_control_access_mode) {
efa27145 3469 case TPACPI_FAN_WR_ACPI_SFAN:
56b6aeb0
HMH
3470 len += sprintf(p + len, " (<level> is 0-7)\n");
3471 break;
3472
3473 default:
3474 len += sprintf(p + len, " (<level> is 0-7, "
fe98a52c 3475 "auto, disengaged, full-speed)\n");
56b6aeb0
HMH
3476 break;
3477 }
3478 }
18ad7996 3479
efa27145 3480 if (fan_control_commands & TPACPI_FAN_CMD_ENABLE)
56b6aeb0
HMH
3481 len += sprintf(p + len, "commands:\tenable, disable\n"
3482 "commands:\twatchdog <timeout> (<timeout> is 0 (off), "
3483 "1-120 (seconds))\n");
1da177e4 3484
efa27145 3485 if (fan_control_commands & TPACPI_FAN_CMD_SPEED)
56b6aeb0
HMH
3486 len += sprintf(p + len, "commands:\tspeed <speed>"
3487 " (<speed> is 0-65535)\n");
3488
3489 return len;
78f81cc4
BD
3490}
3491
18ad7996
HMH
3492static int fan_write_cmd_level(const char *cmd, int *rc)
3493{
3494 int level;
3495
a12095c2 3496 if (strlencmp(cmd, "level auto") == 0)
efa27145 3497 level = TP_EC_FAN_AUTO;
fe98a52c
HMH
3498 else if ((strlencmp(cmd, "level disengaged") == 0) |
3499 (strlencmp(cmd, "level full-speed") == 0))
efa27145 3500 level = TP_EC_FAN_FULLSPEED;
a12095c2 3501 else if (sscanf(cmd, "level %d", &level) != 1)
18ad7996
HMH
3502 return 0;
3503
fe98a52c 3504 if ((*rc = fan_set_level_safe(level)) == -ENXIO)
18ad7996
HMH
3505 printk(IBM_ERR "level command accepted for unsupported "
3506 "access mode %d", fan_control_access_mode);
3507
3508 return 1;
3509}
3510
3511static int fan_write_cmd_enable(const char *cmd, int *rc)
3512{
3513 if (strlencmp(cmd, "enable") != 0)
3514 return 0;
3515
3516 if ((*rc = fan_set_enable()) == -ENXIO)
3517 printk(IBM_ERR "enable command accepted for unsupported "
3518 "access mode %d", fan_control_access_mode);
3519
3520 return 1;
3521}
3522
3523static int fan_write_cmd_disable(const char *cmd, int *rc)
3524{
3525 if (strlencmp(cmd, "disable") != 0)
3526 return 0;
3527
3528 if ((*rc = fan_set_disable()) == -ENXIO)
3529 printk(IBM_ERR "disable command accepted for unsupported "
3530 "access mode %d", fan_control_access_mode);
3531
3532 return 1;
3533}
3534
3535static int fan_write_cmd_speed(const char *cmd, int *rc)
3536{
3537 int speed;
3538
a8b7a662
HMH
3539 /* TODO:
3540 * Support speed <low> <medium> <high> ? */
3541
18ad7996
HMH
3542 if (sscanf(cmd, "speed %d", &speed) != 1)
3543 return 0;
3544
3545 if ((*rc = fan_set_speed(speed)) == -ENXIO)
3546 printk(IBM_ERR "speed command accepted for unsupported "
3547 "access mode %d", fan_control_access_mode);
3548
3549 return 1;
3550}
3551
16663a87
HMH
3552static int fan_write_cmd_watchdog(const char *cmd, int *rc)
3553{
3554 int interval;
3555
3556 if (sscanf(cmd, "watchdog %d", &interval) != 1)
3557 return 0;
3558
3559 if (interval < 0 || interval > 120)
3560 *rc = -EINVAL;
3561 else
3562 fan_watchdog_maxinterval = interval;
3563
3564 return 1;
3565}
3566
18ad7996
HMH
3567static int fan_write(char *buf)
3568{
3569 char *cmd;
3570 int rc = 0;
3571
3572 while (!rc && (cmd = next_cmd(&buf))) {
efa27145 3573 if (!((fan_control_commands & TPACPI_FAN_CMD_LEVEL) &&
18ad7996 3574 fan_write_cmd_level(cmd, &rc)) &&
efa27145 3575 !((fan_control_commands & TPACPI_FAN_CMD_ENABLE) &&
18ad7996 3576 (fan_write_cmd_enable(cmd, &rc) ||
16663a87
HMH
3577 fan_write_cmd_disable(cmd, &rc) ||
3578 fan_write_cmd_watchdog(cmd, &rc))) &&
efa27145 3579 !((fan_control_commands & TPACPI_FAN_CMD_SPEED) &&
18ad7996
HMH
3580 fan_write_cmd_speed(cmd, &rc))
3581 )
3582 rc = -EINVAL;
16663a87
HMH
3583 else if (!rc)
3584 fan_watchdog_reset();
18ad7996
HMH
3585 }
3586
3587 return rc;
3588}
3589
a5763f22
HMH
3590static struct ibm_struct fan_driver_data = {
3591 .name = "fan",
3592 .read = fan_read,
3593 .write = fan_write,
3594 .exit = fan_exit,
a5763f22
HMH
3595};
3596
56b6aeb0
HMH
3597/****************************************************************************
3598 ****************************************************************************
3599 *
3600 * Infrastructure
3601 *
3602 ****************************************************************************
3603 ****************************************************************************/
3604
3605/* /proc support */
3606static struct proc_dir_entry *proc_dir = NULL;
16663a87 3607
56b6aeb0 3608/* Subdriver registry */
a5763f22
HMH
3609static LIST_HEAD(tpacpi_all_drivers);
3610
1da177e4 3611
56b6aeb0
HMH
3612/*
3613 * Module and infrastructure proble, init and exit handling
3614 */
1da177e4 3615
fe08bc4b 3616#ifdef CONFIG_THINKPAD_ACPI_DEBUG
a5763f22 3617static const char * __init str_supported(int is_supported)
fe08bc4b 3618{
a5763f22 3619 static char text_unsupported[] __initdata = "not supported";
fe08bc4b 3620
a5763f22 3621 return (is_supported)? &text_unsupported[4] : &text_unsupported[0];
fe08bc4b
HMH
3622}
3623#endif /* CONFIG_THINKPAD_ACPI_DEBUG */
3624
a5763f22 3625static int __init ibm_init(struct ibm_init_struct *iibm)
1da177e4
LT
3626{
3627 int ret;
a5763f22 3628 struct ibm_struct *ibm = iibm->data;
1da177e4
LT
3629 struct proc_dir_entry *entry;
3630
a5763f22
HMH
3631 BUG_ON(ibm == NULL);
3632
3633 INIT_LIST_HEAD(&ibm->all_drivers);
3634
92641177 3635 if (ibm->flags.experimental && !experimental)
1da177e4
LT
3636 return 0;
3637
fe08bc4b
HMH
3638 dbg_printk(TPACPI_DBG_INIT,
3639 "probing for %s\n", ibm->name);
3640
a5763f22
HMH
3641 if (iibm->init) {
3642 ret = iibm->init(iibm);
5fba344c
HMH
3643 if (ret > 0)
3644 return 0; /* probe failed */
3645 if (ret)
1da177e4 3646 return ret;
a5763f22 3647
92641177 3648 ibm->flags.init_called = 1;
1da177e4
LT
3649 }
3650
8d376cd6
HMH
3651 if (ibm->acpi) {
3652 if (ibm->acpi->hid) {
3653 ret = register_tpacpi_subdriver(ibm);
3654 if (ret)
3655 goto err_out;
3656 }
5fba344c 3657
8d376cd6
HMH
3658 if (ibm->acpi->notify) {
3659 ret = setup_acpi_notify(ibm);
3660 if (ret == -ENODEV) {
3661 printk(IBM_NOTICE "disabling subdriver %s\n",
3662 ibm->name);
3663 ret = 0;
3664 goto err_out;
3665 }
3666 if (ret < 0)
3667 goto err_out;
5fba344c 3668 }
5fba344c
HMH
3669 }
3670
fe08bc4b
HMH
3671 dbg_printk(TPACPI_DBG_INIT,
3672 "%s installed\n", ibm->name);
3673
78f81cc4
BD
3674 if (ibm->read) {
3675 entry = create_proc_entry(ibm->name,
3676 S_IFREG | S_IRUGO | S_IWUSR,
3677 proc_dir);
3678 if (!entry) {
3679 printk(IBM_ERR "unable to create proc entry %s\n",
3680 ibm->name);
5fba344c
HMH
3681 ret = -ENODEV;
3682 goto err_out;
78f81cc4
BD
3683 }
3684 entry->owner = THIS_MODULE;
3685 entry->data = ibm;
8d376cd6 3686 entry->read_proc = &dispatch_procfs_read;
78f81cc4 3687 if (ibm->write)
8d376cd6 3688 entry->write_proc = &dispatch_procfs_write;
92641177 3689 ibm->flags.proc_created = 1;
78f81cc4 3690 }
1da177e4 3691
a5763f22
HMH
3692 list_add_tail(&ibm->all_drivers, &tpacpi_all_drivers);
3693
1da177e4 3694 return 0;
5fba344c
HMH
3695
3696err_out:
fe08bc4b
HMH
3697 dbg_printk(TPACPI_DBG_INIT,
3698 "%s: at error exit path with result %d\n",
3699 ibm->name, ret);
3700
5fba344c
HMH
3701 ibm_exit(ibm);
3702 return (ret < 0)? ret : 0;
1da177e4
LT
3703}
3704
3705static void ibm_exit(struct ibm_struct *ibm)
3706{
fe08bc4b 3707 dbg_printk(TPACPI_DBG_EXIT, "removing %s\n", ibm->name);
a5763f22
HMH
3708
3709 list_del_init(&ibm->all_drivers);
3710
8d376cd6 3711 if (ibm->flags.acpi_notify_installed) {
fe08bc4b
HMH
3712 dbg_printk(TPACPI_DBG_EXIT,
3713 "%s: acpi_remove_notify_handler\n", ibm->name);
8d376cd6
HMH
3714 BUG_ON(!ibm->acpi);
3715 acpi_remove_notify_handler(*ibm->acpi->handle,
3716 ibm->acpi->type,
3717 dispatch_acpi_notify);
3718 ibm->flags.acpi_notify_installed = 0;
3719 ibm->flags.acpi_notify_installed = 0;
5fba344c 3720 }
1da177e4 3721
92641177 3722 if (ibm->flags.proc_created) {
fe08bc4b
HMH
3723 dbg_printk(TPACPI_DBG_EXIT,
3724 "%s: remove_proc_entry\n", ibm->name);
1da177e4 3725 remove_proc_entry(ibm->name, proc_dir);
92641177 3726 ibm->flags.proc_created = 0;
5fba344c 3727 }
1da177e4 3728
8d376cd6 3729 if (ibm->flags.acpi_driver_registered) {
fe08bc4b
HMH
3730 dbg_printk(TPACPI_DBG_EXIT,
3731 "%s: acpi_bus_unregister_driver\n", ibm->name);
8d376cd6
HMH
3732 BUG_ON(!ibm->acpi);
3733 acpi_bus_unregister_driver(ibm->acpi->driver);
3734 kfree(ibm->acpi->driver);
3735 ibm->acpi->driver = NULL;
3736 ibm->flags.acpi_driver_registered = 0;
5fba344c
HMH
3737 }
3738
92641177 3739 if (ibm->flags.init_called && ibm->exit) {
5fba344c 3740 ibm->exit();
92641177 3741 ibm->flags.init_called = 0;
1da177e4 3742 }
a5763f22
HMH
3743
3744 dbg_printk(TPACPI_DBG_INIT, "finished removing %s\n", ibm->name);
1da177e4
LT
3745}
3746
56b6aeb0
HMH
3747/* Probing */
3748
3749static char *ibm_thinkpad_ec_found = NULL;
3750
3751static char* __init check_dmi_for_ec(void)
1da177e4 3752{
56b6aeb0
HMH
3753 struct dmi_device *dev = NULL;
3754 char ec_fw_string[18];
1da177e4 3755
56b6aeb0
HMH
3756 /*
3757 * ThinkPad T23 or newer, A31 or newer, R50e or newer,
3758 * X32 or newer, all Z series; Some models must have an
3759 * up-to-date BIOS or they will not be detected.
3760 *
3761 * See http://thinkwiki.org/wiki/List_of_DMI_IDs
3762 */
3763 while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
3764 if (sscanf(dev->name,
3765 "IBM ThinkPad Embedded Controller -[%17c",
3766 ec_fw_string) == 1) {
3767 ec_fw_string[sizeof(ec_fw_string) - 1] = 0;
3768 ec_fw_string[strcspn(ec_fw_string, " ]")] = 0;
3769 return kstrdup(ec_fw_string, GFP_KERNEL);
78f81cc4 3770 }
1da177e4 3771 }
56b6aeb0 3772 return NULL;
1da177e4
LT
3773}
3774
5fba344c
HMH
3775static int __init probe_for_thinkpad(void)
3776{
3777 int is_thinkpad;
3778
3779 if (acpi_disabled)
3780 return -ENODEV;
3781
3782 /*
3783 * Non-ancient models have better DMI tagging, but very old models
3784 * don't.
3785 */
3786 is_thinkpad = dmi_name_in_vendors("ThinkPad");
3787
3788 /* ec is required because many other handles are relative to it */
8d376cd6 3789 IBM_ACPIHANDLE_INIT(ec);
5fba344c
HMH
3790 if (!ec_handle) {
3791 if (is_thinkpad)
3792 printk(IBM_ERR
3793 "Not yet supported ThinkPad detected!\n");
3794 return -ENODEV;
3795 }
3796
0dcef77c
HMH
3797 /*
3798 * Risks a regression on very old machines, but reduces potential
3799 * false positives a damn great deal
3800 */
3801 if (!is_thinkpad)
3802 is_thinkpad = dmi_name_in_vendors("IBM");
3803
3804 if (!is_thinkpad && !force_load)
3805 return -ENODEV;
3806
5fba344c
HMH
3807 return 0;
3808}
3809
3810
56b6aeb0 3811/* Module init, exit, parameters */
1da177e4 3812
a5763f22
HMH
3813static struct ibm_init_struct ibms_init[] __initdata = {
3814 {
3815 .init = thinkpad_acpi_driver_init,
3816 .data = &thinkpad_acpi_driver_data,
3817 },
3818 {
3819 .init = hotkey_init,
3820 .data = &hotkey_driver_data,
3821 },
3822 {
3823 .init = bluetooth_init,
3824 .data = &bluetooth_driver_data,
3825 },
3826 {
3827 .init = wan_init,
3828 .data = &wan_driver_data,
3829 },
3830 {
3831 .init = video_init,
3832 .data = &video_driver_data,
3833 },
3834 {
3835 .init = light_init,
3836 .data = &light_driver_data,
3837 },
3838#ifdef CONFIG_THINKPAD_ACPI_DOCK
3839 {
3840 .init = dock_init,
3841 .data = &dock_driver_data[0],
3842 },
3843 {
3844 .data = &dock_driver_data[1],
3845 },
3846#endif
3847#ifdef CONFIG_THINKPAD_ACPI_BAY
3848 {
3849 .init = bay_init,
3850 .data = &bay_driver_data,
3851 },
3852#endif
3853 {
3854 .init = cmos_init,
3855 .data = &cmos_driver_data,
3856 },
3857 {
3858 .init = led_init,
3859 .data = &led_driver_data,
3860 },
3861 {
3862 .init = beep_init,
3863 .data = &beep_driver_data,
3864 },
3865 {
3866 .init = thermal_init,
3867 .data = &thermal_driver_data,
3868 },
3869 {
3870 .data = &ecdump_driver_data,
3871 },
3872 {
3873 .init = brightness_init,
3874 .data = &brightness_driver_data,
3875 },
3876 {
3877 .data = &volume_driver_data,
3878 },
3879 {
3880 .init = fan_init,
3881 .data = &fan_driver_data,
3882 },
3883};
3884
3945ac36 3885static int __init set_ibm_param(const char *val, struct kernel_param *kp)
1da177e4
LT
3886{
3887 unsigned int i;
a5763f22 3888 struct ibm_struct *ibm;
1da177e4 3889
a5763f22
HMH
3890 for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
3891 ibm = ibms_init[i].data;
3892 BUG_ON(ibm == NULL);
3893
3894 if (strcmp(ibm->name, kp->name) == 0 && ibm->write) {
3895 if (strlen(val) > sizeof(ibms_init[i].param) - 2)
78f81cc4 3896 return -ENOSPC;
a5763f22
HMH
3897 strcpy(ibms_init[i].param, val);
3898 strcat(ibms_init[i].param, ",");
78f81cc4
BD
3899 return 0;
3900 }
a5763f22 3901 }
1da177e4 3902
1da177e4
LT
3903 return -EINVAL;
3904}
3905
56b6aeb0
HMH
3906static int experimental;
3907module_param(experimental, int, 0);
3908
132ce091
HMH
3909static u32 dbg_level;
3910module_param_named(debug, dbg_level, uint, 0);
3911
0dcef77c
HMH
3912static int force_load;
3913module_param(force_load, int, 0);
3914
ecf2a80a
HMH
3915static int fan_control_allowed;
3916module_param_named(fan_control, fan_control_allowed, int, 0);
3917
1da177e4
LT
3918#define IBM_PARAM(feature) \
3919 module_param_call(feature, set_ibm_param, NULL, NULL, 0)
3920
78f81cc4
BD
3921IBM_PARAM(hotkey);
3922IBM_PARAM(bluetooth);
3923IBM_PARAM(video);
3924IBM_PARAM(light);
85998248 3925#ifdef CONFIG_THINKPAD_ACPI_DOCK
78f81cc4 3926IBM_PARAM(dock);
63e5f248 3927#endif
85998248 3928#ifdef CONFIG_THINKPAD_ACPI_BAY
78f81cc4 3929IBM_PARAM(bay);
85998248 3930#endif /* CONFIG_THINKPAD_ACPI_BAY */
78f81cc4
BD
3931IBM_PARAM(cmos);
3932IBM_PARAM(led);
3933IBM_PARAM(beep);
3934IBM_PARAM(ecdump);
3935IBM_PARAM(brightness);
3936IBM_PARAM(volume);
3937IBM_PARAM(fan);
3938
1def7115 3939static int __init thinkpad_acpi_module_init(void)
1da177e4
LT
3940{
3941 int ret, i;
3942
54ae1501 3943 /* Driver-level probe */
5fba344c
HMH
3944 ret = probe_for_thinkpad();
3945 if (ret)
3946 return ret;
1da177e4 3947
54ae1501 3948 /* Driver initialization */
60eb0b35 3949 ibm_thinkpad_ec_found = check_dmi_for_ec();
8d376cd6
HMH
3950 IBM_ACPIHANDLE_INIT(ecrd);
3951 IBM_ACPIHANDLE_INIT(ecwr);
1da177e4 3952
8d376cd6 3953 proc_dir = proc_mkdir(IBM_PROC_DIR, acpi_root_dir);
1da177e4 3954 if (!proc_dir) {
8d376cd6 3955 printk(IBM_ERR "unable to create proc dir " IBM_PROC_DIR);
1def7115 3956 thinkpad_acpi_module_exit();
1da177e4
LT
3957 return -ENODEV;
3958 }
3959 proc_dir->owner = THIS_MODULE;
78f81cc4 3960
54ae1501
HMH
3961 ret = platform_driver_register(&tpacpi_pdriver);
3962 if (ret) {
3963 printk(IBM_ERR "unable to register platform driver\n");
3964 thinkpad_acpi_module_exit();
3965 return ret;
3966 }
176750d6
HMH
3967 ret = tpacpi_create_driver_attributes(&tpacpi_pdriver.driver);
3968 if (ret) {
3969 printk(IBM_ERR "unable to create sysfs driver attributes\n");
3970 thinkpad_acpi_module_exit();
3971 return ret;
3972 }
3973
54ae1501
HMH
3974
3975 /* Device initialization */
3976 tpacpi_pdev = platform_device_register_simple(IBM_DRVR_NAME, -1,
3977 NULL, 0);
3978 if (IS_ERR(tpacpi_pdev)) {
3979 ret = PTR_ERR(tpacpi_pdev);
3980 tpacpi_pdev = NULL;
3981 printk(IBM_ERR "unable to register platform device\n");
3982 thinkpad_acpi_module_exit();
3983 return ret;
3984 }
3985 tpacpi_hwmon = hwmon_device_register(&tpacpi_pdev->dev);
3986 if (IS_ERR(tpacpi_hwmon)) {
3987 ret = PTR_ERR(tpacpi_hwmon);
3988 tpacpi_hwmon = NULL;
3989 printk(IBM_ERR "unable to register hwmon device\n");
3990 thinkpad_acpi_module_exit();
3991 return ret;
3992 }
a5763f22
HMH
3993 for (i = 0; i < ARRAY_SIZE(ibms_init); i++) {
3994 ret = ibm_init(&ibms_init[i]);
3995 if (ret >= 0 && *ibms_init[i].param)
3996 ret = ibms_init[i].data->write(ibms_init[i].param);
1da177e4 3997 if (ret < 0) {
1def7115 3998 thinkpad_acpi_module_exit();
1da177e4
LT
3999 return ret;
4000 }
4001 }
4002
4003 return 0;
4004}
4005
1def7115 4006static void thinkpad_acpi_module_exit(void)
56b6aeb0 4007{
a5763f22
HMH
4008 struct ibm_struct *ibm, *itmp;
4009
4010 list_for_each_entry_safe_reverse(ibm, itmp,
4011 &tpacpi_all_drivers,
4012 all_drivers) {
4013 ibm_exit(ibm);
4014 }
56b6aeb0 4015
a5763f22 4016 dbg_printk(TPACPI_DBG_INIT, "finished subdriver exit path...\n");
56b6aeb0 4017
54ae1501
HMH
4018 if (tpacpi_hwmon)
4019 hwmon_device_unregister(tpacpi_hwmon);
4020
4021 if (tpacpi_pdev)
4022 platform_device_unregister(tpacpi_pdev);
4023
176750d6 4024 tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver);
54ae1501
HMH
4025 platform_driver_unregister(&tpacpi_pdriver);
4026
56b6aeb0 4027 if (proc_dir)
8d376cd6 4028 remove_proc_entry(IBM_PROC_DIR, acpi_root_dir);
56b6aeb0 4029
8d376cd6 4030 kfree(ibm_thinkpad_ec_found);
56b6aeb0
HMH
4031}
4032
1def7115
HMH
4033module_init(thinkpad_acpi_module_init);
4034module_exit(thinkpad_acpi_module_exit);
This page took 0.428925 seconds and 5 git commands to generate.