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