Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.11 637 lines 15 kB view raw
1/* 2 * Driver for Dell laptop extras 3 * 4 * Copyright (c) Red Hat <mjg@redhat.com> 5 * 6 * Based on documentation in the libsmbios package, Copyright (C) 2005 Dell 7 * Inc. 8 * 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. 12 */ 13 14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 15 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> 23#include <linux/io.h> 24#include <linux/power_supply.h> 25#include <linux/acpi.h> 26#include <linux/mm.h> 27#include <linux/i8042.h> 28#include <linux/slab.h> 29#include <linux/debugfs.h> 30#include <linux/seq_file.h> 31#include "../../firmware/dcdbas.h" 32 33#define BRIGHTNESS_TOKEN 0x7d 34 35/* This structure will be modified by the firmware when we enter 36 * system management mode, hence the volatiles */ 37 38struct calling_interface_buffer { 39 u16 class; 40 u16 select; 41 volatile u32 input[4]; 42 volatile u32 output[4]; 43} __packed; 44 45struct calling_interface_token { 46 u16 tokenID; 47 u16 location; 48 union { 49 u16 value; 50 u16 stringlength; 51 }; 52}; 53 54struct calling_interface_structure { 55 struct dmi_header header; 56 u16 cmdIOAddress; 57 u8 cmdIOCode; 58 u32 supportedCmds; 59 struct calling_interface_token tokens[]; 60} __packed; 61 62struct quirk_entry { 63 u8 touchpad_led; 64}; 65 66static struct quirk_entry *quirks; 67 68static struct quirk_entry quirk_dell_vostro_v130 = { 69 .touchpad_led = 1, 70}; 71 72static int dmi_matched(const struct dmi_system_id *dmi) 73{ 74 quirks = dmi->driver_data; 75 return 1; 76} 77 78static int da_command_address; 79static int da_command_code; 80static int da_num_tokens; 81static struct calling_interface_token *da_tokens; 82 83static struct platform_driver platform_driver = { 84 .driver = { 85 .name = "dell-laptop", 86 .owner = THIS_MODULE, 87 } 88}; 89 90static struct platform_device *platform_device; 91static struct backlight_device *dell_backlight_device; 92 93static const struct dmi_system_id dell_device_table[] __initconst = { 94 { 95 .ident = "Dell laptop", 96 .matches = { 97 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 98 DMI_MATCH(DMI_CHASSIS_TYPE, "8"), 99 }, 100 }, 101 { 102 .matches = { 103 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 104 DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /*Laptop*/ 105 }, 106 }, 107 { 108 .ident = "Dell Computer Corporation", 109 .matches = { 110 DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), 111 DMI_MATCH(DMI_CHASSIS_TYPE, "8"), 112 }, 113 }, 114 { } 115}; 116MODULE_DEVICE_TABLE(dmi, dell_device_table); 117 118static struct dmi_system_id dell_quirks[] = { 119 { 120 .callback = dmi_matched, 121 .ident = "Dell Vostro V130", 122 .matches = { 123 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 124 DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V130"), 125 }, 126 .driver_data = &quirk_dell_vostro_v130, 127 }, 128 { 129 .callback = dmi_matched, 130 .ident = "Dell Vostro V131", 131 .matches = { 132 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 133 DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"), 134 }, 135 .driver_data = &quirk_dell_vostro_v130, 136 }, 137 { 138 .callback = dmi_matched, 139 .ident = "Dell Vostro 3350", 140 .matches = { 141 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 142 DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3350"), 143 }, 144 .driver_data = &quirk_dell_vostro_v130, 145 }, 146 { 147 .callback = dmi_matched, 148 .ident = "Dell Vostro 3555", 149 .matches = { 150 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 151 DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3555"), 152 }, 153 .driver_data = &quirk_dell_vostro_v130, 154 }, 155 { 156 .callback = dmi_matched, 157 .ident = "Dell Inspiron N311z", 158 .matches = { 159 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 160 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N311z"), 161 }, 162 .driver_data = &quirk_dell_vostro_v130, 163 }, 164 { 165 .callback = dmi_matched, 166 .ident = "Dell Inspiron M5110", 167 .matches = { 168 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 169 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"), 170 }, 171 .driver_data = &quirk_dell_vostro_v130, 172 }, 173 { 174 .callback = dmi_matched, 175 .ident = "Dell Vostro 3360", 176 .matches = { 177 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 178 DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3360"), 179 }, 180 .driver_data = &quirk_dell_vostro_v130, 181 }, 182 { 183 .callback = dmi_matched, 184 .ident = "Dell Vostro 3460", 185 .matches = { 186 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 187 DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3460"), 188 }, 189 .driver_data = &quirk_dell_vostro_v130, 190 }, 191 { 192 .callback = dmi_matched, 193 .ident = "Dell Vostro 3560", 194 .matches = { 195 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 196 DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3560"), 197 }, 198 .driver_data = &quirk_dell_vostro_v130, 199 }, 200 { 201 .callback = dmi_matched, 202 .ident = "Dell Vostro 3450", 203 .matches = { 204 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 205 DMI_MATCH(DMI_PRODUCT_NAME, "Dell System Vostro 3450"), 206 }, 207 .driver_data = &quirk_dell_vostro_v130, 208 }, 209 { 210 .callback = dmi_matched, 211 .ident = "Dell Inspiron 5420", 212 .matches = { 213 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 214 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5420"), 215 }, 216 .driver_data = &quirk_dell_vostro_v130, 217 }, 218 { 219 .callback = dmi_matched, 220 .ident = "Dell Inspiron 5520", 221 .matches = { 222 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 223 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5520"), 224 }, 225 .driver_data = &quirk_dell_vostro_v130, 226 }, 227 { 228 .callback = dmi_matched, 229 .ident = "Dell Inspiron 5720", 230 .matches = { 231 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 232 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5720"), 233 }, 234 .driver_data = &quirk_dell_vostro_v130, 235 }, 236 { 237 .callback = dmi_matched, 238 .ident = "Dell Inspiron 7420", 239 .matches = { 240 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 241 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7420"), 242 }, 243 .driver_data = &quirk_dell_vostro_v130, 244 }, 245 { 246 .callback = dmi_matched, 247 .ident = "Dell Inspiron 7520", 248 .matches = { 249 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 250 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7520"), 251 }, 252 .driver_data = &quirk_dell_vostro_v130, 253 }, 254 { 255 .callback = dmi_matched, 256 .ident = "Dell Inspiron 7720", 257 .matches = { 258 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 259 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7720"), 260 }, 261 .driver_data = &quirk_dell_vostro_v130, 262 }, 263 { } 264}; 265 266static struct calling_interface_buffer *buffer; 267static struct page *bufferpage; 268static DEFINE_MUTEX(buffer_mutex); 269 270static int hwswitch_state; 271 272static void get_buffer(void) 273{ 274 mutex_lock(&buffer_mutex); 275 memset(buffer, 0, sizeof(struct calling_interface_buffer)); 276} 277 278static void release_buffer(void) 279{ 280 mutex_unlock(&buffer_mutex); 281} 282 283static void __init parse_da_table(const struct dmi_header *dm) 284{ 285 /* Final token is a terminator, so we don't want to copy it */ 286 int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1; 287 struct calling_interface_token *new_da_tokens; 288 struct calling_interface_structure *table = 289 container_of(dm, struct calling_interface_structure, header); 290 291 /* 4 bytes of table header, plus 7 bytes of Dell header, plus at least 292 6 bytes of entry */ 293 294 if (dm->length < 17) 295 return; 296 297 da_command_address = table->cmdIOAddress; 298 da_command_code = table->cmdIOCode; 299 300 new_da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) * 301 sizeof(struct calling_interface_token), 302 GFP_KERNEL); 303 304 if (!new_da_tokens) 305 return; 306 da_tokens = new_da_tokens; 307 308 memcpy(da_tokens+da_num_tokens, table->tokens, 309 sizeof(struct calling_interface_token) * tokens); 310 311 da_num_tokens += tokens; 312} 313 314static void __init find_tokens(const struct dmi_header *dm, void *dummy) 315{ 316 switch (dm->type) { 317 case 0xd4: /* Indexed IO */ 318 case 0xd5: /* Protected Area Type 1 */ 319 case 0xd6: /* Protected Area Type 2 */ 320 break; 321 case 0xda: /* Calling interface */ 322 parse_da_table(dm); 323 break; 324 } 325} 326 327static int find_token_location(int tokenid) 328{ 329 int i; 330 for (i = 0; i < da_num_tokens; i++) { 331 if (da_tokens[i].tokenID == tokenid) 332 return da_tokens[i].location; 333 } 334 335 return -1; 336} 337 338static struct calling_interface_buffer * 339dell_send_request(struct calling_interface_buffer *buffer, int class, 340 int select) 341{ 342 struct smi_cmd command; 343 344 command.magic = SMI_CMD_MAGIC; 345 command.command_address = da_command_address; 346 command.command_code = da_command_code; 347 command.ebx = virt_to_phys(buffer); 348 command.ecx = 0x42534931; 349 350 buffer->class = class; 351 buffer->select = select; 352 353 dcdbas_smi_request(&command); 354 355 return buffer; 356} 357 358static struct dentry *dell_laptop_dir; 359 360static int dell_debugfs_show(struct seq_file *s, void *data) 361{ 362 int status; 363 364 get_buffer(); 365 dell_send_request(buffer, 17, 11); 366 status = buffer->output[1]; 367 release_buffer(); 368 369 seq_printf(s, "status:\t0x%X\n", status); 370 seq_printf(s, "Bit 0 : Hardware switch supported: %lu\n", 371 status & BIT(0)); 372 seq_printf(s, "Bit 1 : Wifi locator supported: %lu\n", 373 (status & BIT(1)) >> 1); 374 seq_printf(s, "Bit 2 : Wifi is supported: %lu\n", 375 (status & BIT(2)) >> 2); 376 seq_printf(s, "Bit 3 : Bluetooth is supported: %lu\n", 377 (status & BIT(3)) >> 3); 378 seq_printf(s, "Bit 4 : WWAN is supported: %lu\n", 379 (status & BIT(4)) >> 4); 380 seq_printf(s, "Bit 5 : Wireless keyboard supported: %lu\n", 381 (status & BIT(5)) >> 5); 382 seq_printf(s, "Bit 8 : Wifi is installed: %lu\n", 383 (status & BIT(8)) >> 8); 384 seq_printf(s, "Bit 9 : Bluetooth is installed: %lu\n", 385 (status & BIT(9)) >> 9); 386 seq_printf(s, "Bit 10: WWAN is installed: %lu\n", 387 (status & BIT(10)) >> 10); 388 seq_printf(s, "Bit 16: Hardware switch is on: %lu\n", 389 (status & BIT(16)) >> 16); 390 seq_printf(s, "Bit 17: Wifi is blocked: %lu\n", 391 (status & BIT(17)) >> 17); 392 seq_printf(s, "Bit 18: Bluetooth is blocked: %lu\n", 393 (status & BIT(18)) >> 18); 394 seq_printf(s, "Bit 19: WWAN is blocked: %lu\n", 395 (status & BIT(19)) >> 19); 396 397 seq_printf(s, "\nhwswitch_state:\t0x%X\n", hwswitch_state); 398 seq_printf(s, "Bit 0 : Wifi controlled by switch: %lu\n", 399 hwswitch_state & BIT(0)); 400 seq_printf(s, "Bit 1 : Bluetooth controlled by switch: %lu\n", 401 (hwswitch_state & BIT(1)) >> 1); 402 seq_printf(s, "Bit 2 : WWAN controlled by switch: %lu\n", 403 (hwswitch_state & BIT(2)) >> 2); 404 seq_printf(s, "Bit 7 : Wireless switch config locked: %lu\n", 405 (hwswitch_state & BIT(7)) >> 7); 406 seq_printf(s, "Bit 8 : Wifi locator enabled: %lu\n", 407 (hwswitch_state & BIT(8)) >> 8); 408 seq_printf(s, "Bit 15: Wifi locator setting locked: %lu\n", 409 (hwswitch_state & BIT(15)) >> 15); 410 411 return 0; 412} 413 414static int dell_debugfs_open(struct inode *inode, struct file *file) 415{ 416 return single_open(file, dell_debugfs_show, inode->i_private); 417} 418 419static const struct file_operations dell_debugfs_fops = { 420 .owner = THIS_MODULE, 421 .open = dell_debugfs_open, 422 .read = seq_read, 423 .llseek = seq_lseek, 424 .release = single_release, 425}; 426 427static int dell_send_intensity(struct backlight_device *bd) 428{ 429 int ret = 0; 430 431 get_buffer(); 432 buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN); 433 buffer->input[1] = bd->props.brightness; 434 435 if (buffer->input[0] == -1) { 436 ret = -ENODEV; 437 goto out; 438 } 439 440 if (power_supply_is_system_supplied() > 0) 441 dell_send_request(buffer, 1, 2); 442 else 443 dell_send_request(buffer, 1, 1); 444 445out: 446 release_buffer(); 447 return 0; 448} 449 450static int dell_get_intensity(struct backlight_device *bd) 451{ 452 int ret = 0; 453 454 get_buffer(); 455 buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN); 456 457 if (buffer->input[0] == -1) { 458 ret = -ENODEV; 459 goto out; 460 } 461 462 if (power_supply_is_system_supplied() > 0) 463 dell_send_request(buffer, 0, 2); 464 else 465 dell_send_request(buffer, 0, 1); 466 467 ret = buffer->output[1]; 468 469out: 470 release_buffer(); 471 return ret; 472} 473 474static const struct backlight_ops dell_ops = { 475 .get_brightness = dell_get_intensity, 476 .update_status = dell_send_intensity, 477}; 478 479static void touchpad_led_on(void) 480{ 481 int command = 0x97; 482 char data = 1; 483 i8042_command(&data, command | 1 << 12); 484} 485 486static void touchpad_led_off(void) 487{ 488 int command = 0x97; 489 char data = 2; 490 i8042_command(&data, command | 1 << 12); 491} 492 493static void touchpad_led_set(struct led_classdev *led_cdev, 494 enum led_brightness value) 495{ 496 if (value > 0) 497 touchpad_led_on(); 498 else 499 touchpad_led_off(); 500} 501 502static struct led_classdev touchpad_led = { 503 .name = "dell-laptop::touchpad", 504 .brightness_set = touchpad_led_set, 505 .flags = LED_CORE_SUSPENDRESUME, 506}; 507 508static int touchpad_led_init(struct device *dev) 509{ 510 return led_classdev_register(dev, &touchpad_led); 511} 512 513static void touchpad_led_exit(void) 514{ 515 led_classdev_unregister(&touchpad_led); 516} 517 518static int __init dell_init(void) 519{ 520 int max_intensity = 0; 521 int ret; 522 523 if (!dmi_check_system(dell_device_table)) 524 return -ENODEV; 525 526 quirks = NULL; 527 /* find if this machine support other functions */ 528 dmi_check_system(dell_quirks); 529 530 dmi_walk(find_tokens, NULL); 531 532 if (!da_tokens) { 533 pr_info("Unable to find dmi tokens\n"); 534 return -ENODEV; 535 } 536 537 ret = platform_driver_register(&platform_driver); 538 if (ret) 539 goto fail_platform_driver; 540 platform_device = platform_device_alloc("dell-laptop", -1); 541 if (!platform_device) { 542 ret = -ENOMEM; 543 goto fail_platform_device1; 544 } 545 ret = platform_device_add(platform_device); 546 if (ret) 547 goto fail_platform_device2; 548 549 /* 550 * Allocate buffer below 4GB for SMI data--only 32-bit physical addr 551 * is passed to SMI handler. 552 */ 553 bufferpage = alloc_page(GFP_KERNEL | GFP_DMA32); 554 if (!bufferpage) { 555 ret = -ENOMEM; 556 goto fail_buffer; 557 } 558 buffer = page_address(bufferpage); 559 560 if (quirks && quirks->touchpad_led) 561 touchpad_led_init(&platform_device->dev); 562 563 dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); 564 565#ifdef CONFIG_ACPI 566 /* In the event of an ACPI backlight being available, don't 567 * register the platform controller. 568 */ 569 if (acpi_video_backlight_support()) 570 return 0; 571#endif 572 573 get_buffer(); 574 buffer->input[0] = find_token_location(BRIGHTNESS_TOKEN); 575 if (buffer->input[0] != -1) { 576 dell_send_request(buffer, 0, 2); 577 max_intensity = buffer->output[3]; 578 } 579 release_buffer(); 580 581 if (max_intensity) { 582 struct backlight_properties props; 583 memset(&props, 0, sizeof(struct backlight_properties)); 584 props.type = BACKLIGHT_PLATFORM; 585 props.max_brightness = max_intensity; 586 dell_backlight_device = backlight_device_register("dell_backlight", 587 &platform_device->dev, 588 NULL, 589 &dell_ops, 590 &props); 591 592 if (IS_ERR(dell_backlight_device)) { 593 ret = PTR_ERR(dell_backlight_device); 594 dell_backlight_device = NULL; 595 goto fail_backlight; 596 } 597 598 dell_backlight_device->props.brightness = 599 dell_get_intensity(dell_backlight_device); 600 backlight_update_status(dell_backlight_device); 601 } 602 603 return 0; 604 605fail_backlight: 606 free_page((unsigned long)bufferpage); 607fail_buffer: 608 platform_device_del(platform_device); 609fail_platform_device2: 610 platform_device_put(platform_device); 611fail_platform_device1: 612 platform_driver_unregister(&platform_driver); 613fail_platform_driver: 614 kfree(da_tokens); 615 return ret; 616} 617 618static void __exit dell_exit(void) 619{ 620 debugfs_remove_recursive(dell_laptop_dir); 621 if (quirks && quirks->touchpad_led) 622 touchpad_led_exit(); 623 backlight_device_unregister(dell_backlight_device); 624 if (platform_device) { 625 platform_device_unregister(platform_device); 626 platform_driver_unregister(&platform_driver); 627 } 628 kfree(da_tokens); 629 free_page((unsigned long)buffer); 630} 631 632module_init(dell_init); 633module_exit(dell_exit); 634 635MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); 636MODULE_DESCRIPTION("Dell laptop driver"); 637MODULE_LICENSE("GPL");