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 #include <linux/module.h>
15 #include <linux/kernel.h>
16 #include <linux/init.h>
17 #include <linux/platform_device.h>
18 #include <linux/backlight.h>
19 #include <linux/err.h>
20 #include <linux/dmi.h>
22 #include <linux/rfkill.h>
23 #include <linux/power_supply.h>
24 #include <linux/acpi.h>
26 #include <linux/i8042.h>
27 #include <linux/slab.h>
28 #include <linux/debugfs.h>
29 #include <linux/seq_file.h>
30 #include "../../firmware/dcdbas.h"
32 #define BRIGHTNESS_TOKEN 0x7d
34 /* This structure will be modified by the firmware when we enter
35 * system management mode, hence the volatiles */
37 struct calling_interface_buffer {
40 volatile u32 input[4];
41 volatile u32 output[4];
44 struct calling_interface_token {
53 struct calling_interface_structure {
54 struct dmi_header header;
58 struct calling_interface_token tokens[];
61 static int da_command_address;
62 static int da_command_code;
63 static int da_num_tokens;
64 static struct calling_interface_token *da_tokens;
66 static struct platform_driver platform_driver = {
68 .name = "dell-laptop",
73 static struct platform_device *platform_device;
74 static struct backlight_device *dell_backlight_device;
75 static struct rfkill *wifi_rfkill;
76 static struct rfkill *bluetooth_rfkill;
77 static struct rfkill *wwan_rfkill;
79 static const struct dmi_system_id __initdata dell_device_table[] = {
81 .ident = "Dell laptop",
83 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
84 DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
89 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
90 DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/
94 .ident = "Dell Computer Corporation",
96 DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
97 DMI_MATCH(DMI_CHASSIS_TYPE, "8"),
103 static struct dmi_system_id __devinitdata dell_blacklist[] = {
104 /* Supported by compal-laptop */
106 .ident = "Dell Mini 9",
108 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
109 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 910"),
113 .ident = "Dell Mini 10",
115 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
116 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1010"),
120 .ident = "Dell Mini 10v",
122 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
123 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1011"),
127 .ident = "Dell Mini 1012",
129 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
130 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1012"),
134 .ident = "Dell Inspiron 11z",
136 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
137 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1110"),
141 .ident = "Dell Mini 12",
143 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
144 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 1210"),
150 static struct calling_interface_buffer *buffer;
151 static struct page *bufferpage;
152 static DEFINE_MUTEX(buffer_mutex);
154 static int hwswitch_state;
156 static void get_buffer(void)
158 mutex_lock(&buffer_mutex);
159 memset(buffer, 0, sizeof(struct calling_interface_buffer));
162 static void release_buffer(void)
164 mutex_unlock(&buffer_mutex);
167 static void __init parse_da_table(const struct dmi_header *dm)
169 /* Final token is a terminator, so we don't want to copy it */
170 int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1;
171 struct calling_interface_structure *table =
172 container_of(dm, struct calling_interface_structure, header);
174 /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
180 da_command_address = table->cmdIOAddress;
181 da_command_code = table->cmdIOCode;
183 da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
184 sizeof(struct calling_interface_token),
190 memcpy(da_tokens+da_num_tokens, table->tokens,
191 sizeof(struct calling_interface_token) * tokens);
193 da_num_tokens += tokens;
196 static void __init find_tokens(const struct dmi_header *dm, void *dummy)
199 case 0xd4: /* Indexed IO */
201 case 0xd5: /* Protected Area Type 1 */
203 case 0xd6: /* Protected Area Type 2 */
205 case 0xda: /* Calling interface */
211 static int find_token_location(int tokenid)
214 for (i = 0; i < da_num_tokens; i++) {
215 if (da_tokens[i].tokenID == tokenid)
216 return da_tokens[i].location;
222 static struct calling_interface_buffer *
223 dell_send_request(struct calling_interface_buffer *buffer, int class,
226 struct smi_cmd command;
228 command.magic = SMI_CMD_MAGIC;
229 command.command_address = da_command_address;
230 command.command_code = da_command_code;
231 command.ebx = virt_to_phys(buffer);
232 command.ecx = 0x42534931;
234 buffer->class = class;
235 buffer->select = select;
237 dcdbas_smi_request(&command);
242 /* Derived from information in DellWirelessCtl.cpp:
243 Class 17, select 11 is radio control. It returns an array of 32-bit values.
245 Input byte 0 = 0: Wireless information
247 result[0]: return code
249 Bit 0: Hardware switch supported
250 Bit 1: Wifi locator supported
251 Bit 2: Wifi is supported
252 Bit 3: Bluetooth is supported
253 Bit 4: WWAN is supported
254 Bit 5: Wireless keyboard supported
256 Bit 8: Wifi is installed
257 Bit 9: Bluetooth is installed
258 Bit 10: WWAN is installed
260 Bit 16: Hardware switch is on
261 Bit 17: Wifi is blocked
262 Bit 18: Bluetooth is blocked
263 Bit 19: WWAN is blocked
265 result[2]: NVRAM size in bytes
266 result[3]: NVRAM format version number
268 Input byte 0 = 2: Wireless switch configuration
269 result[0]: return code
271 Bit 0: Wifi controlled by switch
272 Bit 1: Bluetooth controlled by switch
273 Bit 2: WWAN controlled by switch
275 Bit 7: Wireless switch config locked
276 Bit 8: Wifi locator enabled
278 Bit 15: Wifi locator setting locked
282 static int dell_rfkill_set(void *data, bool blocked)
284 int disable = blocked ? 1 : 0;
285 unsigned long radio = (unsigned long)data;
286 int hwswitch_bit = (unsigned long)data - 1;
290 dell_send_request(buffer, 17, 11);
292 /* If the hardware switch controls this radio, and the hardware
293 switch is disabled, don't allow changing the software state.
294 If the hardware switch is reported as not supported, always
295 fire the SMI to toggle the killswitch. */
296 if ((hwswitch_state & BIT(hwswitch_bit)) &&
297 !(buffer->output[1] & BIT(16)) &&
298 (buffer->output[1] & BIT(0))) {
303 buffer->input[0] = (1 | (radio<<8) | (disable << 16));
304 dell_send_request(buffer, 17, 11);
311 static void dell_rfkill_query(struct rfkill *rfkill, void *data)
314 int bit = (unsigned long)data + 16;
315 int hwswitch_bit = (unsigned long)data - 1;
318 dell_send_request(buffer, 17, 11);
319 status = buffer->output[1];
322 rfkill_set_sw_state(rfkill, !!(status & BIT(bit)));
324 if (hwswitch_state & (BIT(hwswitch_bit)))
325 rfkill_set_hw_state(rfkill, !(status & BIT(16)));
328 static const struct rfkill_ops dell_rfkill_ops = {
329 .set_block = dell_rfkill_set,
330 .query = dell_rfkill_query,
333 static struct dentry *dell_laptop_dir;
335 static int dell_debugfs_show(struct seq_file *s, void *data)
340 dell_send_request(buffer, 17, 11);
341 status = buffer->output[1];
344 seq_printf(s, "status:\t0x%X\n", status);
345 seq_printf(s, "Bit 0 : Hardware switch supported: %lu\n",
347 seq_printf(s, "Bit 1 : Wifi locator supported: %lu\n",
348 (status & BIT(1)) >> 1);
349 seq_printf(s, "Bit 2 : Wifi is supported: %lu\n",
350 (status & BIT(2)) >> 2);
351 seq_printf(s, "Bit 3 : Bluetooth is supported: %lu\n",
352 (status & BIT(3)) >> 3);
353 seq_printf(s, "Bit 4 : WWAN is supported: %lu\n",
354 (status & BIT(4)) >> 4);
355 seq_printf(s, "Bit 5 : Wireless keyboard supported: %lu\n",
356 (status & BIT(5)) >> 5);
357 seq_printf(s, "Bit 8 : Wifi is installed: %lu\n",
358 (status & BIT(8)) >> 8);
359 seq_printf(s, "Bit 9 : Bluetooth is installed: %lu\n",
360 (status & BIT(9)) >> 9);
361 seq_printf(s, "Bit 10: WWAN is installed: %lu\n",
362 (status & BIT(10)) >> 10);
363 seq_printf(s, "Bit 16: Hardware switch is on: %lu\n",
364 (status & BIT(16)) >> 16);
365 seq_printf(s, "Bit 17: Wifi is blocked: %lu\n",
366 (status & BIT(17)) >> 17);
367 seq_printf(s, "Bit 18: Bluetooth is blocked: %lu\n",
368 (status & BIT(18)) >> 18);
369 seq_printf(s, "Bit 19: WWAN is blocked: %lu\n",
370 (status & BIT(19)) >> 19);
372 seq_printf(s, "\nhwswitch_state:\t0x%X\n", hwswitch_state);
373 seq_printf(s, "Bit 0 : Wifi controlled by switch: %lu\n",
374 hwswitch_state & BIT(0));
375 seq_printf(s, "Bit 1 : Bluetooth controlled by switch: %lu\n",
376 (hwswitch_state & BIT(1)) >> 1);
377 seq_printf(s, "Bit 2 : WWAN controlled by switch: %lu\n",
378 (hwswitch_state & BIT(2)) >> 2);
379 seq_printf(s, "Bit 7 : Wireless switch config locked: %lu\n",
380 (hwswitch_state & BIT(7)) >> 7);
381 seq_printf(s, "Bit 8 : Wifi locator enabled: %lu\n",
382 (hwswitch_state & BIT(8)) >> 8);
383 seq_printf(s, "Bit 15: Wifi locator setting locked: %lu\n",
384 (hwswitch_state & BIT(15)) >> 15);
389 static int dell_debugfs_open(struct inode *inode, struct file *file)
391 return single_open(file, dell_debugfs_show, inode->i_private);
394 static const struct file_operations dell_debugfs_fops = {
395 .owner = THIS_MODULE,
396 .open = dell_debugfs_open,
399 .release = single_release,
402 static void dell_update_rfkill(struct work_struct *ignored)
407 dell_send_request(buffer, 17, 11);
408 status = buffer->output[1];
411 /* if hardware rfkill is not supported, set it explicitly */
412 if (!(status & BIT(0))) {
414 dell_rfkill_set((void *)1, !((status & BIT(17)) >> 17));
415 if (bluetooth_rfkill)
416 dell_rfkill_set((void *)2, !((status & BIT(18)) >> 18));
418 dell_rfkill_set((void *)3, !((status & BIT(19)) >> 19));
422 dell_rfkill_query(wifi_rfkill, (void *)1);
423 if (bluetooth_rfkill)
424 dell_rfkill_query(bluetooth_rfkill, (void *)2);
426 dell_rfkill_query(wwan_rfkill, (void *)3);
428 static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
431 static int __init dell_setup_rfkill(void)
436 if (dmi_check_system(dell_blacklist)) {
437 printk(KERN_INFO "dell-laptop: Blacklisted hardware detected - "
438 "not enabling rfkill\n");
443 dell_send_request(buffer, 17, 11);
444 status = buffer->output[1];
445 buffer->input[0] = 0x2;
446 dell_send_request(buffer, 17, 11);
447 hwswitch_state = buffer->output[1];
450 if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
451 wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev,
453 &dell_rfkill_ops, (void *) 1);
458 ret = rfkill_register(wifi_rfkill);
463 if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
464 bluetooth_rfkill = rfkill_alloc("dell-bluetooth",
465 &platform_device->dev,
466 RFKILL_TYPE_BLUETOOTH,
467 &dell_rfkill_ops, (void *) 2);
468 if (!bluetooth_rfkill) {
472 ret = rfkill_register(bluetooth_rfkill);
477 if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
478 wwan_rfkill = rfkill_alloc("dell-wwan",
479 &platform_device->dev,
481 &dell_rfkill_ops, (void *) 3);
486 ret = rfkill_register(wwan_rfkill);
493 rfkill_destroy(wwan_rfkill);
494 if (bluetooth_rfkill)
495 rfkill_unregister(bluetooth_rfkill);
497 rfkill_destroy(bluetooth_rfkill);
499 rfkill_unregister(wifi_rfkill);
501 rfkill_destroy(wifi_rfkill);
506 static void dell_cleanup_rfkill(void)
509 rfkill_unregister(wifi_rfkill);
510 rfkill_destroy(wifi_rfkill);
512 if (bluetooth_rfkill) {
513 rfkill_unregister(bluetooth_rfkill);
514 rfkill_destroy(bluetooth_rfkill);
517 rfkill_unregister(wwan_rfkill);
518 rfkill_destroy(wwan_rfkill);
522 static int dell_send_intensity(struct backlight_device *bd)
527 buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
528 buffer->input[1] = bd->props.brightness;
530 if (buffer->input[0] == -1) {
535 if (power_supply_is_system_supplied() > 0)
536 dell_send_request(buffer, 1, 2);
538 dell_send_request(buffer, 1, 1);
545 static int dell_get_intensity(struct backlight_device *bd)
550 buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN);
552 if (buffer->input[0] == -1) {
557 if (power_supply_is_system_supplied() > 0)
558 dell_send_request(buffer, 0, 2);
560 dell_send_request(buffer, 0, 1);
566 return buffer->output[1];
569 static const struct backlight_ops dell_ops = {
570 .get_brightness = dell_get_intensity,
571 .update_status = dell_send_intensity,
574 static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
577 static bool extended;
582 if (unlikely(data == 0xe0)) {
585 } else if (unlikely(extended)) {
588 schedule_delayed_work(&dell_rfkill_work,
589 round_jiffies_relative(HZ));
598 static int __init dell_init(void)
600 int max_intensity = 0;
603 if (!dmi_check_system(dell_device_table))
606 dmi_walk(find_tokens, NULL);
609 printk(KERN_INFO "dell-laptop: Unable to find dmi tokens\n");
613 ret = platform_driver_register(&platform_driver);
615 goto fail_platform_driver;
616 platform_device = platform_device_alloc("dell-laptop", -1);
617 if (!platform_device) {
619 goto fail_platform_device1;
621 ret = platform_device_add(platform_device);
623 goto fail_platform_device2;
626 * Allocate buffer below 4GB for SMI data--only 32-bit physical addr
627 * is passed to SMI handler.
629 bufferpage = alloc_page(GFP_KERNEL | GFP_DMA32);
633 buffer = page_address(bufferpage);
634 mutex_init(&buffer_mutex);
636 ret = dell_setup_rfkill();
639 printk(KERN_WARNING "dell-laptop: Unable to setup rfkill\n");
643 ret = i8042_install_filter(dell_laptop_i8042_filter);
646 "dell-laptop: 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:*");