2 * Driver for Dell laptop extras
4 * Copyright (c) Red Hat <mjg@redhat.com>
6 * Based on documentation in the libsmbios package, Copyright (C) 2005 Dell
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
14 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
16 #include <linux/module.h>
17 #include <linux/kernel.h>
18 #include <linux/init.h>
19 #include <linux/platform_device.h>
20 #include <linux/backlight.h>
21 #include <linux/err.h>
22 #include <linux/dmi.h>
24 #include <linux/rfkill.h>
25 #include <linux/power_supply.h>
26 #include <linux/acpi.h>
28 #include <linux/i8042.h>
29 #include <linux/slab.h>
30 #include <linux/debugfs.h>
31 #include <linux/seq_file.h>
32 #include "../../firmware/dcdbas.h"
34 #define BRIGHTNESS_TOKEN 0x7d
36 /* This structure will be modified by the firmware when we enter
37 * system management mode, hence the volatiles */
39 struct calling_interface_buffer
{
42 volatile u32 input
[4];
43 volatile u32 output
[4];
46 struct calling_interface_token
{
55 struct calling_interface_structure
{
56 struct dmi_header header
;
60 struct calling_interface_token tokens
[];
63 static int da_command_address
;
64 static int da_command_code
;
65 static int da_num_tokens
;
66 static struct calling_interface_token
*da_tokens
;
68 static struct platform_driver platform_driver
= {
70 .name
= "dell-laptop",
75 static struct platform_device
*platform_device
;
76 static struct backlight_device
*dell_backlight_device
;
77 static struct rfkill
*wifi_rfkill
;
78 static struct rfkill
*bluetooth_rfkill
;
79 static struct rfkill
*wwan_rfkill
;
81 static const struct dmi_system_id __initdata dell_device_table
[] = {
83 .ident
= "Dell laptop",
85 DMI_MATCH(DMI_SYS_VENDOR
, "Dell Inc."),
86 DMI_MATCH(DMI_CHASSIS_TYPE
, "8"),
91 DMI_MATCH(DMI_SYS_VENDOR
, "Dell Inc."),
92 DMI_MATCH(DMI_CHASSIS_TYPE
, "9"), /*Laptop*/
96 .ident
= "Dell Computer Corporation",
98 DMI_MATCH(DMI_SYS_VENDOR
, "Dell Computer Corporation"),
99 DMI_MATCH(DMI_CHASSIS_TYPE
, "8"),
105 static struct dmi_system_id __devinitdata dell_blacklist
[] = {
106 /* Supported by compal-laptop */
108 .ident
= "Dell Mini 9",
110 DMI_MATCH(DMI_SYS_VENDOR
, "Dell Inc."),
111 DMI_MATCH(DMI_PRODUCT_NAME
, "Inspiron 910"),
115 .ident
= "Dell Mini 10",
117 DMI_MATCH(DMI_SYS_VENDOR
, "Dell Inc."),
118 DMI_MATCH(DMI_PRODUCT_NAME
, "Inspiron 1010"),
122 .ident
= "Dell Mini 10v",
124 DMI_MATCH(DMI_SYS_VENDOR
, "Dell Inc."),
125 DMI_MATCH(DMI_PRODUCT_NAME
, "Inspiron 1011"),
129 .ident
= "Dell Mini 1012",
131 DMI_MATCH(DMI_SYS_VENDOR
, "Dell Inc."),
132 DMI_MATCH(DMI_PRODUCT_NAME
, "Inspiron 1012"),
136 .ident
= "Dell Inspiron 11z",
138 DMI_MATCH(DMI_SYS_VENDOR
, "Dell Inc."),
139 DMI_MATCH(DMI_PRODUCT_NAME
, "Inspiron 1110"),
143 .ident
= "Dell Mini 12",
145 DMI_MATCH(DMI_SYS_VENDOR
, "Dell Inc."),
146 DMI_MATCH(DMI_PRODUCT_NAME
, "Inspiron 1210"),
152 static struct calling_interface_buffer
*buffer
;
153 static struct page
*bufferpage
;
154 static DEFINE_MUTEX(buffer_mutex
);
156 static int hwswitch_state
;
158 static void get_buffer(void)
160 mutex_lock(&buffer_mutex
);
161 memset(buffer
, 0, sizeof(struct calling_interface_buffer
));
164 static void release_buffer(void)
166 mutex_unlock(&buffer_mutex
);
169 static void __init
parse_da_table(const struct dmi_header
*dm
)
171 /* Final token is a terminator, so we don't want to copy it */
172 int tokens
= (dm
->length
-11)/sizeof(struct calling_interface_token
)-1;
173 struct calling_interface_structure
*table
=
174 container_of(dm
, struct calling_interface_structure
, header
);
176 /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
182 da_command_address
= table
->cmdIOAddress
;
183 da_command_code
= table
->cmdIOCode
;
185 da_tokens
= krealloc(da_tokens
, (da_num_tokens
+ tokens
) *
186 sizeof(struct calling_interface_token
),
192 memcpy(da_tokens
+da_num_tokens
, table
->tokens
,
193 sizeof(struct calling_interface_token
) * tokens
);
195 da_num_tokens
+= tokens
;
198 static void __init
find_tokens(const struct dmi_header
*dm
, void *dummy
)
201 case 0xd4: /* Indexed IO */
203 case 0xd5: /* Protected Area Type 1 */
205 case 0xd6: /* Protected Area Type 2 */
207 case 0xda: /* Calling interface */
213 static int find_token_location(int tokenid
)
216 for (i
= 0; i
< da_num_tokens
; i
++) {
217 if (da_tokens
[i
].tokenID
== tokenid
)
218 return da_tokens
[i
].location
;
224 static struct calling_interface_buffer
*
225 dell_send_request(struct calling_interface_buffer
*buffer
, int class,
228 struct smi_cmd command
;
230 command
.magic
= SMI_CMD_MAGIC
;
231 command
.command_address
= da_command_address
;
232 command
.command_code
= da_command_code
;
233 command
.ebx
= virt_to_phys(buffer
);
234 command
.ecx
= 0x42534931;
236 buffer
->class = class;
237 buffer
->select
= select
;
239 dcdbas_smi_request(&command
);
244 /* Derived from information in DellWirelessCtl.cpp:
245 Class 17, select 11 is radio control. It returns an array of 32-bit values.
247 Input byte 0 = 0: Wireless information
249 result[0]: return code
251 Bit 0: Hardware switch supported
252 Bit 1: Wifi locator supported
253 Bit 2: Wifi is supported
254 Bit 3: Bluetooth is supported
255 Bit 4: WWAN is supported
256 Bit 5: Wireless keyboard supported
258 Bit 8: Wifi is installed
259 Bit 9: Bluetooth is installed
260 Bit 10: WWAN is installed
262 Bit 16: Hardware switch is on
263 Bit 17: Wifi is blocked
264 Bit 18: Bluetooth is blocked
265 Bit 19: WWAN is blocked
267 result[2]: NVRAM size in bytes
268 result[3]: NVRAM format version number
270 Input byte 0 = 2: Wireless switch configuration
271 result[0]: return code
273 Bit 0: Wifi controlled by switch
274 Bit 1: Bluetooth controlled by switch
275 Bit 2: WWAN controlled by switch
277 Bit 7: Wireless switch config locked
278 Bit 8: Wifi locator enabled
280 Bit 15: Wifi locator setting locked
284 static int dell_rfkill_set(void *data
, bool blocked
)
286 int disable
= blocked
? 1 : 0;
287 unsigned long radio
= (unsigned long)data
;
288 int hwswitch_bit
= (unsigned long)data
- 1;
292 dell_send_request(buffer
, 17, 11);
294 /* If the hardware switch controls this radio, and the hardware
295 switch is disabled, don't allow changing the software state.
296 If the hardware switch is reported as not supported, always
297 fire the SMI to toggle the killswitch. */
298 if ((hwswitch_state
& BIT(hwswitch_bit
)) &&
299 !(buffer
->output
[1] & BIT(16)) &&
300 (buffer
->output
[1] & BIT(0))) {
305 buffer
->input
[0] = (1 | (radio
<<8) | (disable
<< 16));
306 dell_send_request(buffer
, 17, 11);
313 static void dell_rfkill_query(struct rfkill
*rfkill
, void *data
)
316 int bit
= (unsigned long)data
+ 16;
317 int hwswitch_bit
= (unsigned long)data
- 1;
320 dell_send_request(buffer
, 17, 11);
321 status
= buffer
->output
[1];
324 rfkill_set_sw_state(rfkill
, !!(status
& BIT(bit
)));
326 if (hwswitch_state
& (BIT(hwswitch_bit
)))
327 rfkill_set_hw_state(rfkill
, !(status
& BIT(16)));
330 static const struct rfkill_ops dell_rfkill_ops
= {
331 .set_block
= dell_rfkill_set
,
332 .query
= dell_rfkill_query
,
335 static struct dentry
*dell_laptop_dir
;
337 static int dell_debugfs_show(struct seq_file
*s
, void *data
)
342 dell_send_request(buffer
, 17, 11);
343 status
= buffer
->output
[1];
346 seq_printf(s
, "status:\t0x%X\n", status
);
347 seq_printf(s
, "Bit 0 : Hardware switch supported: %lu\n",
349 seq_printf(s
, "Bit 1 : Wifi locator supported: %lu\n",
350 (status
& BIT(1)) >> 1);
351 seq_printf(s
, "Bit 2 : Wifi is supported: %lu\n",
352 (status
& BIT(2)) >> 2);
353 seq_printf(s
, "Bit 3 : Bluetooth is supported: %lu\n",
354 (status
& BIT(3)) >> 3);
355 seq_printf(s
, "Bit 4 : WWAN is supported: %lu\n",
356 (status
& BIT(4)) >> 4);
357 seq_printf(s
, "Bit 5 : Wireless keyboard supported: %lu\n",
358 (status
& BIT(5)) >> 5);
359 seq_printf(s
, "Bit 8 : Wifi is installed: %lu\n",
360 (status
& BIT(8)) >> 8);
361 seq_printf(s
, "Bit 9 : Bluetooth is installed: %lu\n",
362 (status
& BIT(9)) >> 9);
363 seq_printf(s
, "Bit 10: WWAN is installed: %lu\n",
364 (status
& BIT(10)) >> 10);
365 seq_printf(s
, "Bit 16: Hardware switch is on: %lu\n",
366 (status
& BIT(16)) >> 16);
367 seq_printf(s
, "Bit 17: Wifi is blocked: %lu\n",
368 (status
& BIT(17)) >> 17);
369 seq_printf(s
, "Bit 18: Bluetooth is blocked: %lu\n",
370 (status
& BIT(18)) >> 18);
371 seq_printf(s
, "Bit 19: WWAN is blocked: %lu\n",
372 (status
& BIT(19)) >> 19);
374 seq_printf(s
, "\nhwswitch_state:\t0x%X\n", hwswitch_state
);
375 seq_printf(s
, "Bit 0 : Wifi controlled by switch: %lu\n",
376 hwswitch_state
& BIT(0));
377 seq_printf(s
, "Bit 1 : Bluetooth controlled by switch: %lu\n",
378 (hwswitch_state
& BIT(1)) >> 1);
379 seq_printf(s
, "Bit 2 : WWAN controlled by switch: %lu\n",
380 (hwswitch_state
& BIT(2)) >> 2);
381 seq_printf(s
, "Bit 7 : Wireless switch config locked: %lu\n",
382 (hwswitch_state
& BIT(7)) >> 7);
383 seq_printf(s
, "Bit 8 : Wifi locator enabled: %lu\n",
384 (hwswitch_state
& BIT(8)) >> 8);
385 seq_printf(s
, "Bit 15: Wifi locator setting locked: %lu\n",
386 (hwswitch_state
& BIT(15)) >> 15);
391 static int dell_debugfs_open(struct inode
*inode
, struct file
*file
)
393 return single_open(file
, dell_debugfs_show
, inode
->i_private
);
396 static const struct file_operations dell_debugfs_fops
= {
397 .owner
= THIS_MODULE
,
398 .open
= dell_debugfs_open
,
401 .release
= single_release
,
404 static void dell_update_rfkill(struct work_struct
*ignored
)
409 dell_send_request(buffer
, 17, 11);
410 status
= buffer
->output
[1];
413 /* if hardware rfkill is not supported, set it explicitly */
414 if (!(status
& BIT(0))) {
416 dell_rfkill_set((void *)1, !((status
& BIT(17)) >> 17));
417 if (bluetooth_rfkill
)
418 dell_rfkill_set((void *)2, !((status
& BIT(18)) >> 18));
420 dell_rfkill_set((void *)3, !((status
& BIT(19)) >> 19));
424 dell_rfkill_query(wifi_rfkill
, (void *)1);
425 if (bluetooth_rfkill
)
426 dell_rfkill_query(bluetooth_rfkill
, (void *)2);
428 dell_rfkill_query(wwan_rfkill
, (void *)3);
430 static DECLARE_DELAYED_WORK(dell_rfkill_work
, dell_update_rfkill
);
433 static int __init
dell_setup_rfkill(void)
438 if (dmi_check_system(dell_blacklist
)) {
439 pr_info("Blacklisted hardware detected - not enabling rfkill\n");
444 dell_send_request(buffer
, 17, 11);
445 status
= buffer
->output
[1];
446 buffer
->input
[0] = 0x2;
447 dell_send_request(buffer
, 17, 11);
448 hwswitch_state
= buffer
->output
[1];
451 if ((status
& (1<<2|1<<8)) == (1<<2|1<<8)) {
452 wifi_rfkill
= rfkill_alloc("dell-wifi", &platform_device
->dev
,
454 &dell_rfkill_ops
, (void *) 1);
459 ret
= rfkill_register(wifi_rfkill
);
464 if ((status
& (1<<3|1<<9)) == (1<<3|1<<9)) {
465 bluetooth_rfkill
= rfkill_alloc("dell-bluetooth",
466 &platform_device
->dev
,
467 RFKILL_TYPE_BLUETOOTH
,
468 &dell_rfkill_ops
, (void *) 2);
469 if (!bluetooth_rfkill
) {
473 ret
= rfkill_register(bluetooth_rfkill
);
478 if ((status
& (1<<4|1<<10)) == (1<<4|1<<10)) {
479 wwan_rfkill
= rfkill_alloc("dell-wwan",
480 &platform_device
->dev
,
482 &dell_rfkill_ops
, (void *) 3);
487 ret
= rfkill_register(wwan_rfkill
);
494 rfkill_destroy(wwan_rfkill
);
495 if (bluetooth_rfkill
)
496 rfkill_unregister(bluetooth_rfkill
);
498 rfkill_destroy(bluetooth_rfkill
);
500 rfkill_unregister(wifi_rfkill
);
502 rfkill_destroy(wifi_rfkill
);
507 static void dell_cleanup_rfkill(void)
510 rfkill_unregister(wifi_rfkill
);
511 rfkill_destroy(wifi_rfkill
);
513 if (bluetooth_rfkill
) {
514 rfkill_unregister(bluetooth_rfkill
);
515 rfkill_destroy(bluetooth_rfkill
);
518 rfkill_unregister(wwan_rfkill
);
519 rfkill_destroy(wwan_rfkill
);
523 static int dell_send_intensity(struct backlight_device
*bd
)
528 buffer
->input
[0] = find_token_location(BRIGHTNESS_TOKEN
);
529 buffer
->input
[1] = bd
->props
.brightness
;
531 if (buffer
->input
[0] == -1) {
536 if (power_supply_is_system_supplied() > 0)
537 dell_send_request(buffer
, 1, 2);
539 dell_send_request(buffer
, 1, 1);
546 static int dell_get_intensity(struct backlight_device
*bd
)
551 buffer
->input
[0] = find_token_location(BRIGHTNESS_TOKEN
);
553 if (buffer
->input
[0] == -1) {
558 if (power_supply_is_system_supplied() > 0)
559 dell_send_request(buffer
, 0, 2);
561 dell_send_request(buffer
, 0, 1);
567 return buffer
->output
[1];
570 static const struct backlight_ops dell_ops
= {
571 .get_brightness
= dell_get_intensity
,
572 .update_status
= dell_send_intensity
,
575 static bool dell_laptop_i8042_filter(unsigned char data
, unsigned char str
,
578 static bool extended
;
583 if (unlikely(data
== 0xe0)) {
586 } else if (unlikely(extended
)) {
589 schedule_delayed_work(&dell_rfkill_work
,
590 round_jiffies_relative(HZ
));
599 static int __init
dell_init(void)
601 int max_intensity
= 0;
604 if (!dmi_check_system(dell_device_table
))
607 dmi_walk(find_tokens
, NULL
);
610 pr_info("Unable to find dmi tokens\n");
614 ret
= platform_driver_register(&platform_driver
);
616 goto fail_platform_driver
;
617 platform_device
= platform_device_alloc("dell-laptop", -1);
618 if (!platform_device
) {
620 goto fail_platform_device1
;
622 ret
= platform_device_add(platform_device
);
624 goto fail_platform_device2
;
627 * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
628 * is passed to SMI handler.
630 bufferpage
= alloc_page(GFP_KERNEL
| GFP_DMA32
);
634 buffer
= page_address(bufferpage
);
635 mutex_init(&buffer_mutex
);
637 ret
= dell_setup_rfkill();
640 pr_warn("Unable to setup rfkill\n");
644 ret
= i8042_install_filter(dell_laptop_i8042_filter
);
646 pr_warn("Unable to install key filter\n");
650 dell_laptop_dir
= debugfs_create_dir("dell_laptop", NULL
);
651 if (dell_laptop_dir
!= NULL
)
652 debugfs_create_file("rfkill", 0444, dell_laptop_dir
, NULL
,
656 /* In the event of an ACPI backlight being available, don't
657 * register the platform controller.
659 if (acpi_video_backlight_support())
664 buffer
->input
[0] = find_token_location(BRIGHTNESS_TOKEN
);
665 if (buffer
->input
[0] != -1) {
666 dell_send_request(buffer
, 0, 2);
667 max_intensity
= buffer
->output
[3];
672 struct backlight_properties props
;
673 memset(&props
, 0, sizeof(struct backlight_properties
));
674 props
.type
= BACKLIGHT_PLATFORM
;
675 props
.max_brightness
= max_intensity
;
676 dell_backlight_device
= backlight_device_register("dell_backlight",
677 &platform_device
->dev
,
682 if (IS_ERR(dell_backlight_device
)) {
683 ret
= PTR_ERR(dell_backlight_device
);
684 dell_backlight_device
= NULL
;
688 dell_backlight_device
->props
.brightness
=
689 dell_get_intensity(dell_backlight_device
);
690 backlight_update_status(dell_backlight_device
);
696 i8042_remove_filter(dell_laptop_i8042_filter
);
697 cancel_delayed_work_sync(&dell_rfkill_work
);
699 dell_cleanup_rfkill();
701 free_page((unsigned long)bufferpage
);
703 platform_device_del(platform_device
);
704 fail_platform_device2
:
705 platform_device_put(platform_device
);
706 fail_platform_device1
:
707 platform_driver_unregister(&platform_driver
);
708 fail_platform_driver
:
713 static void __exit
dell_exit(void)
715 debugfs_remove_recursive(dell_laptop_dir
);
716 i8042_remove_filter(dell_laptop_i8042_filter
);
717 cancel_delayed_work_sync(&dell_rfkill_work
);
718 backlight_device_unregister(dell_backlight_device
);
719 dell_cleanup_rfkill();
720 if (platform_device
) {
721 platform_device_unregister(platform_device
);
722 platform_driver_unregister(&platform_driver
);
725 free_page((unsigned long)buffer
);
728 module_init(dell_init
);
729 module_exit(dell_exit
);
731 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
732 MODULE_DESCRIPTION("Dell laptop driver");
733 MODULE_LICENSE("GPL");
734 MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*");
735 MODULE_ALIAS("dmi:*svnDellInc.:*:ct9:*");
736 MODULE_ALIAS("dmi:*svnDellComputerCorporation.:*:ct8:*");