1 /* 2 * Samsung Laptop driver 3 * 4 * Copyright (C) 2009,2011 Greg Kroah-Hartman (gregkh@suse.de) 5 * Copyright (C) 2009,2011 Novell Inc. 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 as published by 9 * the Free Software Foundation. 10 * 11 */ 12 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 13 14 #include <linux/kernel.h> 15 #include <linux/init.h> 16 #include <linux/module.h> 17 #include <linux/delay.h> 18 #include <linux/pci.h> 19 #include <linux/backlight.h> 20 #include <linux/leds.h> 21 #include <linux/fb.h> 22 #include <linux/dmi.h> 23 #include <linux/platform_device.h> 24 #include <linux/rfkill.h> 25 #include <linux/acpi.h> 26 #include <linux/seq_file.h> 27 #include <linux/debugfs.h> 28 #include <linux/ctype.h> 29 #include <linux/efi.h> 30 #include <linux/suspend.h> 31 #include <acpi/video.h> 32 33 /* 34 * This driver is needed because a number of Samsung laptops do not hook 35 * their control settings through ACPI. So we have to poke around in the 36 * BIOS to do things like brightness values, and "special" key controls. 37 */ 38 39 /* 40 * We have 0 - 8 as valid brightness levels. The specs say that level 0 should 41 * be reserved by the BIOS (which really doesn't make much sense), we tell 42 * userspace that the value is 0 - 7 and then just tell the hardware 1 - 8 43 */ 44 #define MAX_BRIGHT 0x07 45 46 47 #define SABI_IFACE_MAIN 0x00 48 #define SABI_IFACE_SUB 0x02 49 #define SABI_IFACE_COMPLETE 0x04 50 #define SABI_IFACE_DATA 0x05 51 52 #define WL_STATUS_WLAN 0x0 53 #define WL_STATUS_BT 0x2 54 55 /* Structure get/set data using sabi */ 56 struct sabi_data { 57 union { 58 struct { 59 u32 d0; 60 u32 d1; 61 u16 d2; 62 u8 d3; 63 }; 64 u8 data[11]; 65 }; 66 }; 67 68 struct sabi_header_offsets { 69 u8 port; 70 u8 re_mem; 71 u8 iface_func; 72 u8 en_mem; 73 u8 data_offset; 74 u8 data_segment; 75 }; 76 77 struct sabi_commands { 78 /* 79 * Brightness is 0 - 8, as described above. 80 * Value 0 is for the BIOS to use 81 */ 82 u16 get_brightness; 83 u16 set_brightness; 84 85 /* 86 * first byte: 87 * 0x00 - wireless is off 88 * 0x01 - wireless is on 89 * second byte: 90 * 0x02 - 3G is off 91 * 0x03 - 3G is on 92 * TODO, verify 3G is correct, that doesn't seem right... 93 */ 94 u16 get_wireless_button; 95 u16 set_wireless_button; 96 97 /* 0 is off, 1 is on */ 98 u16 get_backlight; 99 u16 set_backlight; 100 101 /* 102 * 0x80 or 0x00 - no action 103 * 0x81 - recovery key pressed 104 */ 105 u16 get_recovery_mode; 106 u16 set_recovery_mode; 107 108 /* 109 * on seclinux: 0 is low, 1 is high, 110 * on swsmi: 0 is normal, 1 is silent, 2 is turbo 111 */ 112 u16 get_performance_level; 113 u16 set_performance_level; 114 115 /* 0x80 is off, 0x81 is on */ 116 u16 get_battery_life_extender; 117 u16 set_battery_life_extender; 118 119 /* 0x80 is off, 0x81 is on */ 120 u16 get_usb_charge; 121 u16 set_usb_charge; 122 123 /* the first byte is for bluetooth and the third one is for wlan */ 124 u16 get_wireless_status; 125 u16 set_wireless_status; 126 127 /* 0x80 is off, 0x81 is on */ 128 u16 get_lid_handling; 129 u16 set_lid_handling; 130 131 /* 0x81 to read, (0x82 | level << 8) to set, 0xaabb to enable */ 132 u16 kbd_backlight; 133 134 /* 135 * Tell the BIOS that Linux is running on this machine. 136 * 81 is on, 80 is off 137 */ 138 u16 set_linux; 139 }; 140 141 struct sabi_performance_level { 142 const char *name; 143 u16 value; 144 }; 145 146 struct sabi_config { 147 int sabi_version; 148 const char *test_string; 149 u16 main_function; 150 const struct sabi_header_offsets header_offsets; 151 const struct sabi_commands commands; 152 const struct sabi_performance_level performance_levels[4]; 153 u8 min_brightness; 154 u8 max_brightness; 155 }; 156 157 static const struct sabi_config sabi_configs[] = { 158 { 159 /* I don't know if it is really 2, but it it is 160 * less than 3 anyway */ 161 .sabi_version = 2, 162 163 .test_string = "SECLINUX", 164 165 .main_function = 0x4c49, 166 167 .header_offsets = { 168 .port = 0x00, 169 .re_mem = 0x02, 170 .iface_func = 0x03, 171 .en_mem = 0x04, 172 .data_offset = 0x05, 173 .data_segment = 0x07, 174 }, 175 176 .commands = { 177 .get_brightness = 0x00, 178 .set_brightness = 0x01, 179 180 .get_wireless_button = 0x02, 181 .set_wireless_button = 0x03, 182 183 .get_backlight = 0x04, 184 .set_backlight = 0x05, 185 186 .get_recovery_mode = 0x06, 187 .set_recovery_mode = 0x07, 188 189 .get_performance_level = 0x08, 190 .set_performance_level = 0x09, 191 192 .get_battery_life_extender = 0xFFFF, 193 .set_battery_life_extender = 0xFFFF, 194 195 .get_usb_charge = 0xFFFF, 196 .set_usb_charge = 0xFFFF, 197 198 .get_wireless_status = 0xFFFF, 199 .set_wireless_status = 0xFFFF, 200 201 .get_lid_handling = 0xFFFF, 202 .set_lid_handling = 0xFFFF, 203 204 .kbd_backlight = 0xFFFF, 205 206 .set_linux = 0x0a, 207 }, 208 209 .performance_levels = { 210 { 211 .name = "silent", 212 .value = 0, 213 }, 214 { 215 .name = "normal", 216 .value = 1, 217 }, 218 { }, 219 }, 220 .min_brightness = 1, 221 .max_brightness = 8, 222 }, 223 { 224 .sabi_version = 3, 225 226 .test_string = "SwSmi@", 227 228 .main_function = 0x5843, 229 230 .header_offsets = { 231 .port = 0x00, 232 .re_mem = 0x04, 233 .iface_func = 0x02, 234 .en_mem = 0x03, 235 .data_offset = 0x05, 236 .data_segment = 0x07, 237 }, 238 239 .commands = { 240 .get_brightness = 0x10, 241 .set_brightness = 0x11, 242 243 .get_wireless_button = 0x12, 244 .set_wireless_button = 0x13, 245 246 .get_backlight = 0x2d, 247 .set_backlight = 0x2e, 248 249 .get_recovery_mode = 0xff, 250 .set_recovery_mode = 0xff, 251 252 .get_performance_level = 0x31, 253 .set_performance_level = 0x32, 254 255 .get_battery_life_extender = 0x65, 256 .set_battery_life_extender = 0x66, 257 258 .get_usb_charge = 0x67, 259 .set_usb_charge = 0x68, 260 261 .get_wireless_status = 0x69, 262 .set_wireless_status = 0x6a, 263 264 .get_lid_handling = 0x6d, 265 .set_lid_handling = 0x6e, 266 267 .kbd_backlight = 0x78, 268 269 .set_linux = 0xff, 270 }, 271 272 .performance_levels = { 273 { 274 .name = "normal", 275 .value = 0, 276 }, 277 { 278 .name = "silent", 279 .value = 1, 280 }, 281 { 282 .name = "overclock", 283 .value = 2, 284 }, 285 { }, 286 }, 287 .min_brightness = 0, 288 .max_brightness = 8, 289 }, 290 { }, 291 }; 292 293 /* 294 * samsung-laptop/ - debugfs root directory 295 * f0000_segment - dump f0000 segment 296 * command - current command 297 * data - current data 298 * d0, d1, d2, d3 - data fields 299 * call - call SABI using command and data 300 * 301 * This allow to call arbitrary sabi commands wihout 302 * modifying the driver at all. 303 * For example, setting the keyboard backlight brightness to 5 304 * 305 * echo 0x78 > command 306 * echo 0x0582 > d0 307 * echo 0 > d1 308 * echo 0 > d2 309 * echo 0 > d3 310 * cat call 311 */ 312 313 struct samsung_laptop_debug { 314 struct dentry *root; 315 struct sabi_data data; 316 u16 command; 317 318 struct debugfs_blob_wrapper f0000_wrapper; 319 struct debugfs_blob_wrapper data_wrapper; 320 struct debugfs_blob_wrapper sdiag_wrapper; 321 }; 322 323 struct samsung_laptop; 324 325 struct samsung_rfkill { 326 struct samsung_laptop *samsung; 327 struct rfkill *rfkill; 328 enum rfkill_type type; 329 }; 330 331 struct samsung_laptop { 332 const struct sabi_config *config; 333 334 void __iomem *sabi; 335 void __iomem *sabi_iface; 336 void __iomem *f0000_segment; 337 338 struct mutex sabi_mutex; 339 340 struct platform_device *platform_device; 341 struct backlight_device *backlight_device; 342 343 struct samsung_rfkill wlan; 344 struct samsung_rfkill bluetooth; 345 346 struct led_classdev kbd_led; 347 int kbd_led_wk; 348 struct workqueue_struct *led_workqueue; 349 struct work_struct kbd_led_work; 350 351 struct samsung_laptop_debug debug; 352 struct samsung_quirks *quirks; 353 354 struct notifier_block pm_nb; 355 356 bool handle_backlight; 357 bool has_stepping_quirk; 358 359 char sdiag[64]; 360 }; 361 362 struct samsung_quirks { 363 bool broken_acpi_video; 364 bool four_kbd_backlight_levels; 365 bool enable_kbd_backlight; 366 bool use_native_backlight; 367 bool lid_handling; 368 }; 369 370 static struct samsung_quirks samsung_unknown = {}; 371 372 static struct samsung_quirks samsung_broken_acpi_video = { 373 .broken_acpi_video = true, 374 }; 375 376 static struct samsung_quirks samsung_use_native_backlight = { 377 .use_native_backlight = true, 378 }; 379 380 static struct samsung_quirks samsung_np740u3e = { 381 .four_kbd_backlight_levels = true, 382 .enable_kbd_backlight = true, 383 }; 384 385 static struct samsung_quirks samsung_lid_handling = { 386 .lid_handling = true, 387 }; 388 389 static bool force; 390 module_param(force, bool, 0); 391 MODULE_PARM_DESC(force, 392 "Disable the DMI check and forces the driver to be loaded"); 393 394 static bool debug; 395 module_param(debug, bool, S_IRUGO | S_IWUSR); 396 MODULE_PARM_DESC(debug, "Debug enabled or not"); 397 398 static int sabi_command(struct samsung_laptop *samsung, u16 command, 399 struct sabi_data *in, 400 struct sabi_data *out) 401 { 402 const struct sabi_config *config = samsung->config; 403 int ret = 0; 404 u16 port = readw(samsung->sabi + config->header_offsets.port); 405 u8 complete, iface_data; 406 407 mutex_lock(&samsung->sabi_mutex); 408 409 if (debug) { 410 if (in) 411 pr_info("SABI command:0x%04x " 412 "data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}", 413 command, in->d0, in->d1, in->d2, in->d3); 414 else 415 pr_info("SABI command:0x%04x", command); 416 } 417 418 /* enable memory to be able to write to it */ 419 outb(readb(samsung->sabi + config->header_offsets.en_mem), port); 420 421 /* write out the command */ 422 writew(config->main_function, samsung->sabi_iface + SABI_IFACE_MAIN); 423 writew(command, samsung->sabi_iface + SABI_IFACE_SUB); 424 writeb(0, samsung->sabi_iface + SABI_IFACE_COMPLETE); 425 if (in) { 426 writel(in->d0, samsung->sabi_iface + SABI_IFACE_DATA); 427 writel(in->d1, samsung->sabi_iface + SABI_IFACE_DATA + 4); 428 writew(in->d2, samsung->sabi_iface + SABI_IFACE_DATA + 8); 429 writeb(in->d3, samsung->sabi_iface + SABI_IFACE_DATA + 10); 430 } 431 outb(readb(samsung->sabi + config->header_offsets.iface_func), port); 432 433 /* write protect memory to make it safe */ 434 outb(readb(samsung->sabi + config->header_offsets.re_mem), port); 435 436 /* see if the command actually succeeded */ 437 complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE); 438 iface_data = readb(samsung->sabi_iface + SABI_IFACE_DATA); 439 440 /* iface_data = 0xFF happens when a command is not known 441 * so we only add a warning in debug mode since we will 442 * probably issue some unknown command at startup to find 443 * out which features are supported */ 444 if (complete != 0xaa || (iface_data == 0xff && debug)) 445 pr_warn("SABI command 0x%04x failed with" 446 " completion flag 0x%02x and interface data 0x%02x", 447 command, complete, iface_data); 448 449 if (complete != 0xaa || iface_data == 0xff) { 450 ret = -EINVAL; 451 goto exit; 452 } 453 454 if (out) { 455 out->d0 = readl(samsung->sabi_iface + SABI_IFACE_DATA); 456 out->d1 = readl(samsung->sabi_iface + SABI_IFACE_DATA + 4); 457 out->d2 = readw(samsung->sabi_iface + SABI_IFACE_DATA + 2); 458 out->d3 = readb(samsung->sabi_iface + SABI_IFACE_DATA + 1); 459 } 460 461 if (debug && out) { 462 pr_info("SABI return data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}", 463 out->d0, out->d1, out->d2, out->d3); 464 } 465 466 exit: 467 mutex_unlock(&samsung->sabi_mutex); 468 return ret; 469 } 470 471 /* simple wrappers usable with most commands */ 472 static int sabi_set_commandb(struct samsung_laptop *samsung, 473 u16 command, u8 data) 474 { 475 struct sabi_data in = { { { .d0 = 0, .d1 = 0, .d2 = 0, .d3 = 0 } } }; 476 477 in.data[0] = data; 478 return sabi_command(samsung, command, &in, NULL); 479 } 480 481 static int read_brightness(struct samsung_laptop *samsung) 482 { 483 const struct sabi_config *config = samsung->config; 484 const struct sabi_commands *commands = &samsung->config->commands; 485 struct sabi_data sretval; 486 int user_brightness = 0; 487 int retval; 488 489 retval = sabi_command(samsung, commands->get_brightness, 490 NULL, &sretval); 491 if (retval) 492 return retval; 493 494 user_brightness = sretval.data[0]; 495 if (user_brightness > config->min_brightness) 496 user_brightness -= config->min_brightness; 497 else 498 user_brightness = 0; 499 500 return user_brightness; 501 } 502 503 static void set_brightness(struct samsung_laptop *samsung, u8 user_brightness) 504 { 505 const struct sabi_config *config = samsung->config; 506 const struct sabi_commands *commands = &samsung->config->commands; 507 u8 user_level = user_brightness + config->min_brightness; 508 509 if (samsung->has_stepping_quirk && user_level != 0) { 510 /* 511 * short circuit if the specified level is what's already set 512 * to prevent the screen from flickering needlessly 513 */ 514 if (user_brightness == read_brightness(samsung)) 515 return; 516 517 sabi_set_commandb(samsung, commands->set_brightness, 0); 518 } 519 520 sabi_set_commandb(samsung, commands->set_brightness, user_level); 521 } 522 523 static int get_brightness(struct backlight_device *bd) 524 { 525 struct samsung_laptop *samsung = bl_get_data(bd); 526 527 return read_brightness(samsung); 528 } 529 530 static void check_for_stepping_quirk(struct samsung_laptop *samsung) 531 { 532 int initial_level; 533 int check_level; 534 int orig_level = read_brightness(samsung); 535 536 /* 537 * Some laptops exhibit the strange behaviour of stepping toward 538 * (rather than setting) the brightness except when changing to/from 539 * brightness level 0. This behaviour is checked for here and worked 540 * around in set_brightness. 541 */ 542 543 if (orig_level == 0) 544 set_brightness(samsung, 1); 545 546 initial_level = read_brightness(samsung); 547 548 if (initial_level <= 2) 549 check_level = initial_level + 2; 550 else 551 check_level = initial_level - 2; 552 553 samsung->has_stepping_quirk = false; 554 set_brightness(samsung, check_level); 555 556 if (read_brightness(samsung) != check_level) { 557 samsung->has_stepping_quirk = true; 558 pr_info("enabled workaround for brightness stepping quirk\n"); 559 } 560 561 set_brightness(samsung, orig_level); 562 } 563 564 static int update_status(struct backlight_device *bd) 565 { 566 struct samsung_laptop *samsung = bl_get_data(bd); 567 const struct sabi_commands *commands = &samsung->config->commands; 568 569 set_brightness(samsung, bd->props.brightness); 570 571 if (bd->props.power == FB_BLANK_UNBLANK) 572 sabi_set_commandb(samsung, commands->set_backlight, 1); 573 else 574 sabi_set_commandb(samsung, commands->set_backlight, 0); 575 576 return 0; 577 } 578 579 static const struct backlight_ops backlight_ops = { 580 .get_brightness = get_brightness, 581 .update_status = update_status, 582 }; 583 584 static int seclinux_rfkill_set(void *data, bool blocked) 585 { 586 struct samsung_rfkill *srfkill = data; 587 struct samsung_laptop *samsung = srfkill->samsung; 588 const struct sabi_commands *commands = &samsung->config->commands; 589 590 return sabi_set_commandb(samsung, commands->set_wireless_button, 591 !blocked); 592 } 593 594 static struct rfkill_ops seclinux_rfkill_ops = { 595 .set_block = seclinux_rfkill_set, 596 }; 597 598 static int swsmi_wireless_status(struct samsung_laptop *samsung, 599 struct sabi_data *data) 600 { 601 const struct sabi_commands *commands = &samsung->config->commands; 602 603 return sabi_command(samsung, commands->get_wireless_status, 604 NULL, data); 605 } 606 607 static int swsmi_rfkill_set(void *priv, bool blocked) 608 { 609 struct samsung_rfkill *srfkill = priv; 610 struct samsung_laptop *samsung = srfkill->samsung; 611 const struct sabi_commands *commands = &samsung->config->commands; 612 struct sabi_data data; 613 int ret, i; 614 615 ret = swsmi_wireless_status(samsung, &data); 616 if (ret) 617 return ret; 618 619 /* Don't set the state for non-present devices */ 620 for (i = 0; i < 4; i++) 621 if (data.data[i] == 0x02) 622 data.data[1] = 0; 623 624 if (srfkill->type == RFKILL_TYPE_WLAN) 625 data.data[WL_STATUS_WLAN] = !blocked; 626 else if (srfkill->type == RFKILL_TYPE_BLUETOOTH) 627 data.data[WL_STATUS_BT] = !blocked; 628 629 return sabi_command(samsung, commands->set_wireless_status, 630 &data, &data); 631 } 632 633 static void swsmi_rfkill_query(struct rfkill *rfkill, void *priv) 634 { 635 struct samsung_rfkill *srfkill = priv; 636 struct samsung_laptop *samsung = srfkill->samsung; 637 struct sabi_data data; 638 int ret; 639 640 ret = swsmi_wireless_status(samsung, &data); 641 if (ret) 642 return ; 643 644 if (srfkill->type == RFKILL_TYPE_WLAN) 645 ret = data.data[WL_STATUS_WLAN]; 646 else if (srfkill->type == RFKILL_TYPE_BLUETOOTH) 647 ret = data.data[WL_STATUS_BT]; 648 else 649 return ; 650 651 rfkill_set_sw_state(rfkill, !ret); 652 } 653 654 static struct rfkill_ops swsmi_rfkill_ops = { 655 .set_block = swsmi_rfkill_set, 656 .query = swsmi_rfkill_query, 657 }; 658 659 static ssize_t get_performance_level(struct device *dev, 660 struct device_attribute *attr, char *buf) 661 { 662 struct samsung_laptop *samsung = dev_get_drvdata(dev); 663 const struct sabi_config *config = samsung->config; 664 const struct sabi_commands *commands = &config->commands; 665 struct sabi_data sretval; 666 int retval; 667 int i; 668 669 /* Read the state */ 670 retval = sabi_command(samsung, commands->get_performance_level, 671 NULL, &sretval); 672 if (retval) 673 return retval; 674 675 /* The logic is backwards, yeah, lots of fun... */ 676 for (i = 0; config->performance_levels[i].name; ++i) { 677 if (sretval.data[0] == config->performance_levels[i].value) 678 return sprintf(buf, "%s\n", config->performance_levels[i].name); 679 } 680 return sprintf(buf, "%s\n", "unknown"); 681 } 682 683 static ssize_t set_performance_level(struct device *dev, 684 struct device_attribute *attr, const char *buf, 685 size_t count) 686 { 687 struct samsung_laptop *samsung = dev_get_drvdata(dev); 688 const struct sabi_config *config = samsung->config; 689 const struct sabi_commands *commands = &config->commands; 690 int i; 691 692 if (count < 1) 693 return count; 694 695 for (i = 0; config->performance_levels[i].name; ++i) { 696 const struct sabi_performance_level *level = 697 &config->performance_levels[i]; 698 if (!strncasecmp(level->name, buf, strlen(level->name))) { 699 sabi_set_commandb(samsung, 700 commands->set_performance_level, 701 level->value); 702 break; 703 } 704 } 705 706 if (!config->performance_levels[i].name) 707 return -EINVAL; 708 709 return count; 710 } 711 712 static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO, 713 get_performance_level, set_performance_level); 714 715 static int read_battery_life_extender(struct samsung_laptop *samsung) 716 { 717 const struct sabi_commands *commands = &samsung->config->commands; 718 struct sabi_data data; 719 int retval; 720 721 if (commands->get_battery_life_extender == 0xFFFF) 722 return -ENODEV; 723 724 memset(&data, 0, sizeof(data)); 725 data.data[0] = 0x80; 726 retval = sabi_command(samsung, commands->get_battery_life_extender, 727 &data, &data); 728 729 if (retval) 730 return retval; 731 732 if (data.data[0] != 0 && data.data[0] != 1) 733 return -ENODEV; 734 735 return data.data[0]; 736 } 737 738 static int write_battery_life_extender(struct samsung_laptop *samsung, 739 int enabled) 740 { 741 const struct sabi_commands *commands = &samsung->config->commands; 742 struct sabi_data data; 743 744 memset(&data, 0, sizeof(data)); 745 data.data[0] = 0x80 | enabled; 746 return sabi_command(samsung, commands->set_battery_life_extender, 747 &data, NULL); 748 } 749 750 static ssize_t get_battery_life_extender(struct device *dev, 751 struct device_attribute *attr, 752 char *buf) 753 { 754 struct samsung_laptop *samsung = dev_get_drvdata(dev); 755 int ret; 756 757 ret = read_battery_life_extender(samsung); 758 if (ret < 0) 759 return ret; 760 761 return sprintf(buf, "%d\n", ret); 762 } 763 764 static ssize_t set_battery_life_extender(struct device *dev, 765 struct device_attribute *attr, 766 const char *buf, size_t count) 767 { 768 struct samsung_laptop *samsung = dev_get_drvdata(dev); 769 int ret, value; 770 771 if (!count || kstrtoint(buf, 0, &value) != 0) 772 return -EINVAL; 773 774 ret = write_battery_life_extender(samsung, !!value); 775 if (ret < 0) 776 return ret; 777 778 return count; 779 } 780 781 static DEVICE_ATTR(battery_life_extender, S_IWUSR | S_IRUGO, 782 get_battery_life_extender, set_battery_life_extender); 783 784 static int read_usb_charge(struct samsung_laptop *samsung) 785 { 786 const struct sabi_commands *commands = &samsung->config->commands; 787 struct sabi_data data; 788 int retval; 789 790 if (commands->get_usb_charge == 0xFFFF) 791 return -ENODEV; 792 793 memset(&data, 0, sizeof(data)); 794 data.data[0] = 0x80; 795 retval = sabi_command(samsung, commands->get_usb_charge, 796 &data, &data); 797 798 if (retval) 799 return retval; 800 801 if (data.data[0] != 0 && data.data[0] != 1) 802 return -ENODEV; 803 804 return data.data[0]; 805 } 806 807 static int write_usb_charge(struct samsung_laptop *samsung, 808 int enabled) 809 { 810 const struct sabi_commands *commands = &samsung->config->commands; 811 struct sabi_data data; 812 813 memset(&data, 0, sizeof(data)); 814 data.data[0] = 0x80 | enabled; 815 return sabi_command(samsung, commands->set_usb_charge, 816 &data, NULL); 817 } 818 819 static ssize_t get_usb_charge(struct device *dev, 820 struct device_attribute *attr, 821 char *buf) 822 { 823 struct samsung_laptop *samsung = dev_get_drvdata(dev); 824 int ret; 825 826 ret = read_usb_charge(samsung); 827 if (ret < 0) 828 return ret; 829 830 return sprintf(buf, "%d\n", ret); 831 } 832 833 static ssize_t set_usb_charge(struct device *dev, 834 struct device_attribute *attr, 835 const char *buf, size_t count) 836 { 837 struct samsung_laptop *samsung = dev_get_drvdata(dev); 838 int ret, value; 839 840 if (!count || kstrtoint(buf, 0, &value) != 0) 841 return -EINVAL; 842 843 ret = write_usb_charge(samsung, !!value); 844 if (ret < 0) 845 return ret; 846 847 return count; 848 } 849 850 static DEVICE_ATTR(usb_charge, S_IWUSR | S_IRUGO, 851 get_usb_charge, set_usb_charge); 852 853 static int read_lid_handling(struct samsung_laptop *samsung) 854 { 855 const struct sabi_commands *commands = &samsung->config->commands; 856 struct sabi_data data; 857 int retval; 858 859 if (commands->get_lid_handling == 0xFFFF) 860 return -ENODEV; 861 862 memset(&data, 0, sizeof(data)); 863 retval = sabi_command(samsung, commands->get_lid_handling, 864 &data, &data); 865 866 if (retval) 867 return retval; 868 869 return data.data[0] & 0x1; 870 } 871 872 static int write_lid_handling(struct samsung_laptop *samsung, 873 int enabled) 874 { 875 const struct sabi_commands *commands = &samsung->config->commands; 876 struct sabi_data data; 877 878 memset(&data, 0, sizeof(data)); 879 data.data[0] = 0x80 | enabled; 880 return sabi_command(samsung, commands->set_lid_handling, 881 &data, NULL); 882 } 883 884 static ssize_t get_lid_handling(struct device *dev, 885 struct device_attribute *attr, 886 char *buf) 887 { 888 struct samsung_laptop *samsung = dev_get_drvdata(dev); 889 int ret; 890 891 ret = read_lid_handling(samsung); 892 if (ret < 0) 893 return ret; 894 895 return sprintf(buf, "%d\n", ret); 896 } 897 898 static ssize_t set_lid_handling(struct device *dev, 899 struct device_attribute *attr, 900 const char *buf, size_t count) 901 { 902 struct samsung_laptop *samsung = dev_get_drvdata(dev); 903 int ret, value; 904 905 if (!count || kstrtoint(buf, 0, &value) != 0) 906 return -EINVAL; 907 908 ret = write_lid_handling(samsung, !!value); 909 if (ret < 0) 910 return ret; 911 912 return count; 913 } 914 915 static DEVICE_ATTR(lid_handling, S_IWUSR | S_IRUGO, 916 get_lid_handling, set_lid_handling); 917 918 static struct attribute *platform_attributes[] = { 919 &dev_attr_performance_level.attr, 920 &dev_attr_battery_life_extender.attr, 921 &dev_attr_usb_charge.attr, 922 &dev_attr_lid_handling.attr, 923 NULL 924 }; 925 926 static int find_signature(void __iomem *memcheck, const char *testStr) 927 { 928 int i = 0; 929 int loca; 930 931 for (loca = 0; loca < 0xffff; loca++) { 932 char temp = readb(memcheck + loca); 933 934 if (temp == testStr[i]) { 935 if (i == strlen(testStr)-1) 936 break; 937 ++i; 938 } else { 939 i = 0; 940 } 941 } 942 return loca; 943 } 944 945 static void samsung_rfkill_exit(struct samsung_laptop *samsung) 946 { 947 if (samsung->wlan.rfkill) { 948 rfkill_unregister(samsung->wlan.rfkill); 949 rfkill_destroy(samsung->wlan.rfkill); 950 samsung->wlan.rfkill = NULL; 951 } 952 if (samsung->bluetooth.rfkill) { 953 rfkill_unregister(samsung->bluetooth.rfkill); 954 rfkill_destroy(samsung->bluetooth.rfkill); 955 samsung->bluetooth.rfkill = NULL; 956 } 957 } 958 959 static int samsung_new_rfkill(struct samsung_laptop *samsung, 960 struct samsung_rfkill *arfkill, 961 const char *name, enum rfkill_type type, 962 const struct rfkill_ops *ops, 963 int blocked) 964 { 965 struct rfkill **rfkill = &arfkill->rfkill; 966 int ret; 967 968 arfkill->type = type; 969 arfkill->samsung = samsung; 970 971 *rfkill = rfkill_alloc(name, &samsung->platform_device->dev, 972 type, ops, arfkill); 973 974 if (!*rfkill) 975 return -EINVAL; 976 977 if (blocked != -1) 978 rfkill_init_sw_state(*rfkill, blocked); 979 980 ret = rfkill_register(*rfkill); 981 if (ret) { 982 rfkill_destroy(*rfkill); 983 *rfkill = NULL; 984 return ret; 985 } 986 return 0; 987 } 988 989 static int __init samsung_rfkill_init_seclinux(struct samsung_laptop *samsung) 990 { 991 return samsung_new_rfkill(samsung, &samsung->wlan, "samsung-wlan", 992 RFKILL_TYPE_WLAN, &seclinux_rfkill_ops, -1); 993 } 994 995 static int __init samsung_rfkill_init_swsmi(struct samsung_laptop *samsung) 996 { 997 struct sabi_data data; 998 int ret; 999 1000 ret = swsmi_wireless_status(samsung, &data); 1001 if (ret) { 1002 /* Some swsmi laptops use the old seclinux way to control 1003 * wireless devices */ 1004 if (ret == -EINVAL) 1005 ret = samsung_rfkill_init_seclinux(samsung); 1006 return ret; 1007 } 1008 1009 /* 0x02 seems to mean that the device is no present/available */ 1010 1011 if (data.data[WL_STATUS_WLAN] != 0x02) 1012 ret = samsung_new_rfkill(samsung, &samsung->wlan, 1013 "samsung-wlan", 1014 RFKILL_TYPE_WLAN, 1015 &swsmi_rfkill_ops, 1016 !data.data[WL_STATUS_WLAN]); 1017 if (ret) 1018 goto exit; 1019 1020 if (data.data[WL_STATUS_BT] != 0x02) 1021 ret = samsung_new_rfkill(samsung, &samsung->bluetooth, 1022 "samsung-bluetooth", 1023 RFKILL_TYPE_BLUETOOTH, 1024 &swsmi_rfkill_ops, 1025 !data.data[WL_STATUS_BT]); 1026 if (ret) 1027 goto exit; 1028 1029 exit: 1030 if (ret) 1031 samsung_rfkill_exit(samsung); 1032 1033 return ret; 1034 } 1035 1036 static int __init samsung_rfkill_init(struct samsung_laptop *samsung) 1037 { 1038 if (samsung->config->sabi_version == 2) 1039 return samsung_rfkill_init_seclinux(samsung); 1040 if (samsung->config->sabi_version == 3) 1041 return samsung_rfkill_init_swsmi(samsung); 1042 return 0; 1043 } 1044 1045 static void samsung_lid_handling_exit(struct samsung_laptop *samsung) 1046 { 1047 if (samsung->quirks->lid_handling) 1048 write_lid_handling(samsung, 0); 1049 } 1050 1051 static int __init samsung_lid_handling_init(struct samsung_laptop *samsung) 1052 { 1053 int retval = 0; 1054 1055 if (samsung->quirks->lid_handling) 1056 retval = write_lid_handling(samsung, 1); 1057 1058 return retval; 1059 } 1060 1061 static int kbd_backlight_enable(struct samsung_laptop *samsung) 1062 { 1063 const struct sabi_commands *commands = &samsung->config->commands; 1064 struct sabi_data data; 1065 int retval; 1066 1067 if (commands->kbd_backlight == 0xFFFF) 1068 return -ENODEV; 1069 1070 memset(&data, 0, sizeof(data)); 1071 data.d0 = 0xaabb; 1072 retval = sabi_command(samsung, commands->kbd_backlight, 1073 &data, &data); 1074 1075 if (retval) 1076 return retval; 1077 1078 if (data.d0 != 0xccdd) 1079 return -ENODEV; 1080 return 0; 1081 } 1082 1083 static int kbd_backlight_read(struct samsung_laptop *samsung) 1084 { 1085 const struct sabi_commands *commands = &samsung->config->commands; 1086 struct sabi_data data; 1087 int retval; 1088 1089 memset(&data, 0, sizeof(data)); 1090 data.data[0] = 0x81; 1091 retval = sabi_command(samsung, commands->kbd_backlight, 1092 &data, &data); 1093 1094 if (retval) 1095 return retval; 1096 1097 return data.data[0]; 1098 } 1099 1100 static int kbd_backlight_write(struct samsung_laptop *samsung, int brightness) 1101 { 1102 const struct sabi_commands *commands = &samsung->config->commands; 1103 struct sabi_data data; 1104 1105 memset(&data, 0, sizeof(data)); 1106 data.d0 = 0x82 | ((brightness & 0xFF) << 8); 1107 return sabi_command(samsung, commands->kbd_backlight, 1108 &data, NULL); 1109 } 1110 1111 static void kbd_led_update(struct work_struct *work) 1112 { 1113 struct samsung_laptop *samsung; 1114 1115 samsung = container_of(work, struct samsung_laptop, kbd_led_work); 1116 kbd_backlight_write(samsung, samsung->kbd_led_wk); 1117 } 1118 1119 static void kbd_led_set(struct led_classdev *led_cdev, 1120 enum led_brightness value) 1121 { 1122 struct samsung_laptop *samsung; 1123 1124 samsung = container_of(led_cdev, struct samsung_laptop, kbd_led); 1125 1126 if (value > samsung->kbd_led.max_brightness) 1127 value = samsung->kbd_led.max_brightness; 1128 else if (value < 0) 1129 value = 0; 1130 1131 samsung->kbd_led_wk = value; 1132 queue_work(samsung->led_workqueue, &samsung->kbd_led_work); 1133 } 1134 1135 static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) 1136 { 1137 struct samsung_laptop *samsung; 1138 1139 samsung = container_of(led_cdev, struct samsung_laptop, kbd_led); 1140 return kbd_backlight_read(samsung); 1141 } 1142 1143 static void samsung_leds_exit(struct samsung_laptop *samsung) 1144 { 1145 if (!IS_ERR_OR_NULL(samsung->kbd_led.dev)) 1146 led_classdev_unregister(&samsung->kbd_led); 1147 if (samsung->led_workqueue) 1148 destroy_workqueue(samsung->led_workqueue); 1149 } 1150 1151 static int __init samsung_leds_init(struct samsung_laptop *samsung) 1152 { 1153 int ret = 0; 1154 1155 samsung->led_workqueue = create_singlethread_workqueue("led_workqueue"); 1156 if (!samsung->led_workqueue) 1157 return -ENOMEM; 1158 1159 if (kbd_backlight_enable(samsung) >= 0) { 1160 INIT_WORK(&samsung->kbd_led_work, kbd_led_update); 1161 1162 samsung->kbd_led.name = "samsung::kbd_backlight"; 1163 samsung->kbd_led.brightness_set = kbd_led_set; 1164 samsung->kbd_led.brightness_get = kbd_led_get; 1165 samsung->kbd_led.max_brightness = 8; 1166 if (samsung->quirks->four_kbd_backlight_levels) 1167 samsung->kbd_led.max_brightness = 4; 1168 1169 ret = led_classdev_register(&samsung->platform_device->dev, 1170 &samsung->kbd_led); 1171 } 1172 1173 if (ret) 1174 samsung_leds_exit(samsung); 1175 1176 return ret; 1177 } 1178 1179 static void samsung_backlight_exit(struct samsung_laptop *samsung) 1180 { 1181 if (samsung->backlight_device) { 1182 backlight_device_unregister(samsung->backlight_device); 1183 samsung->backlight_device = NULL; 1184 } 1185 } 1186 1187 static int __init samsung_backlight_init(struct samsung_laptop *samsung) 1188 { 1189 struct backlight_device *bd; 1190 struct backlight_properties props; 1191 1192 if (!samsung->handle_backlight) 1193 return 0; 1194 1195 memset(&props, 0, sizeof(struct backlight_properties)); 1196 props.type = BACKLIGHT_PLATFORM; 1197 props.max_brightness = samsung->config->max_brightness - 1198 samsung->config->min_brightness; 1199 1200 bd = backlight_device_register("samsung", 1201 &samsung->platform_device->dev, 1202 samsung, &backlight_ops, 1203 &props); 1204 if (IS_ERR(bd)) 1205 return PTR_ERR(bd); 1206 1207 samsung->backlight_device = bd; 1208 samsung->backlight_device->props.brightness = read_brightness(samsung); 1209 samsung->backlight_device->props.power = FB_BLANK_UNBLANK; 1210 backlight_update_status(samsung->backlight_device); 1211 1212 return 0; 1213 } 1214 1215 static umode_t samsung_sysfs_is_visible(struct kobject *kobj, 1216 struct attribute *attr, int idx) 1217 { 1218 struct device *dev = container_of(kobj, struct device, kobj); 1219 struct platform_device *pdev = to_platform_device(dev); 1220 struct samsung_laptop *samsung = platform_get_drvdata(pdev); 1221 bool ok = true; 1222 1223 if (attr == &dev_attr_performance_level.attr) 1224 ok = !!samsung->config->performance_levels[0].name; 1225 if (attr == &dev_attr_battery_life_extender.attr) 1226 ok = !!(read_battery_life_extender(samsung) >= 0); 1227 if (attr == &dev_attr_usb_charge.attr) 1228 ok = !!(read_usb_charge(samsung) >= 0); 1229 if (attr == &dev_attr_lid_handling.attr) 1230 ok = !!(read_lid_handling(samsung) >= 0); 1231 1232 return ok ? attr->mode : 0; 1233 } 1234 1235 static struct attribute_group platform_attribute_group = { 1236 .is_visible = samsung_sysfs_is_visible, 1237 .attrs = platform_attributes 1238 }; 1239 1240 static void samsung_sysfs_exit(struct samsung_laptop *samsung) 1241 { 1242 struct platform_device *device = samsung->platform_device; 1243 1244 sysfs_remove_group(&device->dev.kobj, &platform_attribute_group); 1245 } 1246 1247 static int __init samsung_sysfs_init(struct samsung_laptop *samsung) 1248 { 1249 struct platform_device *device = samsung->platform_device; 1250 1251 return sysfs_create_group(&device->dev.kobj, &platform_attribute_group); 1252 1253 } 1254 1255 static int show_call(struct seq_file *m, void *data) 1256 { 1257 struct samsung_laptop *samsung = m->private; 1258 struct sabi_data *sdata = &samsung->debug.data; 1259 int ret; 1260 1261 seq_printf(m, "SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n", 1262 samsung->debug.command, 1263 sdata->d0, sdata->d1, sdata->d2, sdata->d3); 1264 1265 ret = sabi_command(samsung, samsung->debug.command, sdata, sdata); 1266 1267 if (ret) { 1268 seq_printf(m, "SABI command 0x%04x failed\n", 1269 samsung->debug.command); 1270 return ret; 1271 } 1272 1273 seq_printf(m, "SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n", 1274 sdata->d0, sdata->d1, sdata->d2, sdata->d3); 1275 return 0; 1276 } 1277 1278 static int samsung_debugfs_open(struct inode *inode, struct file *file) 1279 { 1280 return single_open(file, show_call, inode->i_private); 1281 } 1282 1283 static const struct file_operations samsung_laptop_call_io_ops = { 1284 .owner = THIS_MODULE, 1285 .open = samsung_debugfs_open, 1286 .read = seq_read, 1287 .llseek = seq_lseek, 1288 .release = single_release, 1289 }; 1290 1291 static void samsung_debugfs_exit(struct samsung_laptop *samsung) 1292 { 1293 debugfs_remove_recursive(samsung->debug.root); 1294 } 1295 1296 static int samsung_debugfs_init(struct samsung_laptop *samsung) 1297 { 1298 struct dentry *dent; 1299 1300 samsung->debug.root = debugfs_create_dir("samsung-laptop", NULL); 1301 if (!samsung->debug.root) { 1302 pr_err("failed to create debugfs directory"); 1303 goto error_debugfs; 1304 } 1305 1306 samsung->debug.f0000_wrapper.data = samsung->f0000_segment; 1307 samsung->debug.f0000_wrapper.size = 0xffff; 1308 1309 samsung->debug.data_wrapper.data = &samsung->debug.data; 1310 samsung->debug.data_wrapper.size = sizeof(samsung->debug.data); 1311 1312 samsung->debug.sdiag_wrapper.data = samsung->sdiag; 1313 samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag); 1314 1315 dent = debugfs_create_u16("command", S_IRUGO | S_IWUSR, 1316 samsung->debug.root, &samsung->debug.command); 1317 if (!dent) 1318 goto error_debugfs; 1319 1320 dent = debugfs_create_u32("d0", S_IRUGO | S_IWUSR, samsung->debug.root, 1321 &samsung->debug.data.d0); 1322 if (!dent) 1323 goto error_debugfs; 1324 1325 dent = debugfs_create_u32("d1", S_IRUGO | S_IWUSR, samsung->debug.root, 1326 &samsung->debug.data.d1); 1327 if (!dent) 1328 goto error_debugfs; 1329 1330 dent = debugfs_create_u16("d2", S_IRUGO | S_IWUSR, samsung->debug.root, 1331 &samsung->debug.data.d2); 1332 if (!dent) 1333 goto error_debugfs; 1334 1335 dent = debugfs_create_u8("d3", S_IRUGO | S_IWUSR, samsung->debug.root, 1336 &samsung->debug.data.d3); 1337 if (!dent) 1338 goto error_debugfs; 1339 1340 dent = debugfs_create_blob("data", S_IRUGO | S_IWUSR, 1341 samsung->debug.root, 1342 &samsung->debug.data_wrapper); 1343 if (!dent) 1344 goto error_debugfs; 1345 1346 dent = debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR, 1347 samsung->debug.root, 1348 &samsung->debug.f0000_wrapper); 1349 if (!dent) 1350 goto error_debugfs; 1351 1352 dent = debugfs_create_file("call", S_IFREG | S_IRUGO, 1353 samsung->debug.root, samsung, 1354 &samsung_laptop_call_io_ops); 1355 if (!dent) 1356 goto error_debugfs; 1357 1358 dent = debugfs_create_blob("sdiag", S_IRUGO | S_IWUSR, 1359 samsung->debug.root, 1360 &samsung->debug.sdiag_wrapper); 1361 if (!dent) 1362 goto error_debugfs; 1363 1364 return 0; 1365 1366 error_debugfs: 1367 samsung_debugfs_exit(samsung); 1368 return -ENOMEM; 1369 } 1370 1371 static void samsung_sabi_exit(struct samsung_laptop *samsung) 1372 { 1373 const struct sabi_config *config = samsung->config; 1374 1375 /* Turn off "Linux" mode in the BIOS */ 1376 if (config && config->commands.set_linux != 0xff) 1377 sabi_set_commandb(samsung, config->commands.set_linux, 0x80); 1378 1379 if (samsung->sabi_iface) { 1380 iounmap(samsung->sabi_iface); 1381 samsung->sabi_iface = NULL; 1382 } 1383 if (samsung->f0000_segment) { 1384 iounmap(samsung->f0000_segment); 1385 samsung->f0000_segment = NULL; 1386 } 1387 1388 samsung->config = NULL; 1389 } 1390 1391 static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca, 1392 unsigned int ifaceP) 1393 { 1394 const struct sabi_config *config = samsung->config; 1395 1396 printk(KERN_DEBUG "This computer supports SABI==%x\n", 1397 loca + 0xf0000 - 6); 1398 1399 printk(KERN_DEBUG "SABI header:\n"); 1400 printk(KERN_DEBUG " SMI Port Number = 0x%04x\n", 1401 readw(samsung->sabi + config->header_offsets.port)); 1402 printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n", 1403 readb(samsung->sabi + config->header_offsets.iface_func)); 1404 printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n", 1405 readb(samsung->sabi + config->header_offsets.en_mem)); 1406 printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n", 1407 readb(samsung->sabi + config->header_offsets.re_mem)); 1408 printk(KERN_DEBUG " SABI data offset = 0x%04x\n", 1409 readw(samsung->sabi + config->header_offsets.data_offset)); 1410 printk(KERN_DEBUG " SABI data segment = 0x%04x\n", 1411 readw(samsung->sabi + config->header_offsets.data_segment)); 1412 1413 printk(KERN_DEBUG " SABI pointer = 0x%08x\n", ifaceP); 1414 } 1415 1416 static void __init samsung_sabi_diag(struct samsung_laptop *samsung) 1417 { 1418 int loca = find_signature(samsung->f0000_segment, "SDiaG@"); 1419 int i; 1420 1421 if (loca == 0xffff) 1422 return ; 1423 1424 /* Example: 1425 * Ident: @SDiaG@686XX-N90X3A/966-SEC-07HL-S90X3A 1426 * 1427 * Product name: 90X3A 1428 * BIOS Version: 07HL 1429 */ 1430 loca += 1; 1431 for (i = 0; loca < 0xffff && i < sizeof(samsung->sdiag) - 1; loca++) { 1432 char temp = readb(samsung->f0000_segment + loca); 1433 1434 if (isalnum(temp) || temp == '/' || temp == '-') 1435 samsung->sdiag[i++] = temp; 1436 else 1437 break ; 1438 } 1439 1440 if (debug && samsung->sdiag[0]) 1441 pr_info("sdiag: %s", samsung->sdiag); 1442 } 1443 1444 static int __init samsung_sabi_init(struct samsung_laptop *samsung) 1445 { 1446 const struct sabi_config *config = NULL; 1447 const struct sabi_commands *commands; 1448 unsigned int ifaceP; 1449 int ret = 0; 1450 int i; 1451 int loca; 1452 1453 samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff); 1454 if (!samsung->f0000_segment) { 1455 if (debug || force) 1456 pr_err("Can't map the segment at 0xf0000\n"); 1457 ret = -EINVAL; 1458 goto exit; 1459 } 1460 1461 samsung_sabi_diag(samsung); 1462 1463 /* Try to find one of the signatures in memory to find the header */ 1464 for (i = 0; sabi_configs[i].test_string != NULL; ++i) { 1465 samsung->config = &sabi_configs[i]; 1466 loca = find_signature(samsung->f0000_segment, 1467 samsung->config->test_string); 1468 if (loca != 0xffff) 1469 break; 1470 } 1471 1472 if (loca == 0xffff) { 1473 if (debug || force) 1474 pr_err("This computer does not support SABI\n"); 1475 ret = -ENODEV; 1476 goto exit; 1477 } 1478 1479 config = samsung->config; 1480 commands = &config->commands; 1481 1482 /* point to the SMI port Number */ 1483 loca += 1; 1484 samsung->sabi = (samsung->f0000_segment + loca); 1485 1486 /* Get a pointer to the SABI Interface */ 1487 ifaceP = (readw(samsung->sabi + config->header_offsets.data_segment) & 0x0ffff) << 4; 1488 ifaceP += readw(samsung->sabi + config->header_offsets.data_offset) & 0x0ffff; 1489 1490 if (debug) 1491 samsung_sabi_infos(samsung, loca, ifaceP); 1492 1493 samsung->sabi_iface = ioremap_nocache(ifaceP, 16); 1494 if (!samsung->sabi_iface) { 1495 pr_err("Can't remap %x\n", ifaceP); 1496 ret = -EINVAL; 1497 goto exit; 1498 } 1499 1500 /* Turn on "Linux" mode in the BIOS */ 1501 if (commands->set_linux != 0xff) { 1502 int retval = sabi_set_commandb(samsung, 1503 commands->set_linux, 0x81); 1504 if (retval) { 1505 pr_warn("Linux mode was not set!\n"); 1506 ret = -ENODEV; 1507 goto exit; 1508 } 1509 } 1510 1511 /* Check for stepping quirk */ 1512 if (samsung->handle_backlight) 1513 check_for_stepping_quirk(samsung); 1514 1515 pr_info("detected SABI interface: %s\n", 1516 samsung->config->test_string); 1517 1518 exit: 1519 if (ret) 1520 samsung_sabi_exit(samsung); 1521 1522 return ret; 1523 } 1524 1525 static void samsung_platform_exit(struct samsung_laptop *samsung) 1526 { 1527 if (samsung->platform_device) { 1528 platform_device_unregister(samsung->platform_device); 1529 samsung->platform_device = NULL; 1530 } 1531 } 1532 1533 static int samsung_pm_notification(struct notifier_block *nb, 1534 unsigned long val, void *ptr) 1535 { 1536 struct samsung_laptop *samsung; 1537 1538 samsung = container_of(nb, struct samsung_laptop, pm_nb); 1539 if (val == PM_POST_HIBERNATION && 1540 samsung->quirks->enable_kbd_backlight) 1541 kbd_backlight_enable(samsung); 1542 1543 if (val == PM_POST_HIBERNATION && samsung->quirks->lid_handling) 1544 write_lid_handling(samsung, 1); 1545 1546 return 0; 1547 } 1548 1549 static int __init samsung_platform_init(struct samsung_laptop *samsung) 1550 { 1551 struct platform_device *pdev; 1552 1553 pdev = platform_device_register_simple("samsung", -1, NULL, 0); 1554 if (IS_ERR(pdev)) 1555 return PTR_ERR(pdev); 1556 1557 samsung->platform_device = pdev; 1558 platform_set_drvdata(samsung->platform_device, samsung); 1559 return 0; 1560 } 1561 1562 static struct samsung_quirks *quirks; 1563 1564 static int __init samsung_dmi_matched(const struct dmi_system_id *d) 1565 { 1566 quirks = d->driver_data; 1567 return 0; 1568 } 1569 1570 static struct dmi_system_id __initdata samsung_dmi_table[] = { 1571 { 1572 .matches = { 1573 DMI_MATCH(DMI_SYS_VENDOR, 1574 "SAMSUNG ELECTRONICS CO., LTD."), 1575 DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ 1576 }, 1577 }, 1578 { 1579 .matches = { 1580 DMI_MATCH(DMI_SYS_VENDOR, 1581 "SAMSUNG ELECTRONICS CO., LTD."), 1582 DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */ 1583 }, 1584 }, 1585 { 1586 .matches = { 1587 DMI_MATCH(DMI_SYS_VENDOR, 1588 "SAMSUNG ELECTRONICS CO., LTD."), 1589 DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ 1590 }, 1591 }, 1592 { 1593 .matches = { 1594 DMI_MATCH(DMI_SYS_VENDOR, 1595 "SAMSUNG ELECTRONICS CO., LTD."), 1596 DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ 1597 }, 1598 }, 1599 /* DMI ids for laptops with bad Chassis Type */ 1600 { 1601 .ident = "R40/R41", 1602 .matches = { 1603 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1604 DMI_MATCH(DMI_PRODUCT_NAME, "R40/R41"), 1605 DMI_MATCH(DMI_BOARD_NAME, "R40/R41"), 1606 }, 1607 }, 1608 /* Specific DMI ids for laptop with quirks */ 1609 { 1610 .callback = samsung_dmi_matched, 1611 .ident = "N150P", 1612 .matches = { 1613 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1614 DMI_MATCH(DMI_PRODUCT_NAME, "N150P"), 1615 DMI_MATCH(DMI_BOARD_NAME, "N150P"), 1616 }, 1617 .driver_data = &samsung_use_native_backlight, 1618 }, 1619 { 1620 .callback = samsung_dmi_matched, 1621 .ident = "N145P/N250P/N260P", 1622 .matches = { 1623 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1624 DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"), 1625 DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"), 1626 }, 1627 .driver_data = &samsung_use_native_backlight, 1628 }, 1629 { 1630 .callback = samsung_dmi_matched, 1631 .ident = "N150/N210/N220", 1632 .matches = { 1633 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1634 DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"), 1635 DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"), 1636 }, 1637 .driver_data = &samsung_broken_acpi_video, 1638 }, 1639 { 1640 .callback = samsung_dmi_matched, 1641 .ident = "NF110/NF210/NF310", 1642 .matches = { 1643 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1644 DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"), 1645 DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"), 1646 }, 1647 .driver_data = &samsung_broken_acpi_video, 1648 }, 1649 { 1650 .callback = samsung_dmi_matched, 1651 .ident = "X360", 1652 .matches = { 1653 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1654 DMI_MATCH(DMI_PRODUCT_NAME, "X360"), 1655 DMI_MATCH(DMI_BOARD_NAME, "X360"), 1656 }, 1657 .driver_data = &samsung_broken_acpi_video, 1658 }, 1659 { 1660 .callback = samsung_dmi_matched, 1661 .ident = "N250P", 1662 .matches = { 1663 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1664 DMI_MATCH(DMI_PRODUCT_NAME, "N250P"), 1665 DMI_MATCH(DMI_BOARD_NAME, "N250P"), 1666 }, 1667 .driver_data = &samsung_use_native_backlight, 1668 }, 1669 { 1670 .callback = samsung_dmi_matched, 1671 .ident = "NC210", 1672 .matches = { 1673 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1674 DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"), 1675 DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"), 1676 }, 1677 .driver_data = &samsung_broken_acpi_video, 1678 }, 1679 { 1680 .callback = samsung_dmi_matched, 1681 .ident = "730U3E/740U3E", 1682 .matches = { 1683 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1684 DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"), 1685 }, 1686 .driver_data = &samsung_np740u3e, 1687 }, 1688 { 1689 .callback = samsung_dmi_matched, 1690 .ident = "300V3Z/300V4Z/300V5Z", 1691 .matches = { 1692 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1693 DMI_MATCH(DMI_PRODUCT_NAME, "300V3Z/300V4Z/300V5Z"), 1694 }, 1695 .driver_data = &samsung_lid_handling, 1696 }, 1697 { }, 1698 }; 1699 MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); 1700 1701 static struct platform_device *samsung_platform_device; 1702 1703 static int __init samsung_init(void) 1704 { 1705 struct samsung_laptop *samsung; 1706 int ret; 1707 1708 if (efi_enabled(EFI_BOOT)) 1709 return -ENODEV; 1710 1711 quirks = &samsung_unknown; 1712 if (!force && !dmi_check_system(samsung_dmi_table)) 1713 return -ENODEV; 1714 1715 samsung = kzalloc(sizeof(*samsung), GFP_KERNEL); 1716 if (!samsung) 1717 return -ENOMEM; 1718 1719 mutex_init(&samsung->sabi_mutex); 1720 samsung->handle_backlight = true; 1721 samsung->quirks = quirks; 1722 1723 #ifdef CONFIG_ACPI 1724 if (samsung->quirks->broken_acpi_video) 1725 acpi_video_set_dmi_backlight_type(acpi_backlight_vendor); 1726 if (samsung->quirks->use_native_backlight) 1727 acpi_video_set_dmi_backlight_type(acpi_backlight_native); 1728 1729 if (acpi_video_get_backlight_type() != acpi_backlight_vendor) 1730 samsung->handle_backlight = false; 1731 #endif 1732 1733 ret = samsung_platform_init(samsung); 1734 if (ret) 1735 goto error_platform; 1736 1737 ret = samsung_sabi_init(samsung); 1738 if (ret) 1739 goto error_sabi; 1740 1741 ret = samsung_sysfs_init(samsung); 1742 if (ret) 1743 goto error_sysfs; 1744 1745 ret = samsung_backlight_init(samsung); 1746 if (ret) 1747 goto error_backlight; 1748 1749 ret = samsung_rfkill_init(samsung); 1750 if (ret) 1751 goto error_rfkill; 1752 1753 ret = samsung_leds_init(samsung); 1754 if (ret) 1755 goto error_leds; 1756 1757 ret = samsung_lid_handling_init(samsung); 1758 if (ret) 1759 goto error_lid_handling; 1760 1761 ret = samsung_debugfs_init(samsung); 1762 if (ret) 1763 goto error_debugfs; 1764 1765 samsung->pm_nb.notifier_call = samsung_pm_notification; 1766 register_pm_notifier(&samsung->pm_nb); 1767 1768 samsung_platform_device = samsung->platform_device; 1769 return ret; 1770 1771 error_debugfs: 1772 samsung_lid_handling_exit(samsung); 1773 error_lid_handling: 1774 samsung_leds_exit(samsung); 1775 error_leds: 1776 samsung_rfkill_exit(samsung); 1777 error_rfkill: 1778 samsung_backlight_exit(samsung); 1779 error_backlight: 1780 samsung_sysfs_exit(samsung); 1781 error_sysfs: 1782 samsung_sabi_exit(samsung); 1783 error_sabi: 1784 samsung_platform_exit(samsung); 1785 error_platform: 1786 kfree(samsung); 1787 return ret; 1788 } 1789 1790 static void __exit samsung_exit(void) 1791 { 1792 struct samsung_laptop *samsung; 1793 1794 samsung = platform_get_drvdata(samsung_platform_device); 1795 unregister_pm_notifier(&samsung->pm_nb); 1796 1797 samsung_debugfs_exit(samsung); 1798 samsung_lid_handling_exit(samsung); 1799 samsung_leds_exit(samsung); 1800 samsung_rfkill_exit(samsung); 1801 samsung_backlight_exit(samsung); 1802 samsung_sysfs_exit(samsung); 1803 samsung_sabi_exit(samsung); 1804 samsung_platform_exit(samsung); 1805 1806 kfree(samsung); 1807 samsung_platform_device = NULL; 1808 } 1809 1810 module_init(samsung_init); 1811 module_exit(samsung_exit); 1812 1813 MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>"); 1814 MODULE_DESCRIPTION("Samsung Backlight driver"); 1815 MODULE_LICENSE("GPL"); 1816