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